VHDL · Chapter 9.5 · Finite State Machines
The One-Process FSM
The one-process FSM puts the state register, the next-state decode, and the outputs all in a single clocked process, so everything including the outputs is registered. This style has real attractions. It is compact, there is no separate combinational process to accidentally leave incomplete, so the classic next-state or output latch cannot happen, and the outputs come out registered, stable and glitch-free by construction. The price is timing. Because the outputs are registered, they appear one cycle later than the combinational outputs of a two-process machine, and same-cycle Mealy outputs are awkward to express. This lesson shows the one-process template, its registered-output behaviour, and when to prefer it.
Foundation13 min readVHDLFSMOne-ProcessRegistered OutputsCoding StyleRTL
1. Engineering intuition — everything registered in one place
The one-process FSM treats the whole machine as one clocked block: at each edge it decides the next state and drives the outputs, all into flip-flops. There is no separate combinational stage — the next state and the outputs are computed and registered together. The upside is robustness and stability: every output is a clean registered signal (no glitches), and there is no combinational process whose missing default could infer a latch. The downside is that registering the outputs costs a cycle of latency and makes truly combinational (same-cycle Mealy) outputs hard. It is a "compact and safe, but a cycle slower" style.
2. Formal explanation — state and outputs together, all clocked
library ieee; use ieee.std_logic_1164.all;
type state_t is (IDLE, RUN, DONE);
signal state : state_t; -- no separate next_state signal needed
fsm : process (clk, rst_n)
begin
if rst_n = '0' then
state <= IDLE;
busy <= '0'; done <= '0'; -- registered outputs reset too
elsif rising_edge(clk) then
busy <= '0'; done <= '0'; -- default the registered outputs each cycle
case state is
when IDLE => if go = '1' then state <= RUN; end if;
when RUN => busy <= '1';
if fin = '1' then state <= DONE; end if;
when DONE => done <= '1';
state <= IDLE;
end case;
end if;
end process;Everything is inside one rising_edge(clk) process: the state advances by assigning state <= ... directly
(no next_state signal), and the outputs are assigned registered values. The outputs are defaulted each
cycle (a clocked default — they hold otherwise, but defaulting makes the per-state value explicit). Because the
outputs are registered, they reflect the state with one clock of delay.
3. Production RTL — a registered-output controller
library ieee; use ieee.std_logic_1164.all;
type st_t is (IDLE, REQ, WAITA, DONE);
signal st : st_t;
fsm : process (clk, rst_n)
begin
if rst_n = '0' then
st <= IDLE; req <= '0'; busy <= '0';
elsif rising_edge(clk) then
req <= '0'; busy <= '0'; -- registered output defaults
case st is
when IDLE => if start = '1' then st <= REQ; end if;
when REQ => req <= '1'; busy <= '1'; st <= WAITA;
when WAITA => req <= '1'; busy <= '1'; if ack = '1' then st <= DONE; end if;
when DONE => busy <= '1'; st <= IDLE;
end case;
end if;
end process;What hardware does this become? A state register and output registers, all driven by the same clocked
logic. req/busy come out of flip-flops — glitch-free and timing-clean — but they appear one cycle after the
state logic decides them, compared with the combinational outputs of the two-process version. There is no
combinational FSM process at all, so the missing-default latch bug is impossible here.
4. Hardware interpretation — registered state and outputs
5. Simulation interpretation — outputs lag by a cycle
One-process FSM: registered outputs appear one cycle after the deciding state logic
8 cycles6. Debugging example — the output that is a cycle late
Expected: an FSM output aligned with its state (as in the two-process style). Observed: the output is consistently one cycle later than expected, breaking handshake timing with another block. Root cause: the one-process style registers the outputs, so they lag the state by a clock; the surrounding logic assumed combinational (same-cycle) outputs. Fix: either account for the one-cycle output latency in the interface, or use a two-process style with combinational outputs if same-cycle timing is required (or compute the output for the state being entered). Engineering takeaway: one-process FSM outputs are registered and therefore a cycle late — great for glitch-free timing, but you must budget the extra cycle or switch styles if you need same-cycle outputs.
-- One-process: 'req' is registered, so it asserts one cycle after entering REQ.
-- If a same-cycle output is needed, drive it combinationally (two-process) or decode the NEXT state.
when IDLE => if start = '1' then st <= REQ; req <= '1'; end if; -- assert as you DECIDE to enter REQ7. Common mistakes & what to watch for
- Forgetting outputs are registered (a cycle late). Budget the latency or use combinational outputs.
- Trying to express a same-cycle Mealy output. Awkward in one-process; use two/three-process or a separate combinational output.
- Not defaulting registered outputs each cycle. Without a per-cycle default they hold (a register) — fine if intended, a bug if you wanted them to return to inactive.
- Mixing combinational and registered intent unclearly. One-process registers everything; be deliberate about what should be combinational.
- Assuming one-process is always simpler. For combinational-output controllers the two-process style is often clearer; choose by the output timing you need.
8. Engineering insight & continuity
The one-process FSM trades the two-process style's combinational outputs for compactness and registered, glitch-free outputs — at the cost of a cycle of output latency and clumsy same-cycle Mealy. It is an excellent choice when you want registered outputs anyway (clean timing, driving other domains) and want to avoid any combinational-FSM latch risk. The complementary style goes the other way: the next lesson, The Three-Process FSM, splits state, next-state, and output logic into separate processes for maximum clarity and flexible output styling, after which FSM Output Logic treats Moore/Mealy/registered outputs as a design choice in their own right.