《深入理解阻塞和非阻塞赋值.ppt》由会员分享,可在线阅读,更多相关《深入理解阻塞和非阻塞赋值.ppt(70页珍藏版)》请在三一文库上搜索。
1、深入理解阻塞和非阻塞赋值的不同,概述,1、阻塞赋值对应的电路往往与触发沿没有关系,只与输入电平的变化有关系。阻塞赋值符号: = 2 非阻塞赋值对应的电路结构往往与触发沿有关系,只有在触发沿时才有可能发生赋值的情况。非阻塞赋值符号: =,阻塞赋值和非阻塞赋值,阻塞(Blocking)赋值方式 “= ”, 如 b = a; 赋值语句执行完后,块才结束。 b的值在赋值语句执行完后立刻就改变的。 非阻塞(Non_Blocking)赋值方式 “”,如 b = a; 块结束后才完成赋值操作。 b的值并不是立刻就改变的。 这是一种比较常用的赋值方法(特别在编写可综合时序模块时)。,阻塞赋值,阻塞赋值操作符用
2、等号(即 = )表示。阻塞赋值时先计算等 号右手方向(RHS)部分的值,这时赋值语句不允许任何别的 语句的干扰,直到现行的赋值完成时刻,即把RHS赋值给 LHS 的时刻,它才允许别的赋值语句的执行。 一般可综合的阻塞赋值操作在RHS不能设定有延迟,(即使 是零延迟也不允许)。若在RHS 加上延迟,则在延迟期间会阻 止赋值语句的执行, 延迟后才执行赋值,这种赋值语句是不可 综合的,在需要综合的模块设计中不可使用这种风格的代码。 阻塞赋值的执行可以认为是只有一个步骤的操作: 所谓阻塞的概念是指在同一个always块中,其后面的赋值语 句从概念上(即使不设定延迟)是在前一句赋值语句结束后再 开始赋值
3、的。,非阻塞赋值,非阻塞赋值操作符用小于等于号 (即 = )表示。在赋值操 作时刻开始时计算非阻塞赋值符的RHS表达式,赋值操作时刻 结束时更新LHS。在计算非阻塞赋值的RHS表达式和更新LHS期 间,其他的Verilog语句,包括其他的Verilog非阻塞赋值语句 都能同时计算RHS表达式和更新LHS。非阻塞赋值允许其他的 Verilog语句同时进行操作。 非阻塞赋值的操作可以看作为两个步骤的过程: 1)在赋值时刻开始时,计算非阻塞赋值RHS表达式。 2)在赋值时刻结束时,更新非阻塞赋值LHS表达式。 非阻塞赋值操作只能用于对寄存器类型变量进行赋值,因此只 能用在“initial”块和“al
4、ways”块等过程块中。非阻塞赋值不 允许用于连续赋值。,难点,1 何时使用非阻塞赋值,何时使用阻塞赋值才能设计出符合要求的电路。 2 在可综合风格的Verilog模块的设计中,为什么还要用非阻塞赋值,以及符合IEEE 标准的Verilog 仿真器究竟如何来处理非阻塞赋值的仿真。 本小节明确地提出可综合的Verilog模块编程在使用赋 值操作时应注意的要点,按照这些要点来编写代码就 可以避免在Verilog 仿真时出现冒险和竞争的现象,非阻塞赋初值导致错误,阻塞赋初值正确,综合后波形对比,仿真波形对比,要 点,1 在描述组合逻辑的always块中用阻塞赋值,则综 合成组合逻辑的电路结构。 2
5、在描述时序逻辑的always块中用非阻塞赋值,则 综合成时序逻辑的电路结构。 RHS 方程式右手方向的表达式或变量可分别缩写为: RHS表达式或RHS变量。 LHS 方程式左手方向的表达式或变量可分别缩写为: LHS表达式或LHS变量。,深入理解阻塞的概念,阻塞赋值的执行可以认为是只有一个步骤的操作: 计算RHS并更新LHS,此时不能允许有来自任何其他Verilog 语句的干扰。 所谓阻塞的概念是指在同一个always块中,其后面的赋 值语句从概念上(即使不设定延迟)是在前一句赋值语句结 束后再开始赋值的。 如果在一个过程块中阻塞赋值的RHS变量正好是另一个过 程块中阻塞赋值的LHS变量,这两
6、个过程块又用同一个时钟 沿触发,这时阻塞赋值操作会出现问题,即如果阻塞赋值的 次序安排不好,就会出现竞争。若这两个阻塞赋值操作用同 一个时钟沿触发,则执行的次序是无法确定的。,深入理解非阻塞的概念,非阻塞赋值的操作可以看作为两个步骤的过程: 1 在赋值时刻开始时,计算非阻塞赋值RHS表达式。 2 在赋值时刻结束时,更新非阻塞赋值LHS表达式。 非阻塞赋值操作只能用于对寄存器类型变量进行 赋值,因此只能用在“initial”块和“always”块等过程块中。非阻塞赋值不允许用于连续赋值。,阻塞赋值,非阻塞赋值,阻塞赋值,wire din reg a, b, c; always (posedge
7、ck) begin a= din; b=a; c=b; end,非阻塞赋值,wire din reg a, b, c; always (posedge ck) begin a= din; b=a; c=b; end,wire din reg a, b, c; always (posedge ck) begin a= din; b=a; c=b; end,阻塞赋值,非阻塞赋值,wire din reg a, b, c; always (posedge ck) begin a= din; b=a; c=b; end,D,CK,Q,DIN,CK,C,C=DIN;,CK,C,A,B,Verilog模块
8、编程要点,1 时序电路建模时,用非阻塞赋值。 2 锁存器电路建模时,用非阻塞赋值。 3 用always块建立组合逻辑模型时,用阻塞赋值。 4 在同一个always块中建立时序和组合逻辑电路 时,用非阻塞赋值。 5 在同一个always块中不要既用非阻塞赋值又用阻 塞赋值。 6 不要在一个以上的always块中为同一个变量赋值。 7 用$strobe系统任务来显示用非阻塞赋值的变量值 8 在赋值时不要使用 #0 延迟,Verilog 的层次化事件队列,说明,事件是按照一定的规则被加入到5 个区域中任意一个区域的,但是只从其中的“活跃事件”区域出队,出队之后该事件将会立刻执行。 仿真器首先按照仿真
9、时间对事件进行排序,然后再在当前仿真时间里按照事件的优先级顺序进行排序。 活跃事件是优先级最高的事件。在活跃事件之间,它们的执行顺序是随机的。阻塞赋值(=)、连续赋值Cassign) 以及非阻塞赋值的右式计算等都属于活跃事件。 将来仿真时间里的所有事件都将暂时存放在将来事件队列中。当仿真器前进到某个时刻后,该时刻的所有事件也会被分类到当前仿真时间事件队列中,而仿真时刻未到的事件则仍然留在将来事件队列中。,自触发always块,例3 使用阻塞赋值的非自触发振荡器 module osc1 (clk); output clk; reg clk; initial #10 clk = 0; always
10、 (clk) #10 clk = clk; endmodule,例4 采用非阻塞赋值的自触发振荡器,module osc2 (clk); output clk; reg clk; initial #10 clk = 0; always (clk) #10 clk = clk; Endmodule,移位寄存器模型,下图表示是一个简单的移位寄存器方框图。,例5 不正确地使用的阻塞赋值来描述移位寄存器。(方式 #1),module pipeb1 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2, q1; alway
11、s (posedge clk) begin q1 = d; q2 = q1; q3 = q2; end endmodule,D的值赋给Q1以后,再执行Q2 = Q1;同样在Q2的值更新以后,才执行Q3 = Q2。这样,最终的计算结果就是Q3 = D。即在每一个clk边沿,输入值被无延迟地传到q3的输出。这很明显并没有建立一个流水线而只是为一个寄存器建模-,例6 用阻塞赋值来描述移位寄存器也是可行的,但这种风格并不好。(方式 #2 ) module pipeb2 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2
12、, q1; always (posedge clk) begin q3 = q2; q2 = q1; q1 = d; end endmodule 阻塞赋值被仔细地安排了次序以使得行为仿真正确。这种建模同样也可以得到正确的综合结果,例7 不好的用阻塞赋值来描述移位时序逻辑的风格(方式 #3) module pipeb3 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2, q1; always (posedge clk) q1 = d; always (posedge clk) q2 = q1; always (
13、posedge clk) q3 = q2; endmodule,阻塞赋值”被安排在不同的always块里面。这样Verilog标准允许以任意的次序来仿真执行3个always块-这也许会使得该流水线仿真结果产生错误,因为这产生了Verilog竞争条件。由不同的always块执行顺序会产生不同的结果。尽管这样,它的综合结果将是正确的! 这就意味着综合前仿真和综合后仿真不匹配,例8 不好的用阻塞赋值来描述移位时序逻辑的风格(方式 #4) module pipeb4 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2,
14、 q1; always (posedge clk) q2 = q1; always (posedge clk) q3 = q2; always (posedge clk) q1 = d; endmodule,颠倒了一下赋值次序,对实际仿真次序却不产生决定作用 ,综合结果是对的,但是仿真结果也许不正确,例9 正确的用非阻塞赋值来描述时序逻辑的设计风格 #1 module pipen1 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2, q1; always (posedge clk) begin q1 = d;
15、 q2 = q1; q3 = q2; end endmodule,注意,*仿真器首先按照仿真时间对事件进行排序,然后再在当前仿真时间里按照事件的优先级顺序进行排序。 *活跃事件是优先级最高的事件。在活跃事件之间,它们的执行顺序是随机的。阻塞赋值(=)、连续赋值(assign)以及非阻塞赋值的右式计算等都属于活跃事件。,例10 正确的用非阻塞赋值来描述时序逻辑的设计风格 #2 module pipen2 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2, q1; always (posedge clk) beg
16、in q3 = q2; q2 = q1; q1 = d; end endmodule,例11 正确的用非阻塞赋值来描述时序逻辑的设计风格 #3 module pipen3 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2, q1; always (posedge clk) q1 = d; always (posedge clk) q2 = q1; always (posedge clk) q3 = q2; endmodule,例12 正确的用非阻塞赋值来描述时序逻辑的设计风格 #4 module pipen4
17、 (q3, d, clk); output 7:0 q3; input 7:0 d; input clk; reg 7:0 q3, q2, q1; always (posedge clk) q2 = q1; always (posedge clk) q3 = q2; always (posedge clk) q1 = d; endmodule,例题总结,四种阻塞赋值设计方式中有一种可以保证仿真正确 四种阻塞赋值设计方式中有三种可以保证综合正确 四种非阻塞赋值设计方式全部可以保证仿真正确 四种非阻塞赋值设计方式全部可以保证综合正确 虽然在一个always块中正确的安排赋值顺序, 用阻塞赋值也可以
18、实现移位寄存器时序流水线逻 辑。但是,用非阻塞赋值实现同一时序逻辑要相对 简单,而且,非阻塞赋值可以保证仿真和综合的结 果都是一致和正确的。因此建议大家在编写Verilog 时序逻辑时要用非阻塞赋值的方式。,时序反馈移位寄存器建模,线性反馈移位寄存器(Linear Feedback Shift-Register 简称LFSR)是带反馈回路的 时序逻辑 。,module lfsrb1 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1; assign n1 = q1 q3; always (posedge
19、clk or negedge pre_n) if (!pre_n) begin q3 = 1b1; q2 = 1b1; q1 = 1b1; end,else begin q3 = q2; q2 = n1; q1 = q3; end endmodule,线性反馈移位寄存器,RTL级电路,思考:真正的LFSR电路图是什么样子?,用阻塞赋值描述的线性反馈移位寄存器,其功能正确,但模型的含义较难理解。 module lfsrb2 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or n
20、egedge pre_n) if (!pre_n) q3,q2,q1 = 3b111; else q3,q2,q1 = q2,(q1q3),q3; endmodule 把所有赋值弄到一个等式的方式(one-line equations)来避免使用临时变量编码显得更难于理解尤其当涉及的表达式更大更长时,编写代码和调试都变得比较困难,因此不鼓励使用这种风格,用非阻塞语句描述的LFSR,可综合,其功能正确。,module lfsrb1 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; wire n1; assign n1 =
21、 q1 q3; always (posedge clk or negedge pre_n) if (!pre_n) begin q3 = 1b1; q2 = 1b1; q1 = 1b1; end,else begin q3 = q2; q2 = n1; q1 = q3; end endmodule,RTL级电路,例 用非阻塞语句描述的LFSR,可综合其功能正确。 module lfsrn2 (q3, clk, pre_n); output q3; input clk, pre_n; reg q3, q2, q1; always (posedge clk or negedge pre_n) if
22、 (!pre_n) q3,q2,q1 = 3b111; else q3,q2,q1 = q2,(q1q3),q3; endmodule,移位寄存器设计,1 含同步预置功能的移位寄存器设计 REG86:0=REG87:1;,工作时序图,模式可控的移位寄存器设计,2 使用移位操作符设计移位寄存器,移位寄存器的RTL图,阻塞赋值及一些简单的例子,例13 module dffb (q, d, clk, rst); output q; input d, clk, rst; reg q; always (posedge clk) if (rst) q = 1b0; else q = d; endmodul
23、e,虽然可行也很简单,但我们不建议这种用阻塞赋值来描述D触发器模型的风格。 如果要把所有的模块写到一个always块里,是可以采用阻塞赋值得到正确的建模、仿真并综合成期望的逻辑。但是,这种想法将导致使用阻塞赋值的习惯,而在较为复杂的多个always块的情况下可能会导致竞争冒险,例14 使用非阻塞赋值来描述D触发器是建议使用的风格 module dffx (q, d, clk, rst); output q; input d, clk, rst; reg q; always (posedge clk) if (rst) q = 1b0; else q = d; endmodule,小结,从上面介
24、绍的移位寄存器的例子以及LFSR 的例子,建议使用非阻塞赋值实现时序逻辑。 而用非阻塞赋值语句实现锁存器也是最为安全 的 。 原则1 :时序电路建模时,用非阻塞赋值。 原则2 :锁存器电路建模时,用非阻塞赋值。,组合逻辑使用阻塞赋值,用Verilog可以有很多种方法为组合逻辑建模,但是当使用always块来为组合逻辑建模时,应该使用阻塞赋值(blocking assignment)。 如果在某个always块里面只有一个赋值(表达),使用阻塞或者非阻塞赋值都可以正确工作。但是如果您对养成好的编码习惯有兴趣的话,还是要“总是用阻塞赋值对组合逻辑建模”。 对于简单的组合alwasys块不仅可以用于
25、时序逻辑,也可以用于组合逻辑,但是当always块中有多个赋值语句时使用了不含延迟(delay)的非阻塞赋值会造成仿真不正确,或者要使仿真正确您需要另外的添加敏感事件列表(sensitivity list entries),和“多输入路径”(multiple passes)来贯穿always 块以使得仿真正确。因而从仿真的时间效率角度看也不合算。,例module ao4 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2; always (a or b or c or d) begin tmp1 = a end endm
26、odule y输出建立在3个依次执行的顺序上。由于非阻塞赋值的LHS变量值更新是 在对RHS表达式估值之后,所以tmp1和tmp2的值仍然是该always块上一个 输入口的值而不是在这一个仿真时间步(simulation time step)结束 时被更新的值。因此y的值将受旧的tmp1和tmp2影响,而不是这次扫描过 的always块内被更新的值。,例 module ao5 (y, a, b, c, d); output y; input a, b, c, d; reg y, tmp1, tmp2;5 always (a or b or c or d or tmp1 or tmp2) beg
27、in tmp1 = a end endmodule,不同之处在于tmp1和tmp2被添加到事件列表中去了,当非阻塞赋值更新LHS变量时,always块将会“自触发”并使用最新的tmp1和tmp2来更新y输出。现在y输出值正确了因为增加使用了两条“登入路径”(two passes)贯穿整个always块。使用更多的“登入路径”来贯穿always块等于降低仿真器的性能,因此如果可以有合理的一些代码变化可以取代这 种用法的话,就尽量避免这种用法。,例 使用阻塞赋值实现组合逻辑是推荐使用的编码风格。 module ao2 (y, a, b, c, d); output y; input a, b, c
28、, d; reg y, tmp1, tmp2; always (a or b or c or d) begin tmp1 = a end endmodule,小结,原则3 :用always块描述组合逻辑时,应采 用阻塞赋值语句,时序和组合的混合逻辑使用非阻塞赋值,有时候将简单的组合逻辑和时序逻辑写在一起很方便。当把 组合逻辑和时序逻辑写到一个always块中时,应遵从时序逻辑建模的原则,使用非阻塞赋值,如例所示。 例2 在一个always块中同时实现组合逻辑和时序逻辑 module nbex2 (q, a, b, clk, rst_n); output q; input clk, rst_n;
29、 input a, b; reg q; always (posedge clk or negedge rst_n) if (!rst_n) q = 1b0; / 时序逻辑 else q = a b;/ 异或,为组合逻辑 endmodule,例 将组合和时序逻辑分别写在两个always块中,一 个是纯粹的时序逻辑(使用非阻塞赋值),另一个是纯粹的组合逻辑(使用阻塞赋值)建模 module nbex1 (q, a, b, clk, rst_n); output q; input clk, rst_n; input a, b; reg q, y; always (a or b) y = a b; a
30、lways (posedge clk or negedge rst_n) if (!rst_n) q = 1b0; else q = y; endmodule,小结,原则4:在同一个always块中描述时序和组合逻辑混合电路时,用非阻塞赋值。,其它混合“阻塞”与“非阻塞”赋值建模方针,Verilog语法并没有禁止将阻塞和非阻塞赋 值自由地组合在一个always块里。虽然 Verilog语法是允许这种写法的,但我们不建 议在可综合模块的编写中采用这种风格,例24 在always块中同时使用阻塞和非阻塞赋值的例子。 module ba_nba2 (q, a, b, clk, rst_n); out
31、put q; input a, b, rst_n; input clk; reg q; always (posedge clk or negedge rst_n) begin: ff reg tmp; if (!rst_n) q = 1b0; else begin tmp = a end end endmodule,仿真和综合都将是正确的,因为“阻塞”与“非阻塞”赋值不是针对同一个变量来的。尽管这可以“正常工作”,但是不推荐这种风格。,例 对同一变量既进行阻塞赋值,又进行非阻塞赋值有时会产生综合错误。 module ba_nba6 (q, a, b, clk, rst_n); output q
32、; input a, b, rst_n; input clk; reg q, tmp; always (posedge clk or negedge rst_n) if (!rst_n) q = 1b0; / 对 q进行阻塞赋值 else begin tmp = a / 对 q进行非阻塞赋值 end endmodule,小结,原则5:不要在同一个always块中同时使用阻塞和非阻塞赋值,对同一变量进行多次赋值,在一个以上always块中对同一个变量进行多 次赋值可能会导致竞争冒险,即使使用非阻 塞赋值也可能产生竞争冒险。在下例中,两 个always块都对输出q进行赋值。由于两个 always块
33、执行的顺序是随机的,所以仿真时 会产生竞争冒险,例25 使用非阻塞赋值语句,由于两个always块对同一变量q赋值产生竞争冒险的程序: module badcode1 (q, d1, d2, clk, rst_n); output q; input d1, d2, clk, rst_n; reg q; always (posedge clk or negedge rst_n) if (!rst_n) q = 1b0; else q = d1; always (posedge clk or negedge rst_n) if (!rst_n) q = 1b0; else q = d2; endm
34、odule,在synplify Pro下综合,产生错误信息: E: Only one always block may assign a given variable q,综合出现的错误,小结,原则6:严禁在多个always块中对同一个变量赋值,常见的对于非阻塞赋值的误解,1 非阻塞赋值和$display 误解1: “使用$display命令不能用来显示非阻塞语句的赋值” 事实是: 非阻塞语句的赋值在所有的$display命令执行以后才更新数值 2 #0 延时赋值 误解2: “0延时把赋值强制到仿真时间步的末尾” 事实是: 0延时将赋值事件强制加入停止运行事件队列中。,例 module dis
35、play_cmds; reg a; initial $monitor(“$monitor: a = %b“, a); initial begin $strobe (“$strobe : a = %b“, a); a = 0; a = 1; $display (“$display: a = %b“, a); #1 $finish; end endmodule,下面是上面模块的仿真结果说明$display命令的执行是安排在活动事件队列中,但排在非阻塞赋值数据更新事件之前。 $display: a = 0 $monitor: a = 1 $strobe : a = 1,例28 module nb_s
36、chedule1; reg a, b; initial begin a = 0; b = 1; a = b; b = a; $monitor (“%0dns: $monitor: a=%b b=%b“, $stime, a, b); $display (“%0dns: $display: a=%b b=%b“, $stime, a, b); $strobe (“%0dns: $strobe : a=%b b=%bn“, $stime, a, b); #0 $display (“%0dns: #0 : a=%b b=%b“, $stime, a, b); #1 $monitor (“%0dns:
37、 $monitor: a=%b b=%b“, $stime, a, b); $display (“%0dns: $display: a=%b b=%b“, $stime, a, b); $strobe (“%0dns: $strobe : a=%b b=%bn“, $stime, a, b); $display (“%0dns: #0 : a=%b b=%b“, $stime, a, b);,#1 $finish; end endmodule 下面是上面模块的仿真结果说明0延时命令在非阻塞赋值事件发生前,在停止运行事件队列中执行。 0ns: $display: a=0 b=1 0ns: #0
38、: a=0 b=1 0ns: $monitor: a=1 b=0 0ns: $strobe : a=1 b=0 1ns: $display: a=1 b=0 1ns: #0 : a=1 b=0 1ns: $monitor: a=1 b=0 1ns: $strobe : a=1 b=0,小结,原则7:用$strobe系统任务来显示用非阻塞赋值的变量值,对同一变量进行多次非阻塞赋值,误解3: “在Verilog语法标准中未定义可在同一个always块中对某同一变量 进行多次非阻塞赋值”。 事实是: Verilog标准定义了在同一个always块中可对某同一变量进行多次非阻塞赋值但多次赋值中,只有最
39、后一次赋值对该变量起作用。 “非阻塞赋值按照语句的顺序执行,请看下例: initial begin a = 0; a = 1; end 执行该模块时,有两个非阻塞赋值更新事件加入到非阻塞赋值更新队列。以 前的规则要求将非阻塞赋值更新事件按照它们在源文件的顺序加入队列,这 便要求按照事件在源文件中的顺序,将事件从队列中取出并执行。因此,在 仿真第一步结束的时刻,变量a被设置为0,然后为1。” 结论:最后一个非阻塞赋值决定了变量的值。,总结,原则1:时序电路建模时,用非阻塞赋值。 原则2:锁存器电路建模时,用非阻塞赋值。 原则3:用always块写组合逻辑时,采用阻塞赋值。 原则4:在同一个always块中同时建立时序和组合逻辑电路时,用非阻塞赋值。 原则5:在同一个always块中不要同时使用非阻塞赋值和阻塞赋值。 原则6:不要在多个always块中为同一个变量赋值。 原则7:用$strobe系统任务来显示用非阻塞赋值的变量值 原则8:在赋值时不要使用 #0 延迟,
链接地址:https://www.31doc.com/p-2157930.html