! Testbenches for Sequential Circuits... also, Components Lecture L04 18-545 Advanced Digital Design ECE Department Many elements Don Thomas, 2014, used with permission with credit to G. Larson
State Transition Diagram: Revisited Today's example FSM Specification Four States: JAN, FEB, MAR, APR Three Outputs: A, B, C One Input: G Reset Notation: outputs B and C are asserted JAN A G' G FEB AB G Don t care G or G' APR MAR BC G' ABC 2
state assignment from previous example SystemVerilog explicit FSM style module todaysfsm (input logic g, clock, reset_n, output logic a, b, c); enum logic [1:0] {JAN = 2'b00, FEB = 2'b01, MAR = 2'b10, APR = 2'b11} state, nextstate; always_ff @(posedge clock, negedge reset_n) if (~reset_n) state <= JAN; else state <= nextstate; always_comb // next state generator unique case (state) JAN: nextstate = FEB; FEB: nextstate = MAR; MAR: nextstate = (g)? FEB : APR; APR: nextstate = (g)? FEB : JAN; case continued module: todaysfsm declares enumerated variables with constant values (similar to software) the state register, which follows the rules! 3
SystemVerilog explicit FSM style module todaysfsm (input logic g, clock, reset_n, output logic a, b, c);... stuff... Reset JAN A FEB AB always_comb // next state generator unique case (state) JAN: nextstate = FEB; FEB: nextstate = MAR; MAR: nextstate = (g)? FEB : APR; APR: nextstate = (g)? FEB : JAN; case continued G' G G module: todaysfsm APR MAR BC G' ABC 4
SystemVerilog explicit FSM style module todaysfsm (input logic g, clock, reset_n, output logic a, b, c);.. stuff.. Reset JAN A G' APR BC G G' FEB AB G MAR ABC always_comb begin // output logic a = 0; b = 0; c = 0; unique case (state) JAN: a = 1; FEB: {a, b} = 2'b11; MAR: begin a = 1; b = 1; c = 1; APR: begin b = 1; c = 1; case module: todaysfsm
The testbench How are you going to test the FSM? - S a sequence of inputs and clocks to the circuit - How do you create a sequence of inputs and clocks? An implicit FSM - looks like a program but with @ - next state via procedural flow can use for, if, case, - output logic interspersed here, g <= 1 (or 0) output update done via <= module tb; logic clock, g; initial begin g <= 0; // initial value @ (posedge clock); // goes to FEB @ (posedge clock); // goes to MAR @ (posedge clock); // goes to APR g <= 1; @ (posedge clock); // goes to FEB @ (posedge clock); // goes to MAR @ (posedge clock); // goes to FEB @ (posedge clock); // goes to MAR g <= 0; @ (posedge clock); // goes to APR @ (posedge clock); // goes to JAN @ (posedge clock); $finish; module: tb 6
Reset An implicit FSM How to read the testbench g=0 g=0 g=0 g=1 module tb; logic clock, g; initial begin g <= 0; // initial value @ (posedge clock); // goes to FEB @ (posedge clock); // goes to MAR @ (posedge clock); // goes to APR g <= 1; @ (posedge clock); // goes to FEB @ (posedge clock); // goes to MAR @ (posedge clock); // goes to FEB @ (posedge clock); // goes to MAR g <= 0; @ (posedge clock); // goes to APR @ (posedge clock); // goes to JAN @ (posedge clock); $finish; module: tb @(posedge clk) corresponds to a transition in the diagram at left This implicit FSM creates the sequence of clocks and inputs to define a path through every state and on every transition condition Reset Hmm, did we take every transition with every input value? JAN G' A APR BC G G' FEB G AB MAR ABC
Viewing the FSM Reset Testbench must still provide all FSM inputs (clock, reset) Generate clock and reset normally New Magic: use.name to get enumerated value as a string module tb; logic clock, reset_n, a, b, c, g; fsmfullyencoded dut(.*); // normal clock/reset block initial begin clock = 0; reset_n = 0; reset_n <= #1 1; forever #5 clock = ~clock; // Monitor initial begin $monitor("state=%s, in(%b), out(%b%b%b)", dut.state.name, g, a, b, c); enum logic [1:0] {JAN = 2'b00, FEB = 2'b01, MAR = 2'b10, APR = 2'b11} state, nextstate; // Generate transitions initial begin period means g <= 0; // initial value "inside" so @ (posedge clock); // goes to FEB dut.state is the @ (posedge clock); // goes to MAR state signal inside @ (posedge clock); // goes to APR the dut module g <= 1; @ (posedge clock); // goes to FEB @ (posedge clock); // goes to MAR JAN G' A APR BC G G' FEB G AB MAR ABC
q0 A q1 Another FSM clk rstn d1 d0 D Clk D Clk q1 q0 F module fsm (input logic A, clock, reset_n, output logic F); logic q0, q1; always_ff @(posedge clock, negedge reset_n) begin if (~reset_n) {q1, q0} <= 2'b0; else begin // next state q1 <= q0 & A; q0 <= q1 A; assign F = ~(A & q1); // output logic module: fsm Slightly different description style - Combined NS logic with FSM update in always_ff - Output logic still in separate assign (or always_comb) Nothing wrong with this - Note that output logic must be separate if it s a Mealy machine 9
Notice the next state update q0 A q1 d1 d0 D Clk D Clk q1 q0 F 1 else begin // next state update 2 q1 <= q0 & x; 3 q0 <= q1 x; clk rstn Isn t q1 on line 3 the same value as q1 on line 2? - Can t I just write q0 <= (q0 & x) x; - no -- remember <= is a concurrent assignment - q0, q1 change simultaneously Does the update match the diagram - yes SystemVerilog will generate the logic shown 10
Concurrent assignment! q0 A q1 d1 d0 D Clk D Clk q1 q0 F 1 else begin // next state 2 q1 <= q0 & x; 3 q0 <= q1 x; clk rstn The point is - Both assignments are made to appear as if they happen at the same instantaneous time (the clock edge) - cause that s how they happen in real hardware - They re concurrent - Interesting I could have written lines 2 and 3 in either order 11
Try writing this in software To create the same behavior - need an extra temp register/variable! - Why? always_ff @(posedge clk) begin q1 <= q0 & x; q0 <= q1 x; How might you write this in C? q0 A d1 D Clk q1 F for (a bunch of times) begin q1 = q0 & x; q0 = q1 x; No! printf (...stuff); q1 clk rstn d0 D Clk q0 for (a bunch of times) begin q1_temp = q0 & x; q0 = q1 x; q1 = q1_temp; printf (...stuff); 12
Sequential Components
Sequential Components Registers: Store multi-bit values Counters: Change multi-bit output value in up/down fashion Shift Registers: Change multi-bit output values in left/right fashion 14
Component: Register We ll use a symbol similar to this: ld_l cl_l action 0 0 N/A 0 1 load D 1 0 clear 1 1 hold Register inputs/outputs - ld_l: load enable. Only load on clock edge when asserted - cl_l: synchronously set to zero (different from reset, how?) - clock with edge specifier - D input(s) and output(s) name reset is asynchronous - Assume that only one of the control inputs can be asserted. If none are asserted, then the register holds its value - Note, we might not always show all the control inputs, or there might be added ones not shown here - there are many varieties of registers with different functions ld_l cl_l reset_l clock D 6 6 bitwidth 18-240 L14 15
Register: SystemVerilog Let's build a register A single bit flip flop module dff (input logic d, clk, reset_l, output logic q); always_ff @(posedge clk, negedge reset_l) if (~reset_l) q <= 0; else q <= d; module: dff A poor A better attempt attempt building at a 6-bit a 6-bit register register - works, - procedural but ugly style Use - But, structural the what "parameter" if style I want a keyword 20-bit register? module register module register #(parameter (input logic WIDTH [5:0] = D, 6) (input input logic logic [WIDTH-1:0] clk, reset_l, D, input output logic [5:0] ); clk, reset_l, output logic [WIDTH-1:0] ); dff always_ff b0(d[0], @(posedge clk, reset_l, clk, [0]); always_ff dff b1(d[1], @(posedge negedge clk, reset_l, reset_l) clk, [1]); dff b2(d[2], if (~reset_l) negedge clk, reset_l, reset_l) [2]); dff b3(d[3], if (~reset_l) <= 0; clk, reset_l, [3]); dff b4(d[4], else <= <= 0; clk, D; reset_l, [4]); dff b5(d[5], else <= clk, D; reset_l, [5]); module: register module: register 18-240 L14 16
Component: Shift Register Mission: Store a multi-bit value like a register, but allow for the value to be shifted by a single position - Some Shift Registers are left-shift, others are right-shift - Some Shift Registers can shift either direction Useful for serial communication, power-of-two math 1 0 1 1 1 0 0 1 1 0 1 1 1 0 0 1 Left Shift Right Shift 0 1 1 1 0 0 1 0 0 1 0 1 1 1 0 0 Bits get loaded either in parallel or serial (deping on how the register was constructed) - And can be read in either form as well - PISO = Parallel In, Serial Out (PIPO, SIPO, SISO also exist) 18-240 L15 17
Shift Register: SystemVerilog module ShiftReg_PISO_Right #(parameter w = 8) (output logic lowbit, input logic [w-1:0] d, input logic clock, ld_l, sh_l); load q q d logic [w-1:0] q; assign lowbit = q[0]; always_ff @(posedge clock) if (~ld_l) q <= d; else if (~sh_l) q <= q >> 1; // SV shift operator fills // with zeros module: ShiftReg_PISO_Right shift q right 0 q Another good alternative: q <= {1'b0, q[w-1:1]}; lowbit lowbit 18-240 L15 18
Component: Counter Mission: Increment or decrement a multi-bit value, which is stored like a register - N-bit counter: counts from zero to 2 N -1 - Counts mod 2 N : zero 2 N -1 zero Technically, an N-bit FSM with a looped state transition diagram Easier to think about as register and adder components Variations - Count down, count up/down - BCD counter: 0 9 - Cascadable, with a count-enable 000 clock /clear 001 010 011 111 110 Adder D 4 0001 4 101 100 4 18-240 L15 19
SystemVerilog: Counter Features of this counter - Synchronous clear - Enable: Lets us control when it counts - Parameterizable for width defaults to four bits - Must be enabled to clear Facilitates cascading this could have been written the other way - What if nothing is enabled? Is this the only counter there is on the whole planet? - Nope, we could have one with async reset, decrement, - BCD counter is fairly common module counter #(parameter w = 4) (input logic clock, input logic clear_l, input logic enable_l, output logic [w-1:0] q); always_ff @(posedge clock) if (~clear_l && ~enable_l) q <= 0; else if (~enable_l) q <= q + 1; module: counter 18-240 L15 20
Summary Testing FSMs - Implicit FSM in the testbench <= is very, very!= to = - Non blocking vs blocking is essential to do FSMs right! Components - Register - Shift Register - Counter 21