The short answer: A finite state machine in Verilog has three parts: a state register (a clocked always block), next-state logic (a combinational always block with a case on the current state) and output logic. Writing them as separate blocks (the two-block or three-block style) is the clearest and safest approach. Name states with localparam, always include a default branch and a reset state, and register outputs that leave the block.
FSMs are where control logic lives: protocol handlers, arbiters, controllers, sequence detectors. They’re also the most common “write this on the whiteboard” interview question. This post focuses on how to code them well; for Mealy vs Moore theory, see my Mealy vs Moore post.
- The structure
- A complete example: traffic light controller
- One, two or three blocks?
- State encoding
- Mistakes I see in FSM code
Table of Contents
Part 1: The structure

Part 2: A complete example: traffic light controller
Green for 20 cycles, yellow for 4, red for 16, then back to green. A small timer counts the cycles in each state.
module traffic_light (
input wire clk,
input wire rst_n,
output reg red, yellow, green
);
localparam [1:0] S_GREEN = 2'd0,
S_YELLOW = 2'd1,
S_RED = 2'd2;
localparam GREEN_T = 20, YELLOW_T = 4, RED_T = 16;
reg [1:0] state, next;
reg [4:0] timer;
wire done;
// 1) state register
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= S_RED;
else state <= next;
// timer: restarts on every state change
always @(posedge clk or negedge rst_n)
if (!rst_n) timer <= 0;
else if (next != state) timer <= 0;
else timer <= timer + 1'b1;
assign done = (state == S_GREEN && timer == GREEN_T-1) ||
(state == S_YELLOW && timer == YELLOW_T-1) ||
(state == S_RED && timer == RED_T-1);
// 2) next-state logic
always @(*) begin
next = state; // default: stay
case (state)
S_GREEN: if (done) next = S_YELLOW;
S_YELLOW: if (done) next = S_RED;
S_RED: if (done) next = S_GREEN;
default: next = S_RED; // recover from illegal states
endcase
end
// 3) output logic (Moore: depends on state only)
always @(*) begin
{red, yellow, green} = 3'b000;
case (state)
S_GREEN: green = 1'b1;
S_YELLOW: yellow = 1'b1;
default: red = 1'b1;
endcase
end
endmodule
Part 3: One, two or three blocks?
| Style | How it looks | My view |
|---|---|---|
| One block | State and outputs all in one clocked always block | Compact, outputs are registered, but next-state logic is hidden and outputs lag by a cycle |
| Two blocks | Clocked state register + combinational next state and outputs | Clear and common |
| Three blocks | Separate state register, next-state logic and output logic | Clearest for larger FSMs; my default |
Part 4: State encoding
- Binary: log2(N) flops. Compact; good for ASICs.
- One-hot: one flop per state, very simple next-state logic. Popular on FPGAs.
- Gray: one bit changes per transition, useful when states are sampled in another clock domain.
- Synthesis tools can extract FSMs and re-encode them; write clean code and let the tool choose unless you have a reason.
Part 5: Mistakes I see in FSM code
- No default assignment for next, creating latches.
- No default branch, so an illegal state (after a glitch or a bad reset) can lock the FSM forever.
- Mixing state update and output logic in confusing ways, so outputs are a cycle late without anyone noticing.
- Combinational outputs driving other blocks or chip pins. Register them if the timing or glitches matter.
- Magic numbers for states instead of named localparams.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Previous in my Verilog series: Flip-flops, latches, registers and counters. Next: Delays and timing checks.
FAQ
How do I write an FSM in Verilog?
Use a clocked always block for the state register and a combinational always block with a case statement for the next state and outputs. Name the states with localparam.
Which FSM coding style is best?
The two- or three-block style. It separates memory from logic, so it’s easier to read, review and debug.
Should FSM outputs be registered?
If they go to other blocks or off-chip, usually yes: it removes glitches and makes timing easier, at the cost of a cycle of latency or a slightly different design.
What is one-hot encoding?
Using one flip-flop per state, with exactly one bit set. It uses more flops but very simple, fast next-state logic.
