Verilog Crash Course Lesson 5 / 8
Lesson 5 · Putting It Together

Finite State Machines in Verilog

Learning objective Translate an FSM you'd already draw as a state diagram into idiomatic Verilog, using exactly the tools from Lessons 2–4: localparam for state encoding, one clocked block for the state register, and combinational blocks (with the latch rule enforced) for next-state and output logic.

You already know what an FSM is: a set of states, transitions between them triggered by inputs, and outputs that depend on the state (Moore) or on the state and current input together (Mealy). Nothing about that theory changes. What's new is purely mechanical — how those three pieces map onto Verilog constructs you already have.

The mapping

FSM conceptVerilog construct
The current state, stored as memory A reg updated in an always @(posedge clk) block, non-blocking (Lesson 4) — this is the only piece of real memory in the whole machine.
State names localparam constants, so you write S_RED instead of a bare number.
Transition logic (what the next state should be) An always @(*) block with a case on the current state — combinational, blocking (Lesson 3).
Output logic Another combinational block (or folded into the same one) — a case keyed on state (Moore) or on state and input (Mealy).

Worked example: a traffic light

Three states, one input (tick, pulses once per timing interval), Moore outputs (each light depends only on the current state):

module traffic_light_fsm (
    input  wire clk,
    input  wire rst,
    input  wire tick,
    output reg  red,
    output reg  yellow,
    output reg  green
);
    localparam [1:0] S_RED = 2'd0, S_GREEN = 2'd1, S_YELLOW = 2'd2;
    reg [1:0] state, next_state;

    // Block 1: the state register — the only sequential element here.
    always @(posedge clk) begin
        if (rst) state <= S_RED;
        else     state <= next_state;
    end

    // Block 2: next-state logic — purely combinational.
    always @(*) begin
        case (state)
            S_RED:    next_state = tick ? S_GREEN  : S_RED;
            S_GREEN:  next_state = tick ? S_YELLOW : S_GREEN;
            S_YELLOW: next_state = tick ? S_RED     : S_YELLOW;
            default:  next_state = S_RED;
        endcase
    end

    // Block 3: output logic (Moore — depends only on state).
    always @(*) begin
        {red, yellow, green} = 3'b000;   // defensive default — see callout below
        case (state)
            S_RED:    red    = 1'b1;
            S_GREEN:  green  = 1'b1;
            S_YELLOW: yellow = 1'b1;
            default:  red    = 1'b1;
        endcase
    end
endmodule

Full source: code/traffic_light_fsm.v.

This is the three-block style: one sequential block, two combinational blocks. You'll also see a two-block style that merges next-state and output logic into one always @(*) — fine for small Moore machines, but the three-block split scales better once output logic gets complicated or you move to Mealy outputs, because you can reason about "what determines the next state" and "what determines the outputs" completely separately.

Defensive default assignment {red, yellow, green} = 3'b000; at the top of the output block, before the case, assigns every output on every path unconditionally — then the case only needs to override what's different per state. This is the single most reliable way to avoid the latch-inference bug from Lesson 3 in FSM output logic, where it's easy to forget a light in one branch. The default: branch in the next-state case serves the same purpose: it guarantees a defined next state even for an unreachable/corrupted encoding of state (relevant if the FSM ever ends up in an unexpected state, e.g. from a glitch).

S_RED S_GREEN S_YELLOW
red
yellow
green

The highlighted circle is state right now; the lit lamp is this module's Moore output for that state. Each click of Pulse tick simulates one posedge clk with tick = 1 — watch state advance exactly one step around the cycle, matching the case in Block 2.

Q: In the traffic light FSM, why is state declared as a reg updated in a clocked block, while next_state is only ever assigned in a combinational block?
state is the machine's real memory and must persist between clock edges, so it needs a clocked, non-blocking assignment; next_state is just this cycle's computed answer to 'what should state become,' with no memory of its own, so it belongs in combinational logic. Because next_state doesn't need to be declared reg at all. Because this is a Mealy machine and Moore machines don't use next_state.
Try it Extend traffic_light_fsm with a fourth state, S_YELLOW_BLINK, entered from S_RED when a new input fault is high (checked instead of the normal tick transition), and add it to both the next-state case and the output case. This forces you to touch every place a new state has to be registered — a good check for whether you've internalized the three-block structure.

New terms — state register, next-state logic, Moore/Mealy machine, localparam — are in the glossary.

Previous← Sequential Logic Contents⌂ All lessons