VHDL · Chapter 9.9 · Finite State Machines
Common FSM Bugs
This closing lesson is a catalog. Across the module, each FSM concept came with a failure mode, and here they are gathered into one reviewable list with fixes so you can check any state machine against it. The recurring offenders are no reset that causes an illegal power-up, missing defaults that infer latches, incomplete or unreachable transitions that leave stuck or dead states, encoding-dependent code that breaks when re-encoded, glitchy combinational outputs on critical signals, registered outputs that land a cycle off, and no recovery from illegal states. Each has a specific, simple fix you have already seen in earlier lessons. Run a new FSM through this list and the common bugs are caught before they ever reach silicon.
Foundation14 min readVHDLFSMDebuggingChecklistReliabilityRTL
1. Engineering intuition — most FSM bugs are a handful of patterns
State machines fail in predictable ways. After enough of them, you stop debugging from scratch and start checking a short list: did it reset, are all outputs and the next state defaulted, can every state be reached and left, are the outputs the right kind and timing, and does it recover from illegal states. Almost every FSM bug is one of these, and each maps to a one-line fix you already know. A good FSM review is just running the machine against this catalog.
2. The FSM bug catalog
-- BUG SYMPTOM FIX
-- 1 No reset on state register illegal/garbage power-up start reset state to a known value (Mod 8)
-- 2 Missing default (next_state/output) inferred latch default next_state<=state + all outputs
-- 3 Incomplete transitions FSM stuck in a state give every state a reachable exit
-- 4 Unreachable / dead state state never used / never left remove it or wire its entry/exit
-- 5 Encoding-dependent code breaks when re-encoded compare enum NAMES, not bit patterns (9.3)
-- 6 Glitchy combinational output spurious pulse on enable/strobe register critical/CDC outputs (9.7)
-- 7 Registered output a cycle off output mis-timed by one clock decode next_state for registered outputs
-- 8 No illegal-state recovery hangs after an upset when others => IDLE + safe-FSM attr (9.8)
-- 9 Incomplete sensitivity (comb proc) sim/synth mismatch process(all) (5.2)Each row is a failure you met in this module; together they cover the overwhelming majority of FSM defects. The fixes are mechanical — defaults, reset, full transitions, name-based comparisons, output registering, and recovery — which is why an FSM written with the module's templates is usually correct by construction.
3. Production RTL — the bug-free FSM template
library ieee; use ieee.std_logic_1164.all;
type state_t is (IDLE, RUN, DONE);
signal state, next_state : state_t;
-- state register: RESET to known state (fixes bug 1).
sreg : process (clk, rst_n) begin
if rst_n = '0' then state <= IDLE;
elsif rising_edge(clk) then state <= next_state;
end if;
end process;
-- next-state + outputs: process(all) (bug 9), defaults (bug 2), full transitions (bugs 3-4),
-- names not bits (bug 5), recovery (bug 8).
comb : process (all) begin
next_state <= state; busy <= '0'; done <= '0'; -- defaults
case state is
when IDLE => if go = '1' then next_state <= RUN; end if;
when RUN => busy <= '1'; if fin = '1' then next_state <= DONE; end if;
when DONE => done <= '1'; next_state <= IDLE;
when others => next_state <= IDLE; -- safe recovery
end case;
end process;
-- register any critical/CDC output (bugs 6-7) in a separate clocked process, decoding next_state.What hardware does this become? A correct controller — reset state register, latch-free defaulted next-state and output logic, full coverage with safe recovery, name-based and encoding-independent — with critical outputs registered separately. Following the module's templates, all nine catalog bugs are designed out from the start.
4. Hardware interpretation — where each bug lives
5. Simulation interpretation — a buggy FSM vs the fixed one
Stuck FSM (missing transition) vs the fixed FSM that advances
8 cycles6. Debugging example — systematic FSM triage
Expected: a working controller. Observed: it misbehaves somehow. Method (run the catalog): Does it start correctly? — check reset (bug 1). Latch warnings? — check defaults (bug 2). Stuck in a state? — check that state's transitions (bug 3). A state never entered? — unreachable (bug 4). Breaks after re-synthesis? — encoding-dependent code (bug 5). Spurious pulse downstream? — glitchy output, register it (bug 6). Output mis-timed by a cycle? — registered-output alignment (bug 7). Hangs after an upset? — no illegal-state recovery (bug 8). Sim/synth disagree on the logic? — incomplete sensitivity (bug 9). Engineering takeaway: FSM debugging is a checklist, not detective work — match the symptom to the catalog and apply the known fix.
-- Symptom: stuck in RUN. Catalog bug 3 (incomplete transition). Fix: add the exit.
when RUN => busy <= '1'; if fin = '1' then next_state <= DONE; end if; -- the missing exit7. Common mistakes & what to watch for
- Skipping the reset/default basics. Most FSM bugs are a missing reset or a missing default; check these first.
- Assuming a stuck FSM is mysterious. It is almost always a missing or unreachable transition.
- Hard-coding state bit patterns. Use enum names so re-encoding cannot break the logic.
- Leaving critical outputs combinational. Register enables/strobes/CDC outputs to avoid glitches.
- Omitting illegal-state recovery. Add
when othersrecovery and the safe-FSM attribute for upset tolerance.
8. Engineering insight & continuity
Common FSM bugs are a small, recurring set, each with a known fix, so the practical skill is not clever debugging but disciplined review: reset, defaults, complete and reachable transitions, name-based encoding-independence, the right output style and timing, and illegal-state recovery. The module's templates bake these in, making a correct FSM the default rather than the achievement. This completes Finite State Machines — the controller pattern at the heart of digital design. The curriculum now turns from building blocks to reuse and abstraction: Module 10 — Packages and Reuse shows how to share types, constants, and subprograms across a design so the patterns you have learned scale to large, maintainable systems.