State Machines (Mealy/Moore)
Design and implementation of Finite State Machines (FSM) in VHDL: Moore and Mealy.
Finite State Machines (FSM)
An FSM (Finite State Machine) is a sequential circuit that:
- Is in one state at a time
- Transitions between states based on conditions (depend on inputs)
- Produces outputs based on the state (and optionally inputs)
Moore vs Mealy
| Moore | Mealy | |
|---|---|---|
| Outputs depend on | State only | State + Inputs |
| Outputs | More stable | Faster response |
| Latency | 1 extra cycle | Immediate response |
| Recommended for FPGA | Yes (simpler to synthesize) | Possible but watch for glitches |
2-Process Model (Recommended)
Example: 101 sequence detector (Moore).
| Current State | Input i_data | Next State | Output o_found |
|---|---|---|---|
| IDLE | 0 | IDLE | 0 |
| IDLE | 1 | GOT_1 | 0 |
| GOT_1 | 1 | GOT_1 | 0 |
| GOT_1 | 0 | GOT_10 | 0 |
| GOT_10 | 1 | FOUND | 0 |
| GOT_10 | 0 | IDLE | 0 |
| FOUND | x | IDLE | 1 |
The most common style for FPGA: one process for state, one for outputs.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity fsm_detector is
port (
i_clk : in std_logic;
i_rst : in std_logic;
i_data : in std_logic;
o_found : out std_logic
);
end entity fsm_detector;
architecture rtl of fsm_detector is
-- State type declaration
type t_state is (IDLE, GOT_1, GOT_10, FOUND);
signal r_state : t_state;
signal w_next : t_state;
begin
-- Process 1: state register (sequential)
p_state_reg : process(i_clk)
begin
if rising_edge(i_clk) then
if i_rst = '1' then
r_state <= IDLE;
else
r_state <= w_next;
end if;
end if;
end process p_state_reg;
-- Process 2: transition logic (combinational)
p_next_state : process(r_state, i_data)
begin
w_next <= r_state; -- default: stay in current state
case r_state is
when IDLE =>
if i_data = '1' then
w_next <= GOT_1;
end if;
when GOT_1 =>
if i_data = '0' then
w_next <= GOT_10;
else
w_next <= GOT_1;
end if;
when GOT_10 =>
if i_data = '1' then
w_next <= FOUND;
else
w_next <= IDLE;
end if;
when FOUND =>
w_next <= IDLE;
when others =>
w_next <= IDLE;
end case;
end process p_next_state;
-- Moore outputs: depend only on r_state
o_found <= '1' when r_state = FOUND else '0';
end architecture rtl;3-Process Model (Variant)
A third process for outputs (useful when outputs are complex).
-- Process 3: output logic (combinational - Moore)
p_outputs : process(r_state)
begin
-- Default values (avoids latches)
o_found <= '0';
o_active <= '0';
case r_state is
when FOUND =>
o_found <= '1';
when GOT_1 | GOT_10 =>
o_active <= '1';
when others =>
null;
end case;
end process p_outputs;Complete Example: Traffic Light FSM
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity traffic_light is
generic (
g_CLK_FREQ : integer := 100_000_000 -- 100 MHz
);
port (
i_clk : in std_logic;
i_rst : in std_logic;
o_red : out std_logic;
o_amber : out std_logic;
o_green : out std_logic
);
end entity traffic_light;
architecture rtl of traffic_light is
type t_light is (RED, AMBER, GREEN);
constant c_RED_DUR : integer := 30 * g_CLK_FREQ; -- 30s
constant c_AMBER_DUR : integer := 3 * g_CLK_FREQ; -- 3s
constant c_GREEN_DUR : integer := 25 * g_CLK_FREQ; -- 25s
signal r_state : t_light;
signal r_counter : integer range 0 to c_RED_DUR;
begin
p_fsm : process(i_clk)
begin
if rising_edge(i_clk) then
if i_rst = '1' then
r_state <= RED;
r_counter <= 0;
else
r_counter <= r_counter + 1;
case r_state is
when RED =>
if r_counter = c_RED_DUR - 1 then
r_state <= GREEN;
r_counter <= 0;
end if;
when GREEN =>
if r_counter = c_GREEN_DUR - 1 then
r_state <= AMBER;
r_counter <= 0;
end if;
when AMBER =>
if r_counter = c_AMBER_DUR - 1 then
r_state <= RED;
r_counter <= 0;
end if;
end case;
end if;
end if;
end process p_fsm;
-- Moore outputs
o_red <= '1' when r_state = RED else '0';
o_amber <= '1' when r_state = AMBER else '0';
o_green <= '1' when r_state = GREEN else '0';
end architecture rtl;| State | Duration | Next State |
|---|---|---|
| RED | 30 s | GREEN |
| GREEN | 25 s | AMBER |
| AMBER | 3 s | RED |
FSM Best Practices
1. Always initialize at reset
if i_rst = '1' then
r_state <= IDLE; -- defined starting state
end if;2. Cover all states with when others
when others =>
r_state <= IDLE; -- safe default state3. Default output values
-- At beginning of output process
o_valid <= '0';
o_data <= (others => '0');
-- Then override in each case4. One enumerated type per FSM
type t_uart_state is (IDLE, START, DATA, PARITY, STOP);
-- Each FSM has its own type → clear namingState Encoding
The synthesizer automatically chooses the encoding. It can be forced:
| Encoding | Usage | Resources |
|---|---|---|
| Binary | Few states | Minimum flip-flops |
| One-hot | Many states | More flip-flops, faster |
| Gray | Sequential counters | Single-bit transitions |
On modern FPGAs, letting the synthesizer decide is generally optimal.
FSM Categories (Pedroni)
It is useful to classify state machines into three categories by complexity:
Category 1 — Regular FSM (simple)
The machine transitions only based on external input signals. No timing, no recursion.
This is the classic FSM: sequence detector, protocol controller, command decoder.
-- Example: rising edge detector
type t_edge is (IDLE, DETECT);
signal r_state : t_edge;
process(i_clk)
begin
if rising_edge(i_clk) then
if i_rst = '1' then
r_state <= IDLE;
else
case r_state is
when IDLE => if i_sig = '1' then r_state <= DETECT; end if;
when DETECT => r_state <= IDLE;
when others => r_state <= IDLE;
end case;
end if;
end if;
end process;
o_pulse <= '1' when r_state = DETECT else '0';Category 2 — Timed FSM (with counter)
The machine waits a fixed number of cycles before transitioning. An internal counter measures elapsed time.
Use cases: traffic lights, PWM generators, startup delays.
-- FSM with internal counter
type t_timed is (WAIT_LOW, WAIT_HIGH);
signal r_state : t_timed;
signal r_timer : unsigned(19 downto 0) := (others => '0');
constant c_T_LOW : unsigned(19 downto 0) := to_unsigned(499_999, 20); -- 5 ms @ 100 MHz
constant c_T_HIGH : unsigned(19 downto 0) := to_unsigned(999_999, 20); -- 10 ms @ 100 MHz
process(i_clk)
begin
if rising_edge(i_clk) then
if i_rst = '1' then
r_state <= WAIT_LOW;
r_timer <= (others => '0');
else
r_timer <= r_timer + 1;
case r_state is
when WAIT_LOW =>
if r_timer = c_T_LOW then
r_state <= WAIT_HIGH;
r_timer <= (others => '0');
end if;
when WAIT_HIGH =>
if r_timer = c_T_HIGH then
r_state <= WAIT_LOW;
r_timer <= (others => '0');
end if;
when others =>
r_state <= WAIT_LOW;
r_timer <= (others => '0');
end case;
end if;
end if;
end process;
o_out <= '1' when r_state = WAIT_HIGH else '0';Best practice: reset the counter (
r_timer <= (others => '0')) whenever you change state to guarantee precise timing.
Category 3 — Recursive FSM (variable parameter)
The machine visits states a variable number of times based on an input parameter (N cycles, N bytes, etc.).
Use cases: serializers, SPI/UART controllers, compression engines.
-- 8-bit serializer: sends 8 bits one by one
type t_ser is (IDLE, SHIFT);
signal r_state : t_ser;
signal r_shreg : std_logic_vector(7 downto 0);
signal r_count : unsigned(2 downto 0);
process(i_clk)
begin
if rising_edge(i_clk) then
if i_rst = '1' then
r_state <= IDLE;
r_count <= (others => '0');
o_bit <= '0';
o_done <= '0';
else
o_done <= '0';
case r_state is
when IDLE =>
if i_valid = '1' then
r_shreg <= i_data;
r_count <= (others => '0');
r_state <= SHIFT;
end if;
when SHIFT =>
o_bit <= r_shreg(7);
r_shreg <= r_shreg(6 downto 0) & '0'; -- left shift
r_count <= r_count + 1;
if r_count = 7 then
o_done <= '1';
r_state <= IDLE;
end if;
when others =>
r_state <= IDLE;
end case;
end if;
end if;
end process;Summary Table
| Category | Transition trigger | Examples |
|---|---|---|
| Regular | External inputs | Sequence detector, decoder |
| Timed | Internal counter (fixed N) | Traffic lights, PWM, startup delay |
| Recursive | Internal counter (variable N) | UART TX, SPI, serializer |
Safe State Machines
An FPGA may land in an undefined state at power-up or after a glitch. A safe FSM explicitly handles all unexpected cases.
-- Always include when others
case r_state is
when IDLE => ...
when RUN => ...
when DONE => ...
when others =>
-- Unknown state: return to safe state
r_state <= IDLE;
end case;-- Force one-hot encoding to reduce illegal states (Vivado)
attribute fsm_encoding : string;
attribute fsm_encoding of r_state : signal is "one_hot";On FPGAs, flip-flops do not reset automatically at power-up. Always initialize at reset and always cover
when others.