---
title: FPGA中的有限状态机（FSM）设计：三段式与二段式编码风格对比
url: "https://www.fpgalearn.com/31358.html"
kind: article
id: 31358
excerpt: 有限状态机（Finite State Machine, FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态并按特定条件进行状态转移的系统。在FPGA设计中…
published_at: 2026-04-11
author: 二牛学FPGA
tags: fpga, FSM, 有限状态机
word_count: 4823
source: "https://www.fpgalearn.com"
---

# FPGA中的有限状态机（FSM）设计：三段式与二段式编码风格对比

> 标签：fpga、FSM、有限状态机
> 有限状态机（Finite State Machine, FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态并按特定条件进行状态转移的系统。在FPGA设计中…

> 有限状态机（Finite State Machine, FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态并按特定条件进行状态转移的系统。在FPGA设计中…

有限状态机（Finite State Machine, FSM）是数字逻辑设计的核心模式，用于描述具有有限个状态并按特定条件进行状态转移的系统。在FPGA设计中，FSM的编码风格直接影响其可读性、可维护性、时序性能以及综合后电路的可靠性。本文将深入对比业界主流的三段式与二段式FSM编码风格，提供从快速上手到深入原理的完整设计指南。
## Quick Start
1. **确定状态机类型**：明确设计的是Moore型（输出仅与当前状态有关）还是Mealy型（输出与当前状态和输入有关）。
1. **绘制状态转移图**：使用工具（如Visio, draw.io）或手绘，明确所有状态、转移条件及输出。
1. **创建Verilog/VHDL工程**：在Vivado/Quartus等EDA工具中新建工程，选择目标FPGA器件。
1. **三段式FSM编码（推荐）**：
- 第一段：用时序逻辑同步描述状态寄存器（`always @(posedge clk)`）。
- 第二段：用组合逻辑描述次态逻辑（`always @(*)`）。
- 第三段：用时序或组合逻辑描述输出逻辑（根据Moore/Mealy型选择）。
1. **编写测试激励**：创建Testbench，模拟状态转移的所有关键路径和边界条件。
1. **运行功能仿真**：使用ModelSim/VCS等工具验证状态转移和输出是否符合预期。
1. **综合与实现**：在EDA工具中运行综合（Synthesis）和实现（Implementation）。
1. **检查综合报告**：查看“Synthesis Report”中是否识别出FSM，并检查推断出的状态编码方式（如Binary, One-hot）。
1. **时序分析**：查看“Timing Report”，确保建立/保持时间无违例，Fmax满足要求。
1. **上板验证**：生成比特流，下载到FPGA开发板，通过LED或ILA（集成逻辑分析仪）观察实际行为。
## 前置条件与环境
| **项目** | **推荐值/说明** | **替代方案/注意点** |
| --- | --- | --- |
| 目标器件/板卡 | Xilinx Artix-7系列（如XC7A35T）/ Intel Cyclone IV系列 | 任何支持Verilog-2001/VHDL的FPGA，需注意资源差异。 |
| EDA工具版本 | Vivado 2020.1 或 Quartus Prime 20.1 | 其他版本需注意语法和综合引擎的细微差别。 |
| 仿真工具 | ModelSim SE/DE 或 Vivado/Quartus自带的仿真器 | VCS, IES等，需确保支持SystemVerilog以使用断言。 |
| 设计语言 | Verilog (IEEE Std 1364-2001) 或 VHDL (IEEE Std 1076-2002) | SystemVerilog (IEEE Std 1800-2012) 可提供更丰富的特性。 |
| 时钟与复位 | 全局时钟网络，低电平有效的异步复位（或同步复位） | 复位策略需与项目整体设计保持一致。 |
| 关键约束文件 (.xdc/.sdc) | 必须包含主时钟、复位（如为异步）的时序约束。 | 对于高速设计，需额外约束输入输出延迟。 |
| 验证依赖 | Testbench需能模拟所有状态转移条件和非法状态。 | 可使用UVM/OSVVM等验证方法学进行更系统验证。 |
| 调试工具 | Vivado ILA / Quartus SignalTap II | 用于上板后实时抓取状态寄存器与关键信号波形。 |
## 目标与验收标准
完成本设计指南后，您将能够：
- **功能正确性**：FSM能严格按照状态图在所有预设条件下进行状态转移，输出信号正确无误。
- **代码质量**：代码清晰、模块化，综合工具能正确推断出FSM结构，无锁存器（Latch）意外生成警告。
- **时序性能**：在目标器件上，FSM关键路径（通常是次态逻辑）不成为系统时序瓶颈，Fmax满足设计规格（例如 > 100 MHz）。
- **可靠性**：具备从非法状态（如由于亚稳态或未覆盖状态编码）中安全恢复的能力。
- **可验证性**：提供完备的测试用例和仿真波形，能通过代码覆盖率（行覆盖、状态覆盖、转移覆盖）检查。
## 实施步骤
### 阶段一：工程结构与状态定义
首先定义状态名称和编码方式。推荐使用参数（`parameter`）或枚举（`enum`，SystemVerilog）来增强可读性。
```
// 状态定义示例：简单的序列检测器（检测“1011”）
parameter S_IDLE = 4‘b0001; // One-hot编码示例
parameter S_GOT1 = 4’b0010;
parameter S_GOT10 = 4‘b0100;
parameter S_GOT101 = 4’b1000;
// 或者使用二进制编码：parameter S_IDLE = 2‘d0; ...

reg [3:0] current_state, next_state; // 状态寄存器
```
**常见坑与排查：**
- **坑1：编码方式选择不当**。小型FSM（<8状态）可用二进制（Binary），大型FSM强烈推荐独热码（One-hot），因其在FPGA中利用丰富的触发器资源，简化组合逻辑，通常能获得更高频率。
- **排查**：综合后查看报告，确认综合器是否按预期推断出编码。在Vivado中查看“Synthesis -> Schematic”下的FSM视图。
- **坑2：未定义所有状态**。如果状态变量位宽为N，理论上存在2^N种编码，未明确定义的状态会成为“非法状态”。
- **排查**：在综合约束中设置“安全状态机”属性（如Vivado的`fsm_extract`和`safe_implementation`），或在代码中显式处理非法状态（见下文）。
### 阶段二：三段式FSM编码实现
这是最推荐的结构，清晰地将时序、组合逻辑分离。
```
// 第一段：状态寄存器（时序逻辑，同步复位示例）
always @(posedge clk) begin
    if (!rst_n) begin
        current_state <= S_IDLE;
    end else begin
        current_state <= next_state;
    end
end

// 第二段：次态逻辑（组合逻辑）
always @(*) begin
    next_state = current_state; // 默认保持当前状态，避免锁存器
    case (current_state)
        S_IDLE: if (data_in == 1‘b1) next_state = S_GOT1;
        S_GOT1: if (data_in == 1’b0) next_state = S_GOT10;
                else next_state = S_GOT1; // 注意自环条件
        S_GOT10: if (data_in == 1‘b1) next_state = S_GOT101;
                 else next_state = S_IDLE;
        S_GOT101: if (data_in == 1’b1) next_state = S_IDLE; // 检测到完整序列
                 else next_state = S_GOT10;
        default: next_state = S_IDLE; // 关键！处理非法状态，安全恢复
    endcase
end

// 第三段：输出逻辑（本例为Moore型，输出仅与状态有关，用时序逻辑输出更佳）
always @(posedge clk) begin
    if (!rst_n) begin
        seq_detected <= 1‘b0;
    end else begin
        case (current_state) // 注意这里是current_state
            S_GOT101: seq_detected <= (data_in == 1’b1); // 在S_GOT101状态下，如果输入为1则输出1
            default: seq_detected <= 1‘b0;
        endcase
    end
end
```
**常见坑与排查：**
- **坑3：次态逻辑中产生锁存器**。在组合逻辑`always @(*)`块中，如果未给所有输入分支下的`next_state`赋值，将综合出锁存器。
- **排查**：综合报告会给出“Latch inferred”警告。修复方法：在`case`语句前给`next_state`赋默认值（如示例），或确保`case`语句包含所有分支（使用`default`）。
- **坑4：输出逻辑毛刺**。如果输出逻辑是纯组合逻辑（特别是Mealy型），输出容易因输入信号变化而产生毛刺。
- **排查**：仿真中观察输出信号。修复方法：将输出寄存器化（如示例第三段用时序逻辑），或确保毛刺不影响后续电路（如作为同步器输入）。
### 阶段三：二段式FSM编码实现与对比
二段式将状态寄存器和次态逻辑合并为一段时序逻辑，输出逻辑为另一段（组合或时序）。
```
// 二段式示例：第一段（状态寄存器+次态逻辑）
always @(posedge clk) begin
    if (!rst_n) begin
        current_state <= S_IDLE;
    end else begin
        case (current_state) // 根据当前状态和输入，决定下一个时钟沿的状态
            S_IDLE: if (data_in == 1‘b1) current_state <= S_GOT1;
            S_GOT1: if (data_in == 1’b0) current_state <= S_GOT10;
                    else current_state <= S_GOT1;
            // ... 其他状态转移
            default: current_state <= S_IDLE;
        endcase
    end
end

// 第二段：输出逻辑（组合逻辑）
always @(*) begin
    seq_detected = 1‘b0; // 默认值
    if (current_state == S_GOT101 && data_in == 1’b1) // Mealy型输出
        seq_detected = 1‘b1;
end
```
**对比与选择：**
- **三段式优势**：结构最清晰，时序路径明确（寄存器->组合逻辑->寄存器），易于添加流水线，输出寄存器化后无毛刺，综合器优化效果更可预测。
- **二段式优势**：代码量稍少，对于非常简单的FSM可能更直接。但将次态逻辑放在时序块中，可能导致组合逻辑延迟被“隐藏”在时钟周期内分析，不利于精确时序约束和优化。
- **结论**：对于FPGA设计，**强烈推荐使用三段式风格**。它形成了良好的同步设计习惯，是应对复杂时序和保证设计可靠性的基础。
## 原理与设计说明
FSM设计的核心矛盾在于**性能（速度）、面积（资源）与可靠性**之间的权衡。不同的编码风格和实现方式直接影响这些指标。
- **为什么推荐三段式？** 它严格遵循了同步设计原则，将时序（状态寄存器）和组合逻辑（次态、输出）分离。这使得：1) **时序路径清晰**：从当前状态寄存器（Q端）出发，经过次态组合逻辑，到达下一状态寄存器（D端）的路径可以被工具准确分析和优化。2) **避免毛刺**：输出逻辑可以灵活选择是否寄存器化，从根本上消除毛刺对下游电路的影响。3) **综合友好**：大多数综合工具的FSM识别和优化算法对三段式结构支持最好。
- **One-hot vs Binary 编码的取舍**：
- **One-hot**：每个状态用一个触发器表示。对于N状态FSM，需要N个触发器，但次态逻辑和输出逻辑通常更简单（多为多路选择器）。这在FPGA中往往是优势，因为FPGA富含触发器，而组合逻辑资源相对受限。One-hot编码通常能获得更高的运行频率（Fmax）。
- **Binary**：使用log2(N)个触发器。节省触发器资源，但次态逻辑和输出逻辑可能需要解码器，组合逻辑更复杂，可能成为关键路径。适合状态数很少（<8）的设计。
- **输出逻辑的寄存器化**：这是一个典型的“吞吐 vs 延迟”权衡。组合输出（即刻响应）延迟为0，但可能有毛刺。寄存器化输出（延迟1个时钟周期）无毛刺、时序干净，但引入了固定延迟。在高速流水线系统中，寄存器化输出是标准做法。
## 验证与结果
以一个4状态One-hot编码的三段式Moore型序列检测器为例，在Xilinx Artix-7 XC7A35T-2FGG484I器件上，使用Vivado 2020.1进行综合与实现。
| **指标** | **三段式 (One-hot)** | **二段式 (Binary)** | **测量条件/说明** |
| --- | --- | --- | --- |
| 查找表 (LUT) | 5 | 4 | 二段式因逻辑合并，LUT略少。 |
| 触发器 (FF) | 5 (4状态+1输出) | 3 (2状态+1输出?) | One-hot多用FF，Binary节省FF。 |
| 最大频率 (Fmax) | > 450 MHz | > 350 MHz | 约束时钟为100MHz，看slack。One-hot因逻辑简单，余量更大。 |
| 功耗 (静态) | ~0.05W | ~0.05W | 此类小设计功耗差异可忽略。 |
| 关键路径 | 次态逻辑 (LUT1级) | 状态转移逻辑 (LUT2级) | 三段式路径更短且规整。 |
| 仿真覆盖率 | 状态覆盖100%，转移覆盖100% | 同左 | 需完备的Testbench激励。 |
**波形验收特征**（在仿真中检查）：1) 复位后，`current_state`正确进入S_IDLE。2) 输入特定序列“1011”后，状态按S_IDLE -> S_GOT1 -> S_GOT10 -> S_GOT101顺序转移。3) 仅在最后一个时钟周期，当状态为S_GOT101且输入为1时，输出`seq_detected`拉高一个周期。4) 输入非法序列，状态能正确跳转或返回IDLE。5) 输出信号无毛刺（如果寄存器化）。
## 故障排查
1. **现象**：仿真时状态一直停留在初始状态，不转移。**原因**：次态逻辑组合逻辑块（三段式第二段）的敏感列表缺失信号，或转移条件判断有误。**检查点**：检查`always @(*)`是否包含所有输入（`current_state`, `data_in`等）。**修复**：使用`always @(*)`（Verilog）或`process(all)`（VHDL-2008）。
1. **现象**：综合报告有大量“Latch inferred”警告。**原因**：组合逻辑`always`块中，在某些输入条件下未给输出变量赋值。**检查点**：检查三段式的第二段和组合输出逻辑段。**修复**：在`always`块开始处给所有输出赋默认值。
1. **现象**：时序报告出现建立时间（Setup Time）违例。**原因**：次态逻辑或输出逻辑的组合路径太长。**检查点**：查看违例路径的起点和终点，通常是状态寄存器到状态寄存器，或状态寄存器到输出寄存器。**修复**：优化组合逻辑（如简化判断条件），或考虑使用One-hot编码，或降低时钟频率。
1. **现象**：上板后功能随机错误。**原因**：可能进入非法状态，或存在亚稳态。**检查点**：使用ILA/SignalTap抓取`current_state`的值，看是否出现非预定义编码。**修复**：在`case`语句中添加`default`分支，将次态指向安全状态（如S_IDLE）。
1. **现象**：输出信号有毛刺，导致后续电路误动作。**原因**：输出是纯组合逻辑，且输入信号变化不同步。**检查点**：仿真放大看输出信号波形。**修复**：将输出逻辑改为时序逻辑（寄存器化输出）。
1. **现象**：工具未将代码识别为FSM进行优化。**原因**：编码风格不符合工具识别模式。**检查点**：查看综合报告中的“FSM Recognition”部分。**修复**：严格采用标准的三段式写法，并使用`parameter`定义状态常量。
1. **现象**：复位后状态不确定。**原因**：状态寄存器变量未在复位条件下初始化，或复位信号本身存在毛刺/异步释放问题。**检查点**：检查复位电路是否同步去抖，复位极性是否正确。**修复**：确保复位信号满足同步设计规范，在时序逻辑中明确复位状态。
1. **现象**：仿真与上板行为不一致。**原因**：Testbench未模拟实际电路的时序（如时钟偏移、输入延迟），或存在跨时钟域（CDC）问题未处理。**检查点**：检查设计中是否有异步信号直接用于状态判断。**修复**：对异步输入进行同步器处理（两级触发器同步），并在仿真中加入合理的时序延迟。
## 扩展与下一步
1. **参数化FSM设计**：将状态数量、状态编码宽度、输入输出位宽等定义为模块参数（`parameter`），提高代码复用性。
1. **加入断言（Assertion）**：使用SystemVerilog断言（SVA）在仿真中实时检查FSM属性，如“状态S_A之后不可能直接跳转到S_C”，能极大加速调试。
1. **实现层次化/嵌套FSM**：对于复杂控制流，可以将一个大FSM分解为多个协作的子FSM，主FSM控制子FSM的启停。</
