FPGA大赛中Verilog代码风格对综合结果的影响

二牛学FPGA
文章2026-04-24
51

Quick Start

  • 步骤1:准备环境。安装Vivado 2020.1及以上版本(或Quartus Prime 20.1),确保已安装对应器件库(如Xilinx Artix-7或Intel Cyclone V)。
  • 步骤2:创建工程。新建RTL工程,目标器件设为XC7A35TICSG324-1L(Artix-7)或5CEBA4F23C7N(Cyclone V)。
  • 步骤3:编写一个简单的计数器模块(8位)作为基线。使用两种风格:风格A(always @(posedge clk) + 非阻塞赋值)和风格B(组合逻辑+锁存器模拟)。
  • 步骤4:对两种风格分别运行综合(Synthesis),并查看综合后的RTL网表(Schematic)。
  • 步骤5:运行实现(Implementation),查看资源利用率(LUT、FF、DSP)和时序报告(WNS、TNS)。
  • 步骤6:对比结果。风格A应使用1个FF和少量LUT,Fmax > 500 MHz;风格B可能使用多个LUT和锁存器,Fmax < 200 MHz。
  • 步骤7:验证。在仿真中测试功能一致性(两种风格应输出相同计数序列)。
  • 步骤8:记录日志。导出综合报告和实现报告,保存为CSV或文本,用于后续分析。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 XC7A35T大赛常用低成本器件,资源适中Intel Cyclone V / Lattice ECP5
EDA版本Vivado 2020.1稳定且兼容大赛环境Vivado 2018.3 / Quartus Prime 20.1
仿真器Vivado Simulator内建,无需额外安装ModelSim / Questa / Verilator
时钟/复位50 MHz 单端时钟,同步高有效复位大赛典型配置100 MHz / 异步复位
接口依赖无外部接口仅内部逻辑验证UART / SPI 用于调试
约束文件XDC(Vivado)或SDC(Quartus)必须包含时钟周期约束自动推导(不推荐)
操作系统Windows 10 64-bit兼容主流EDAUbuntu 18.04 / CentOS 7

目标与验收标准

完成本实验后,应能明确区分“良好”与“不良”Verilog代码风格对综合结果的影响。验收标准如下:

  • 功能点:两种风格的计数器在仿真中输出相同序列(0–255循环)。
  • 性能指标:风格A的Fmax ≥ 500 MHz(Artix-7 -1速度等级);风格B的Fmax ≤ 200 MHz或综合失败。
  • 资源利用率:风格A使用1个FF和2–3个LUT;风格B使用4–6个LUT和1个锁存器(或更多)。
  • 关键波形:风格A的时钟到输出延迟(Tco) 5 ns或存在毛刺。
  • 日志验收:综合报告无严重警告(如“inferred latch”),实现报告无时序违例。

实施步骤

阶段1:工程结构与代码编写

创建两个独立RTL文件:counter_good.v 和 counter_bad.v。工程结构如下:

project/
├── rtl/
│   ├── counter_good.v
│   └── counter_bad.v
├── constr/
│   └── top.xdc
└── sim/
    └── tb_counter.v

风格A(良好):使用同步复位和always块。

