HomeFPGA from ScratchDay 11
DAY 11 · HDL FUNDAMENTALS

Finite State Machines on FPGA

By EcrioniX · Updated Jun 11, 2026

FSMs are how digital hardware makes decisions over time. A traffic light, a UART protocol controller, a CPU's fetch-decode-execute cycle — all FSMs. Mastering the 3-always-block style in Verilog is one of the most valuable skills in FPGA/ASIC design. This lesson builds a traffic light FSM from state diagram to tested Verilog.

1. Moore vs Mealy

Moore FSMMealy FSM
Output depends onState onlyState + current inputs
Output timingRegistered — glitch-freeCombinational — faster but can glitch
Typical useFPGA outputs, control signalsTight-latency handshakes

We build a Moore FSM — outputs are stable and registered, which makes timing analysis easy on FPGAs.

2. The 3-always-block style

✅ The golden pattern

Block 1 (clocked) — state register: state <= next_state
Block 2 (combinational) — next-state logic: compute next_state from state + inputs
Block 3 (combinational) — output logic: compute outputs from state only (Moore)

3. Traffic light FSM — state diagram

GREEN red=0 amb=0 grn=1 AMBER red=0 amb=1 grn=0 RED red=1 amb=0 grn=0 AMB2 red=1 amb=1 grn=0 tick tick tick tick
Traffic light FSM: GREEN → AMBER → RED → AMB2 → GREEN. Each transition on a timer tick.

4. traffic_light.v — design

traffic_light.v — Moore FSM traffic light
// Traffic light Moore FSM
// tick input is a slow enable pulse (e.g. from clkdiv at 1 Hz)
module traffic_light (
    input  wire clk,
    input  wire rst,
    input  wire tick,   // advance state once per tick
    output reg  red,    // 1 = red light on
    output reg  amber,  // 1 = amber light on
    output reg  green   // 1 = green light on
);
    // State encoding
    localparam GREEN = 2'd0, AMBER = 2'd1, RED = 2'd2, AMB2 = 2'd3;
    reg [1:0] state, next_state;

    // Block 1: state register
    always @(posedge clk) begin
        if (rst) state <= GREEN;
        else     state <= next_state;
    end

    // Block 2: next-state logic (combinational)
    always @(*) begin
        next_state = state;  // default: hold
        if (tick) begin
            case (state)
                GREEN: next_state = AMBER;
                AMBER: next_state = RED;
                RED:   next_state = AMB2;
                AMB2:  next_state = GREEN;
                default: next_state = GREEN;
            endcase
        end
    end

    // Block 3: output logic (Moore — state only)
    always @(*) begin
        {red, amber, green} = 3'b000; // default off
        case (state)
            GREEN: green = 1'b1;
            AMBER: amber = 1'b1;
            RED:   red   = 1'b1;
            AMB2: {red, amber} = 2'b11;
        endcase
    end
endmodule

5. Testbench

tb_traffic_light.v — testbench
`timescale 1ns/1ps
module tb_traffic_light;
    reg  clk=0,rst=1,tick=0;
    wire red,amber,green;
    traffic_light dut(.clk(clk),.rst(rst),.tick(tick),.red(red),.amber(amber),.green(green));
    always #5 clk=~clk;
    integer errors=0;
    task chk(input er,ea,eg);
        if({red,amber,green}!={er,ea,eg})begin $display("FAIL r=%b a=%b g=%b exp=%b%b%b",red,amber,green,er,ea,eg);errors=errors+1;end
        else $display("ok   r=%b a=%b g=%b",red,amber,green);
    endtask
    task do_tick; tick=1;@(posedge clk);#1;tick=0;endtask
    initial begin
        @(posedge clk);rst=0;@(posedge clk);#1;
        chk(0,0,1); // GREEN
        do_tick; chk(0,1,0); // AMBER
        do_tick; chk(1,0,0); // RED
        do_tick; chk(1,1,0); // AMB2
        do_tick; chk(0,0,1); // back to GREEN
        if(errors==0) $display("ALL TESTS PASSED"); else $display("%0d FAILED",errors);
        $finish;
    end
endmodule
expected output
ok   r=0 a=0 g=1
ok   r=0 a=1 g=0
ok   r=1 a=0 g=0
ok   r=1 a=1 g=0
ok   r=0 a=0 g=1
ALL TESTS PASSED

🎯 Day 11 takeaways

FAQ

What is an FSM?

A design pattern where a system is always in one of a finite set of states, transitioning based on inputs and producing outputs per state (Moore) or state+input (Mealy).

Moore vs Mealy?

Moore: outputs = f(state) only — registered and glitch-free. Mealy: outputs = f(state, inputs) — faster response but can glitch.

3-always-block style?

Separates state register (clocked), next-state logic (combinational), and output logic (combinational). Cleaner, easier to synthesize and debug.

Previous
← Day 10: Debouncing a button

← Full roadmap