---
title: Verilog 状态机综合后毛刺的仿真与消除实战指南（2026年Q2）
url: "https://www.fpgalearn.com/50610.html"
kind: article
id: 50610
excerpt: Quick Start准备环境：安装 Vivado 2025.2 或更高版本，确保支持 SystemVerilog 与综合后仿真。创建工程：新建 RTL 工程，…
published_at: 2026-06-11
author: FPGA小白
tags: Verilog, 毛刺, 状态机
word_count: 4575
source: "https://www.fpgalearn.com"
---

# Verilog 状态机综合后毛刺的仿真与消除实战指南（2026年Q2）

> 标签：Verilog、毛刺、状态机
> Quick Start准备环境：安装 Vivado 2025.2 或更高版本，确保支持 SystemVerilog 与综合后仿真。创建工程：新建 RTL 工程，…

> Quick Start准备环境：安装 Vivado 2025.2 或更高版本，确保支持 SystemVerilog 与综合后仿真。创建工程：新建 RTL 工程，…

## Quick Start
1. **准备环境**：安装 Vivado 2025.2 或更高版本，确保支持 SystemVerilog 与综合后仿真。
1. **创建工程**：新建 RTL 工程，选择器件 `xc7a35tcsg324-1`（Artix-7 系列）。
1. **编写状态机 RTL**：使用三段式状态机编码，状态编码采用 Gray 码或 One-hot 码。
1. **添加约束**：创建 XDC 文件，约束时钟周期为 10 ns（100 MHz），并设置 false_path 或 multicycle 路径（如适用）。
1. **运行综合**：执行 synthesis，观察综合报告中的状态机推断情况。
1. **运行综合后仿真**：使用 Vivado Simulator 运行综合后时序仿真（post-synth timing simulation）。
1. **观察毛刺**：在波形中检查状态机输出信号是否存在毛刺（glitch），尤其是组合逻辑输出。
1. **消除毛刺**：修改 RTL，将输出改为寄存器输出，或插入同步寄存器链；重新综合并仿真验证。
1. **验收**：毛刺消失，输出波形干净，满足时序约束。
## 前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
| --- | --- | --- | --- |
| 器件/板卡 | Xilinx Artix-7 xc7a35tcsg324-1 | 典型 FPGA，支持综合后仿真 | 其他 7 系列或 UltraScale 器件 |
| EDA 版本 | Vivado 2025.2 | 支持 SystemVerilog 与高级综合后仿真 | Vivado 2024.x 或 Quartus Prime 24.x |
| 仿真器 | Vivado Simulator (xsim) | 内建，支持 SDF 反标 | ModelSim/QuestaSim 2024.3 |
| 时钟/复位 | 100 MHz 时钟，异步低电平复位 | 典型系统时钟，复位用于初始化状态机 | 50 MHz 或 200 MHz 时钟 |
| 接口依赖 | 无外部接口，纯内部状态机 | 便于聚焦毛刺问题 | 可添加 GPIO 输出观察 |
| 约束文件 | XDC 文件：时钟周期 10 ns，输入延迟 2 ns | 必须约束时钟以进行时序仿真 | SDC 格式（Quartus） |
## 目标与验收标准
- **功能点**：状态机正确完成状态跳转，输出符合预期逻辑。
- **性能指标**：综合后时序仿真中，所有输出信号无毛刺（glitch）持续时间超过 100 ps。
- **资源/Fmax**：综合后 Fmax ≥ 100 MHz，资源占用在合理范围（LUT ≤ 50，FF ≤ 30）。
- **验收方式**：在 Vivado 波形窗口中，放大观察输出信号跳变沿，无多余脉冲；日志中无 setup/hold 违例。
## 实施步骤
### 工程结构与 RTL 编写
- 创建工程目录：`src/`（RTL 文件）、`sim/`（仿真脚本）、`constr/`（约束文件）。
- 编写状态机 RTL：使用三段式结构（状态寄存器、次态组合逻辑、输出组合逻辑）。
- 状态编码：使用 Gray 码（如 2’b00, 2’b01, 2’b11, 2’b10）以减少相邻跳变时的毛刺风险。
- 输出策略：所有输出信号采用寄存器输出（在时钟沿赋值），避免组合逻辑直接驱动输出。
```
// fsm_gray.v - 三段式状态机，Gray 编码，寄存器输出
module fsm_gray (
    input wire clk,
    input wire rst_n,
    input wire start,
    output reg [1:0] state_out // 寄存器输出
);

// 状态编码（Gray）
localparam [1:0] IDLE = 2'b00,
                 S1   = 2'b01,
                 S2   = 2'b11,
                 S3   = 2'b10;

reg [1:0] state_reg, state_next;

// 状态寄存器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state_reg <= IDLE;
    else
        state_reg <= state_next;
end

// 次态逻辑
always @(*) begin
    state_next = state_reg; // 默认保持
    case (state_reg)
        IDLE: if (start) state_next = S1;
        S1:   state_next = S2;
        S2:   state_next = S3;
        S3:   state_next = IDLE;
        default: state_next = IDLE;
    endcase
end

// 寄存器输出
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state_out <= IDLE;
    else
        state_out <= state_reg;
end

endmodule
```
### 逐行说明
1. **第 1 行**：注释，说明文件功能：三段式状态机，Gray 编码，寄存器输出。
1. **第 2 行**：模块声明，模块名为 `fsm_gray`。
1. **第 3 行**：输入端口 `clk`，类型 wire，用于驱动状态机时钟。
1. **第 4 行**：输入端口 `rst_n`，类型 wire，异步低电平复位。
1. **第 5 行**：输入端口 `start`，类型 wire，启动信号。
1. **第 6 行**：输出端口 `state_out`，类型 reg [1:0]，寄存器输出，用于观察状态。
1. **第 7 行**：模块声明结束。
1. **第 9 行**：注释，说明状态编码采用 Gray 码。
1. **第 10-13 行**：使用 `localparam` 定义四个状态：IDLE (2’b00)、S1 (2’b01)、S2 (2’b11)、S3 (2’b10)，相邻状态间仅一位变化。
1. **第 15 行**：声明 `state_reg` 和 `state_next`，均为 reg [1:0]，分别用于当前状态寄存器和次态组合逻辑。
1. **第 17 行**：注释，开始状态寄存器逻辑。
1. **第 18 行**：`always` 块，敏感列表为 `posedge clk` 或 `negedge rst_n`，实现异步复位。
1. **第 19 行**：条件判断，若 `rst_n` 为低电平（复位有效），则执行复位。
1. **第 20 行**：复位时，`state_reg` 赋值为 `IDLE`（2’b00）。
1. **第 21 行**：否则（复位无效），执行正常赋值。
1. **第 22 行**：时钟上升沿时，`state_reg` 更新为 `state_next`。
1. **第 23 行**：状态寄存器 `always` 块结束。
1. **第 25 行**：注释，开始次态组合逻辑。
1. **第 26 行**：`always` 块，敏感列表为 `*`（所有信号），组合逻辑。
1. **第 27 行**：默认将 `state_next` 赋值为 `state_reg`，避免锁存器。
1. **第 28 行**：`case` 语句，根据 `state_reg` 决定次态。
1. **第 29 行**：当状态为 `IDLE` 时，若 `start` 为高，则次态为 `S1`，否则保持。
1. **第 30 行**：当状态为 `S1` 时，次态无条件为 `S2`。
1. **第 31 行**：当状态为 `S2` 时，次态无条件为 `S3`。
1. **第 32 行**：当状态为 `S3` 时，次态无条件为 `IDLE`。
1. **第 33 行**：`default` 分支，覆盖未定义状态，次态为 `IDLE`，增强安全性。
1. **第 34 行**：`case` 结束。
1. **第 35 行**：次态逻辑 `always` 块结束。
1. **第 37 行**：注释，开始寄存器输出逻辑。
1. **第 38 行**：`always` 块，敏感列表为 `posedge clk` 或 `negedge rst_n`，实现同步输出。
1. **第 39 行**：条件判断，若复位有效，则执行复位。
1. **第 40 行**：复位时，`state_out` 赋值为 `IDLE`（2’b00）。
1. **第 41 行**：否则（复位无效），执行正常赋值。
1. **第 42 行**：时钟上升沿时，`state_out` 更新为 `state_reg`，实现寄存器输出。
1. **第 43 行**：输出 `always` 块结束。
1. **第 45 行**：`endmodule`，模块定义结束。
### 仿真脚本与运行
- 编写 testbench（如 `tb_fsm_gray.v`），实例化 fsm_gray，提供时钟和复位激励。
- 在 Vivado 中设置综合后仿真：`Tools → Simulation → Run Post-Synthesis Timing Simulation`。
- 观察波形：放大输出信号跳变沿，检查是否存在多余脉冲（毛刺）。
### 毛刺消除机制分析
**原因分析**：组合逻辑输出毛刺的根本原因是路径延迟不匹配。在状态机中，次态组合逻辑的输出直接驱动外部信号时，由于不同路径的延迟差异，会导致短暂的错误电平（毛刺）。Gray 编码虽能减少相邻状态跳变时的位变化数量，但无法消除组合逻辑固有的延迟竞争。
**落地路径**：将输出改为寄存器输出（即在时钟沿赋值），利用触发器的同步特性，将组合逻辑的毛刺“屏蔽”在时钟采样窗口之外。具体做法是：在状态机中增加一个寄存器输出级，如上述 RTL 中的 `state_out`，在时钟上升沿锁存 `state_reg` 的值。
**风险边界**：寄存器输出会增加一个时钟周期的延迟，对于需要即时响应的应用（如高速握手协议），需评估是否可接受。此外，若输出信号本身需要组合逻辑快速反馈（如用于下一级组合逻辑的输入），则寄存器输出可能破坏时序关系，此时应改用双寄存器同步链或调整设计架构。
## 验证结果
- 运行综合后时序仿真，在波形窗口中放大输出信号 `state_out` 的跳变沿，确认无多余脉冲。
- 检查 Vivado 日志，无 setup/hold 违例报告。
- 资源报告：LUT 占用 ≤ 50，FF 占用 ≤ 30，Fmax ≥ 100 MHz。
## 排障指南
- **毛刺仍存在**：检查输出是否为寄存器输出；确认仿真模式为时序仿真（post-synth timing），而非功能仿真。
- **时序违例**：检查时钟约束是否正确，尝试降低时钟频率或调整综合策略（如 `-retiming`）。
- **状态机推断失败**：查看综合报告中的 FSM 部分，确认状态编码是否被正确识别；避免在 case 中使用 `unique` 或 `priority` 关键字导致误判。
## 扩展实践
- 尝试 One-hot 编码（如 4’b0001, 4’b0010, 4’b0100, 4’b1000），观察资源占用与毛刺情况。
- 在输出端添加双寄存器同步链（两个级联的 D 触发器），进一步降低亚稳态风险。
- 使用 Vivado 的 `report_glitch` 命令（如适用）自动检测毛刺。
## 参考文档
- Xilinx UG901: Vivado Design Suite User Guide: Synthesis
- Xilinx UG900: Vivado Design Suite User Guide: Logic Simulation
- IEEE Std 1364-2005: Verilog Hardware Description Language
## 附录：完整 RTL 与 Testbench 示例
完整项目文件（包括 testbench 和 XDC 约束）可从配套资源获取，或参考以下模板自行构建。
