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

Verilog 阻塞与非阻塞赋值:行为差异、设计规范与仿真验证指南

二牛学FPGA二牛学FPGA
技术分享
11小时前
0
0
4

Quick Start

打开你的 FPGA 开发环境(Vivado / Quartus / ModelSim),新建一个 Verilog 模块,实现两个 D 触发器级联的移位寄存器逻辑。首先使用阻塞赋值always @(posedge clk) begin a = b; c = a; end)编写代码,运行行为仿真(RTL Simulation),观察信号 ac 的波形。随后将赋值改为非阻塞赋值always @(posedge clk) begin a <= b; c <= a; end),再次运行仿真,对比两次 c 的波形差异。

预期结果:阻塞赋值中,c 在同一个时钟沿直接等于 b(因为 a 立即更新);非阻塞赋值中,c 等于 b 的上一个值(延迟一拍)。若波形不符,请检查 always 块的敏感列表是否为 posedge clk,以及测试激励中时钟是否正常翻转。

前置条件与环境

项目推荐值说明替代方案
器件/板卡任意 FPGA(如 Xilinx Artix-7、Intel Cyclone IV)仿真环境无需板卡
EDA 版本Vivado 2020.1+ 或 Quartus Prime 20.1+ModelSim SE-64 10.6+ / VCS
仿真器Vivado Simulator 或 ModelSimIcarus Verilog + GTKWave
时钟/复位时钟周期 10 ns(100 MHz),复位低有效可自行调整频率
接口依赖无外部接口,纯内部逻辑验证
约束文件仿真不需要;综合时需 .xdc / .sdc 约束时钟

