State machines#
Learning goals#
Apply basic features of the language to describe state machines
Introductory problem#
Implement a traffic light controller with the following requirements:
The lights cycle through the following sequence:
🔴 three time-units
🟡 one time-unit
🟢 three time-units
back to 1.
Must have the following interface:
module traffic_light_controller #(
int unsigned RED_DURATION = 3,
int unsigned YELLOW_DURATION = 1,
int unsigned GREEN_DURATION = 3
) (
input clk,
rst,
output red,
yellow,
green
);
Implementation on the board#
Use the RGB LED
RGB0
as output.Use a time-unit of one second.
Tasks#
Read section 6 of SV Guide.
Quiz#
TODO
Mini-lecture#
Enumerations#
Moore and Mealy FSM#
systemverilog.dev/4.html => 4.2.1
One-, two-, and three-block FSM#
systemverilog.dev/4.html => 4.2.4
Solution for the introductory problem#
module traffic_light_controller #(
int unsigned RED_DURATION = 3,
int unsigned YELLOW_DURATION = 1,
int unsigned GREEN_DURATION = 3
) (
input clk,
rst,
output red,
yellow,
green
);
function automatic int unsigned max_of_three(int unsigned a, int unsigned b, int unsigned c);
return a >= b ? ((a >= c) ? a : c) : b >= c ? b : c;
endfunction
localparam int unsigned MaxDuration = max_of_three(RED_DURATION, YELLOW_DURATION, GREEN_DURATION);
typedef enum {
RED,
YELLOW,
GREEN
} st_t;
st_t st, stn;
logic [$clog2(MaxDuration) -1 : 0] delay_cntr, state_start_timestamp, state_start_timestampn;
always_ff @(posedge clk or posedge rst) begin
st <= rst ? st.first : stn;
delay_cntr <= rst ? 0 : delay_cntr + 1;
state_start_timestamp <= rst ? 0 : state_start_timestampn;
end
always_comb begin
stn = st;
state_start_timestampn = state_start_timestamp;
unique case (st)
// Assume that the clock has the frequency of the minimum
// duration – in our case 1.
RED:
if (delay_cntr == state_start_timestamp + $bits(delay_cntr)'(RED_DURATION)) begin
stn = st.next;
state_start_timestampn = delay_cntr;
end
YELLOW:
if (delay_cntr == state_start_timestamp + $bits(delay_cntr)'(YELLOW_DURATION)) begin
stn = st.next;
state_start_timestampn = delay_cntr;
end
GREEN:
if (delay_cntr == state_start_timestamp + $bits(delay_cntr)'(GREEN_DURATION)) begin
stn = RED;
state_start_timestampn = delay_cntr;
end
endcase
end
assign red = (st == RED) ? 1 : 0;
assign yellow = (st == YELLOW) ? 1 : 0;
assign green = (st == GREEN) ? 1 : 0;
endmodule
Warning
During implementation I made the following mistake by writing
state_start_timestamp <= state_start_timestampn;
instead of
state_start_timestamp <= rst ? 0 : state_start_timestampn;
in the sequential block, which led to the following cryptic error in Vivado:
ERROR: [Synth 8-91] ambiguous clock in event control ...
If we don’t use rst
in our statement above, then this statement must be executed in both of posedge clk
and posedge rst
events. Synthesizers typically infer a flip-flop if they see that a variable is updated on the positive or negative edge of a signal. However there are typically no flip-flops that are updated using two separate clock signals. That is where the ambiguity comes from. Now the error this not cryptic anymore, at least to me.
module tb;
logic clk = 0, rst = 0;
logic red, yellow, green;
always #1 clk <= ~clk;
traffic_light_controller dut (.*);
initial begin
rst = 0;
#2 rst = 1;
#1 rst = 0;
wait (dut.st == dut.RED);
assert (red);
#1; // Otherwise @(posedge clk) is still true
repeat (dut.RED_DURATION) @(posedge clk);
#1 assert (dut.st == dut.YELLOW); // #1 causes to sample after the signals have settled.
assert (yellow);
repeat (dut.YELLOW_DURATION) wait (clk == 0) wait (clk == 1);
#1 assert (dut.st == dut.GREEN);
assert (green);
repeat (dut.GREEN_DURATION) wait (clk == 0) wait (clk == 1);
#1 assert (dut.st == dut.RED);
assert (red);
#10 $finish;
end
initial begin
$dumpfile("signals.fst");
$dumpvars;
end
endmodule
module traffic_light_controller_boolean #(
int unsigned RED_DURATION = 3,
int unsigned YELLOW_DURATION = 1,
int unsigned GREEN_DURATION = 3,
int unsigned CLKDIV_FOR_1S = 100e6 // 1 s
) (
input clk,
input [3:0] btn,
output [2:0] RGB0
);
logic clk_1s, rst, red, yellow, green;
clkdiv #(CLKDIV_FOR_1S) clkdiv_1s (
.i(clk),
.o(clk_1s)
);
traffic_light_controller #(
.RED_DURATION(RED_DURATION),
.YELLOW_DURATION(YELLOW_DURATION),
.GREEN_DURATION(GREEN_DURATION)
) tlc_i (
.clk(clk_1s),
.*
);
assign RGB0 = {1'b0, green | yellow, red | yellow};
assign rst = |btn;
endmodule
Homework#
Augment the traffic light controller design above so that an ambulance can cross in an emergency case.
Requirements:
Augment the interface with the input
emergency
. If this input is active, then the traffic light controller leaves immediately 🔴 state.Draw a state diagram
Your testbench must also verify the requirement 1
Implement the previous problem using one-block Mealy, two-block Mealy, and three-block Moore FSM.