0
0
VerilogHow-ToBeginner · 4 min read

How to Design FSM in Verilog: Simple Guide with Example

To design a FSM in Verilog, define states using parameter or typedef enum, create registers for current and next states, and use an always block to update states on clock edges. Use a combinational block to decide the next state based on inputs and current state.
📐

Syntax

An FSM in Verilog typically has three parts:

  • State definition: Use parameter or typedef enum to name states.
  • State registers: Hold the current and next state.
  • State transition logic: Use always @(posedge clk) to update current state and a combinational always @(*) block to decide next state.
verilog
typedef enum logic [1:0] {
    IDLE = 2'b00,
    STATE1 = 2'b01,
    STATE2 = 2'b10
} state_t;

state_t current_state, next_state;

// State update
always @(posedge clk or posedge reset) begin
    if (reset)
        current_state <= IDLE;
    else
        current_state <= next_state;
end

// Next state logic
always @(*) begin
    case (current_state)
        IDLE: next_state = (start) ? STATE1 : IDLE;
        STATE1: next_state = (cond) ? STATE2 : IDLE;
        STATE2: next_state = IDLE;
        default: next_state = IDLE;
    endcase
end
💻

Example

This example shows a simple FSM with three states: IDLE, STATE1, and STATE2. It transitions based on input signals start and cond. The FSM resets to IDLE on reset.

verilog
module simple_fsm(
    input logic clk,
    input logic reset,
    input logic start,
    input logic cond,
    output logic [1:0] state_out
);

    typedef enum logic [1:0] {
        IDLE = 2'b00,
        STATE1 = 2'b01,
        STATE2 = 2'b10
    } state_t;

    state_t current_state, next_state;

    // State update
    always_ff @(posedge clk or posedge reset) begin
        if (reset)
            current_state <= IDLE;
        else
            current_state <= next_state;
    end

    // Next state logic
    always_comb begin
        case (current_state)
            IDLE: next_state = (start) ? STATE1 : IDLE;
            STATE1: next_state = (cond) ? STATE2 : IDLE;
            STATE2: next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    assign state_out = current_state;

endmodule
⚠️

Common Pitfalls

  • Missing reset logic: Without reset, FSM may start in unknown state.
  • Incorrect sensitivity list: Use always_ff @(posedge clk or posedge reset) for state updates and always_comb for next state logic to avoid latches.
  • Not covering all states in case: Always include a default case to avoid inferred latches.
  • Mixing blocking and non-blocking assignments: Use non-blocking (<=) in sequential blocks and blocking (=) in combinational blocks.
verilog
/* Wrong: Missing reset and blocking assignment in sequential block */
always @(posedge clk) begin
    current_state <= next_state; // changed to non-blocking assignment
end

/* Correct: Use non-blocking and reset */
always_ff @(posedge clk or posedge reset) begin
    if (reset)
        current_state <= IDLE;
    else
        current_state <= next_state;
end
📊

Quick Reference

ConceptDescription
State DefinitionUse parameter or typedef enum to name states clearly.
State RegistersUse registers to hold current and next state.
Sequential BlockUpdate current state on clock edge with reset.
Combinational BlockDecide next state based on inputs and current state.
ResetAlways include reset to initialize FSM state.
Default CaseInclude default in case statements to avoid latches.

Key Takeaways

Define states clearly using typedef enum or parameters for readability.
Use non-blocking assignments in sequential always blocks for state updates.
Always include reset logic to initialize the FSM safely.
Use combinational always blocks for next state logic with full case coverage.
Avoid mixing blocking and non-blocking assignments to prevent simulation mismatches.