tag 标签: 阻塞赋值

相关博文
  • 热度 23
    2016-4-10 13:51
    1533 次阅读|
    1 个评论
       Verilog HDL  语言中存在两种赋值语言 :非阻塞型赋值语句,阻塞型赋值语句            在Verilog HDL中阻塞赋值"="和非阻塞赋值"="有着很大的不同.个人认为,作为初学者要掌握可综合风格的Verilog模块编程的8个原则,在综合布局布线的仿真中避免出现竞争冒险现象。(关于竞争冒险,后续会有单独章节记录)   (1)  时序电路建模时,用非阻塞赋值。   (2)  锁存器电路建模时,用非阻塞赋值。   (3)  用always块建立组合逻辑模型时,用阻塞赋值。   (4)  在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值。   (5)  在同一个always块中不要既用非阻塞赋值又用阻塞赋值。   (6)  不要在一个以上的always块中为同一个变量赋值。   (7)  用$strobe系统任务来显示用非阻塞赋值的变量值。   (8)  在赋值时不要使用 #0延时。     用"="或者是"="实际上对应的是不同的硬件电路 1.  非阻塞型语句 以赋值操作符“=”来标识的赋值操作称为“非阻塞型过程赋值( NonblockingAssignment)”。 非阻塞型过程赋值语句的特点是: ( 1) 在 begin-end 串行语句块中,一条非阻塞过程语句的执行不会阻塞下一条语句的执行,也就是说在本条非阻塞型过程赋值语句对应的赋值操作执行完之前,下一条语句也可以开始执行。 ( 2) 仿真过程在遇到非阻塞型过程赋值语句后首先计算其右端赋值表达式的值,然后等到仿真时间步结束时再将该计算结果赋值变量。也就是说,这种情况下的赋值操作是在同一仿真时刻上的其他普通操作结束后才得到执行的   下面看一下例程非阻塞赋值的代码和仿真: module non_block(clk,rst_n,a,b,c,out); input clk; input rst_n;// _n低电平有效 input a,b,c; output reg  out; /*output reg的使用.reg是寄存器型变量的意思,一般在用时序逻辑编程时定义.而output代表此变量为输出。实际上两者并没有直接联系,但是如果这个变量既是时序逻辑(寄存器型)变量又要求输出,就既需要output,也需要reg...一般always@语句中赋值的变量都要声明为reg型*/ reg   d; always@(posedge clk or negedge rst_n) if(!rst_n) out = 2'b0;//三位数相加以1+1+1=3为例 转化为二进制 位宽两位 else begin d = a+b; out = d + c; end endmodule RTL视图: 仿真结果: 下面我们改变一下代码 always@(posedge clk or negedge rst_n) if(!rst_n) out = 2'b0;//三位数相加以1+1+1=3为例 转化为二进制 位宽两位 else begin //d = a+b; out = d + c;//注意改变后的代码 d = a+b; end RTL视图: 从上面两张RTL视图中我们可以看到,在非阻塞赋值语句改变代码后,RTL结果不变 仿真结果:   2. 阻塞型语句    以赋值操作符“ =”来标识的赋值操作称为“阻塞型过程赋值( blocking Assignment)”。非阻塞型过程赋值语句的特点是: ( 1) 串行块( begin-end)中的各条阻塞型过程赋值语句将以它们在顺序块后排 列次序依次得到执行。 ( 2)阻塞型过程赋值语句的执行过程是:首先计算右端赋值表达式的值,然后 立即将计算结果赋值给“ =”左端的被赋值变量。   阻塞型过程赋值语句的这两个特点表明:仿真进程在遇到阻塞型过程赋值 语句时将计算表达式的值并立即将其结果赋给等式左边的被赋值变量;在串行语 句块中,下一条语句的执行会被本条阻塞型过程赋值语句所阻塞,只有在当前这 条阻塞型过程赋值语句所对应的赋值操作执行完后下一条语句才能开始执行。 下面看一下例程阻塞赋值的代码和仿真: always@(posedge clk or negedge rst_n) if(!rst_n)     out = 2'b0;//三位数相加以1+1+1=3为例 转化为二进制 位宽两位 else begin     d = a+b;     out = d + c; end RTL视图: 仿真结果: 下面我们改变一下代码 if(!rst_n)     out = 2'b0; else begin     //d = a+b;     out = d + c;//注意改变后的代码     d = a+b; end RTL视图: 改变后与改变前的图明显不同,多出一个寄存器,我们可以看到,阻塞语句中最后生成结果与程序语句的先后顺序很重要的关联 仿真结果:   以上的仿真代码: `timescale 1ns/1ns `define clock_period 20   //定义时钟 module block_tb; reg clk; reg rst_n; reg a,b,c; wire  out;                  block block0(.clk(clk),.rst_n(rst_n),.a(a),.b(b),.c(c),.out(out)); //block block0(clk,rst_n,a,b,c,out); initial clk = 1; always#(`clock_period/2) clk = ~clk; initial begin rst_n = 1'b0; a = 0; b = 0; c = 0; #(`clock_period*200 + 1); rst_n = 1'b1; #(`clock_period*200); a = 0 ; b = 0 ; c = 0; #(`clock_period*200); a = 0 ; b = 0 ; c = 1; #(`clock_period*200); a = 0 ; b = 1 ; c = 0; #(`clock_period*200); a = 0 ; b = 1 ; c = 1; #(`clock_period*200); a = 1 ; b = 0 ; c = 0; #(`clock_period*200); a = 1 ; b = 0 ; c = 1; #(`clock_period*200); a = 1 ; b = 1 ; c = 0; #(`clock_period*200); a = 1 ; b = 1 ; c = 1; #(`clock_period*200); #(`clock_period*200); $stop; end endmodule 注:例化中注释的部分是Augus失误所写成的代码,但是仿真结果全都都对,Augus当时像发现"新大陆"一样激动,在此解释一下,正确的例化: block block0(.clk(clk),.rst_n(rst_n),.a(a),.b(b),.c(c),.out(out)); //block block0(clk,rst_n,a,b,c,out);   而我失误写的虽然对了,原因是该程序相对简单且端口较少,且 默认来模块定义的接口来一一对应, 例化(XXX)的顺序和定义的端口顺序相同,所以侥幸未出现失误   由于本人能力有限,出错之处的请各位给予帮助. 谢谢! PS:  前期多为整理(博主使用例程自己学习过程中的理解记录的笔记)  更多资料参考                 发烧友小梅哥专版 http://bbs.elecfans.com/zhuti_fpga_1.html                     梦翼师兄的炼狱传奇
  • 热度 19
    2015-3-24 13:41
    1039 次阅读|
    0 个评论
       EDN博客精华文章   作者: tengjingshu   在很多时候都要对输入脉冲进行边沿检测,如PS/2时序,ps2_data数据在ps2_clk时钟下降沿接收。   边沿检测Verilog程序代码:   布线布局后仿真波形如下图: 点击看原图   可以注意到其中的移位寄存器用了非阻塞赋值(=) ps2_clk_r0=ps2_clk; ps2_clk_r1=ps2_clk_r0; ps2_clk_r2=ps2_clk_r1;   如果用阻塞赋值的话,综合的时候会把其中两个寄存器去点,用阻塞赋值(=) ps2_clk_r0=ps2_clk; ps2_clk_r1=ps2_clk_r0; ps2_clk_r2=ps2_clk_r1;   会出来这样的警告:   WARNING:Xst:646 - Signal ps2_clk_r0 is assigned but never used.       Register ps2_clk_r2 equivalent to ps2_clk_r1 has been removed       Found 1-bit register for signal ps2_clk_r1.   WARNING:Xst:2677 - Node ps2_clk_r1 of sequential type is unconnected in block DetecEdge. 点击看原图   从RTL可以看到,只剩ps2_clk_r1一个D触发器。 点击看原图   上图是非阻塞赋值综合后的RTL,可以看出,有三个D触发器做移位寄存器。通过移位,对边沿进行检测。   程序工程文件下载: http://space.ednchina.com/upload/2009/3/24/86bb5e63-8959-47c4-af52-10ac330554d3.rar   代码还有一种写法:   布线布局后仿真波形和之前程序的仿真波形一样 点击看原图   程序文件下载: http://space.ednchina.com/upload/2009/3/24/5286d476-c43c-4b99-b658-5d53865ce19a.rar    参考资料:   1)verilog的PS2键盘解码——特权’s blog    http://blog.ednchina.com/ilove314/153929/message.aspx   2) 模拟PS2协议,丢掉4x4键盘,来用标准键盘吧!_阿虚的电子小屋    http://hi.baidu.com/aokikyon/blog/item/e46dc2368d9f76350a55a99a.html
  • 热度 13
    2015-3-24 13:33
    999 次阅读|
    0 个评论
       Verilog HDL中,有两种过程赋值方式,即 阻塞赋值 (blocking)和非阻塞赋值(nonblocking)。阻塞赋值执行时,RHS(right hand statement)估值与更新LHS(left hand statement)值一次执行完成,计算完毕,立即更新。在执行时阻塞同块中的其他语句的执行。阻塞式(blocking)的操作符为 “ = ”。它的执行很像传统程序设计语言。非阻塞赋值RHS估值与更新LHS值分两步执行。在单位仿真周期开始时RHS估值,在同一单位仿真周期末更新LHS 值,不阻塞同块中其他语句的执行。非阻塞式(non-blocking)的操作符为 “ = ”,它的执行更像并行电路,使描述电路更自然。阻塞赋值与非阻塞赋值是Verilog HDL程序设计的难点,它们既有共同点,也有差异,深入剖析其异同,对于硬件程序的开发具有重大意义。   1 Verilog 事件处理机制    层积事件列 (The Stratified Event Queue)是一个事件管理概念模型,而非硬件逻辑。模型内事件的具体实现与EDA软件生产商的算法策略有关。在IEEE-2001中,Verilog把事件分为5个不同部分,按照时间顺序如图1所示。   触发的任何事件可以加入到这5个事件列中的任何事件列中,但只能从活跃事件列中移出。即上面的5个事件列中的事件最后都将被激活而放入活跃事件列中。层积事件列是层次模型,层积事件列的执行顺序是按优先级排列的。任何EDA软件都只能执行活跃事件。其他事件列都按优先级级别依次激活本列事件以供执行。   1.1 活跃事件列   由图1可见,大部分事件都被放入活跃事件列。活跃事件列里包括非阻塞赋值RHS估值。但是,非阻塞赋值的更新不是在活跃事件列,它被列成独立的非阻塞更新事件列。活跃事件列是仿真的执行源,从一开始执行活跃事件列到活跃事件列执行完毕称为一个仿真周期。活跃事件列中的事件可以触发活跃或非活跃等其他事件。当活跃事件列中的所有事件执行完后,EDA软件会按优先级依次触发其余事件列以供仿真执行。但在当前活跃事件列中的事件执行顺序是不确定的。    1.2 非活跃事件列   发生在当前仿真时间里并且在活跃事件列执行完后执行的事件列,即非活跃事件列执行优先级仅次于活跃事件列。如带PLI例程的回调过程(tf_synchronize()、vpi_register_cb(cb_readwrite))。非活跃事件列中的事件亦可以触发其他事件。如果触发了优先级更高的活跃事件,非活跃事件列中的其余事件执行后移。   1.3 非阻塞赋值更新事件列   活跃事件列中的每个非阻塞赋值RHS估值,都会触发一个与之对应的非阻塞赋值更新事件,这些事件被放在非阻塞赋值更新事件列中,执行优先级次于活跃与非活跃事件列。非阻塞赋值更新事件亦可以触发其他事件。若在非阻塞赋值更新事件列中,存在多个对同一变量的先后赋值,只有最后一个有效,其余值将被覆盖。   1.4 监控事件列   监控事件列被放在非阻塞赋值更新事件列后。由此可见,用监控事件列中的监控命令监控得到的值都是赋值后的值,活跃事件列$display系统命令则可以查看非阻塞更新前的值。   1.5 未来事件列   在执行事件时,如果事件含有延时,为不阻碍仿真的继续执行,该事件将被挂起而放入未来事件列。未来事件包含未来非活跃事件和未来非阻塞赋值更新事件。   理解阻塞与非阻塞赋值就需要深入理解层积事件列,层积事件列反应了Verilog事件处理机制。   2 应用及分析   通常非阻塞赋值产生寄存器等存储元件,对应的物理器件是带存贮功能的元件,如寄存器、触发器等。阻塞赋值则对应网线(wire)类型,通常与物理连线对应。这是两种赋值方式的最明显的差异,也是时序逻辑用非阻塞、组合逻辑用阻塞的重要原因。但这并不是绝对的,事实上阻塞赋值对应网线(wire)型,亦可对应寄存器(reg)型;阻塞赋值也能生成存贮元件,因此不能片面理解。在组合逻辑里,锁存器可能引发测试问题,带来隐患。说明在建模时,首先要从硬件出发来考虑问题,应先在头脑中形成电路结构,由于赋值方式的不同,综合结果差异甚大,运用不当很可能会导致建模失败。阻塞赋值在时序逻辑中亦有着重要应用,在需要实时更新的组合逻辑中只有阻塞赋值能满足要求。   以下示例代码的功能是计算传送过来的data中1和0的个数。   reg count0,count1;   always @(posedge clk,negedge Rst_n)   begin   if(!Rst_n)   ...   else   begin   count0 = 0;                        //语句1    count1 = 0;                         //语句2   for(i = 0;i = 11;i = i+1)   begin   if(data == 1)   count1 = count1 + 1;        //语句3   else if(data == 0)   count0 = count0 - 1;       //语句4   else   count0 = count0 + 0;     //防止生成锁存器   end   end   end   在这段代码里,count0、count1的值必须在每次计数之前被清零,count0、count1必须实时更新。显然,只有 阻塞赋值 能满足要求。非阻塞赋值分两步完成,所有的更新事件在单位仿真周期末同时执行,只有最后一个值有效,所以非阻塞赋值无法完成计数任务。阻塞赋值却能很好地胜任,因为阻塞赋值估值和更新一次性完成。   事件上,在时序逻辑中经常碰到上述实时更新问题,非阻塞赋值往往无法实现,如用阻塞赋值则可很好地解决问题。   正如阻塞赋值在时序逻辑中有重要应用一样,非阻塞赋值在组合逻辑中亦有不可替代的应用。在组合逻辑中用非阻塞赋值可以把组合逻辑改造成流水线。可执行如下所示纯组合逻辑代码,将生成纯组合逻辑,综合结果如图2所示。   input a,b,c,clk,sel;   output out;   reg out,temp;   always @(posedge clk)   begin   temp  = a b;     //语句1   if(sel)   out  = temp | c;   //语句2   else   out  = c;       //语句3   end   若把上面代码中语句1、语句2、语句3阻塞赋值(" = ")改为非阻塞赋值(" = "),则综合结果如图3所示。      流水线设计方法在高性能、需经常进行大规模运算的组合逻辑中可以到广泛运用。   在组合逻辑中,如在begin、end块中同时有许多非阻塞赋值,则它们的赋值顺序是并发的。实际上它们赋予的都是上一个时钟送入寄存器的值。这与使用同一时钟沿触发的许多在同一个使能控制信号下赋值完全一致,并且这种赋值因为数据保存在寄存器中,当时钟沿到来时都已稳定,所以存入的数值是可靠的。用这种方法可以避免由组合逻辑产生的竞争冒险 。   在相关应用中,非阻塞赋值能较好地解决零时刻竞争冒险问题。因为非阻塞赋值分两步完成,非阻塞赋值更新事件是在所有活跃与非活跃事件执行完之后执行,能确保所有敏感变量值在零时刻都被触发 。   在同一always块混合使用阻塞赋值与非阻塞赋值,利弊共存,混合使用的结果可能事半功倍,亦可能功亏一篑。只有了解其处理机制,深刻理解阻塞与非阻塞赋值底层实现的异同,方可灵活运用。   本文通过Verilog事件处理机制,详细讨论了阻塞与非阻塞赋值的区别、联系及其应用示例。由本文可知,阻塞与非阻塞赋值灵活多变,底层实现也差异甚大。因而在数字电路设计时,依据预期功能,从硬件实现出发,斟酌差异,仔细选用阻塞与非阻塞赋值才能有效避免出错,缩短开发周期。
  • 热度 16
    2015-3-14 20:56
    848 次阅读|
    0 个评论
      阻塞和非阻塞语句作为verilog HDL语言的最大难点之一,一直困扰着FPGA设计者,即使是一个颇富经验的设计工程师,也很容易在这个点上犯下一些不必要的错误。阻塞和非阻塞可以说是血脉相连,但是又有着本质的差别。理解不清或运用不当,都往往会导致设计工程达不到预期的效果,而其中的错误又很隐晦。下面我给大家谈谈阻塞和非阻塞语句的本质区别和在FPGA设计中的不同运用。   阻塞语句   顾名思义,即本条语句具有影响下一条语句的作用,在同一个进程always中,一条阻塞赋值语句的执行是立刻影响着下条语句的执行情况和结果。如果该条语句没有执行完,那么下条语句不可能进入执行状态的,因此,从字面层上理解,该条语句阻塞了下面语句的执行。阻塞语句最能体现verilog HDL和C语言之间的血缘关系,比如,在时钟沿触发的always进程里,若先执行b=c,再执行a=b,那么本质上,在一个时钟沿触发里面,a=c成立,即是说,不要b变量,直接在进程里赋值a=c,结果是一样的。这和c语言中b=c,a=b性质相同。   非阻塞语句   非阻塞语句应该来说,更能体现硬件电路的特点。这正是非阻塞语句广泛应用于时序逻辑电路的原因。接上面的例子,如果在一个时钟沿触发的always进程里面,b=c,a=b那么就不可能直接在进程里面赋值a=c.因为c的值要经过两个时钟延迟才传到a里面,即c若从0变为1,那么要经过两个clk上升沿才传到a,a的值才从0变为1。两次赋值正是体现了两个时钟延迟的特点。这种特点即是非阻塞语句非阻塞的的原因导致的,就是说,a=b,不会因为b=c没有执行完毕而不执行,只要时钟触发进程,那么a=b,b=c同时执行。所以,如果c为 1,b为0,a为1的话,那么在在非阻塞语句的进程里面,一个时钟沿到来,由于他们之间是同时执行的,所以把c的1赋给了b,把b的0赋给了a,但是在阻塞语句里面,c的1先给了b,然后b把新赋值的1又给了a,那么a在一个时钟之后即变成了1。(在一次触发进程里,无论是阻塞和非阻塞语句,每条语句只能执行一次)   所以从上面的介绍里面,可以看出,阻塞语句是顺序执行的,而非阻塞语句是同时执行的,那么,如何在设计里面运用好阻塞语句和非阻塞语句呢,总体上来讲,遵循大体原则:阻塞语句运用在组合逻辑电路设计里面,非阻塞语句运用在时序逻辑电路设计里面。但是一般来讲,一个设计往往包含着组合逻辑和时序逻辑。可以再细分为以下几个情况,并可以用阻塞语句和非阻塞语句不同的设计来区别讨论它们之间的优缺点,进一步理解清楚。。。。。。(最直观的说法就是如下仿真一下:观察out1~out4的变化,就明白了! `timescale 1ns/100ps module test1(); reg clk; reg sigin; reg out1; reg out2; reg out3; reg out4; //assign #10 out3 = sigin; always #10 clk=~clk; always #70 sigin = ~sigin; initial begin sigin = 1'b0; clk= 1'b0; out1 =1'b0; out2 =1'b0; end always @(sigin) begin $display('%d',$time); out1=sigin; out2= out1; out3 = sigin; out4 = out3; $display('%d',$time); end endmodule #1: 当为时序逻辑建模,使用“非阻塞赋值”。 #2: 当为锁存器(latch)建模,使用“非阻塞赋值”。 #3: 当用always块为组合逻辑建模,使用“阻塞赋值” #4: 当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。 #5: 不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。 #6: 不要在两个或两个以上always块里面对同一个变量进行赋值。 #7: 使用$strobe以显示已被“非阻塞赋值”的值。 #8: 不要使用#0延迟的赋值。
  • 热度 18
    2013-9-16 10:56
    1157 次阅读|
    0 个评论
      阻塞和非阻塞语句作为verilog HDL语言的最大难点之一,一直困扰着FPGA设计者,即使是一个颇富经验的设计工程师,也很容易在这个点上犯下一些不必要的错误。阻塞和非阻塞可以说是血脉相连,但是又有着本质的差别。理解不清或运用不当,都往往会导致设计工程达不到预期的效果,而其中的错误又很隐晦。下面我给大家谈谈阻塞和非阻塞语句的本质区别和在FPGA设计中的不同运用。   阻塞语句   顾名思义,即本条语句具有影响下一条语句的作用,在同一个进程always中,一条阻塞赋值语句的执行是立刻影响着下条语句的执行情况和结果。如果该条语句没有执行完,那么下条语句不可能进入执行状态的,因此,从字面层上理解,该条语句阻塞了下面语句的执行。阻塞语句最能体现verilog HDL和C语言之间的血缘关系,比如,在时钟沿触发的always进程里,若先执行b=c,再执行a=b,那么本质上,在一个时钟沿触发里面,a=c成立,即是说,不要b变量,直接在进程里赋值a=c,结果是一样的。这和c语言中b=c,a=b性质相同。   非阻塞语句   非阻塞语句应该来说,更能体现硬件电路的特点。这正是非阻塞语句广泛应用于时序逻辑电路的原因。接上面的例子,如果在一个时钟沿触发的always进程里面,b=c,a=b那么就不可能直接在进程里面赋值a=c.因为c的值要经过两个时钟延迟才传到a里面,即c若从0变为1,那么要经过两个clk上升沿才传到a,a的值才从0变为1。两次赋值正是体现了两个时钟延迟的特点。这种特点即是非阻塞语句非阻塞的的原因导致的,就是说,a=b,不会因为b=c没有执行完毕而不执行,只要时钟触发进程,那么a=b,b=c同时执行。所以,如果c为 1,b为0,a为1的话,那么在在非阻塞语句的进程里面,一个时钟沿到来,由于他们之间是同时执行的,所以把c的1赋给了b,把b的0赋给了a,但是在阻塞语句里面,c的1先给了b,然后b把新赋值的1又给了a,那么a在一个时钟之后即变成了1。(在一次触发进程里,无论是阻塞和非阻塞语句,每条语句只能执行一次)   所以从上面的介绍里面,可以看出,阻塞语句是顺序执行的,而非阻塞语句是同时执行的,那么,如何在设计里面运用好阻塞语句和非阻塞语句呢,总体上来讲,遵循大体原则:阻塞语句运用在组合逻辑电路设计里面,非阻塞语句运用在时序逻辑电路设计里面。但是一般来讲,一个设计往往包含着组合逻辑和时序逻辑。可以再细分为以下几个情况,并可以用阻塞语句和非阻塞语句不同的设计来区别讨论它们之间的优缺点,进一步理解清楚。。。。。。(最直观的说法就是如下仿真一下:观察out1~out4的变化,就明白了! `timescale 1ns/100ps module test1(); reg clk; reg sigin; reg out1; reg out2; reg out3; reg out4; //assign #10 out3 = sigin; always #10 clk=~clk; always #70 sigin = ~sigin; initial begin sigin = 1'b0; clk= 1'b0; out1 =1'b0; out2 =1'b0; end always @(sigin) begin $display('%d',$time); out1=sigin; out2= out1; out3 = sigin; out4 = out3; $display('%d',$time); end endmodule #1: 当为时序逻辑建模,使用“非阻塞赋值”。 #2: 当为锁存器(latch)建模,使用“非阻塞赋值”。 #3: 当用always块为组合逻辑建模,使用“阻塞赋值” #4: 当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。 #5: 不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。 #6: 不要在两个或两个以上always块里面对同一个变量进行赋值。 #7: 使用$strobe以显示已被“非阻塞赋值”的值。 #8: 不要使用#0延迟的赋值。
相关资源