在Verilog HDL(硬件描述语言)中,阻塞赋值(blocking assignment)和非阻塞赋值(non-blocking assignment)是两种不同的赋值语句,它们在行为模拟和综合成硬件电路时有着不同的含义和效果。
一. 解释
阻塞赋值(Blocking Assignment):所有的非阻塞赋值语句之间顺序没有关系,前面是否执行完不影响后面的语句,所有的语句都是并发执行。
阻塞赋值使用=
操作符。当一个变量使用阻塞赋值进行更新时,赋值操作会立即完成,这意味着后续的语句将使用新赋的值。在顺序代码块(如always
块)中,阻塞赋值会按照出现的顺序依次执行,直到所有赋值完成。这种赋值方式在行为级仿真中表现得像C语言中的赋值,但在综合时可能导致意外的同步问题。
示例:
reg a, b,c;
always @(*) begin
b = a + 1; // 阻塞赋值
c = b + 1; // 这里的c已经是b+1的值了
end
非阻塞赋值(Non-Blocking Assignment):当前的阻塞赋值语句未完成之前,后面的语句是不执行的。
非阻塞赋值使用<=
操作符。当一个变量使用非阻塞赋值进行更新时,赋值操作不会立即生效,而是会在当前时钟周期的结束时才真正更新变量的值。这意味着在同一个always
块中,所有非阻塞赋值语句都是在时钟边沿后同时更新的。这种方式更符合硬件电路的行为,尤其是在同步数字电路设计中。
示例:
reg a, b;
always @(posedge clk) begin
a <= b + 1; // 非阻塞赋值
b <= a + 1; // 这里的a仍然是上一个时钟周期的值
end
二. 应用场景
阻塞赋值和非阻塞赋值在Verilog HDL中有着不同的应用场景,它们的选择直接影响着代码的可读性、可综合性和最终硬件电路的行为。下面分别讨论这两种赋值方式的典型应用场景:
阻塞赋值的应用场景:
-
组合逻辑电路设计:
当你设计的模块只包含组合逻辑,没有时序逻辑(例如没有触发器和寄存器),阻塞赋值可以自由使用,因为组合逻辑的输出只依赖于输入,没有状态保持的需求。 -
行为级建模或测试平台:
在编写测试平台或者行为级模型时,阻塞赋值可以更容易地理解和调试,因为它们的执行顺序和结果更接近于软件编程中的赋值语句。 -
算法实现:
当Verilog用于算法实现或数据流描述时,阻塞赋值可以更直观地表达算法流程,尤其是在不需要关注具体硬件细节的场合。 -
即时反馈:
如果你需要在赋值后立即使用新值,比如在计算链中,阻塞赋值可以确保这一点。
非阻塞赋值的应用场景:
-
时序逻辑电路设计:
在设计含有时序逻辑的模块时,如状态机、寄存器传输级(RTL)电路等,非阻塞赋值是首选。这是因为它们在时钟边沿统一更新,避免了竞争条件和同步问题。 -
硬件综合:
当你的目标是生成可综合的代码,以便最终转化为实际的硬件电路,非阻塞赋值应当被优先使用。它们更符合硬件工程师的预期,易于理解和维护。 -
多线程或并行处理:
在涉及并发操作的场景中,非阻塞赋值可以避免数据依赖性错误,确保每个操作都在时钟周期的末尾统一更新。 -
状态机实现:
在状态机设计中,非阻塞赋值可以确保状态转移在下一个时钟周期开始时生效,这是状态机正确工作的基础。