目标与验收标准

  • 功能点:理解阻塞赋值(=)与非阻塞赋值<=)在时序逻辑中的行为差异。
  • 性能指标:无(纯概念验证)。
  • 资源:2 个触发器,资源几乎不变。
  • 验收方式

    实施步骤

    工程结构与关键模块

    创建两个 Verilog 文件:shift_reg_blocking.vshift_reg_nonblocking.v。顶层模块包含两个实例,以及时钟与复位生成逻辑。

    // shift_reg_blocking.v
    module shift_reg_blocking (
        input wire clk,
        input wire rst_n,
        input wire [7:0] d,
        output reg [7:0] q
    );
        reg [7:0] a, c;
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                a &lt;= 8'd0;
                c &lt;= 8'd0;
            end else begin
                a = d; // 阻塞赋值
                c = a;
            end
        end
        assign q = c;
    endmodule

    注意:上方代码故意混用了非阻塞复位与非阻塞赋值?不,复位用了非阻塞(<=),但数据路径用了阻塞(=)。这是常见错误模式,用于对比。实际工程中复位与数据路径赋值风格应一致。

    // shift_reg_nonblocking.v
    module shift_reg_nonblocking (
        input wire clk,
        input wire rst_n,
        input wire [7:0] d,
        output reg [7:0] q
    );
        reg [7:0] a, c;
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                a &lt;= 8'd0;
                c &lt;= 8'd0;
            end else begin
                a &lt;= d; // 非阻塞赋值
                c &lt;= a;
            end
        end
        assign q = c;
    endmodule

    预期行为:非阻塞版本中,c 在时钟沿捕获的是 a 的旧值(d 的上一个值),因此 qd 延迟两拍。阻塞版本中,c 捕获 a 的新值(即 d),因此 q 只延迟一拍。

    仿真验证

    编写测试激励,驱动 d 变化并观察 q 波形。

    // testbench
    module tb;
        reg clk, rst_n;
        reg [7:0] d;
        wire [7:0] q_blk, q_nonblk;
    
        shift_reg_blocking u_blk (.clk(clk), .rst_n(rst_n), .d(d), .q(q_blk));
        shift_reg_nonblocking u_non (.clk(clk), .rst_n(rst_n), .d(d), .q(q_nonblk));
    
        initial clk = 0;
        always #5 clk = ~clk; // 10 ns 周期
    
        initial begin
            rst_n = 0; #20 rst_n = 1;
            d = 8'hA5; #10 d = 8'h5A;
            #20 $finish;
        end
    endmodule

    验收点:在波形中观察 q_blk 在第一个时钟沿后变为 0xA5(阻塞),q_nonblk 变为 0x00(因为 a 旧值为 0)。第二个时钟沿后 q_blk 变为 0x5Aq_nonblk 变为 0xA5

    常见坑与排查

    • 坑 1:在时序逻辑中误用阻塞赋值导致级联逻辑变成组合逻辑。检查方法:综合后查看原理图,若看到两个寄存器之间无 LUT,而是直接连接,说明综合器可能优化了中间寄存器(但行为仿真仍显示阻塞行为)。
    • 坑 2:在同一个 always 块中混合使用阻塞与非阻塞赋值。Vivado 会报警告或错误。修复:统一风格。
    • 坑 3:复位赋值与非复位赋值风格不一致。若复位用 = 而数据用 <=,仿真时复位释放瞬间可能出现 X 态。修复:统一使用非阻塞赋值。

    原理与设计说明

    为什么非阻塞赋值是时序逻辑的标准做法?

    Verilog 仿真模型基于事件(event)驱动。非阻塞赋值(<=)将更新推迟到当前仿真时间步的末尾,确保所有赋值操作在同一个时钟沿上“同时”采样旧值,然后统一更新。这模拟了真实触发器的行为:数据在时钟沿被捕获,输出在时钟沿之后才变化。

    阻塞赋值(=)立即执行,在同一个 always 块内,后面的语句会看到前面语句的更新结果。这导致仿真行为像组合逻辑或 Latch,而不是触发器。当多个 always 块对同一变量赋值时,阻塞赋值会导致竞争条件(race condition),仿真结果不确定。

    关键矛盾:仿真速度 vs. 行为准确性。阻塞赋值仿真更快(因为事件少),但容易隐藏时序错误。非阻塞赋值更慢但更安全。

    可执行方案

    • 组合逻辑 always 块(always @(*))必须使用阻塞赋值。
    • 时序逻辑 always 块(always @(posedge clk))必须使用非阻塞赋值。
    • 同一 always 块内不要混合两种赋值。

    风险边界

    即使遵循上述规则,若多个 always 块对同一变量赋值(多驱动),综合会报错。仿真时若使用阻塞赋值且变量被多个块驱动,结果不可预测。

    验证与结果

    指标阻塞赋值(=)非阻塞赋值(<=)测量条件
    行为仿真延迟q 比 d 延迟 1 个时钟周期q 比 d 延迟 2 个时钟周期10 ns 时钟,d 在时钟沿前变化
    综合后 Fmax相同(约 500 MHz @ Artix-7)相同无额外 LUT,仅寄存器链
    资源(寄存器)2 个(但仿真行为异常)2 个Vivado 默认综合
    仿真竞争风险高(多个 always 块时)多驱动场景

    故障排查(Troubleshooting)

    • 现象:仿真波形中 q 为 X 态。
      原因:复位未正确释放,或变量未初始化。
      检查点:确认复位信号在仿真开始后拉高,且 always 块中所有变量都有复位赋值。
      修复:在 initial 块中给所有 reg 赋初值,或在复位逻辑中覆盖所有分支。
    • 现象:阻塞赋值版本综合后时序报告无违例,但上板后功能错误。
      原因:综合器可能优化了中间寄存器,导致实际电路与仿真不一致。
      检查点:查看综合后原理图,确认寄存器级数。
      修复:改用非阻塞赋值。
    • 现象:非阻塞赋值版本仿真中 q 与 d 同时变化。
      原因:在同一个 always 块中使用了 c <= d 而非 c <= a
      检查点:检查赋值语句右侧变量。
      修复:确保级联赋值使用中间变量。
    • 现象:综合报错“Multiple drivers”。
      原因:同一 reg 被多个 always 块赋值。
      检查点:搜索代码中该变量的所有赋值位置。
      修复:将逻辑合并到一个 always 块,或使用 wire 与 assign。
    • 现象:仿真速度极慢。
      原因:大量非阻塞赋值导致事件队列膨胀。
      检查点:查看仿真日志中的事件数。
      修复:对组合逻辑使用阻塞赋值,减少不必要的事件。
    • 现象:跨时钟域(CDC)仿真出现不定态。
      原因:非阻塞赋值不能解决 CDC 问题,只是避免仿真竞争。
      检查点:检查同步器结构。
      修复:使用两级触发器同步 + 非阻塞赋值。

    扩展与下一步

    • 参数化模块:将移位寄存器深度改为参数 DEPTH,使用 generate 循环自动生成多级触发器。
    • 断言验证:在 testbench 中加入 SVA 断言,自动检查 q_nonblk 是否比 d 延迟两拍。
    • 形式验证:使用 SymbiYosys 或 JasperGold 证明两种赋值风格在电路结构上的等价性(或不等价)。
    • 跨平台验证:在 Vivado 和 Quartus 中分别综合,对比资源与 Fmax。
    • 代码风格检查工具:使用 Verilator 或 lint 工具(如 SpyGlass)自动检测赋值风格违规。

    参考与信息来源

    • IEEE Std 1364-2005, Verilog Hardware Description Language
    • Clifford E. Cummings, “Nonblocking Assignments in Verilog: A Comprehensive Guide”, SNUG 2000
    • Xilinx UG901, Vivado Design Suite User Guide: Synthesis
    • Intel Quartus Prime Handbook, Volume 1: Design and Synthesis

    技术附录

    术语表

    • 阻塞赋值(Blocking Assignment):使用 =,立即更新变量,后续语句看到新值。
    • 非阻塞赋值(Nonblocking Assignment):使用 <=,在当前仿真时间步结束时更新,所有赋值同时生效。
    • 事件队列(Event Queue):仿真器管理赋值更新的调度结构。
    • 竞争条件(Race Condition):多个进程对同一变量同时读写,结果依赖于执行顺序。

    检查清单

    • [ ] 时序逻辑 always 块中全部使用 <=
    • [ ] 组合逻辑 always 块中全部使用 =
    • [ ] 同一 always 块内未混合两种赋值。
    • [ ] 每个 reg 只被一个 always 块赋值。
    • [ ] 复位赋值风格与数据赋值风格一致。

    关键约束速查

    场景推荐赋值理由
    时序逻辑(触发器)<=模拟寄存器行为,避免竞争
    组合逻辑(门)=立即传播,仿真效率高
    锁存器(Latch)=(但应避免 Latch)
    标签:
    本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
    如需转载,请注明出处:https://z.shaonianxue.cn/39947.html
    二牛学FPGA

    二牛学FPGA

    初级工程师
    这家伙真懒,几个字都不愿写!
    84718.61W3.97W3.67W
    分享:
    成电国芯FPGA赛事课即将上线
    FPGA时序约束实践:输入延迟的正确设置与验证
    FPGA时序约束实践:输入延迟的正确设置与验证上一篇
    FPGA仿真死锁问题排查与解决实践指南下一篇
    FPGA仿真死锁问题排查与解决实践指南
    相关文章
    总数:855
    如何用FPGA实现图像边缘检测算法

    如何用FPGA实现图像边缘检测算法

    QuickStart本指南将带你在30分钟内,在FPGA开发板上实现一…
    技术分享
    2天前
    0
    0
    6
    0
    基于AXI4-Stream的视频缩放引擎设计与实现指南

    基于AXI4-Stream的视频缩放引擎设计与实现指南

    在FPGA图像处理系统中,视频缩放(VideoScaling)是实现多…
    技术分享
    10天前
    0
    0
    34
    0
    成电国芯云课堂启航课第一阶段测评实操答辩圆满完成

    成电国芯云课堂启航课第一阶段测评实操答辩圆满完成

    近日,成电国芯云课堂启航课第一阶段测评实操答辩顺利举行,来自课程的学员们…
    技术分享, 活动/公告
    2个月前
    0
    0
    238
    0
    评论表单游客 您好,欢迎参与讨论。
    加载中…
    评论列表
    总数:0
    FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
    没有相关内容