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 stateend if;
2. Cover all states with when others
when others => r_state <= IDLE; -- safe default state
3. Default output values
-- At beginning of output processo_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:
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 detectortype 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 countertype 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 MHzconstant c_T_HIGH : unsigned(19 downto 0) := to_unsigned(999_999, 20); -- 10 ms @ 100 MHzprocess(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 onetype 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 otherscase 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.