Why Learn FSMs? Every block in a chip that follows a sequence of steps is controlled by a finite state machine: a UART receiving a byte, a memory controller issuing commands in the right order, a bus interface waiting for a handshake, or a power manager moving between sleep and active modes. Writing FSMs cleanly is one of the most important RTL skills. In this tutorial, you'll learn the standard Verilog FSM structure, how outputs are generated, and what hardware the code becomes.

A finite state machine (FSM) is a sequential circuit that is always in one of a fixed number of states. On each clock edge, it moves to a next state chosen by its current state and its inputs, and it produces outputs based on its state, and sometimes its inputs. In Verilog, an FSM is usually written as a clocked always block that holds the state register and a combinational always block that computes the next state.

12 min read | Intermediate Level

What You'll Learn

  • The difference between Moore and Mealy FSMs, and between binary and one-hot state encoding
  • Write an FSM with the two-block style: a state register and next-state logic
  • Generate outputs combinationally or from registers, and what each style synthesizes into
  • Avoid latches, lost states and reset problems in FSM code

Types of FSMs

FSMs are classified in two independent ways: by how their outputs are produced, and by how their states are encoded in flip-flops.

By output generation:

Moore FSMMealy FSM
Outputs depend onCurrent state onlyCurrent state and current inputs
Output timingChanges only after a clock edgeCan change as soon as an input changes
Number of statesOften moreOften fewer for the same behavior
Glitch riskLow, outputs follow the state registerHigher, input glitches can pass to outputs

By state encoding:

EncodingExample for 4 statesFlip-flops for N statesCharacteristics
Binary00, 01, 10, 11$clog2(N)Fewest flip-flops; more logic to decode each state
One-hot0001, 0010, 0100, 1000NOne flip-flop per state; very simple next-state and output logic; fast
Gray00, 01, 11, 10$clog2(N)One bit changes per transition when states advance in sequence

One-hot encoding is the same idea as a ring counter: each state has its own flip-flop. It is popular on FPGAs, which have many flip-flops, while ASIC designs with many states often use binary encoding to save area. Synthesis tools can also re-encode an FSM automatically when they recognize the structure.

Verilog FSM Structure

An FSM can be written with one always block or with two. The two-block style is recommended because it separates the two kinds of hardware in an FSM:

  • A clocked (sequential) block that holds the current state in a register
  • A combinational block that computes the next state from the current state and the inputs

Outputs are produced either inside the combinational block or with separate assign statements, or from registers when glitch-free outputs are needed.

verilog fsm

The states are given names with localparam, so the code reads IDLE instead of 2'b00, and the encoding can be changed in one place. See Verilog Parameters.


// State encoding: binary, 2 bits for 3 states
localparam IDLE    = 2'b00,
           STATE_1 = 2'b01,
           STATE_2 = 2'b10;

reg [1:0] cur_state;     // Output of the state register
reg [1:0] next_state;    // Output of the next-state logic

Sequential Always Block

The state register changes only on the clock edge. On reset, the FSM goes back to IDLE; otherwise it loads the next state computed by the combinational block. This block uses only non-blocking assignments, which model the register correctly and avoid race conditions with other clocked blocks.


always @ (posedge clk) begin
  // If reset is asserted, go back to IDLE state
  if (! resetn) begin
    cur_state <= IDLE;

  // Else transition to the next state
  end else begin
    cur_state <= next_state;
  end
end

Combinational Always Block

This block computes the next state. It uses always @(*), so it re-evaluates whenever the current state or any input changes, and it uses blocking assignments, as all combinational blocks should. It starts with a default assignment so that every path assigns next_state.


// Combinational always block for next state logic
always @(*) begin
    // Default: stay in the current state unless a transition below applies
    next_state = cur_state;

    case (cur_state)
        IDLE: begin
                if (input_signal)
                  next_state = STATE_1; // Transition to STATE_1 on input_signal
              end

        STATE_1:  begin
                    if (!input_signal)
                      next_state = STATE_2; // Transition to STATE_2 if input_signal is low
                  end

        STATE_2:  next_state = IDLE; // Transition back to IDLE
        default:  next_state = IDLE; // Recover from any unused encoding (2'b11)
    endcase
end

The default next_state = cur_state means "stay here" for every case that does not name a transition. So the FSM waits in IDLE until input_signal goes high, and waits in STATE_1 until it goes low. The default item of the case handles 2'b11, the one encoding that is not a valid state, by returning to IDLE.

Quick Check

