Finite-State Machines in Verilog
Finite-state machines (FSMs) control ordered behavior: wait, start, load, shift, finish, recover. A good FPGA FSM begins as a small diagram and a transition table. The Verilog should then make the state register, next-state logic, and outputs easy to review.
Learning Objectives
By the end of this lesson, you should be able to:
- recognize when an FSM is the right structure;
- draw states and transitions before coding;
- write a two-block Verilog FSM;
- choose Moore or Mealy outputs intentionally;
- test reset, normal transitions, and unexpected states.
When to Use an FSM
Use an FSM when a circuit has a small number of named phases and transitions between them:
- UART transmitter: idle, start bit, data bits, stop bit;
- SPI controller: idle, assert chip select, shift, finish;
- button debounce: idle, wait stable, accept, wait release;
- memory-mapped peripheral: wait request, respond, clear;
- motor controller: idle, precharge, run, fault, recover.
Do not turn every counter into an FSM. Use named states when the names make behavior clearer than raw numeric counts.
State Diagram First
This example controls a simple serial shifter:
The diagram answers important questions before code exists:
- What is the reset state?
- Which inputs cause transitions?
- Can any state get stuck?
- Which state asserts
done?
Two-Block Verilog Template
The first block is combinational next-state and output logic. The second block is the clocked state register.
localparam IDLE = 2'd0;
localparam LOAD = 2'd1;
localparam SHIFT = 2'd2;
localparam DONE = 2'd3;
reg [1:0] state;
reg [1:0] next_state;
reg done;
always @* begin
next_state = state;
done = 1'b0;
case (state)
IDLE: begin
if (start)
next_state = LOAD;
end
LOAD: begin
next_state = SHIFT;
end
SHIFT: begin
if (bit_count_done)
next_state = DONE;
end
DONE: begin
done = 1'b1;
next_state = IDLE;
end
default: begin
next_state = IDLE;
end
endcase
end
always @(posedge clk) begin
if (rst)
state <= IDLE;
else
state <= next_state;
end
Defaults at the top of the combinational block prevent accidental latches. The default case gives the machine a recovery path if the state register ever contains an invalid value.
Moore and Mealy Outputs
| Output type | Depends on | Advantage | Caution |
|---|---|---|---|
| Moore | current state only | stable and easy to review | may respond one cycle later |
| Mealy | current state and inputs | can respond immediately | can glitch if inputs are not stable |
Beginners should start with Moore-style outputs unless the design clearly needs input-dependent output in the same cycle. For FPGA control signals that enable registers, memories, or interfaces, stable registered or Moore-like behavior is often easier to time and debug.
Worked Example: Traffic Light FSM
The following FSM controls a simplified two-road traffic light. Timer logic is separate; the FSM only consumes timer-done inputs.
module traffic_fsm (
input wire clk,
input wire rst,
input wire ns_green_done,
input wire ns_yellow_done,
input wire ew_green_done,
input wire ew_yellow_done,
output reg ns_red,
output reg ns_yellow,
output reg ns_green,
output reg ew_red,
output reg ew_yellow,
output reg ew_green
);
localparam NS_GREEN = 2'd0;
localparam NS_YELLOW = 2'd1;
localparam EW_GREEN = 2'd2;
localparam EW_YELLOW = 2'd3;
reg [1:0] state;
reg [1:0] next_state;
always @* begin
next_state = state;
ns_red = 1'b1;
ns_yellow = 1'b0;
ns_green = 1'b0;
ew_red = 1'b1;
ew_yellow = 1'b0;
ew_green = 1'b0;
case (state)
NS_GREEN: begin
ns_red = 1'b0;
ns_green = 1'b1;
if (ns_green_done)
next_state = NS_YELLOW;
end
NS_YELLOW: begin
ns_red = 1'b0;
ns_yellow = 1'b1;
if (ns_yellow_done)
next_state = EW_GREEN;
end
EW_GREEN: begin
ew_red = 1'b0;
ew_green = 1'b1;
if (ew_green_done)
next_state = EW_YELLOW;
end
EW_YELLOW: begin
ew_red = 1'b0;
ew_yellow = 1'b1;
if (ew_yellow_done)
next_state = NS_GREEN;
end
default: begin
next_state = NS_GREEN;
end
endcase
end
always @(posedge clk) begin
if (rst)
state <= NS_GREEN;
else
state <= next_state;
end
endmodule
The safe default outputs make both roads red before each state overrides the active road. In a real traffic controller, certified safety logic and interlock timing would be required; this example is for FPGA FSM structure, not road deployment.
State Encoding
For small beginner examples, binary localparam states are clear. For larger FPGA designs, synthesis tools may choose one-hot encoding if asked or if their optimization decides it is better.
Common encoding choices:
- binary: fewer state bits, possible deeper decode logic;
- one-hot: one flip-flop per state, often simpler next-state logic;
- Gray-like: only one bit changes between selected states, useful in special cases but not a CDC solution by itself.
Do not depend on a particular physical encoding unless you set it intentionally and verify synthesis results.
Verification Strategy
Test at least these cases:
- reset reaches the intended state;
- each legal transition occurs only when its condition is true;
- outputs match every state;
- the FSM does not skip required states;
- invalid state recovery works if your simulator or formal flow can force an illegal value.
For critical control logic, add assertions such as "both roads are never green" or "done is high only in DONE".
Exercise
Design and simulate a Verilog FSM for a 4-byte SPI transmit controller:
- states:
IDLE,LOAD,SHIFT,NEXT,DONE; - input:
start; - input:
bit_done; - input:
last_byte; - outputs:
load_shift_reg,shift_enable,done; - reset state:
IDLE.
Draw the state diagram first. Then code the two-block FSM and write a testbench that confirms the normal sequence and a no-start idle case.
Common Mistakes
- Coding transitions before drawing the state diagram.
- Missing defaults for outputs or
next_state. - Letting asynchronous button or pin inputs drive transitions directly.
- Using several clocked blocks to assign the same state register.
- Forgetting a reset state.
- Creating unreachable states and never noticing because the testbench is too short.
Summary
FSMs are the control structure for many FPGA systems. Draw the states first, keep next-state logic separate from the state register, use defaults to avoid latches, and verify reset plus every transition. Clear FSM code is easier to synthesize, time, debug, and review.
Next: Verilog Style for Synthesis.
Further Reading
- Vendor HDL coding guides for FSM templates and state encoding.
- Verilator lint documentation for incomplete case and latch warnings.
- Yosys FSM extraction and optimization documentation.
- Clifford Cummings papers on FSM design and nonblocking assignment style.