---
title: FPGA有限状态机（FSM）设计实践指南：三段式与二段式编码风格对比与实现
url: "https://www.fpgalearn.com/31378.html"
kind: article
id: 31378
excerpt: 有限状态机（FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态、并依据特定输入条件进行状态转移的系统。在FPGA设计中，FSM的编码风格直接决定了设计的可…
published_at: 2026-04-11
author: 二牛学FPGA
tags: fpga, FSM, 有限状态机
word_count: 4615
source: "https://www.fpgalearn.com"
---

# FPGA有限状态机（FSM）设计实践指南：三段式与二段式编码风格对比与实现

> 标签：fpga、FSM、有限状态机
> 有限状态机（FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态、并依据特定输入条件进行状态转移的系统。在FPGA设计中，FSM的编码风格直接决定了设计的可…

> 有限状态机（FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态、并依据特定输入条件进行状态转移的系统。在FPGA设计中，FSM的编码风格直接决定了设计的可…

有限状态机（FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态、并依据特定输入条件进行状态转移的系统。在FPGA设计中，FSM的编码风格直接决定了设计的可读性、可维护性、时序性能以及最终的综合实现结果。本指南将系统性地对比业界广泛采用的“三段式”与“二段式”FSM编码风格，提供从快速上手到深入原理的完整实践路径，并明确各自的适用场景与边界条件。
### 快速概览
三段式FSM将逻辑清晰地划分为状态寄存器、次态逻辑和输出逻辑三个部分，结构规整，时序路径明确。二段式FSM则将状态寄存器与次态逻辑（有时包含输出逻辑）合并，代码更紧凑，但对时序收敛要求更高。对于绝大多数FPGA设计，推荐优先采用三段式风格。
### 前置条件与环境
- **硬件平台**：支持任意主流FPGA开发板（如Xilinx Artix-7系列或Intel Cyclone IV系列）。
- **软件工具**：Vivado（Xilinx）或 Quartus Prime（Intel）任一版本，用于综合、实现与仿真。
- **设计语言**：Verilog HDL 或 VHDL，本指南以Verilog为例进行说明。
- **基础知识**：熟悉同步数字电路设计概念、时钟与复位、基本的HDL语法。
### 目标与验收标准
完成本指南的实践后，您将能够：
- 理解三段式与二段式FSM的代码结构与核心差异。
- 独立使用两种风格实现功能相同的状态机模块。
- 通过综合报告，对比分析两种风格在时序性能（最大时钟频率）和资源消耗（LUTs、FFs）上的差异。
- 根据设计需求，合理选择并应用合适的FSM编码风格。
### 实施步骤
#### 步骤一：工程结构与模块定义
为公平对比，我们为两种风格创建接口完全一致的顶层模块。以一个简单的“序列检测器”（检测输入序列“1101”）为例，定义模块接口如下：
```
module seq_detector_3style (
    input wire clk,
    input wire rst_n,
    input wire data_in,
    output reg det_out
);
// 三段式FSM实现将放置于此
endmodule

module seq_detector_2style (
    input wire clk,
    input wire rst_n,
    input wire data_in,
    output reg det_out
);
// 二段式FSM实现将放置于此
endmodule
```
#### 步骤二：三段式FSM实现
三段式风格是推荐的FPGA FSM编码范式，它将逻辑清晰地划分为三个部分，严格遵循同步设计原则。
1. **第一段：状态寄存器（时序逻辑）**
使用同步时序逻辑描述当前状态的存储与更新。这是所有FSM的“记忆”单元。
1. **第二段：次态逻辑（组合逻辑）**
使用纯组合逻辑，根据当前状态和输入条件，计算出下一个时钟周期应跳转到的状态（次态）。
1. **第三段：输出逻辑（组合或时序）**
根据当前状态（摩尔型输出）或当前状态与输入（米利型输出）产生输出信号。为消除毛刺和提高时序，通常建议对输出进行寄存器打拍（即用时序逻辑实现）。
**核心机制分析**：这种分离使得综合工具能清晰识别时序路径的起点（寄存器）和终点（寄存器）。状态转移路径（当前状态 → 次态逻辑 → 状态寄存器）构成一条标准时序路径，便于工具优化以满足时钟约束。输出路径若同样经过寄存器，则输出稳定，无组合逻辑毛刺。
```
// 三段式示例片段（状态定义与第一段）
parameter S_IDLE = 3'd0, S1 = 3'd1, S11 = 3'd2, S110 = 3'd3, S1101 = 3'd4;
reg [2:0] current_state, next_state;

// 第一段：状态寄存器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        current_state <= S_IDLE;
    else
        current_state <= next_state;
end

// 第二段：次态逻辑（组合）
always @(*) begin
    next_state = current_state; // 默认保持当前状态
    case (current_state)
        S_IDLE: next_state = (data_in == 1‘b1) ? S1 : S_IDLE;
        S1:     next_state = (data_in == 1‘b1) ? S11 : S_IDLE;
        // ... 其他状态转移
        default: next_state = S_IDLE;
    endcase
end

// 第三段：输出逻辑（时序，寄存输出）
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        det_out <= 1'b0;
    else begin
        case (current_state)
            S1101: det_out <= 1'b1;
            default: det_out <= 1'b0;
        endcase
    end
end
```
#### 步骤三：二段式FSM实现
二段式风格将上述的第一段（状态寄存器）与第二段（次态逻辑）或第三段（输出逻辑）进行合并，代码行数可能减少，但结构混合。
1. **第一段：合并的时序逻辑**
通常在一个`always @(posedge clk)`块中，既完成状态寄存器的更新，又可能根据某些条件直接产生寄存型的输出。次态计算所需的组合逻辑有时会内嵌在此过程中。
1. **第二段：组合逻辑**
描述剩余的状态转移条件或输出逻辑。有时，整个状态转移和输出都放在组合逻辑块中，而时序块仅负责寄存。
**核心机制与风险**：二段式风格模糊了纯组合的次态计算路径。如果次态逻辑复杂且被置于时序块中通过if-else语句实现，其关键路径可能始于上一个寄存器，经过复杂的多级选择器，再进入当前的状态寄存器，这条路径可能变得冗长，限制`fmax`。逻辑的混合也降低了代码的可读性和可维护性。
```
// 二段式示例片段（一种常见变体）
reg [2:0] state;
// 第一段：时序逻辑，包含状态转移判断
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= S_IDLE;
        det_out <= 1'b0;
    end else begin
        case (state) // 状态转移逻辑内嵌在时序块中
            S_IDLE: begin
                det_out <= 1'b0;
                if (data_in) state <= S1;
            end
            S1: begin
                det_out <= 1'b0;
                if (data_in) state <= S11; else state <= S_IDLE;
            end
            // ... 其他状态，输出可能随状态直接赋值
            S1101: begin
                det_out <= 1'b1; // 输出可能产生毛刺，因为依赖于异步的state变化？
                state <= S_IDLE; // 检测到后回到空闲
            end
            default: state <= S_IDLE;
        endcase
    end
end
// 第二段可能为空，或包含一些额外的组合输出逻辑
```
### 验证与结果分析
将上述两种实现分别进行综合与布局布线（Implementation），重点关注以下报告指标：
| 对比项 | 三段式FSM | 二段式FSM | 分析与说明 |
| --- | --- | --- | --- |
| **最大时钟频率 (fmax)** | 通常更高 | 可能较低 | 三段式时序路径清晰，工具优化效率高。二段式内嵌组合逻辑可能增加关键路径延迟。 |
| **触发器 (FFs) 使用量** | 可能稍多 | 可能稍少 | 三段式对输出进行寄存会多用FFs，但这换来了无毛刺的稳定输出。二段式若输出为组合逻辑，则节省FFs。 |
| **查找表 (LUTs) 使用量** | 与二段式相当或略优 | 与三段式相当 | 两者逻辑功能相同，综合工具优化后，组合逻辑资源消耗差异通常不大。 |
| **输出信号稳定性** | 高（寄存输出） | 可能较低（组合输出） | 三段式的寄存输出消除了毛刺。二段式若采用组合输出，在状态或输入变化时易产生毛刺。 |
| **代码可读性与维护性** | 高 | 较低 | 三段式结构如同文档，状态、转移、输出一目了然。二段式逻辑交织，修改时易出错。 |
### 常见问题与故障排查
- **锁存器（Latch）推断**：在组合逻辑块（`always @(*)`）中，必须为所有可能的输入分支分配输出值，否则会生成不希望的锁存器。确保`case`语句有`default`分支，`if-else`语句完整。
- **输出毛刺**：若输出信号由组合逻辑产生（米利型输出或二段式的组合输出），当输入或状态变化不同步时会产生毛刺。对时序要求高的输出，务必使用寄存器打拍（变为摩尔型输出）。
- **时序违例**：表现为建立时间（Setup Time）或保持时间（Hold Time）违例。检查状态转移路径是否过长。对于三段式，优化次态组合逻辑；对于二段式，考虑将其重构为三段式。使用流水线或输出寄存是改善时序的有效手段。
- **仿真与综合行为不一致**：通常由于使用了不可综合的仿真语句（如`#delay`、`initial`中对寄存器的非复位赋值）或对异步信号处理不当导致。确保设计代码与验证代码分离，并对所有异步输入进行同步处理。
- **状态机卡死或跑飞**：首先检查复位逻辑是否正确生效。其次，使用仿真工具捕获状态转移波形，验证每个转移条件是否与设计意图一致。检查是否遗漏了某些状态或输入条件。
### 设计选择与边界建议
**优先选择三段式FSM**：在绝大多数FPGA项目中，应优先采用三段式风格。其带来的时序性能优势、设计可靠性和团队协作效率的提升，远超过可能增加的微量寄存器资源。它符合综合工具的预期，是稳健设计的基础。
**谨慎考虑二段式FSM的边界**：仅在同时满足以下条件时，方可考虑使用二段式风格：1）状态机极其简单（状态数少于4个，转移条件简单）；2）设计对芯片面积（特别是触发器资源）极度敏感，且经过验证，二段式能带来显著的资源节省；3）对输出毛刺不敏感，或已通过其他方式抑制；4）设计者能完全掌控其时序影响，并确认能满足时序约束。
**扩展应用：安全状态机设计**：对于高可靠性设计，可在三段式基础上增加“看门狗”或“安全状态”。例如，在状态转移的`default`分支中，不仅跳转到空闲态，还可以触发一个错误计数寄存器，当异常转移超过阈值时，强制状态机进入一个已知的安全恢复状态。
### 参考与附录
- **参考代码**：可在提供的示例工程中找到完整的、带有测试平台的序列检测器三段式与二段式实现。
- **综合工具报告解读**：关注“Timing Summary”中的WNS（最差负时序余量）和“Utilization Report”中的FF和LUT利用率。
- **深入阅读**：IEEE Standard for Verilog Hardware Description Language (IEEE Std 1364)。《FPGA权威指南》、《数字设计：原理与实践》中关于状态机设计的章节。
- **附录：状态编码风格**：除编码风格外，状态本身的编码（二进制、格雷码、独热码）也影响性能和资源。在FPGA中，对于状态数不多的情况（如本示例），独热码（One-hot）因占用更多触发器但简化了组合逻辑，常能带来更好的时序性能，值得在实践中尝试对比。