Q: If the first line of the combinational block were next_state = IDLE; instead of next_state = cur_state;, what would happen in STATE_1 while input_signal stays high?

Show Answer

The FSM would return to IDLE on the next clock edge. STATE_1 only assigns next_state when input_signal is low, so in every other case the default applies. With a default of IDLE, every state that should wait instead jumps back to IDLE.

Output Generation

There are three common ways to produce the FSM's outputs.

1. In the combinational block. Assign a default value to every output at the start of the block, then override it in the states or transitions that need it. Assigning outputs in the same case as the next state keeps all the behavior of a state in one place. When several outputs change together, the assignments can be grouped in a task.


always @(*) begin
    next_state    = cur_state;
    output_signal = 1'b0;            // Default output value: no latch can be inferred

    case (cur_state)
        IDLE:     if (input_signal)
                    next_state = STATE_1;

        STATE_1:  if (!input_signal) begin
                    next_state    = STATE_2;
                    output_signal = 1'b1;    // Depends on state AND input: Mealy output
                  end

        STATE_2:  next_state = IDLE;
        default:  next_state = IDLE;
    endcase
end

Here output_signal is 1 during the cycle in which the FSM is in STATE_1 and input_signal is low. Because it depends on an input, it is a Mealy output.

2. With continuous assignments. A Moore output can be written as a comparison on the current state. This is clear for a few outputs, but becomes repetitive when there are many states and outputs.


assign  op_signal = (cur_state == STATE_1) ? 1 : 0;

3. From registers. Computing the output's next value combinationally and storing it in a flip-flop gives a glitch-free output that changes exactly on the clock edge, at the cost of one more flip-flop per output:


// Registered Moore output: high for exactly the cycles the FSM is in STATE_2
always @(posedge clk) begin
  if (!resetn) done <= 1'b0;
  else         done <= (next_state == STATE_2);
end

Using next_state rather than cur_state makes done line up with the state: it goes high on the same edge on which the FSM enters STATE_2.

Where are FSMs Used?

A UART receiver is a classic FSM: it waits in an idle state for the falling edge of a start bit, moves to a state that samples the 8 data bits one per bit time, then checks the stop bit and returns to idle. Each state decides which counter runs and when the received byte is valid. Most protocol controllers, such as SPI, I2C and USB, are built the same way.

Complete Example

Putting the pieces together gives a full module. Its single Moore output, busy, is high whenever the FSM is not in IDLE.


module simple_fsm (
  input      clk,
  input      resetn,        // Active-low synchronous reset
  input      input_signal,
  output     busy           // Moore output
);

  localparam IDLE    = 2'b00,
             STATE_1 = 2'b01,
             STATE_2 = 2'b10;

  reg [1:0] cur_state, next_state;

  // 1. State register
  always @(posedge clk) begin
    if (!resetn) cur_state <= IDLE;
    else         cur_state <= next_state;
  end

  // 2. Next-state logic
  always @(*) begin
    next_state = cur_state;
    case (cur_state)
      IDLE    : if (input_signal)  next_state = STATE_1;
      STATE_1 : if (!input_signal) next_state = STATE_2;
      STATE_2 : next_state = IDLE;
      default : next_state = IDLE;
    endcase
  end

  // 3. Output logic
  assign busy = (cur_state != IDLE);

endmodule

Synthesis result: The tool builds a 2-bit state register (two flip-flops with synchronous reset), a small block of combinational logic for the next state, made of the muxes and gates described by the case statement, and an OR gate for busy, which is 1 whenever either state bit is 1. Many synthesis tools recognize this structure as an FSM, report its states and transitions, and can choose a different encoding, such as one-hot, if it gives better timing.

Memory Controller Command Sequencing

DRAM devices must receive commands in a strict order with minimum delays between them: activate a row, wait, read or write, wait, then precharge before opening another row. The command scheduler in a memory controller is an FSM whose states represent these steps, combined with counters that enforce the required wait times in each state.

Elaborated Schematic

In the elaborated view, the current-state register appears as flip-flops, and the next-state logic appears as multiplexers selected by the current state and the inputs. The two-block coding style maps directly onto three parts of hardware:

  • Present-state logic: the clocked block, which becomes the state register
  • Next-state logic: the combinational block, which becomes muxes and gates
  • Output logic: the output assignments, which become gates driven by the state register (and the inputs, for Mealy outputs)

Keeping these parts separate in the code makes the FSM easier to read, review and change. See Verilog Pattern Detector for a complete FSM example with a testbench.

verilog fsm elaborated view

Quick Check

