0%

2.4 Sequential Logic

2.4 Sequential Logic

Register

Declaration

Reg()

  • A Reg holds its output value until the rising edge of its clock, at which time it takes on the value of its input.
1
val register = Reg(UInt(12.W))
  • By default, every Chisel Module has an implicit clock that is used by every register in the design.
    The implicit clock can be overridden for multi-clock designs.

  • Operations called on a register are performed on the output of the register, and the kind of operations depend on the register’s type.

  • You aren’t restricted to using UInts with registers, you can use any subclass of the base type chisel3.Data. This includes SInt for signed integers and a lot of other things.

1
2
3
4
// It means the value reg is of type UInt,
// and you can do things you can normally do with UInts, like +, -, etc.
val reg: UInt = Reg(UInt(4.W))
val reg: SInt = Reg(SInt(4.W))

RegNext()

  • We didn’t need to specify the register bitwidth this time. It gets inferred from the register’s output connection, in this case io.out.
1
2
3
4
5
6
7
8
9
10
11
12
class RegNextModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(12.W))
val out = Output(UInt(12.W))
})

// With name and init value
val register = RegNext(io.in + 1.U, 0.U)

// register bitwidth is inferred from io.out
io.out := RegNext(io.in + 1.U)
}
  • In the latter case, register name is generated instead of explicity defined.
1
2
3
4
5
reg [11:0] REG; // @[cmd4.sc 8:20]
assign io_out = REG; // @[cmd4.sc 8:10]
always @(posedge clock) begin
REG <= io_in + 12'h1; // @[cmd4.sc 8:27]
end

RegInit()

  • Create a register that resets to a given value with RegInit.
1
2
val myReg = RegInit(UInt(12.W), 0.U)
val myReg = RegInit(0.U(12.W))
  • The generated verilog now has a block that checks if (reset) to reset the register to 0, inside the always @(posedge clock) block.
  • The register is still initialized to random junk before reset is called.
  • The PeekPokeTesters always call reset before running your test, but you can manually call reset as well using the reset(n) function, where reset is high for n cycles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
