Skip to content

VHDL · Chapter 9.4 · Finite State Machines

The Two-Process FSM

With the FSM's parts and choices understood, the question is how to organise them in code, and the most widely recommended answer is the two-process style. One clocked process holds just the state register and its reset, while one combinational process computes the next state and the outputs from the current state and inputs. This split maps directly onto the hardware, with sequential state in one place and combinational logic in the other, which makes the FSM easy to read, review, and get right: the clocked process is trivially correct, and the combinational process is a normal default-then-override block. Both Moore and Mealy outputs fit naturally. This lesson presents the template, explains why it is the standard, and shows the latch-free discipline it relies on.

Foundation14 min readVHDLFSMTwo-ProcessCoding StyleLatch-freeRTL

1. Engineering intuition — separate the state from the logic

An FSM has exactly two kinds of thing in it: the state, which is sequential (it lives in flip-flops, updates on the clock, resets to a known value), and the logic that decides the next state and the outputs, which is combinational. The two-process style puts each in its own process — one clocked, one combinational — so the code mirrors the hardware one-to-one. That separation is why it is the recommended style: each process is simple and checkable on its own, and the boundary between "what is registered" and "what is logic" is explicit rather than tangled together.

2. Formal explanation — the two-process template

two_process_fsm.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;
 
-- PROCESS 1 — clocked: the STATE REGISTER only (with reset). Trivially correct.
state_reg : process (clk, rst_n)
begin
  if rst_n = '0' then        state <= IDLE;
  elsif rising_edge(clk) then state <= next_state;
  end if;
end process;
 
-- PROCESS 2 — combinational: NEXT-STATE + OUTPUTS. Default everything → latch-free.
comb : process (all)
begin
  next_state <= state;                 -- default: stay (no latch on next_state)
  busy       <= '0';                   -- default outputs (no latch)
  done       <= '0';
  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;
  end case;
end process;

The clocked state_reg process does one thing: register next_state into state on the edge, reset to a known start (Module 8). The combinational comb process is process(all) with defaults at the topnext_state <= state plus every output — then a case that overrides per state (Module 6's default-then- override). This guarantees the combinational logic is latch-free, and it holds whether the outputs are Moore (from state) or Mealy (from state and inputs).

3. Production RTL — a two-process handshake controller

two_process_handshake.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all;
type st_t is (IDLE, REQ, WAITA, DONE);
signal st, st_n : st_t;
 
st_reg : process (clk, rst_n)
begin
  if rst_n = '0' then        st <= IDLE;
  elsif rising_edge(clk) then st <= st_n;
  end if;
end process;
 
st_comb : process (all)
begin
  st_n <= st;                          -- defaults
  req  <= '0';  busy <= '0';
  case st is
    when IDLE  => if start = '1' then st_n <= REQ; end if;
    when REQ   => req <= '1'; busy <= '1'; st_n <= WAITA;
    when WAITA => req <= '1'; busy <= '1'; if ack = '1' then st_n <= DONE; end if;
    when DONE  => busy <= '1'; st_n <= IDLE;
  end case;
end process;

What hardware does this become? A state register (the clocked process) plus combinational next-state and output logic (the combinational process): req/busy are Moore outputs decoded from the state, and the transitions decode from state and inputs. The two-process split makes it obvious which flip-flops exist (only st) and which logic is combinational — exactly the structure a reviewer wants to see.

4. Hardware interpretation — code mirrors the hardware

two-process FSM: clocked state register process and combinational next-state/output processnext_statestatecombinational processnext-state + outputs(process(all))clocked processstate register + resetinputsgo, ack, ...outputsbusy, req, done12
The two-process FSM maps one-to-one onto the hardware. The clocked process is the state register (flip-flops, clock, reset). The combinational process is the next-state and output logic. The state register feeds the combinational process (current state), which computes the next state captured on the next edge, plus the outputs. This explicit separation — sequential in one process, combinational in the other — is why the style is readable and reliable: each process is simple, and the register/logic boundary is clear.

5. Simulation interpretation — register advances, logic settles

Two-process FSM: combinational logic computes next_state; the register captures it on the edge

8 cycles
Two-process FSM: combinational logic computes next_state; the register captures it on the edgecomb: start seen → next_state = REQ (this cycle)comb: start seen → nex…reg: state captures next_state → st = REQ on the edgereg: state captures ne…clkstart01000000ack00011000stIDLEIDLEREQWAITADONEIDLEIDLEIDLEnext_stateIDLEREQWAITADONEIDLEIDLEIDLEIDLEt0t1t2t3t4t5t6t7
The combinational process computes next_state from the current state and inputs (next_state leads by being combinational); the clocked process captures it into st on each edge, so st follows next_state by one clock. This clear lead/capture relationship — logic computes, register stores — is exactly what the two-process split makes visible.

6. Debugging example — the latch in the combinational process

Expected: clean combinational next-state/output logic. Observed: synthesis infers a latch on next_state or an output, or the FSM behaves oddly in a state. Root cause: the combinational process did not default next_state (and every output) at the top, so a state/branch that did not assign one left it unassigned → a latch (Module 6.3). Fix: at the top of the combinational process, default next_state <= state and assign every output its inactive value, then override per state. Engineering takeaway: the two-process style is only as safe as its combinational defaults — always default the next state and all outputs first; the clocked process needs no such care (a register holds correctly).

default_in_comb_process.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
comb : process (all) begin
  next_state <= state;       -- default next state (no latch)
  y <= '0';                  -- default every output (no latch)
  case state is
    when S0 => if c = '1' then next_state <= S1; end if;
    when S1 => y <= '1'; next_state <= S0;
  end case;
end process;

7. Common mistakes & what to watch for

  • No default for next_state/outputs. Infers latches; default everything at the top of the combinational process.
  • Putting outputs in the clocked process by accident. That registers them (changing timing); keep combinational outputs in the combinational process (or register deliberately).
  • Incomplete sensitivity on the combinational process. Use process(all).
  • Resetting in the combinational process. Reset belongs in the clocked state-register process.
  • Comparing state to bit patterns. Use enum names (lesson 9.3); keep the FSM encoding-independent.

8. Engineering insight & continuity

The two-process FSM is the standard because it makes the FSM's structure explicit: a trivially-correct clocked state register and a normal latch-free combinational block for next-state and outputs. That clarity is its own correctness — each process is simple, the register/logic boundary is visible, and both Moore and Mealy outputs slot in naturally. It is the style to reach for by default. The next lessons present the alternatives and their trade-offs — the One-Process FSM (everything in one clocked process; more compact but registers the outputs) and the Three-Process FSM (state, next-state, and output split further) — followed by output logic, safe states, and the common FSM bugs.