verilog阻塞与非阻塞的初步理解(三)

下面这段源码是因为习惯不好,出现不正确波形的例子。

module pwm_division(reset,clkin,clkout);
  input reset,clkin;
  output clkout;
  reg clkout;
  reg[7:0] count;
  always @(posedge clkin)
  begin
    if(!reset)
      begin
        clkout<=0;
        count<=8'd0;
      end
    else
      begin
         count<=count+8'd1;
         $display("count1=%d",count);
        if(count<8'd21)
          clkout<=1;
        else 
          begin
            clkout<=0;
            if(count>8'd30)
              begin
               $display("count2=%d",count);
              count=8'd0;//
               $display("count3=%d",count);
             end
          end
           $display("count4=%d",count);
     
      end
      $display("count5=%d",count);
       $strobe("count6=%d",count);
  end
endmodule

$display是后来加进去的。

首先来分析下这段代码。本意是输出一个占空比为1/3的波形,但是结果却是一个20/236的波形。意思也就是count=8'd0这个操作没有起作用。

后来检查到count=8'd0是阻塞赋值,猜测出错的原因可能是同一段always语句中即有非阻塞赋值又有阻塞赋值。改为count<=8'd0;后,波形正确。

进一步分析,在代码中加入系统显示函数,发现了有趣的事情。运行仿真后得到结果:

# count1= 31
# count2= 31
# count3=  0
# count4=  0
# count5=  0
# count6= 32

可以看出,count=8'd0;这个操作是有效的。但是在下一个clk到来时,count的值还是等于原值加1。这是为什么呢?

我暂时理解如下:

阻塞操作是在赋值时右值立即等于左值,而非阻塞操作是先计算右值,而在模块结束是才更新左值。

所以count<=count+8'd1;执行后,count+1的值(右值)已计算,但是真正改变count的值(左值)是在模块的最后。所以会有上面的结果。为了证明上面的理解

我改为count=count+8'd1;执行后,count的值正确。在书上找到一段层次化事件队列的描述支持我这种理解:

参考 《Verilog数字系统设计教程 第三版》197页。

 

上面是错误原因的分析,但是程序肯定不能这样写。导致会出现这种错误的原因是因为程序不严谨,进一步说是因为我刚学verilog,C语言顺序执行的思想已经根深蒂固,而verilog有并行执行的语句,所以很容易出现隐藏的逻辑错误。

posted @ 2014-03-21 09:19  Weyne  阅读(598)  评论(0编辑  收藏  举报