> 文章列表 > Verilog阻塞与非阻塞赋值详解

Verilog阻塞与非阻塞赋值详解

Verilog阻塞与非阻塞赋值详解

基本概念

        关于阻塞赋值=)和非阻塞赋值(<=), 阻塞赋值(=)是顺序敏感的,非阻塞赋值(<=)是顺序独立的。阻塞赋值按它们在程序块中列出的顺序顺序执行。当它们被执行时,它们会立即对抽象 reg 的内容产生影响,阻塞必须在执行下一个赋值之前执行。非阻塞赋值在对左侧抽象 reg 进行赋值之前,评估程序块中每个语句右侧的表达式,并同时执行。
                                                顺序敏感和顺序独立示例

// Blocking assignment executes sequentially .
initial begin
a=#12 1;
b=#3  0;
c=#2  3;// Non-blocking assignment executes in parallel.
initial begin
d <=#12 1;
e <=#3  0;
f <=#2  3;
end

        阻塞赋值是顺序敏感,而非阻塞赋值是顺序独立的。从两份代码所仿真的时序图所示,abc是顺序执行的,而非阻塞赋值def则是同时执行。这也意味着,如果改变阻塞赋值语句顺序,那么会得到不同的结果。如下面2段代码改变阻塞赋值顺序综合出结果不同,前面代码综合出1个DFF,而第二段代码综合出2个DFF。

always @(posedge clk) 
beginrega = data;regb = rega;
end
always @(posedge clk) 
beginregb = rega;rega = data;
end

而对于非阻塞赋值,仅改变语句的顺序并不会改变结果,如下面2段代码综合出的结果是一样的。

reg qa,qb,qc;
always @(posedge clk) 
beginqa <= a;qb <= qa;qc <= qb;
end
reg qa,qb,qc;
always @(posedge clk) 
beginqc <= qb;qb <= qa;qa <= a;
end

为什么always块组合逻辑使用阻塞赋值?

always @(a or b or c or d)     begint1 = a & b;t2 = c | d;out = t1 & t2;end
always @(a or b or c or d or t1 or t2)   begint1 <= a & b;t2 <= c | d;out <= t1 & t2;end

        上面2段代码综合出的结果都是一致的,区别就是当使用非阻塞赋值时,敏感列表需要加上t1t2。对于具有阻塞分配的 always 块,always 块的敏感列表包含组合电路的所有输入 abc 和 d。每次输入改变时,总是阻塞,因此输出结果,必须重新评估。此时 always 块中的语句是按顺序执行的,输入的最新值用于确定 t1 和 t2,最后使用新的 t1 和 t2 计算出。

        在具有非阻塞赋值的 always 块中,语句是同时执行的。因此,当敏感列表中信号改变触发always块执行时,out 将使用 t1 和 t2 的旧值,因为它们的新值尚不可用。为确保在组合电路中具有相同的行为,除了电路的输入信号之外,还应将电路的内部信号 t1 和 t2 放入敏感列表中。每次更新 t1 和 t2 的值时,这将重新触发(重新进入)always 块,使输出最终能够计算其新值。然而,这个模型相对复杂并且可能会造成混淆,所以always模块组合电路的描述应该使用阻塞赋值。

为什么always模块描述时序使用非阻塞?

always @(posedge clk)     begint1 = a & b;t2 = t1 & c;out = t1 & t2;end
always @(posedge clk)   begint1 <= a & b;t2 <= t1 & c;out <= t1 & t2;end


        对于具有阻塞赋值的always块,在clk的每一个上升沿,三个赋值顺序执行。因此,t1 使用 clk 的上升沿处的 a 和 b 的值更新,然后 t2clk上升沿使用 t1 的新值和c的值更新自己的值。最后,使用 t1 和 t2 的新值评估 out。可以看出,t1t2只是用于临时存储,方便对复杂表达式进行分区;它们不代表真正的硬件寄存器输出,甚至可能被优化掉。值得注意的是,组合电路已经优化为 out = t1&t2 = (a&b)&(t1&c) = (a&b)&(a&b&c) = a&b&c = t1&c = t2。

        对于具有非阻塞赋值的 always 块,在clk的每个上升沿处,同时执行三个赋值:(1)t1 在 clk 的上升沿处由 和 b 的值更新(2)同时t2使用旧t1的值(其新值此时不可用)和 在 clk 的上升沿更新值,以及(3)同时使用 t1的旧值和t2的旧值(它们的新值此时不可用)更新 out

        如图所示,阻塞和非阻塞分配描述了完全不同的时序电路。根据阻塞和非阻塞分配的行为,它们分别表示一个和三个触发器。也就是说,当 t1 和 t2 使用阻塞赋值来描述时,它们是组合输出而不是时序输出。因此,该模型可能会非常混乱,所以时序的always模块仅使用非阻塞赋值。

总结:

本文主要介绍阻塞赋值和非阻塞赋值的基本概念和运行机理,以及分析不同always块应该使用阻塞还是非阻塞,在记住相关规则的情况下,能理解原因也是非常重要的。亦安以Clifford E. Cummings的论文中关于阻塞和非阻塞所描述的原则结束本篇文章:

  1. 在时序的模块中使用非阻塞赋值。

  2. 当使用always块来描述组合逻辑时,使用阻塞赋值。

  3. 当在同一个always块中描述时序和组合逻辑时,使用非阻塞赋值。

  4. 在同一个always块中不要混合使用阻塞和非阻塞赋值。

摘自“亦安的数字小站”