State Machines and Control/Datapath Separation
Write a readable FSM with an enumerated type and separate decisions from data calculations.
Three parts to recognize
A synchronous state machine usually contains:
- a state register;
- logic that selects the next state;
- output or control logic.
SystemVerilog makes these roles visible with an enumeration, always_ff, and always_comb.
module burst_controller (
input logic i_clk,
input logic i_rst,
input logic i_start,
input logic i_last,
output logic o_load,
output logic o_busy,
output logic o_done
);
typedef enum logic [1:0] {IDLE, LOAD, RUN, DONE} state_t;
state_t state_q, state_d;
always_ff @(posedge i_clk) begin
if (i_rst)
state_q <= IDLE;
else
state_q <= state_d;
end
always_comb begin
state_d = state_q;
o_load = 1'b0;
o_busy = 1'b0;
o_done = 1'b0;
unique case (state_q)
IDLE: if (i_start) state_d = LOAD;
LOAD: begin
o_load = 1'b1;
state_d = RUN;
end
RUN: begin
o_busy = 1'b1;
if (i_last) state_d = DONE;
end
DONE: begin
o_done = 1'b1;
state_d = IDLE;
end
default: state_d = IDLE;
endcase
end
endmoduleDefault values
The start of the combinational block assigns every output and holds state if no transition applies. This avoids latches and keeps each branch short.
The default branch returns the machine to a known state if the binary encoding becomes illegal. The exact recovery policy depends on safety needs and tool behavior.
unique is not decoration
unique case promises that one branch at most should match and that expected values are covered. The simulator can warn when the promise is broken. Synthesis may also use the information for optimization.
Do not add unique only as a style choice. If several branches can match or an uncovered value is normal, the keyword does not describe reality.
Separate control and datapath
An FSM should generate simple commands such as load, clear, enable, or select. The datapath holds data registers, counters, multiplexers, and operators.
This separation helps verification: control transitions and calculations can be checked independently. It also avoids placing a long arithmetic expression inside already complex transition logic.
Key points
- An enumerated type gives states safe, readable names.
- The state register belongs in
always_ff. - Default combinational assignments prevent latches.
uniqueexpresses a real promise that simulation can check.- Control/datapath separation makes a design easier to review and test.
📝 Test your knowledge - Chapter quiz