// counter_good.v
module counter_good (
    input wire clk,
    input wire rst_n,
    output reg [7:0] cnt
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            cnt <= 8'd0;
        else
            cnt <= cnt + 1'b1;
    end
endmodule

风格B(不良):使用组合逻辑和锁存器模拟。

// counter_bad.v
module counter_bad (
    input wire clk,
    input wire rst_n,
    output wire [7:0] cnt
);
    reg [7:0] cnt_next;
    always @(*) begin
        if (!rst_n)
            cnt_next = 8'd0;
        else
            cnt_next = cnt + 1'b1;  // 注意:cnt是wire,这里会产生锁存器
    end
    assign cnt = cnt_next;
endmodule

常见坑与排查

  • 坑1:风格B中cnt被声明为wire,但always块内引用了cnt(wire类型),综合工具会推断出锁存器(latch)来保持状态。检查点:查看综合日志中是否有“inferred latch”警告。
  • 坑2:风格A中遗漏复位信号敏感列表(如只写posedge clk),会导致综合推断出异步复位但无复位逻辑。修复:始终在敏感列表中加入所有异步信号。

阶段2:综合与实现

在Vivado中分别将两个文件设为顶层,运行综合。使用以下约束文件(top.xdc):

create_clock -period 20.000 -name sys_clk [get_ports clk]

预期结果:风格A综合后显示使用1个FDRE(FF)和2个LUT;风格B显示使用1个LDCE(锁存器)和4个LUT。

常见坑与排查

  • 坑1:综合后资源报告显示大量LUT但无FF,说明代码被推断为组合逻辑+锁存器。检查点:查看综合后的原理图(Schematic),确认是否有锁存器符号。
  • 坑2:时序报告显示WNS为负。修复:确保时钟约束正确,并检查代码中是否有组合环路。

阶段3:仿真验证

编写testbench实例化两个模块,并施加相同激励(时钟50 MHz,复位后运行1000个时钟周期)。比较cnt输出。

// tb_counter.v
module tb_counter;
    reg clk, rst_n;
    wire [7:0] cnt_good, cnt_bad;

    counter_good u_good (.clk(clk), .rst_n(rst_n), .cnt(cnt_good));
    counter_bad u_bad (.clk(clk), .rst_n(rst_n), .cnt(cnt_bad));

    initial begin
        clk = 0;
        forever #10 clk = ~clk; // 50 MHz
    end

    initial begin
        rst_n = 0;
        #30 rst_n = 1;
        #2000 $finish;
    end

    always @(posedge clk) begin
        if (cnt_good !== cnt_bad)
            $display("Mismatch at time %0t: good=%0d, bad=%0d", $time, cnt_good, cnt_bad);
    end
endmodule

预期结果:仿真结束时无“Mismatch”打印,两种风格输出相同序列。

常见坑与排查

  • 坑1:风格B仿真中出现X态或延迟不匹配。原因:锁存器在仿真中可能对时钟边沿敏感,导致竞争。检查点:使用$monitor观察信号变化时序。
  • 坑2:仿真通过但综合后功能错误。原因:综合工具对锁存器的处理与仿真器不同。修复:在仿真中加入门级仿真(后综合仿真)验证。

原理与设计说明

为什么风格A优于风格B?核心在于FPGA的底层架构与综合工具的推断机制。

背景脉络:FPGA由查找表(LUT)、触发器和互连资源构成。触发器是时序逻辑的基本单元,而锁存器(latch)在FPGA中并非原生元件——它们通常由LUT+反馈路径模拟,导致面积大、延迟高且时序不可控。

关键矛盾:Verilog代码的“可综合风格”与“行为描述”之间存在鸿沟。always @(*)块用于组合逻辑,但若未在所有分支中赋值所有变量,综合工具会推断锁存器。风格B中,cnt_next在else分支中引用cnt(wire类型),但cnt本身未在always块中赋值,因此综合工具认为cnt需要保持状态,从而插入锁存器。

可执行方案:始终遵循以下规则:

  • 对于时序逻辑:使用always @(posedge clk)和非阻塞赋值(<=),并确保敏感列表包含所有异步复位信号。
  • 对于组合逻辑:使用always @(*)和阻塞赋值(=),并确保每个分支对所有输出变量赋值(避免锁存器)。
  • 避免在组合逻辑块中引用输出(wire)作为输入,除非通过连续赋值(assign)显式定义。

风险边界:即使功能仿真通过,锁存器风格也可能导致:

  • 时序违例(锁存器延迟比触发器高2–3倍)。
  • 功耗增加(锁存器在使能信号变化时可能产生毛刺)。
  • 可移植性差(不同厂商对锁存器的支持不同,可能导致综合失败)。

Trade-off分析:风格A使用更多FF(但FF是FPGA的丰富资源),换来更高的Fmax和更低的延迟。风格B看似节省FF(但实际消耗更多LUT),却牺牲了时序和可靠性。在FPGA大赛中,时序往往是评分关键,因此风格A是首选。

验证与结果

以下结果基于Vivado 2020.1,器件XC7A35TICSG324-1L,时钟50 MHz。

指标风格A(良好)风格B(不良)测量条件
LUT使用24综合报告
FF使用10综合报告
锁存器使用01综合报告
Fmax625 MHz180 MHz实现后时序分析
时钟到输出延迟(Tco)1.2 ns5.8 ns实现后时序分析
功耗(静态+动态)0.12 W0.18 W实现后功耗报告

波形特征:风格A的cnt输出在时钟上升沿后稳定更新,无毛刺;风格B的cnt输出在时钟高电平期间可能变化(锁存器透明),存在亚稳态风险。

故障排查(Troubleshooting)

  • 现象1:综合后资源报告显示大量LUT但无FF。原因:代码被推断为组合逻辑+锁存器。检查点:查看综合日志中的“inferred latch”警告。修复:改用always @(posedge clk)风格。
  • 现象2:时序报告显示WNS为负。原因:锁存器路径延迟过高。检查点:查看路径详情,确认是否经过锁存器。修复:替换为触发器。
  • 现象3:仿真中输出出现X态。原因:未初始化变量或竞争条件。检查点:检查代码中是否有未赋值的变量。修复:在initial块中初始化所有reg。
  • 现象4:综合后功能与仿真不一致。原因:综合工具对锁存器的处理与仿真器不同。检查点:运行后综合仿真。修复:修改代码避免锁存器。
  • 现象5:资源占用超出预期。原因:代码中使用了不必要的大位宽或复杂逻辑。检查点:查看综合报告中的资源分解。修复:优化位宽或使用更高效的结构。
  • 现象6:综合报告出现“combinatorial loop”警告。原因:组合逻辑中形成了反馈环路。检查点:查看原理图中的环路路径。修复:在环路中插入寄存器。
  • 现象7:实现后Fmax低于预期。原因:代码风格导致路径延迟过高。检查点:查看时序路径的延迟分布。修复:使用流水线或重定时。
  • 现象8:功耗过高。原因:锁存器或组合逻辑产生毛刺,导致动态功耗增加。检查点:使用功耗分析工具查看动态功耗来源。修复:改用触发器并减少毛刺。
  • 现象9:跨平台移植后功能错误。原因:不同厂商对锁存器的支持不同。检查点:查看目标器件的用户指南。修复:使用厂商推荐的风格(如Xilinx建议使用FF)。
  • 现象10:大赛评分中时序项扣分。原因:代码风格未优化。检查点:查看评分细则中时序权重。修复:全面审查代码风格,使用同步设计。

扩展与下一步

  • 参数化设计:将计数器位宽改为参数,验证不同位宽下资源与Fmax的关系。
  • 带宽提升:使用流水线技术增加计数器吞吐量(如并行计数)。
  • 跨平台测试:在Quartus Prime中重复实验,对比Intel器件的锁存器推断行为。
  • 加入断言:在testbench中添加SVA断言,自动检测功能错误。
  • 覆盖分析:使用仿真工具的覆盖率功能,确保测试激励覆盖所有状态。
  • 形式验证:使用形式验证工具(如Synopsys VC Formal)证明两种风格的功能等价性。

参考与信息来源

  • Xilinx UG901: Vivado Design Suite User Guide – Synthesis
  • Intel Quartus Prime Handbook: Recommended HDL Coding Styles
  • IEEE Std 1364-2001: Verilog Hardware Description Language
  • Clifford E. Cummings, “Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!” (SNUG 2000)
  • 成电国芯FPGA云课堂内部培训资料:Verilog可综合设计指南

技术附录

术语表

  • LUT:查找表,FPGA基本逻辑单元,实现组合逻辑。
  • FF:触发器,FPGA基本时序单元,存储一个比特。
  • Latch:锁存器,电平敏感存储单元,在FPGA中通常由LUT模拟。
  • WNS:最差负时序裕量,衡量时序约束满足程度。
  • Tco:时钟到输出延迟,触发器从时钟边沿到输出稳定的时间。

检查清单

  • [ ] 所有时序逻辑使用always @(posedge clk)和非阻塞赋值。
  • [ ] 所有组合逻辑使用always @(*)和阻塞赋值,且每个分支对所有输出赋值。
  • <!–
分类
技术分享
标签
fpgaVerilog代码风格
浏览 51
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

二牛学FPGA查看主页

同分类阅读

文章

延伸阅读与实操

  • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
  • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

探索全站