阻塞赋值和非阻塞赋值
1 阻塞赋值(Blocking Assignment)
阻塞赋值是Verilog HDL中一种赋值方式,使用等号“=”作为运算符。使用阻塞赋值时,赋值语句会立即执行,并且会阻塞后续语句的执行,直到当前赋值完成。它模拟的是串行执行的硬件行为。
1.1 主要特点:
-
立即更新:赋值立即生效,后续语句可以立即看到更新后的值
-
顺序执行:在一个 begin…end 块中,语句是按顺序执行的。下一条语句必须等待上一条阻塞赋值语句执行完毕后才能开始执行。这和C语言或Python等软件编程语言中的赋值行为非常相似。
1.2 主要用途:
-
组合逻辑
always @(*)或always @(a or b ...)块中)。 -
因为组合逻辑的输出在理想情况下就是输入信号变化的即时反映,这种立即更新的特性正好可以模拟组合逻辑的行为
1.3 代码示例:
always @(*) begin
a = 1; // 步骤1:a立即变为1
b = a; // 步骤2:b获得a的新值(1)
c = b + 1; // 步骤3:c = 1 + 1 = 2
end
//执行顺序
// 时刻0: a = 1 → a变为1
// 时刻1: b = a → b变为1(使用更新后的a)
// 时刻2: c = b+1 → c变为2(使用更新后的b)
2 非阻塞赋值(Non-Blocking Assignment)
非阻塞赋值使用小于等于号“<=”作为运算符。它模拟硬件的并发行为,即所有赋值语句在always块开始时同时评估(Evaluate),但实际更新(Update)发生在块结束时或特定事件后。这不会阻塞后续语句的执行。
2.1 主要特点:
-
延迟更新:当仿真器遇到一个非阻塞赋值语句时,它会立即计算右侧表达式(RHS)的值,但不会立刻更新左侧的变量(LHS)。相反,它会将这个“更新计划”放到一个事件队列里。
-
并行执行:在当前仿真时间步(time step)结束时,仿真器才会执行所有被计划的更新。这意味着在一个 begin…end 块中,所有非阻塞赋值语句的RHS都是在“同一时刻”计算的,LHS也是在“同一时刻”更新的。可以理解为它们是并行发生的。
2.2 主要用途:
-
时序逻辑 (always @(posedge clk) 块中)。
-
它完美地模拟了由同一个时钟边沿触发的所有触发器(Flip-Flops)同时采样输入数据,并同时更新自己输出的状态。
2.3 代码示例:
always @(posedge clk) begin
a <= 1; // 记录:a将要变为1
b <= a; // 记录:b将要变为a的旧值
c <= b + 1; // 记录:c将要变为b的旧值+1
end
//执行分为两个阶段:
//阶段1:评估(Evaluate)- 同时进行
// 计算右侧表达式:
// - a的新值 = 1
// - b的新值 = a的当前值(假设为3)
// - c的新值 = b的当前值 + 1(假设b当前为5,则为6)
//阶段2:赋值(Update)- 同时更新
// 在当前时间步结束时,执行所有更新:
//在当前时间片结束时,所有左侧变量同时更新:
//- a = 1
//- b = 3(旧的a值)
//- c = 6(旧的b值+1)
二者的区别比较
| 方面 | 阻塞赋值 (=) | 非阻塞赋值 (<=) |
|---|---|---|
| 符号 | = | <= |
| 执行机制 | 立即执行,阻塞后续语句 | 调度执行,非阻塞后续语句 |
| 模拟行为 | 顺序逻辑,类似软件 | 并发逻辑,类似硬件翻转器 |
| 适用逻辑 | 组合逻辑、测试台 | 时序逻辑、寄存器 |
| 潜在风险 | 可能引入竞争条件 | 如果混合使用,可能不一致 |
| 示例应用 | 临时变量计算 | 移位寄存器或流水线 |
总之,在Verilog设计中,推荐在描述组合逻辑时使用阻塞赋值,在描述时序逻辑时使用非阻塞赋值,以匹配硬件的实际工作原理。