非阻塞赋值是否会导致竞争?
概要:本文探讨了 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 仿真波形结果(非阻塞赋值)

可以看到,a 和 b 的值不断进行交换。
4. 阻塞赋值的对比
如果使用阻塞赋值:
a = b;
b = a;
由于阻塞赋值是“顺序执行”,当前语句执行完毕后才执行下一条,因此:
- a 得到 b 的旧值;
- b 再次被赋值为 a,即 b 依然是原来的 b 的值。
最终,a 和 b 的值都会变成 b 的原始值,交换并未真正发生。
4.1 仿真波形结果(阻塞赋值)

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