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

Verilog 阻塞与非阻塞赋值实战指南:常见误区与正确设计

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

Quick Start:快速上手实验

本指南通过一个简单的移位寄存器实例,帮助你直观理解阻塞赋值(=)与非阻塞赋值(<=)在行为级仿真中的差异。你只需准备一个支持 Verilog 的仿真工具(如 Vivado 2020.1+、ModelSim 或 QuestaSim),并按照以下步骤操作,即可在 10 分钟内复现典型误区并验证正确做法。

前置条件

  • 已安装任一主流 Verilog 仿真工具(Vivado、ModelSim、QuestaSim 等)。
  • 具备基本 Verilog 语法知识,了解 always 块与敏感列表。
  • 建议熟悉波形查看工具(如 Vivado Simulator 或 GTKWave)。

目标与验收标准

  • 目标:通过对比阻塞与非阻塞赋值实现的移位寄存器,理解二者在时序逻辑中的行为差异,并掌握正确使用场景。
  • 验收标准

    实施步骤

    步骤 1:创建工程与顶层模块

    在仿真工具中新建一个工程,并创建一个 Verilog 模块,命名为 shift_register_tb(测试模块)。

    步骤 2:编写非阻塞赋值版移位寄存器

    module shift_nonblocking (
        input clk,
        input rst_n,
        input din,
        output reg dout
    );
        reg [2:0] shift_reg;
    
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n)
                shift_reg &lt;= 3&#039;b0;
            else begin
                shift_reg[0] &lt;= din;
                shift_reg[1] &lt;= shift_reg[0];
                shift_reg[2] &lt;= shift_reg[1];
                dout &lt;= shift_reg[2];
            end
        end
    endmodule

    步骤 3:编写阻塞赋值版移位寄存器(常见误区)

    module shift_blocking (
        input clk,
        input rst_n,
        input din,
        output reg dout
    );
        reg [2:0] shift_reg;
    
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n)
                shift_reg = 3'b0;
            else begin
                shift_reg[0] = din;
                shift_reg[1] = shift_reg[0];
                shift_reg[2] = shift_reg[1];
                dout = shift_reg[2];
            end
        end
    endmodule

    步骤 4:编写测试激励(testbench)

    module tb;
        reg clk, rst_n, din;
        wire dout_nb, dout_b;
    
        shift_nonblocking u_nb (.clk(clk), .rst_n(rst_n), .din(din), .dout(dout_nb));
        shift_blocking     u_b  (.clk(clk), .rst_n(rst_n), .din(din), .dout(dout_b));
    
        initial begin
            clk = 0;
            forever #5 clk = ~clk;
        end
    
        initial begin
            rst_n = 0; din = 0;
            #20 rst_n = 1;
            #10 din = 1;
            #10 din = 0;
            #100 $finish;
        end
    endmodule

    步骤 5:运行仿真并观察波形

    运行仿真至少 200 ns,添加 dout_nbdout_b 到波形窗口。你会看到:

    • 非阻塞赋值版:数据在每个时钟上升沿逐位右移,延迟 3 个时钟周期后出现在 dout_nb
    • 阻塞赋值版:dout_b 在同一个时钟周期内直接跟随 din 变化,没有移位效果——因为阻塞赋值在 always 块内立即更新,导致所有赋值“同时”完成,相当于组合逻辑。

    验证结果

    观察波形后,应确认以下关键差异:

    • 非阻塞赋值shift_reg[0] 在时钟上升沿采样 dinshift_reg[1] 采样上一拍 shift_reg[0],以此类推。输出 dout_nb 在 3 个时钟周期后复制 din 的初始值。
    • 阻塞赋值:由于 shift_reg[0] = din 立即更新,随后 shift_reg[1] = shift_reg[0] 读取的是刚更新的值,导致所有寄存器在同一拍内完成数据传递,失去时序存储特性。

    如果波形符合上述描述,则实验验证通过。

    排障指南

    • 问题:波形中非阻塞版本也没有移位效果——检查敏感列表是否遗漏了 negedge rst_n,或复位逻辑中使用了阻塞赋值。确保 always 块内所有时序赋值均使用 <=
    • 问题:仿真结果与预期完全相反——确认测试激励中 din 的变化时刻是否在时钟沿附近。建议将 din 变化安排在时钟上升沿之后(如 #6#7),避免建立/保持时间冲突。
    • 问题:工具报错“multiple drivers”——检查是否在多个 always 块中对同一 reg 赋值。移位寄存器应只在一个 always 块中描述。

    扩展:深入理解赋值机制

    为什么阻塞赋值不适合时序逻辑?

    阻塞赋值(=)在 always 块内按顺序立即执行,每个赋值语句都会更新目标寄存器的值,并影响后续语句的读取。在时钟沿触发的 always 块中,这会导致同一时钟周期内多个寄存器同时更新,破坏时序逻辑的“锁存-延迟”特性。而非阻塞赋值(<=)将赋值操作推迟到 always 块结束时统一执行,所有右值在块开始时被采样(即上一时钟周期的值),从而正确模拟了触发器的行为。

    风险边界

    • 在组合逻辑 always 块中(敏感列表为电平触发),应使用阻塞赋值,否则会导致仿真与综合不一致。
    • 在同一个 always 块中混合使用阻塞与非阻塞赋值是常见的错误来源,应严格避免。
    • 对于多时钟域或异步复位设计,非阻塞赋值也不能自动解决亚稳态问题,仍需同步器电路。

    参考资源

    • IEEE Std 1364-2001 Verilog 硬件描述语言标准,第 9.2 节“阻塞与非阻塞赋值”。
    • Clifford E. Cummings, “Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!” (SNUG 2000).
    • Vivado Design Suite 用户指南:仿真 (UG900)。

    附录:完整测试代码

    以下为可直接复制到仿真工具中的完整 testbench 代码(包含两个模块):

    // 非阻塞赋值版
    module shift_nonblocking (
        input clk, rst_n, din,
        output reg dout
    );
        reg [2:0] shift_reg;
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) shift_reg &lt;= 3&#039;b0;
            else begin
                shift_reg[0] &lt;= din;
                shift_reg[1] &lt;= shift_reg[0];
                shift_reg[2] &lt;= shift_reg[1];
                dout &lt;= shift_reg[2];
            end
        end
    endmodule
    
    // 阻塞赋值版(常见错误)
    module shift_blocking (
        input clk, rst_n, din,
        output reg dout
    );
        reg [2:0] shift_reg;
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) shift_reg = 3&#039;b0;
            else begin
                shift_reg[0] = din;
                shift_reg[1] = shift_reg[0];
                shift_reg[2] = shift_reg[1];
                dout = shift_reg[2];
            end
        end
    endmodule
    
    // 测试激励
    module tb;
        reg clk, rst_n, din;
        wire dout_nb, dout_b;
    
        shift_nonblocking u_nb (.*);
        shift_blocking     u_b  (.*);
    
        initial begin
            clk = 0;
            forever #5 clk = ~clk;
        end
    
        initial begin
            rst_n = 0; din = 0;
            #20 rst_n = 1;
            #10 din = 1;
            #10 din = 0;
            #100 $finish;
        end
    
        initial begin
            $monitor(&quot;Time=%0t din=%b dout_nb=%b dout_b=%b&quot;, $time, din, dout_nb, dout_b);
        end
    endmodule
    标签:
    本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
    如需转载,请注明出处:https://z.shaonianxue.cn/40773.html
    二牛学FPGA

    二牛学FPGA

    初级工程师
    这家伙真懒,几个字都不愿写!
    91919.32W3.99W3.67W
    分享:
    成电国芯FPGA赛事课即将上线
    Vivado 中 set_max_delay 约束的设计与实施指南
    Vivado 中 set_max_delay 约束的设计与实施指南上一篇
    基于FPGA的SPI Flash控制器设计与调试下一篇
    基于FPGA的SPI Flash控制器设计与调试
    相关文章
    总数:944
    千兆以太网MAC层FPGA设计与验证实施指南

    千兆以太网MAC层FPGA设计与验证实施指南

    本文档提供一套完整的、可综合的千兆以太网媒体访问控制(MAC)层FPGA…
    技术分享
    12天前
    0
    0
    37
    0
    FPGA调试实战:用ILA和VIO,让硬件问题无处可藏

    FPGA调试实战:用ILA和VIO,让硬件问题无处可藏

    你是不是也有过这样的经历?仿真明明跑得好好的,代码一下到板子上,问题就冒…
    技术分享
    1个月前
    0
    0
    221
    0
    基于FPGA的SPI Flash控制器设计与调试

    基于FPGA的SPI Flash控制器设计与调试

    QuickStart准备环境:安装Vivado2021.1及以上…
    技术分享
    19小时前
    0
    0
    2
    0
    评论表单游客 您好,欢迎参与讨论。
    加载中…
    评论列表
    总数:0
    FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
    没有相关内容