1 分钟阅读 次阅读

概要:本文探讨了 Verilog 中非阻塞赋值是否会引发竞争条件的问题。通过实例代码与仿真波形说明,作者指出非阻塞赋值由于其特性,在同一个时钟边沿中不会引发竞争,并与阻塞赋值进行了对比分析。

1. 非阻塞赋值的基本特性

非阻塞赋值使用 <= 符号,属于过程赋值的一种,一般在 always 块中使用。相比于阻塞赋值,非阻塞赋值是在 always 块结束后同时赋值的。

一个常见疑问是:既然是“同时赋值”,那么如果变量之间存在相互依赖,又该如何确定谁依赖谁?更进一步,综合后的真实电路中不存在绝对的“同时”,那么是否存在竞争现象?


2. 示例分析:非阻塞赋值是否会导致竞争

2.1 示例设定

假设初始状态如下:

  • a 初始为 0
  • b 初始为 1

执行如下非阻塞赋值:

a <= b;
b <= a;

问题是:更新后 a 和 b 的值分别是多少?

2.2 非阻塞赋值的执行机制

实际上,非阻塞赋值不会出现上述问题。原因如下:

  • 在同一个时钟边沿触发的 always 块中,所有非阻塞赋值的右侧表达式会​​同时计算​​(使用当前时钟沿的旧值);
  • 左侧变量的更新则​​在块结束后统一执行​​。

因此,右侧的值是基于当前周期的旧值,不依赖左侧变量的新值。这种行为使得变量之间的“交换”操作是安全的。


3. 仿真实验

3.1 设计代码 design.sv

module swap_registers (
    input  logic clk,    // 时钟
    input  logic rst,    // 新增:复位信号
    output logic a,      // 输出寄存器 a
    output logic b       // 输出寄存器 b
);

// 带复位的交换逻辑
always_ff @(posedge clk) begin
    if (rst) begin  // 复位时初始化值
        a <= 0;
        b <= 1;
    end else begin  // 正常交换
        a <= b;
        b <= a;
    end
end
endmodule

3.2 测试平台 testbench.sv

module swap_registers_tb;  // 修改模块名避免冲突
    logic clk, rst;
    logic a, b;

    // 实例化设计模块(DUT)
    swap_registers dut (
        .clk(clk),
        .rst(rst),  // 连接新增的复位信号
        .a(a),
        .b(b)
    );

    // 生成时钟(周期10ns)
    always #5 clk = ~clk;

    // 初始化与测试逻辑
    initial begin
        $dumpfile("dump.vcd");         // 指定波形文件名
        $dumpvars(0, swap_registers_tb); // 记录 test 模块的所有信号
        clk = 0;
        rst = 1;    // 复位有效
        #10;
        rst = 0;    // 释放复位
        #100 $finish; // 仿真100ns后结束
    end

    // 监视信号变化
    initial begin
        $monitor("Time=%0t: a=%b, b=%b", $time, a, b);
    end
endmodule

3.3 仿真波形结果(非阻塞赋值)

alt text
可以看到,a 和 b 的值不断进行交换。


4. 阻塞赋值的对比

如果使用阻塞赋值:

a = b;
b = a;

由于阻塞赋值是“顺序执行”,当前语句执行完毕后才执行下一条,因此:

  • a 得到 b 的旧值;
  • b 再次被赋值为 a,即 b 依然是原来的 b 的值。

最终,a 和 b 的值都会变成 b 的原始值,交换并未真正发生。

4.1 仿真波形结果(阻塞赋值)

alt text


5. 总结

  • 非阻塞赋值不会引发竞争条件;
  • 在同一时钟边沿内,非阻塞赋值的右侧表达式使用旧值计算,左侧在块末统一更新;
  • 非阻塞赋值适用于寄存器之间的同步更新场景;
  • 阻塞赋值适用于组合逻辑或顺序执行的情况,但在时序逻辑中应谨慎使用。

标签:

分类:

更新时间:

留下评论