1. 阻塞赋值(Blocking Assignment)
语法:
=
行为:
顺序执行:代码逐行执行,赋值操作会阻塞后续代码,直到当前赋值完成。
立即生效:赋值后的新值在当前仿真时间步即可被后续代码使用。
适用场景:
组合逻辑:用于描述组合逻辑(如
always @(*)
),需要即时更新信号。Testbench中的顺序操作:在测试平台中按顺序生成激励信号。
代码示例:
always @(*) begin a = x; // 立即赋值,a的值立即更新 b = a + y; // 使用更新后的a计算b end
2. 非阻塞赋值(Non-blocking Assignment)
语法:
<=
行为:
并行执行:所有右侧表达式同时计算,赋值操作在当前时间步结束时统一更新。
保持旧值:在同一个
always
块中,后续代码仍使用赋值前的旧值。
适用场景:
时序逻辑:用于描述触发器或寄存器(如
always @(posedge clk
),避免竞争条件。跨时钟域同步:确保多个寄存器的值同步更新。
代码示例:
always @(posedge clk) begin a <= x; // 计算x的值,但a在时间步结束时更新 b <= a + y; // 使用赋值前的旧a值计算b end
关键区别对比
特性 | 阻塞赋值 (= ) |
非阻塞赋值 (<= ) |
---|---|---|
执行顺序 | 顺序执行,阻塞后续代码 | 并行执行,不阻塞后续代码 |
生效时机 | 立即生效(当前时间步) | 延迟生效(时间步结束时) |
适用逻辑 | 组合逻辑 | 时序逻辑 |
硬件映射 | 组合电路(如多路选择器) | 时序电路(如触发器、寄存器) |
竞争风险 | 高(可能导致仿真与综合不一致) | 低(仿真行为接近实际硬件) |
经典示例
示例1:交换两个值
// 阻塞赋值(顺序执行)
always @(posedge clk) begin
a = b; // a立即变为b的值
b = a; // b变为新的a值(即原b的值)
end
// 结果:a和b的值互换!
// 非阻塞赋值(并行执行)
always @(posedge clk) begin
a <= b; // 记录b的值,但a尚未更新
b <= a; // 记录a的旧值
end
// 结果:a和b的值互换!
阻塞赋值:实际交换(因为立即生效)。
非阻塞赋值:实际交换(因为并行记录旧值)。
总结
阻塞赋值:用于组合逻辑或测试平台的顺序操作。
非阻塞赋值:用于时序逻辑的寄存器更新。
正确使用两种赋值方式是避免Verilog代码出现竞争条件和功能错误的关键!
-
黄金规则:
在
always @(posedge clk)
中始终使用非阻塞赋值。在
always @(*)
中始终使用阻塞赋值。