State machines in Verilog
Turn a state diagram into readable, complete RTL that is easy to verify.
An FSM separates storage from decisions
A finite state machine stores a state and selects the next one from current inputs. A two-block structure makes that separation visible:
- a clocked block stores the state;
- a combinational block calculates the next state and outputs.
module request_controller (
input wire i_clk,
input wire i_reset,
input wire i_start,
input wire i_ack,
output reg o_request,
output reg o_done
);
localparam STATE_IDLE = 2'b00;
localparam STATE_WAIT = 2'b01;
localparam STATE_DONE = 2'b10;
reg [1:0] state_reg;
reg [1:0] state_next;
always @(posedge i_clk) begin
if (i_reset)
state_reg <= STATE_IDLE;
else
state_reg <= state_next;
end
always @* begin
state_next = state_reg;
o_request = 1'b0;
o_done = 1'b0;
case (state_reg)
STATE_IDLE: begin
if (i_start)
state_next = STATE_WAIT;
end
STATE_WAIT: begin
o_request = 1'b1;
if (i_ack)
state_next = STATE_DONE;
end
STATE_DONE: begin
o_done = 1'b1;
state_next = STATE_IDLE;
end
default: begin
state_next = STATE_IDLE;
end
endcase
end
endmoduleo_request stays high until the acknowledgement arrives. o_done is a one-cycle pulse because DONE returns to IDLE on the next edge.
Always provide defaults
At the beginning of the combinational block, the next state keeps the current state and outputs take safe values. Each branch only overrides what changes.
This method avoids latches and makes hold conditions explicit. The default branch also returns to a known state if simulation or hardware reaches an illegal encoding.
Moore or Mealy
A Moore output depends only on the state. In the example, o_request and o_done are Moore outputs.
A Mealy output depends on both state and an input. It can react within the same cycle, but its combinational path then includes that input. For an interface, check that this immediate response creates neither a combinational loop nor a timing problem.
Choosing the state encoding
Binary encoding uses few flip-flops. One-hot encoding uses one flip-flop per state and can simplify decision logic on an FPGA. The best choice depends on the number of states, target device, and timing constraints.
It is often reasonable to let the tool recode the FSM. If the encoding is observable through an interface or is part of a safety mechanism, document and constrain it deliberately.
Adding a counter in the right place
When a state must last several cycles, a counter becomes part of the machine datapath. It must be initialized when the sequence starts, advance in the relevant state, and have an unambiguous completion condition.
Comparing against LIMIT - 1 or against LIMIT changes the duration by one cycle. The testbench should check boundary values, not only an average scenario.
Asynchronous input signals
An FSM does not make an asynchronous input safe. A button, external interrupt, or flag from another clock domain must be synchronized before it controls transitions. A multi-bit bus requires a suitable protocol, not a bank of unrelated bit synchronizers.
Verifying the machine
A useful test covers at least:
- reset from every relevant phase;
- normal transitions;
- an input held high for several cycles;
- immediate and delayed acknowledgement;
- output pulses with their exact duration.
Key points
- The state register belongs in the clocked block.
- Transition decisions belong in the combinational block.
- Complete default assignments prevent latches.
- A Mealy output adds a combinational path from an input.
- An asynchronous input must be handled before the FSM.
📝 Test your knowledge - Chapter quiz