阻塞赋值和非阻塞赋值有什么区别?
更新时间:2026-09-21。问题驱动短答页。完整代码走读见 FPGA 入门 阻塞与非阻塞赋值,组合/时序划分见 Verilog 组合与时序。
一句话回答
描述触发器(时序逻辑)用非阻塞 <=,描述组合逻辑用阻塞 =。 非阻塞语句的右式在当前时间步先全部采样、到时间步末尾再统一更新左式,正好模拟一堆寄存器在同一个时钟沿并行锁存;阻塞语句则立即生效、按书写顺序执行,像 C 语言赋值。
对照
阻塞 = | 非阻塞 <= | |
|---|---|---|
| 更新时机 | 立即,下一句就能看到新值 | 时间步末尾统一更新,块内看到旧值 |
| 模拟对象 | 导线/组合逻辑 | 寄存器/触发器 |
| 适用 always | @(*) 组合块 | @(posedge clk) 时序块 |
| 执行语义 | 顺序 | 块内并行采样 |
正反例
移位寄存器必须非阻塞——三行要表现为同一时钟沿的三级 D 触发器:
verilog
always @(posedge clk) begin
q1 <= d;
q2 <= q1; // 采样的是本周期开始时的旧 q1,而非刚赋的新值
q3 <= q2;
end若误用 =,仿真里 q1 立刻更新,q2 当场拿到新值,三级寄存器被「优化」成一级,行为全错。
组合逻辑用阻塞,且敏感列表用 @(*):
verilog
always @(*) begin
and_out = a & b;
y = and_out | c; // 立即依赖上一句,符合组合链路
end三条工程口诀
- 时序逻辑
always @(posedge clk)里,所有被赋值 reg 一律<=。 - 组合逻辑
always @(*)里用=,且不要遗漏任何右值信号(用@(*)自动补全)。 - 不要在同一个 always 块里对同一变量混用两种赋值,也尽量别一个 always 既有时序又有组合输出。
常见坑
- 在时钟块里用
=实现「先算后存」:仿真看似对,综合出的寄存器级数和预期不符,换工具/换顺序结果漂移。 - 在
@(*)块里用<=:组合逻辑出现「更新延迟一拍」的假象,还可能推断出锁存器。 - 非阻塞块里用「上一句的结果」做下一句输入——块内看到的都是旧值,要拿新值就该拆组合逻辑或单独 wire。
- 以为非阻塞等于「慢一拍」:它不是加延迟,而是同一时间步的并行更新语义。
总结
阻塞 = 立即顺序更新,适合 @(*) 组合逻辑;非阻塞 <= 在时间步末尾统一更新、块内读到旧值,精确模拟同一时钟沿上多个触发器并行采样,因此时序逻辑 @(posedge clk) 必须用 <=,移位寄存器用 = 会被错误压成一级;记住组合用 =、时序用 <=、同一变量两者不混用,就能避免仿真与综合不一致的绝大多数问题。