Verilog 仿真竞争是什么?为什么时序逻辑要用 <=?
更新时间:2026-09-21。问题驱动短答页。赋值规则见 阻塞 vs 非阻塞,亚稳态见 metastability。
一句话回答
Verilog 在同一时钟沿会触发多个 always 块,但标准不规定它们 active 事件区里的执行顺序。若时序逻辑用阻塞 =,一个块在等号右端(RHS)读到的可能是另一个块“本拍刚更新”的值,于是结果取决于块的调度先后,产生 RTL 仿真不确定、且与门级(物理上所有触发器同时翻转)不一致的竞争。非阻塞 <= 把所有 RHS 在时钟沿先采样、把左端(LHS)更新统一推迟到本时间步末尾的 NBA 区,保证大家看到的都是上一拍的值,正好对应硬件行为。
调度机制(简化)
一个时间步内主要有:
- Active 区:计算阻塞 RHS、对非阻塞只先求 RHS,触发 always;
- NBA(Non-Blocking Assign)区:统一执行所有非阻塞的 LHS 更新;
- 之后才进入监控/显示等区域。
verilog
// 危险:两个块都在 posedge clk 用阻塞
always @(posedge clk) a = b; // 若先执行
always @(posedge clk) b = a; // 读到的可能是新 a,顺序相关用 <= 后,两块在 active 区都采样旧值,到 NBA 区才同时换值,谁先调度都一样,和真实触发器一致。
经验法则
- 时序逻辑(
always @(posedge clk)):统一用非阻塞<=。 - 组合逻辑(
always @(*)):用阻塞=,因为本块内就是要立即传递。 - 不要在同一个 always 块里混用
=和<=驱动同一变量。 - 不要用
#0去“手动排序”掩盖竞争,那只是把更新推后一个区、可读性差且不解决根因。
常见坑
- 在时钟块里用
=又把该信号接到另一个时钟块输入,仿真结果依赖文件内顺序。 - RTL 仿真过、门级仿真挂:典型就是时序逻辑误用阻塞。
- 用
#0、加零延时做 workaround,掩盖真正的赋值规则错误。 - testbench 在时钟沿用阻塞驱动 DUT 输入,造成采样/驱动同区竞争;激励通常应在沿外用非阻塞或错开时间驱动。
总结
Verilog 不规定同一时钟沿多个 always 块在 active 区的执行顺序,时序逻辑若用阻塞赋值、RHS 又读到本拍被改的寄存器,就会产生顺序相关、和门级不一致的竞争;非阻塞赋值把所有右端采样放在 active、左端更新统一推到 NBA 区,使所有触发器看到的都是上一拍值,精确对应硬件,所以时序逻辑用 <=、组合逻辑用 = 是必须遵守的铁律。