module RegInitModule(
input clock,
input reset,
input [11:0] io_in,
output [11:0] io_out
);
`ifdef RANDOMIZE_REG_INIT
reg [31:0] _RAND_0;
`endif // RANDOMIZE_REG_INIT
reg [11:0] register; // @[cmd6.sc 7:25]
wire [11:0] _T_1 = io_in + 12'h1; // @[cmd6.sc 8:21]
assign io_out = register; // @[cmd6.sc 9:10]
always @(posedge clock) begin
if (reset) begin // @[cmd6.sc 7:25]
register <= 12'h0; // @[cmd6.sc 7:25]
end else begin
register <= _T_1; // @[cmd6.sc 8:12]
end
end
// Register and memory initialization
`ifdef RANDOMIZE_GARBAGE_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_INVALID_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_REG_INIT
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_MEM_INIT
`define RANDOMIZE
`endif
`ifndef RANDOM
`define RANDOM $random
`endif
`ifdef RANDOMIZE_MEM_INIT
integer initvar;
`endif
`ifndef SYNTHESIS
`ifdef FIRRTL_BEFORE_INITIAL
`FIRRTL_BEFORE_INITIAL
`endif
initial begin
`ifdef RANDOMIZE
`ifdef INIT_RANDOM
`INIT_RANDOM
`endif
`ifndef VERILATOR
`ifdef RANDOMIZE_DELAY
#`RANDOMIZE_DELAY begin end
`else
#0.002 begin end
`endif
`endif
`ifdef RANDOMIZE_REG_INIT
_RAND_0 = {1{`RANDOM}};
register = _RAND_0[11:0];
`endif // RANDOMIZE_REG_INIT
`endif // RANDOMIZE
end // initial
`ifdef FIRRTL_AFTER_INITIAL
`FIRRTL_AFTER_INITIAL
`endif
`endif // SYNTHESIS
endmodule

Test

  • Between calls to poke() and expect(), there is a call to step(1).
    This tells the test harness to tick the clock once, which will cause the register to pass its input to its output.
  • Calling poke() on an input immediately propagates the updated values through combinational logic.
    Calling step() is only needed to update state elements in sequential logic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class RegisterModule extends Module {
val io = IO(new Bundle {
val in = Input(UInt(12.W))
val out = Output(UInt(12.W))
})

val register = Reg(UInt(12.W))
register := io.in + 1.U
io.out := register
}

test(new RegisterModule) { c =>
for (i <- 0 until 100) {
c.io.in.poke(i.U)
c.clock.step(1) // tick
c.io.out.expect((i + 1).U)
}
}

Implicit clock

  • The module has an input for clock (and reset) that you didn’t add - this is the implicit clock.
  • There is a block sectioned off by ifdef Randomize that initialized the register to some random variable before simulation starts. (x state ?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
module RegisterModule(
input clock,
input reset,
input [11:0] io_in,
output [11:0] io_out
);
`ifdef RANDOMIZE_REG_INIT
reg [31:0] _RAND_0;
`endif // RANDOMIZE_REG_INIT
reg [11:0] register; // @[cmd2.sc 7:21]
assign io_out = register; // @[cmd2.sc 9:10]
always @(posedge clock) begin
register <= io_in + 12'h1; // @[cmd2.sc 8:21]
end
// Register and memory initialization
`ifdef RANDOMIZE_GARBAGE_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_INVALID_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_REG_INIT
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_MEM_INIT
`define RANDOMIZE
`endif
`ifndef RANDOM
`define RANDOM $random
`endif
`ifdef RANDOMIZE_MEM_INIT
integer initvar;
`endif
`ifndef SYNTHESIS
`ifdef FIRRTL_BEFORE_INITIAL
`FIRRTL_BEFORE_INITIAL
`endif
initial begin
`ifdef RANDOMIZE
`ifdef INIT_RANDOM
`INIT_RANDOM
`endif
`ifndef VERILATOR
`ifdef RANDOMIZE_DELAY
#`RANDOMIZE_DELAY begin end
`else
#0.002 begin end
`endif
`endif
`ifdef RANDOMIZE_REG_INIT
_RAND_0 = {1{`RANDOM}};
register = _RAND_0[11:0];
`endif // RANDOMIZE_REG_INIT
`endif // RANDOMIZE
end // initial
`ifdef FIRRTL_AFTER_INITIAL
`FIRRTL_AFTER_INITIAL
`endif
`endif // SYNTHESIS
endmodule

Control Flow

Registers are very similar to wires in terms of control flow. They have last connect semantics and can be assigned to conditionally with when, elsewhen, and otherwise.

Shift Register

  • Each element is a single bit wide.
  • Has a 4-bit output signal.
  • Takes a single input bit, which is the next value into the shift register.
  • Outputs the parallel output of the shift register, with the most significant bit being the last element of the shift register and the least significant bit being the first element of the shift register. Cat may come in handy.
  • The output initializes at b0001.
  • Shifts each clock cycle (no enable signal).
  • Note in Chisel, subword assignment IS ILLEGAL; something like out(0) := in will not work.
1
2
3
4
5
6
7
8
9
10
11
class MyShiftRegister(val init: Int = 1) extends Module {
val io = IO(new Bundle {
val in = Input(Bool())
val out = Output(UInt(4.W))
})

val state = RegInit(UInt(4.W), init.U)
val nextState = (state << 1) | io.in
state := nextState
io.out := state
}
1
2
3
4
5
6
7
8
9
10
11
12
reg [3:0] state; // @[cmd14.sc 7:22]
wire [4:0] _T = {state, 1'h0}; // @[cmd14.sc 16:26]
wire [4:0] _GEN_0 = {{4'd0}, io_in}; // @[cmd14.sc 16:32]
wire [4:0] nextState = _T | _GEN_0; // @[cmd14.sc 16:32]
assign io_out = state; // @[cmd14.sc 18:10]
always @(posedge clock) begin
if (reset) begin // @[cmd14.sc 7:22]
state <= 4'h1; // @[cmd14.sc 7:22]
end else begin
state <= nextState[3:0]; // @[cmd14.sc 17:9]
end
end

Parameterized Shift Register

1
2
3
4
5
6
7
8
9
10
11
12
class MyOptionalShiftRegister(val n: Int, val init: BigInt = 1) extends Module {
val io = IO(new Bundle {
val en = Input(Bool())
val in = Input(Bool())
val out = Output(UInt(n.W))
})

val state = RegInit(init.U(n.W))
val nextState = (state << 1) | io.in
when (io.en) { state := nextState }
io.out := state
}
1
2
3
4
5
6
7
8
9
10
11
12
13
reg [4:0] state; // @[cmd21.sc 8:22]
wire [5:0] _T = {state, 1'h0}; // @[cmd21.sc 10:26]
wire [5:0] _GEN_1 = {{5'd0}, io_in}; // @[cmd21.sc 10:32]
wire [5:0] nextState = _T | _GEN_1; // @[cmd21.sc 10:32]
wire [5:0] _GEN_0 = io_en ? nextState : {{1'd0}, state}; // @[cmd21.sc 11:16 cmd21.sc 11:24 cmd21.sc 8:22]
assign io_out = state; // @[cmd21.sc 13:10]
always @(posedge clock) begin
if (reset) begin // @[cmd21.sc 8:22]
state <= 5'h1; // @[cmd21.sc 8:22]
end else begin
state <= _GEN_0[4:0];
end
end

Explicit clock and reset

  • Clocks and resets can be overridden separately or together with withClock() {}, withReset() {}, and withClockAndReset() {}.
  • Clocks have their own type in Chisel Clock and should be declared as such.
  • Reset is always synchronous and of type Bool. Bool can be converted to Clocks by calling asClock() on them, but you should be careful that you aren’t doing something silly.
  • (?) Also note that chisel-testers do not currently have complete support for multi-clock designs.

Multi-Clock Module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// we need to import multi-clock features
import chisel3.experimental.{withClock, withReset, withClockAndReset}

class ClockExamples extends Module {
val io = IO(new Bundle {
val in = Input(UInt(10.W))
val alternateReset = Input(Bool())
val alternateClock = Input(Clock())
val outImplicit = Output(UInt())
val outAlternateReset = Output(UInt())
val outAlternateClock = Output(UInt())
val outAlternateBoth = Output(UInt())
})

val imp = RegInit(0.U(10.W))
imp := io.in
io.outImplicit := imp

withReset(io.alternateReset) {
// everything in this scope with have alternateReset as the reset
val altRst = RegInit(0.U(10.W))
altRst := io.in
io.outAlternateReset := altRst
}

withClock(io.alternateClock) {
val altClk = RegInit(0.U(10.W))
altClk := io.in
io.outAlternateClock := altClk
}

withClockAndReset(io.alternateClock, io.alternateReset) {
val alt = RegInit(0.U(10.W))
alt := io.in
io.outAlternateBoth := alt
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
module ClockExamples(
input clock, // implicit clock
input reset, // implicit reset
input [9:0] io_in,
input io_alternateReset, // alternate reset
input io_alternateClock, // alternate clock
output [9:0] io_outImplicit,
output [9:0] io_outAlternateReset,
output [9:0] io_outAlternateClock,
output [9:0] io_outAlternateBoth
);
reg [9:0] imp; // @[cmd22.sc 14:20]
reg [9:0] REG; // @[cmd22.sc 20:25] altRst
reg [9:0] REG_1; // @[cmd22.sc 26:25] altClk
reg [9:0] REG_2; // @[cmd22.sc 32:22] alt
assign io_outImplicit = imp; // @[cmd22.sc 16:18]
assign io_outAlternateReset = REG; // @[cmd22.sc 22:26]
assign io_outAlternateClock = REG_1; // @[cmd22.sc 28:26]
assign io_outAlternateBoth = REG_2; // @[cmd22.sc 34:25]
always @(posedge clock) begin // for registers with no alternateClock
if (reset) begin // @[cmd22.sc 14:20] // imp, altRst
imp <= 10'h0; // @[cmd22.sc 14:20]
end else begin
imp <= io_in; // @[cmd22.sc 15:7]
end
if (io_alternateReset) begin // @[cmd22.sc 20:25]
REG <= 10'h0; // @[cmd22.sc 20:25]
end else begin
REG <= io_in; // @[cmd22.sc 21:12]
end
end
always @(posedge io_alternateClock) begin // registers with alternateClock
if (reset) begin // @[cmd22.sc 26:25] // altClk, alt
REG_1 <= 10'h0; // @[cmd22.sc 26:25]
end else begin
REG_1 <= io_in; // @[cmd22.sc 27:12]
end
if (io_alternateReset) begin // @[cmd22.sc 32:22]
REG_2 <= 10'h0; // @[cmd22.sc 32:22]
end else begin
REG_2 <= io_in; // @[cmd22.sc 33:9]
end
end
endmodule