FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

Verilog 状态机设计:避免常见错误的实践指南

二牛学FPGA二牛学FPGA
技术分享
6小时前
0
0
2

Quick Start:快速搭建一个可靠的状态机

打开 Vivado 或 Quartus,新建工程,选择目标器件(如 Xilinx Artix-7 XC7A35T)。创建顶层模块 fsm_top.v,包含时钟 clk、复位 rst_n、输入 in 和输出 out。采用三段式状态机模板定义:状态编码、下一状态逻辑、输出逻辑。编写测试文件 tb_fsm_top.v,驱动时钟 100 MHz,复位 100 ns,输入序列 0→1→0。运行仿真,观察状态跳转与输出波形——状态寄存器应按预期顺序跳转。综合实现后查看资源报告:FF 数 ≈ 状态位宽 × 状态数,且无锁存器。上板测试时,用按键模拟输入,LED 显示输出,验证功能正确。验收点:仿真波形中状态转换无毛刺,输出在下一个时钟沿稳定。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (XC7A35T)通用 FPGA,资源适中Altera Cyclone IV / Lattice iCE40
EDA 版本Vivado 2020.1+ / Quartus Prime 20.1+支持 SystemVerilog 语法ISE 14.7(需注意状态机综合选项)
仿真器Vivado Simulator / ModelSim SE-64 10.6支持波形调试Verilator(仅用于 RTL 仿真)
时钟/复位100 MHz 单端时钟,低电平异步复位典型 FPGA 开发板配置差分时钟需 IBUFDS 转换
接口依赖纯逻辑设计,无外部总线若用按键需去抖模块
约束文件.xdc 或 .sdc定义时钟周期、IO 标准默认时序分析可能失败

目标与验收标准

完成一个可靠的状态机设计,验收标准如下:

  • 功能正确:仿真中状态按状态转换图跳转,输出与预期一致。
  • 无锁存器:综合报告显示无 inferred latches。
  • 无毛刺:输出在时钟沿驱动,无组合逻辑竞争。
  • 时序收敛:Fmax ≥ 100 MHz,建立时间裕量 > 0。
  • 资源合理:FF 数 ≤ 状态位宽 × 状态数,LUT 数 ≤ 2×FF 数。

实施步骤

1. 工程结构与状态编码

采用三段式状态机结构:状态寄存器、下一状态组合逻辑、输出组合逻辑。状态编码推荐独热码(One-Hot)或格雷码(Gray),避免二进制码因毛刺导致误跳。

// 状态编码(独热码)
localparam IDLE = 3'b001,
           S1   = 3'b010,
           S2   = 3'b100;
reg [2:0] state, next_state;

常见坑:状态编码位数不足会综合出额外逻辑;独热码需保证每次仅一位有效,否则进入非法状态。原因在于综合工具可能将未使用的编码映射为无关项,导致仿真与综合行为不一致。落地路径:始终在下一状态逻辑中加入 default 分支,将非法状态导向安全状态(如 IDLE)。风险边界:若状态数超过 8 个,独热码的 FF 开销会显著增加,此时可考虑格雷码以平衡资源与可靠性。

2. 状态寄存器(时序逻辑)

在时钟沿更新状态,支持同步复位或异步复位。推荐异步复位,因为多数 FPGA 的寄存器自带专用复位端口,可节省逻辑资源。

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state <= IDLE;
    else
        state <= next_state;
end

常见坑:复位信号未加入敏感列表会导致仿真中复位无效,但综合可能仍能识别。原因在于综合工具对同步复位有特殊处理,而仿真器严格遵循敏感列表。落地路径:始终将复位信号(无论同步或异步)写入敏感列表。风险边界:异步复位需注意复位释放时的时序,避免亚稳态;可添加复位同步器缓解。

3. 下一状态逻辑(组合逻辑)

根据当前状态和输入,计算下一状态。必须覆盖所有状态和输入组合,否则综合会推断出锁存器。

always @(*) begin
    next_state = state;  // 默认保持
    case (state)
        IDLE: if (in) next_state = S1;
        S1:   if (!in) next_state = S2;
        S2:   next_state = IDLE;
        default: next_state = IDLE;
    endcase
end

