Loading header...

Finite-State Machines in VHDL

Finite-state machines, or FSMs, control sequences. They decide when to wait, load, count, transmit, retry, raise an error, or return to idle. VHDL is especially good for FSMs because enumerated state types make the design readable.

Learning Objectives

By the end of this lesson, you should be able to:

  • draw a state diagram before writing RTL;
  • declare an enumerated VHDL state type;
  • separate state register logic from next-state logic;
  • choose Moore or Mealy output style deliberately;
  • avoid latch, reset, unreachable-state, and unsafe-input bugs.

Start with the State Diagram

stateDiagram-v2 [*] --> IDLE IDLE --> LOAD: start LOAD --> SHIFT SHIFT --> DONE: bit_count_done SHIFT --> SHIFT: not done DONE --> IDLE

The diagram is not paperwork. It is the design contract. If the diagram is unclear, the VHDL will usually become unclear too.

VHDL FSM Template

library ieee;
use ieee.std_logic_1164.all;

entity serial_controller is
    port (
        clk            : in  std_logic;
        rst            : in  std_logic;
        start          : in  std_logic;
        bit_count_done : in  std_logic;
        load           : out std_logic;
        shift          : out std_logic;
        done           : out std_logic
    );
end entity serial_controller;

architecture rtl of serial_controller is
    type state_t is (IDLE, LOAD, SHIFT, DONE);
    signal state, next_state : state_t := IDLE;
begin
    process(all)
    begin
        next_state <= state;
        load  <= '0';
        shift <= '0';
        done  <= '0';

        case state is
            when IDLE =>
                if start = '1' then
                    next_state <= LOAD;
                end if;

            when LOAD =>
                load <= '1';
                next_state <= SHIFT;

            when SHIFT =>
                shift <= '1';
                if bit_count_done = '1' then
                    next_state <= DONE;
                end if;

            when DONE =>
                done <= '1';
                next_state <= IDLE;
        end case;
    end process;

    process(clk)
    begin
        if rising_edge(clk) then
            if rst = '1' then
                state <= IDLE;
            else
                state <= next_state;
            end if;
        end if;
    end process;
end architecture rtl;

This is a two-process FSM:

  • one combinational process calculates next_state and outputs;
  • one clocked process stores the current state.

Why Defaults Matter

At the top of the combinational process:

next_state <= state;
load  <= '0';
shift <= '0';
done  <= '0';

These defaults prevent accidental latches and make the inactive output values obvious. Each state only overrides what it needs.

Moore and Mealy Outputs

Output style Depends on Advantage Risk
Moore current state only stable and easy to review may respond one clock later
Mealy current state and inputs can respond immediately can glitch if inputs are not clean

Beginners should start with Moore-style outputs. Use Mealy outputs only when the faster response is needed and the input timing is controlled.

Safe Input Handling

FSM transition inputs must be in the same clock domain as the state register. Inputs from push buttons, connectors, sensors, slow peripherals, or another FPGA clock domain need synchronization before the FSM uses them.

flowchart LR EXT["External input"] --> SYNC["Synchronizer or filter"] SYNC --> FSM["FSM transition logic"] FSM --> REG["State register"] REG --> OUT["Control outputs"]

For a push button, synchronizing alone is not enough; use debounce filtering before treating the signal as a command.

Worked Example: Button Debounce Controller

A simple debounce control FSM can use these states:

State Meaning Exit condition
IDLE waiting for a press synchronized input changes
COUNT input must stay stable timer reaches debounce count
VALID one clean press event issued next clock
WAIT_RELEASE wait until button released synchronized input returns inactive

This organization avoids repeated triggers while the button is held.

Verification Checklist

For every FSM, verify:

  • reset reaches a known legal state;
  • each state has a defined transition for all important input cases;
  • every output has a default value;
  • unreachable states are intentional or removed;
  • simulation covers reset, normal path, hold path, error path, and back-to-back commands;
  • transition inputs are synchronized to the FSM clock.

Exercise

Design a traffic-light FSM with four states:

  • NS_GREEN;
  • NS_YELLOW;
  • EW_GREEN;
  • EW_YELLOW.

Inputs:

  • ns_timer_done;
  • ew_timer_done.

Outputs:

  • ns_red, ns_yellow, ns_green;
  • ew_red, ew_yellow, ew_green.

Write the state type and the transition/output process. Use Moore-style outputs.

Explained Solution

type state_t is (NS_GREEN, NS_YELLOW, EW_GREEN, EW_YELLOW);
signal state, next_state : state_t := NS_GREEN;

process(all)
begin
    next_state <= state;

    ns_red    <= '0';
    ns_yellow <= '0';
    ns_green  <= '0';
    ew_red    <= '0';
    ew_yellow <= '0';
    ew_green  <= '0';

    case state is
        when NS_GREEN =>
            ns_green <= '1';
            ew_red   <= '1';
            if ns_timer_done = '1' then
                next_state <= NS_YELLOW;
            end if;

        when NS_YELLOW =>
            ns_yellow <= '1';
            ew_red    <= '1';
            next_state <= EW_GREEN;

        when EW_GREEN =>
            ns_red   <= '1';
            ew_green <= '1';
            if ew_timer_done = '1' then
                next_state <= EW_YELLOW;
            end if;

        when EW_YELLOW =>
            ns_red    <= '1';
            ew_yellow <= '1';
            next_state <= NS_GREEN;
    end case;
end process;

Add the standard clocked process to store state and reset it to NS_GREEN. In a real traffic controller, timers, all-red clearance, fault handling, lamp monitoring, and safety certification requirements would be mandatory.

Common Mistakes

  • Coding before drawing the state diagram.
  • Missing defaults for outputs or next_state.
  • Letting unsynchronized inputs directly control transitions.
  • Creating too many vague states such as STEP1, STEP2, and STEP3.
  • Mixing output decode into many unrelated processes.
  • Forgetting to test reset from every important operating condition.

Summary

Good VHDL FSMs use named states, clear diagrams, defaults, synchronized inputs, and a clean split between next-state logic and the clocked state register. Start with Moore outputs for clarity, then use Mealy outputs only when the timing need is real.

Next: Exercise: VHDL Counter and Testbench.

Further Reading

Mind Map

mindmap root((VHDL FSMs)) Core concept Named states State register Next state logic Outputs from state Applications Protocol control Debounce logic Sequencers Error recovery Design rules Draw diagram first Default next_state Default outputs Synchronize inputs Practical checks Reset to legal state Simulate all transitions Cover hold paths Review unreachable states Common mistakes Latch outputs Vague state names Mealy glitches Unsafe external inputs