A one-process FSM keeps state and next-state selection in a single clocked block. A two-process FSM splits them into a combinational next-state block and a clocked state update.
A three-process FSM adds a third clocked block that registers the outputs. All three describe the same machine when written carefully; they differ in how the state register shows up to the reader and the tool.
Style | Blocks | State register |
|---|---|---|
One-process | One clocked block | Inside the |
Two-process | One combinational plus one clocked block | A single |
Three-process | Two-process plus a clocked output block | Same state register; outputs move one cycle later. |
The state register infers one flop per state bit in every style. The combinational block infers only gates, provided every branch assigns next and a default covers the rest.
A default that returns to the reset state doubles as the recovery arc of the next lessons. Without either a next = state preamble or a default, an uncovered code holds its value and infers a latch in the combinational block.
The classic bug is a two-process combinational block with an incomplete case, no default, and no preamble assignment. The missing codes keep the old next value, so synthesis builds a latch.
next = state preamble. What does synthesis build?A latch on the next-state signal. The missing code must hold its old value, which is storage, not gates.
The state register is one isolated update line, which keeps sequential state visibly apart from combinational next-state logic. Recognition still has to fire, as the Yosys lesson shows.
The same three-state machine written with one process and with two processes, plus a testbench that steps both through identical inputs.
module fsm_1p (
input logic clk, input logic go, input logic fin,
output logic [1:0] st, output logic busy
);
localparam logic [1:0] IDLE = 2'b00, WORK = 2'b01, DONE = 2'b10;
logic [1:0] state;
initial state = IDLE;
always @(posedge clk) begin
case (state)
IDLE: if (go) state <= WORK;
WORK: if (fin) state <= DONE;
DONE: state <= IDLE;
default: state <= IDLE;
endcase
end
assign st = state;
assign busy = (state == WORK);
endmodule
module fsm_2p (
input logic clk, input logic go, input logic fin,
output logic [1:0] st, output logic busy
);
localparam logic [1:0] IDLE = 2'b00, WORK = 2'b01, DONE = 2'b10;
logic [1:0] state, next;
initial state = IDLE;
always @(posedge clk) state <= next;
always_comb begin
next = state;
case (state)
IDLE: if (go) next = WORK;
WORK: if (fin) next = DONE;
DONE: next = IDLE;
default: next = IDLE;
endcase
end
assign st = state;
assign busy = (state == WORK);
endmodule