Skip to content

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

fsm_bug_catalog.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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

robust_fsm_template.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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

FSM bug catalog mapped to state register, next-state/output logic, and output pathstate registerbug: no reset → illegalstartnext-state / outputlogicbugs: latch, stuck/deadstate, encoding, norecoveryoutput pathbugs: glitch, +1 cycleoffsetfixesreset · defaults · fulltransitions · names ·register · recovery12
Where the common FSM bugs live, mapped to the three blocks. The state register is where reset bugs (illegal power-up) live. The next-state/output logic is where missing-default latches, incomplete/unreachable transitions, encoding-dependent comparisons, and missing illegal-state recovery live. The output path is where glitchy combinational outputs and registered-output cycle offsets live. Each block has a small set of failure modes with known fixes, so a structured review checks each block against its list.

5. Simulation interpretation — a buggy FSM vs the fixed one

Stuck FSM (missing transition) vs the fixed FSM that advances

8 cycles
Stuck FSM (missing transition) vs the fixed FSM that advancesfin asserts: buggy FSM has no RUN→DONE transition → stuck in RUN foreverfin asserts: buggy FSM…fixed FSM has the transition → advances RUN→DONE→IDLEfixed FSM has the tran…clkfin00111111st_buggyRUNRUNRUNRUNRUNRUNRUNRUNst_fixedRUNRUNDONEIDLEIDLEIDLEIDLEIDLEt0t1t2t3t4t5t6t7
The buggy FSM is missing the RUN to DONE transition, so even when fin asserts it stays in RUN forever (bug 3, incomplete transitions). The fixed FSM has the transition and advances normally. A stuck FSM is the single most common symptom, and it almost always means a missing or unreachable transition — caught by checking every state has a reachable exit.

6. 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.

triage_fix_example.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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 exit

7. 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 others recovery 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.