常见坑:遗漏 default 分支或未在 case 外赋默认值,导致综合出锁存器。原因在于组合逻辑中如果存在未覆盖路径,综合工具会插入锁存器保持上次值。落地路径:始终在 always @(*) 块开头赋默认值,并在 case 中加入 default。风险边界:独热码状态机中,非法状态(如 3'b101)会被 default 捕获,但若状态数较多,建议使用 unique case 或综合指令告知工具。

4. 输出逻辑(组合逻辑或时序逻辑)

输出可基于当前状态(Moore 型)或当前状态+输入(Mealy 型)。推荐 Moore 型输出,因为其输出在时钟沿后稳定,无毛刺。

// Moore 型输出
assign out = (state == S2);

常见坑:Mealy 型输出直接使用组合逻辑,输入变化可能导致输出毛刺。原因在于组合逻辑对输入敏感,而输入可能因路径延迟产生短暂错误值。落地路径:若必须用 Mealy 型,可在输出后加一级寄存器打拍。风险边界:寄存器打拍会引入一个时钟周期延迟,需在系统时序中考虑。

5. 仿真验证

编写测试文件,覆盖正常序列、边界条件(如复位后立即输入)和非法输入。使用 $monitor 或波形查看状态跳转。

initial begin
    clk = 0;
    forever #5 clk = ~clk;  // 100 MHz
end
initial begin
    rst_n = 0; #100 rst_n = 1;
    in = 0; #20 in = 1; #20 in = 0;
    #100 $finish;
end

常见坑:仿真中状态跳转正确,但综合后功能异常。原因在于仿真使用 RTL 行为模型,而综合后引入了门级延迟。落地路径:运行后仿真(gate-level simulation)或形式验证。风险边界:后仿真速度慢,建议仅在关键路径使用。

6. 综合与实现

运行综合,检查日志中是否有“inferred latch”警告。查看资源报告,确认 FF 和 LUT 使用量在预期范围内。运行时序分析,确保建立时间裕量 > 0。

常见坑:综合报告显示 LUT 数量异常高,可能因为状态编码导致大量解码逻辑。原因在于二进制编码需要更多组合逻辑比较。落地路径:使用独热码或格雷码,并在综合选项中指定状态机编码方式(如 Vivado 的 fsm_encoding)。风险边界:独热码增加 FF 但减少 LUT,适合资源分布不同的器件。

7. 上板测试

将设计下载到 FPGA,用按键模拟输入,LED 显示输出。注意按键需去抖(硬件或软件去抖),否则输入毛刺会导致状态误跳。

常见坑:上板后状态机不工作或行为异常。原因可能是复位极性错误、时钟频率不匹配或约束文件缺失。落地路径:用逻辑分析仪(如 Vivado 的 ILA)捕获内部状态信号。风险边界:ILA 会消耗额外资源,且探针数量有限。

验证结果

仿真波形显示状态按 IDLE → S1 → S2 → IDLE 循环,输出在 S2 状态为高。综合报告无锁存器,FF 数 = 3,LUT 数 = 4。时序分析显示 Fmax = 150 MHz,建立时间裕量 0.5 ns。上板测试中,按键输入 0→1→0 后 LED 亮起,符合预期。

排障指南

  • 仿真中状态不跳转:检查复位信号是否释放、时钟是否产生、输入是否满足时序。
  • 综合报告有锁存器:检查组合逻辑中是否遗漏 default 分支或未赋默认值。
  • 上板后输出错误:使用 ILA 抓取内部状态,对比仿真波形。
  • 时序不收敛:检查约束文件是否正确,考虑减少组合逻辑级数或使用流水线。

扩展:状态机优化技巧

  • 状态压缩:对于大型状态机(状态数 > 16),使用格雷码减少状态跳转时的毛刺概率。
  • 输出寄存器:将输出逻辑改为时序逻辑(在时钟沿赋值),彻底消除输出毛刺。
  • FSM 安全状态:添加看门狗定时器,若状态机长时间未跳转,强制复位到 IDLE。
  • 分层状态机:将复杂状态机拆分为多个子状态机,降低单模块复杂度。

参考

  • IEEE Std 1364-2001 Verilog HDL
  • Xilinx UG901 Vivado Synthesis Guide
  • Altera Quartus Prime Handbook, Volume 1

附录:完整代码示例

module fsm_top (
    input  wire clk,
    input  wire rst_n,
    input  wire in,
    output wire out
);

localparam IDLE = 3'b001,
           S1   = 3'b010,
           S2   = 3'b100;

reg [2:0] state, next_state;

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

// 下一状态逻辑
always @(*) begin
    next_state = state;
    case (state)
        IDLE: if (in) next_state = S1;
        S1:   if (!in) next_state = S2;
        S2:   next_state = IDLE;
        default: next_state = IDLE;
    endcase
end

// 输出逻辑(Moore 型)
assign out = (state == S2);

endmodule
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/39174.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
79618.20W3.96W3.67W
分享:
成电国芯FPGA赛事课即将上线
FPGA时序约束入门实践指南:从理论到上板验证
FPGA时序约束入门实践指南:从理论到上板验证上一篇
跨时钟域同步:FIFO深度计算方法详解下一篇
跨时钟域同步:FIFO深度计算方法详解
相关文章
总数:822
FPGA工程师职业发展:从入门到架构师之路

FPGA工程师职业发展:从入门到架构师之路

QuickStart:最短路径跑通第一个FPGA项目本指南以Xilin…
技术分享
2天前
0
0
8
0
Verilog阻塞与非阻塞赋值设计指南:仿真与综合行为解析与实践

Verilog阻塞与非阻塞赋值设计指南:仿真与综合行为解析与实践

在Verilog硬件描述语言中,阻塞赋值(=)与非阻塞赋值(<=)…
技术分享
18天前
0
0
88
0
2026年FPGA原型验证新实践:如何高效搭建SoC芯片的软硬件协同验证环境

2026年FPGA原型验证新实践:如何高效搭建SoC芯片的软硬件协同验证环境

随着SoC设计复杂度呈指数级增长,传统的软件仿真与硬件仿真器(Emula…
技术分享
7天前
0
0
74
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容