Q: An FSM has 12 states. How many state flip-flops does it need with binary encoding, and with one-hot encoding?

Show Answer

Binary needs 4 ($clog2(12)), leaving 4 unused encodings. One-hot needs 12, one per state. One-hot uses three times as many flip-flops, but each state is identified by a single bit, so its next-state and output logic is smaller and faster.

Valid/Ready Handshakes

On-chip buses transfer data with a handshake: the sender raises valid and must hold its data until the receiver raises ready. The interface logic on each side is a small FSM with states such as "waiting to send" and "sending", and the transfer happens on the clock edge where both signals are high.

Common Beginner Mistakes

Mistake #1: No default assignment for next_state

Wrong: A combinational block where next_state is assigned only inside if conditions, with no default at the top
Right: Start the block with next_state = cur_state;

Why? In IDLE with input_signal low, no statement assigns next_state, so it must keep its old value and synthesis infers a latch. The same applies to every output assigned in the combinational block. See D Latch.

Mistake #2: Defaulting next_state to IDLE

Wrong: next_state = IDLE; as the first line of the combinational block
Right: next_state = cur_state;, and only use IDLE in the case default item

Why? It avoids the latch, but every state that should wait for a condition jumps back to IDLE instead. The FSM looks correct when the inputs change quickly in a testbench and fails when the real inputs stay high for several cycles.

Mistake #3: Mixing up blocking and non-blocking assignments

Wrong: cur_state = next_state; in the clocked block, or next_state <= ... in the combinational block
Right: Non-blocking (<=) in the clocked block, blocking (=) in the combinational block

Why? A blocking assignment in a clocked block can create race conditions with other clocked blocks that read the state. A non-blocking assignment in the combinational block means later statements in the same block see the old value, so defaults and overrides do not work as written. See Verilog Blocking & Non-Blocking.

Mistake #4: Forgetting the reset or the case default

Wrong: A state register with no reset, or a case with no default item when some encodings are unused
Right: Reset the state register to a defined state, and add default: next_state = IDLE;

Why? Without a reset, cur_state starts as x in simulation, matches no case item, and the FSM never starts. In hardware, it powers up in an unpredictable state. Without a default, an unused encoding reached through an upset has no way back to the valid states.

Practice Exercise

Challenge: Traffic Light Controller

Write a Moore FSM traffic_light with inputs clk, resetn and tick, and three outputs red, yellow and green. It has three states, RED, GREEN and YELLOW, in that order. The FSM moves to the next state only on a clock edge where tick is 1, and otherwise stays in its state. Reset goes to RED. Exactly one output is high in each state.

Hint

Use the same three parts as the complete example. In the combinational block, default next_state to cur_state and change it only when tick is 1. Since the outputs depend only on the state, write them with assign statements.

Solution

module traffic_light (
  input  clk,
  input  resetn,      // Active-low synchronous reset
  input  tick,        // Advance to the next light when 1
  output red,
  output yellow,
  output green
);

  localparam RED    = 2'd0,
             GREEN  = 2'd1,
             YELLOW = 2'd2;

  reg [1:0] cur_state, next_state;

  // State register
  always @(posedge clk) begin
    if (!resetn) cur_state <= RED;
    else         cur_state <= next_state;
  end

  // Next-state logic: advance only on tick
  always @(*) begin
    next_state = cur_state;               // Hold by default
    if (tick) begin
      case (cur_state)
        RED     : next_state = GREEN;
        GREEN   : next_state = YELLOW;
        YELLOW  : next_state = RED;
        default : next_state = RED;
      endcase
    end
  end

  // Moore outputs: depend only on the current state
  assign red    = (cur_state == RED);
  assign green  = (cur_state == GREEN);
  assign yellow = (cur_state == YELLOW);

endmodule

In a real controller, tick would come from a timer, typically a Mod-N counter with a different count for each light. With one-hot encoding (3'b001, 3'b010, 3'b100), each output would simply be one bit of the state register, with no decoding logic at all.

Summary

In this tutorial, you learned:

  • An FSM is always in one of a fixed set of states and moves between them on clock edges
  • Moore outputs depend only on the state; Mealy outputs also depend on the inputs
  • Binary encoding uses the fewest flip-flops; one-hot uses one per state and simplifies the logic
  • The two-block style separates the state register (non-blocking) from the next-state logic (blocking)
  • Default next_state to cur_state so states hold, and use the case default to recover from unused encodings
  • Outputs can be combinational, from continuous assignments, or registered for glitch-free timing
  • Synthesis builds a state register, next-state logic and output logic, and can re-encode the states
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5