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

MooreMealy
Outputs depend onState onlyState + Inputs
OutputsMore stableFaster response
Latency1 extra cycleImmediate response
Recommended for FPGAYes (simpler to synthesize)Possible but watch for glitches

2-Process Model (Recommended)

Example: 101 sequence detector (Moore).

Current StateInput i_dataNext StateOutput o_found
IDLE0IDLE0
IDLE1GOT_10
GOT_11GOT_10
GOT_10GOT_100
GOT_101FOUND0
GOT_100IDLE0
FOUNDxIDLE1

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;
StateDurationNext State
RED30 sGREEN
GREEN25 sAMBER
AMBER3 sRED

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 state

3. Default output values

-- At beginning of output process
o_valid <= '0';
o_data  <= (others => '0');
-- Then override in each case

4. One enumerated type per FSM

type t_uart_state is (IDLE, START, DATA, PARITY, STOP);
-- Each FSM has its own type → clear naming

State Encoding

The synthesizer automatically chooses the encoding. It can be forced:

EncodingUsageResources
BinaryFew statesMinimum flip-flops
One-hotMany statesMore flip-flops, faster
GraySequential countersSingle-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

CategoryTransition triggerExamples
RegularExternal inputsSequence detector, decoder
TimedInternal counter (fixed N)Traffic lights, PWM, startup delay
RecursiveInternal 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.