State Machine Design

Finite State Machine (FSM)
Design & Synthesis in Verilog

Finite State Machines are the backbone of digital controllers, interfaces, and protocol handlers. This comprehensive guide covers FSM architecture (Mealy vs Moore), state encoding strategies (one-hot, binary, gray code), RTL coding patterns, synthesis optimization, and production-grade design techniques used in real ASIC projects from design entry through timing closure.

Mealy & Moore
State Encoding
Synthesis-Optimized
ASIC Ready

What is a Finite State Machine?

A Finite State Machine is a mathematical model of computation consisting of a finite number of discrete states, transitions between those states triggered by inputs, and output logic that may depend on the current state and inputs. FSMs are universally used to model sequential behavior in hardware and software.

The Six Elements of an FSM

Every FSM is defined by:

1
Set of States

Finite collection of discrete states (e.g., IDLE, BUSY, DONE). Typically encoded as binary or one-hot values.

2
Set of Inputs

External signals that trigger state transitions. May include control signals, data, and synchronous clock.

3
Set of Outputs

Signals produced by the FSM, either dependent on state alone (Moore) or state and inputs (Mealy).

4
Initial State

The state entered upon reset. Usually IDLE or a safe known state. Determined by reset logic.

5
Transition Function

Logic that determines next state based on current state and inputs: next_state = f(current_state, inputs)

6
Output Function

Logic that determines outputs. Moore: output = g(state). Mealy: output = h(state, inputs)

Moore vs Mealy Machines

The two fundamental FSM architectures differ in when outputs are generated:

Aspect Moore Machine Mealy Machine
Output Timing Outputs depend ONLY on current state Outputs depend on state AND current inputs
Output Update Synchronous (latched on clock edge) Partially asynchronous (combinational on inputs)
Output Delay Always one clock cycle latency Minimal latency (combinational path)
State Complexity Often requires more states for same behavior Typically fewer states
Area (Gates) Larger (more states = more flip-flops) Smaller (fewer states, combinational logic)
Timing Easier (no combinational paths on output) Harder (combinational logic must close timing)
Verification Simpler (outputs are registered) More complex (need to verify combinational behavior)
Use Case Controllers, simple sequences, glitch-free outputs High-throughput logic, protocol handling, optimized area

Design Choice: Start with Moore FSM for simplicity and robustness. Optimize to Mealy only if timing or area constraints require it. Many production designs use hybrid approaches.

FSM Architecture Diagrams

Moore Machine Architecture: Outputs depend only on current state

┌─────────────────────────────────────────────┐ │ │ │ Inputs ─→ ┌──────────────────┐ Outputs │ │ │ Next State │ ↑ │ │ │ Logic (Comb.) │ │ │ │ └──────────────────┘ │ │ │ ↓ │ │ │ ┌──────────────────┐ │ │ │ ┌─→│ State Register │─────┘ │ │ │ │ (DFF Array) │ │ │ │ └──────────────────┘ │ │ │ │ │ │ │ ┌────────┘ │ │ │ ↓ │ │ └──── Output Logic (Comb.) │ │ [depends on state only] │ └─────────────────────────────────────────────┘

Mealy Machine Architecture: Outputs depend on state AND inputs

┌──────────────────────────────────────────────┐ │ Inputs ──┐ │ │ │ ┌──────────────────┐ Outputs │ │ ├→ │ Output Logic │ ↑ │ │ │ │ (Combinational) │ │ │ │ │ └──────────────────┘ │ │ │ │ │ │ │ │ ┌──────────────────┐ │ │ │ ├→ │ Next State Logic │ │ │ │ │ │ (Combinational) │─────┴─────┤ │ │ └──────────────────┘ │ │ │ ↓ │ │ │ ┌──────────────────┐ │ │ └─→│ State Register │──┐ │ │ │ (DFF Array) │ │ │ │ └──────────────────┘ │ │ │ ↑ │ │ │ └────────────┘ │ └──────────────────────────────────────────────┘

State Encoding Strategies

