Finite-state machines¶
An FSM stores a discrete state and chooses the next state from current state plus inputs.
Design on paper first¶
Write:
- Every state and its meaning.
- Reset state.
- Conditions on each outgoing transition.
- Outputs active in each state or transition.
- Behavior for invalid or simultaneous inputs.
- Timing: whether outputs change on an edge or combinationally.
Moore and Mealy outputs¶
| Style | Output depends on | Effect |
|---|---|---|
| Moore | Current state | Usually stable for a whole cycle |
| Mealy | Current state and current inputs | Can respond within the cycle and can glitch |
Registered outputs often simplify interface timing even when derived from Mealy conditions.
Two-process template¶
type state_t is (IDLE, ACTIVE, DONE);
signal state_q : state_t := IDLE;
signal state_d : state_t;
state_register : process(clk_i)
begin
if rising_edge(clk_i) then
if reset_i = '1' then
state_q <= IDLE;
else
state_q <= state_d;
end if;
end if;
end process;
next_state_logic : process(all)
begin
state_d <= state_q; -- hold by default
case state_q is
when IDLE =>
if start_i = '1' then
state_d <= ACTIVE;
end if;
when ACTIVE =>
if complete_i = '1' then
state_d <= DONE;
end if;
when DONE =>
if acknowledge_i = '1' then
state_d <= IDLE;
end if;
end case;
end process;
busy_o <= '1' when state_q = ACTIVE else '0';
done_o <= '1' when state_q = DONE else '0';
Advantages:
- State register and transition logic are visibly separate.
- Moore outputs are easy to read.
- Waveforms show current and next state.
Risk: forgetting defaults in the combinational process infers latches.
One-process template¶
process(clk_i)
begin
if rising_edge(clk_i) then
if reset_i = '1' then
state_q <= IDLE;
busy_o <= '0';
done_o <= '0';
else
done_o <= '0';
case state_q is
when IDLE =>
busy_o <= '0';
if start_i = '1' then
state_q <= ACTIVE;
busy_o <= '1';
end if;
when ACTIVE =>
if complete_i = '1' then
state_q <= DONE;
busy_o <= '0';
done_o <= '1';
end if;
when DONE =>
if acknowledge_i = '1' then
state_q <= IDLE;
end if;
end case;
end if;
end if;
end process;
Advantages:
- All state and registered outputs update at the edge.
- No accidental combinational latches.
Risk: long machines can become difficult to read, and output latency must be understood carefully.
Both styles are valid. Adopt a team convention and test the timing contract.
Transition priority¶
If two conditions can be true simultaneously, source order in an if/elsif chain creates priority. Document it:
State encoding¶
Vivado may choose:
- Binary: few flip-flops, more decode logic.
- One-hot: one flip-flop per state, often simple/faster decode.
- Gray: one-bit changes between adjacent encoded states when the transition graph allows it.
Do not optimize encoding prematurely. Meet functionality and timing first, then use synthesis reports and constraints.
Verification plan¶
An FSM testbench should cover:
- Reset from each meaningful point.
- Every legal transition.
- Conditions that must remain in the current state.
- Simultaneous/priority conditions.
- Minimum and maximum dwell times.
- Output timing around edges.
- Recovery behavior if illegal states are considered.
Example state assertion:
assert not (busy_o = '1' and done_o = '1')
report "busy and done must never be active together"
severity failure;
Keep a coverage table in the test plan:
| From | Condition | To | Tested? |
|---|---|---|---|
| IDLE | start=1 | ACTIVE | yes |
| ACTIVE | complete=1 | DONE | yes |
| DONE | acknowledge=1 | IDLE | yes |
Common FSM mistakes¶
- Coding before drawing the transition table.
- Leaving a transition condition ambiguous.
- Generating glitches on external control outputs.
- Forgetting default assignments in two-process machines.
- Assuming initial values replace a reset strategy.
- Counting cycles inconsistently: define whether entry counts as cycle zero or one.