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
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_stateand 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.
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, andSTEP3. - 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
- AMD Vivado synthesis guide: FSM coding
- Intel Quartus Prime state machine design recommendations
- GHDL documentation
- Cliff Cummings papers on FSM design and verification