State encoding determines how states are represented in binary. Different encodings have dramatic impacts on area, timing, and power. Choosing the right encoding is a critical design decision.

Binary Encoding

States are represented as sequential binary values. For N states, requires ⌈log₂(N)⌉ bits.

Binary Encoding Example (3 States) // Three states, 2 bits required (log2(3) = 2) parameter [1:0] IDLE = 2'b00; parameter [1:0] BUSY = 2'b01; parameter [1:0] DONE = 2'b10; // NOT used: 2'b11 (unused state)

Advantages:

  • Minimum number of flip-flops (best area efficiency)
  • Good for very simple FSMs (≤8 states)
  • Minimal decoder logic for some transitions

Disadvantages:

  • Complex next-state logic (many don't-cares in truth table)
  • Difficult to optimize; synthesis tool must find minimal logic
  • Unused states can be entered if bugs exist (no error detection)
  • Timing paths often deep (many logic gates)

One-Hot Encoding

Each state is represented by a single bit set to 1. For N states, requires exactly N bits (one per state). All other bits are 0.

One-Hot Encoding Example (3 States) // Three states, 3 bits required (one bit per state) parameter [2:0] IDLE = 3'b001; // Bit 0 = 1 parameter [2:0] BUSY = 3'b010; // Bit 1 = 1 parameter [2:0] DONE = 3'b100; // Bit 2 = 1 // Unused: 3'b000, 3'b011, 3'b101, 3'b110, 3'b111 (five invalid codes)

Advantages:

  • Very simple next-state logic (often just MUXes and ORs)
  • Fast — minimal combinational depth per transition
  • Easy to manually decode (no decoder needed)
  • Unused states easily detected (error checking built-in)
  • Synthesis tools handle one-hot efficiently

Disadvantages:

  • More flip-flops (one per state; N instead of ⌈log₂(N)⌉)
  • Increased area for large state machines (>16 states)
  • Higher power (more flip-flops switching)
  • Unused state space can lead to complex next-state logic to prevent invalid states

Industry Standard: One-hot encoding is preferred for FSMs with <32 states in ASIC and FPGA design. The timing and simplicity gains outweigh the area cost for most applications.

Gray Code Encoding

Gray code (reflected binary) is a binary encoding where adjacent values differ by only one bit. Useful for avoiding glitches in certain transitions.

Gray Code Example // Binary vs Gray for 4 states // Index Binary Gray // 0 00 00 // 1 01 01 // 2 10 11 // 3 11 10 parameter [1:0] IDLE = 2'b00; // Gray 00 parameter [1:0] BUSY = 2'b01; // Gray 01 parameter [1:0] WAIT = 2'b11; // Gray 11 parameter [1:0] DONE = 2'b10; // Gray 10

When to use Gray code:

  • Crossing clock domains (Gray pointers in async FIFOs)
  • Preventing glitches in sensitive output logic
  • Rarely used for main FSM state encoding (one-hot is simpler)

State Encoding Decision Tree

  • <8 states, simple logic: Try binary first, benchmark
  • <32 states, any complexity: Use one-hot (fast, simple)
  • >32 states: Consider binary or hybrid; one-hot becomes area-heavy
  • Clock domain crossing: Use Gray code for pointers
  • Default for production: One-hot; let synthesis optimize if needed

FSM Coding Patterns in Verilog

There are two main approaches to coding FSMs in Verilog: single always block and three always block pattern. Each has tradeoffs in readability, maintainability, and synthesis efficiency.

Single Always Block Pattern

All logic (next state, outputs, state register) in one always block. Simpler but less readable for complex FSMs.

Single Always Block FSM module simple_fsm ( input wire clk, input wire reset_n, input wire go, output reg done ); // State declarations (one-hot) parameter [2:0] IDLE = 3'b001; parameter [2:0] BUSY = 3'b010; parameter [2:0] DONE_ST = 3'b100; reg [2:0] state, next_state; always @(*) begin // Default outputs done = 1'b0; next_state = state; // Default: stay in current state case (state) IDLE : begin if (go) next_state = BUSY; end BUSY : begin next_state = DONE_ST; // Unconditional transition end DONE_ST : begin done = 1'b1; // Output in this state next_state = IDLE; end default : next_state = IDLE; endcase end // State register (sequential) always @(posedge clk or negedge reset_n) begin if (!reset_n) state <= IDLE; else state <= next_state; end endmodule

Drawback: Combinational and sequential logic mixed; harder to optimize.

Three Always Block Pattern (Recommended)

Separates next-state logic, output logic, and state register into three always blocks. Clean, readable, easier for synthesis tools to optimize.

Three Always Block FSM (Recommended) module smart_fsm ( input wire clk, input wire reset_n, input wire go, output wire done ); // State declarations (one-hot) parameter [2:0] IDLE = 3'b001; parameter [2:0] BUSY = 3'b010; parameter [2:0] DONE_ST = 3'b100; reg [2:0] state, next_state; // ───────────────────────────────────────── // Block 1: Next State Logic (Combinational) // ───────────────────────────────────────── always @(*) begin next_state = state; // Default: hold state case (state) IDLE : begin if (go) next_state = BUSY; end BUSY : begin next_state = DONE_ST; end DONE_ST : begin next_state = IDLE; end default : next_state = IDLE; endcase end // ───────────────────────────────────────── // Block 2: State Register (Sequential) // ───────────────────────────────────────── always @(posedge clk or negedge reset_n) begin if (!reset_n) state <= IDLE; else state <= next_state; end // ───────────────────────────────────────── // Block 3: Output Logic (Combinational) // ───────────────────────────────────────── assign done = (state == DONE_ST); endmodule

Advantages:

  • Readable: Each block has a clear purpose
  • Synthesis-friendly: Tools can independently optimize each block
  • Maintainable: Easy to modify states or add outputs
  • Debuggable: State values visible in waveforms
  • Testable: Can monitor state progression in simulation

Moore vs Mealy Implementation

Moore Output Logic: Output depends on state only

Moore: Output Logic // Block 3a: Moore Output (combinational, depends on state only) always @(*) begin case (state) IDLE : led = 3'b001; // Red BUSY : led = 3'b010; // Green DONE_ST : led = 3'b100; // Blue default : led = 3'b000; endcase end

Mealy Output Logic: Output depends on state AND inputs

Mealy: Output Logic // Block 3b: Mealy Output (combinational, depends on state & inputs) always @(*) begin case (state) IDLE : begin if (go) led = 3'b010; // Green when transitioning to BUSY else led = 3'b001; // Red while IDLE end BUSY : begin if (error) led = 3'b000; // Black on error else led = 3'b010; // Green end default : led = 3'b000; endcase end

⚠️ Mealy Hazard: Mealy outputs can glitch if inputs change while in state. For glitch-free outputs, register Mealy outputs with an extra flip-flop or use Moore logic.

FSM Reset & Initialization

Proper reset strategy ensures FSMs enter a safe, known state during power-up or system reset. Poor reset design leads to metastability, invalid states, and unpredictable behavior.

Asynchronous vs Synchronous Reset

Aspect Asynchronous Reset Synchronous Reset
Trigger Independent of clock (reset_n) Synchronized with clock (reset @ posedge clk)
Timing Takes effect immediately Takes effect on next clock edge
Synchronous Design Violates synchronous design principles Strictly synchronous
Metastability Risk High (reset can release near clock edge) Low (synchronous)
Silicon Area Smaller (one control signal per flip-flop) Larger (reset must be synchronized)
Power-Up Essential (system may not be clocking) Cannot initialize (no clock at power-up)
Typical Use Power-on reset (POR) for initial setup Mid-operation reset or test mode

Industry Practice: Use asynchronous reset for initialization (synchronized externally by design team) and optional synchronous reset for mid-operation scenarios. Never mix unsynchronized asynchronous reset directly on flip-flops during operation — use a synchronizer circuit.

Safe Reset Implementation

FSM with Proper Asynchronous Reset module fsm_with_reset ( input wire clk, input wire reset_n, // Active-low, asynchronous input wire trigger, output reg output_sig ); parameter [1:0] IDLE = 2'b00; parameter [1:0] BUSY = 2'b01; parameter [1:0] DONE = 2'b10; reg [1:0] state, next_state; // Next state logic always @(*) begin next_state = state; case (state) IDLE : if (trigger) next_state = BUSY; BUSY : next_state = DONE; DONE : next_state = IDLE; endcase end // State register with asynchronous reset // ⚠️ IMPORTANT: reset_n in sensitivity list (asynchronous) always @(posedge clk or negedge reset_n) begin if (!reset_n) // Reset: return to known state state <= IDLE; else // Normal operation state <= next_state; end // Output logic always @(*) begin case (state) IDLE : output_sig = 1'b0; BUSY : output_sig = 1'b1; DONE : output_sig = 1'b1; endcase end endmodule

Reset best practices:

  • Use asynchronous reset for ALL state flip-flops
  • Include or negedge reset_n in sensitivity list
  • Reset to IDLE or safe state (usually all-zeros)
  • Avoid resetting to non-zero unless necessary
  • Let external synchronizer handle reset timing

Detecting Invalid States

For robustness, add logic to detect entry into undefined states and recover:

Invalid State Detection // At top level or in testbench always @(posedge clk) begin // For one-hot encoding, state should have exactly one bit set // Count number of '1' bits in state if (state != IDLE && state != BUSY && state != DONE) begin $warning("FSM entered invalid state: %b", state); // Force return to IDLE on next clock (or raise error) end end

In production, use formal verification tools (not simulation) to prove FSM cannot enter invalid states.

FSM Synthesis & Timing Optimization

FSM synthesis is one of the most optimized areas in EDA tools, but understanding the process helps you write better code and achieve timing closure more easily.

State Assignment Impact on Timing

The binary encoding affects the complexity of next-state logic, which directly impacts timing:

Binary Encoding: Tight packing of states often produces deep logic trees for some transitions

// Binary: 3 states in 2 bits IDLE = 2'b00 BUSY = 2'b01 DONE = 2'b10 // Next state logic may require many gates: // next_state[0] = (state == IDLE & go) | (state == DONE & ~hold) // Requires multiple gates and muxes

One-Hot Encoding: More flip-flops, but simpler next-state logic

// One-hot: 3 states in 3 bits IDLE = 3'b001 BUSY = 3'b010 DONE = 3'b100 // Next state logic is much simpler: // next_state[BUSY] = current_state[IDLE] & go // next_state[DONE] = current_state[BUSY] // Mostly just muxes and simple gates

Synthesis Tool Guidance: Modern tools prefer one-hot for up to 16-32 states. If timing fails with binary, switch to one-hot before manual optimization.

Timing-Driven Techniques

1. Pipelining FSM Outputs — If output logic is on critical path:

// Instead of direct combinational output // assign output = (state == DONE_ST); // Register the output always @(posedge clk) begin output <= (next_state == DONE_ST); end

2. Early Exit Signals — If you know transition before clock edge:

// Combinational signal for next cycle wire will_be_idle_next = (state == BUSY) && condition; // Use this in downstream logic that can tolerate one-cycle latency

3. Parallel State Encoders — For high-frequency designs:

// Maintain multiple state encodings in parallel wire [7:0] state_binary, state_onehot; // Binary for compact logic, one-hot for fast decoding // Choose which to use based on timing

Avoiding Synthesis Pitfalls

Incomplete Case Statements: Can infer latches or unexpected logic

❌ Bad: Incomplete Case always @(*) begin case (state) IDLE : next_state = go ? BUSY : IDLE; BUSY : next_state = DONE; // Missing: DONE case — synthesizer may infer latch! endcase end

✓ Good: Complete Case

always @(*) begin next_state = state; // Default case (state) IDLE : next_state = go ? BUSY : IDLE; BUSY : next_state = DONE; DONE : next_state = IDLE; default : next_state = IDLE; endcase end

Complete Production-Grade FSM Example

A real-world example: a simple protocol controller (e.g., for SPI, I2C handshake) demonstrating all best practices.

Complete FSM: Protocol Handshake Controller module protocol_fsm ( input wire clk, input wire reset_n, input wire start, input wire data_ready, input wire ack, output wire transmit, output wire receive, output wire done ); // One-hot state encoding parameter [3:0] IDLE = 4'b0001; parameter [3:0] REQ_DATA = 4'b0010; parameter [3:0] WAIT_ACK = 4'b0100; parameter [3:0] COMPLETE = 4'b1000; reg [3:0] state, next_state; // ───────────────────────────────────────────────────── // BLOCK 1: Next State Logic (Combinational) // ───────────────────────────────────────────────────── always @(*) begin next_state = state; // Default: hold state case (state) IDLE : begin if (start) next_state = REQ_DATA; end REQ_DATA : begin if (data_ready) next_state = WAIT_ACK; end WAIT_ACK : begin if (ack) next_state = COMPLETE; end COMPLETE : begin next_state = IDLE; // Always return to IDLE end default : next_state = IDLE; endcase end // ───────────────────────────────────────────────────── // BLOCK 2: State Register (Sequential) // ───────────────────────────────────────────────────── always @(posedge clk or negedge reset_n) begin if (!reset_n) state <= IDLE; else state <= next_state; end // ───────────────────────────────────────────────────── // BLOCK 3: Output Logic (Combinational / Moore) // ───────────────────────────────────────────────────── assign transmit = (state == REQ_DATA); // TX pulse assign receive = (state == WAIT_ACK); // RX pulse assign done = (state == COMPLETE); // Completion flag endmodule

Design highlights:

Common Pitfalls & Best Practices

Learn from the mistakes of others and design FSMs that synthesize efficiently, close timing, and are easy to verify.

⚠️
Pitfall: Unused States

Problem: Binary encoding leaves unused state codes. If entered due to bugs, FSM gets stuck or behaves unpredictably.

Solution: Use one-hot (only valid states representable) or add explicit guards to return to IDLE from invalid states.

⚠️
Pitfall: Incomplete Sensitivity List

Problem: Missing signals in @(*) causes simulation-synthesis mismatch.

Solution: Always use @(*) for combinational logic — it's equivalent to listing all inputs.

⚠️
Pitfall: Mealy Glitches

Problem: Mealy outputs glitch when inputs change asynchronously to state transitions.

Solution: Use Moore logic or register Mealy outputs. For glitch-free outputs, Moore is always preferred.

⚠️
Pitfall: Non-Deterministic Behavior

Problem: FSM stuck in state if input conditions never met. No escape path to IDLE.

Solution: Design FSMs with guaranteed escapes. Use watchdog timers or always include timeout paths.

Best: Defensive Reset

Include explicit default cases that return to IDLE. Use asynchronous reset on all flip-flops. Test reset path thoroughly.

Best: Documentation

Include state diagrams in comments. State names must be descriptive. Document transition conditions. Version control the FSM design document alongside RTL.

Taking FSM Design Further

Now that you understand FSM fundamentals, explore advanced topics that appear in production designs.

Topics to Explore Next

Recommended Learning Path

  • Hierarchical FSMs: FSMs controlling other FSMs; state explosion solutions
  • Formal Verification: Prove FSM properties using model checking; catch bugs pre-synthesis
  • Clock Domain Crossing: Safe FSM state crossing between clock domains
  • Low-Power FSMs: Clock gating, power domains, sleep states
  • Parameterized FSMs: Generate FSMs of different sizes with SystemVerilog
  • Hybrid Encodings: Combine binary and one-hot for area-timing tradeoffs

Synthesis Tool Directives

Most synthesis tools support pragmas to hint FSM optimization:

// Synopsys Synthesis Pragmas (examples) /* synopsys enum_state current_state */ // Hint: current_state is an FSM /* synopsys encoding_one_hot */ // Force one-hot encoding /* synopsys safe_fsm */ // Enable FSM safety checks

Consult your synthesis tool documentation for exact syntax. Modern tools are smart enough to recognize FSMs automatically.