verilog中的阻塞赋值与非阻塞赋值详解

网上看到的觉得不错分享下

组合逻辑的always模块中使用阻塞赋值;
时序逻辑的always模块中使用非阻塞赋值;
可以这样理解,组合中计算马上赋值,时序逻辑中上升沿计算,下降沿赋值

关键是组合逻辑中是实时变化的,而时序逻辑中一个cycle才变化一次
比如:
always @(a or b)
begin
    c = a + b;
end
always @(posedge clk)
begin
    if(rst)
      c <= 0;
   else
     c <= a + b;
end
在组合逻辑的always block中,a和b的变化都会引起c值的变化;
而时序逻辑中c至少会维持一个clock cycle,也就是说如果a和b的变化导致c变化的时间是在下一个clock的上升沿,而不会让c立刻改变

阻塞赋值“=”与非阻塞赋值“<=”的本质区别在于:
非阻塞赋值语句右端表达式计算完后并不立即赋值给左端,而是同时启动下一条语句继续执行,可以将其理解为所有的右端表达式RHS1、RHS2等在进程开始时同时计算,计算完后 ,等进程结束时同时分别赋给左端变量LHS1、LHS2等;
而阻塞赋值语句在每个右端表达式计算完后立即赋给左端变量,即赋值语句LHS1=RHS1执行完后LHS1是立即更新的,同时只有LHS1=RHS1执行完后才可执行语句LHS1=RHS2,依次类推。前一条语句的执行结果直接影响到后面语句的执行结果。

补充文章:
在always语句块中,verilog语言支持两种类型的赋值:阻塞赋值和非阻塞赋值。阻塞赋值使用“=”语句;非阻塞赋值使用“<=”语句。注意,千万不要将这两种赋值方法与assign赋值语句混淆起来,assign赋值语句根本不允许出现在always语句块中。
     位于begin/end块内的多条阻塞赋值语句是串行执行的,这一点同标准的程序设计语言是相同的。但是多条非阻塞赋值语句却是并行执行的,这些非阻塞赋值语句都会在其中任何一条语句执行完成之前开始执行。这正是硬件电路的特点,因为实际的逻辑门电路都是独立运转的,而不是等到其他门电路运转结束之后自己才开始运转。
    下面我们以描述移位寄存器的两种方法为例来讲述两种赋值类型的区别。在下面的这种描述中,第一个触发器中的数据被移到第二个触发器中,第二个触发器中的数据被移到第三个触发器中……如此继续下去,直到最后一个触发器中的数据被移出该寄存器为止。
verilog中的阻塞赋值与非阻塞赋值详解_第1张图片  1   module  shiftreg ( input  clk,  2                     input  sin,  3                    outout  reg  [ 3 : 0 ]q); // 这是正确使用非阻塞赋值的实例  4          always  @( posedge  clk)  5          begin  6         q[ 0 <=  sin; // 非阻塞赋值:<=  7         q[ 1 <=  q[ 0 ];  8         q[ 2 <=  q[ 1 ]  9         q[ 3 <=  q[ 2 ]; 10          // 这里写作q <= {q[2:0],sin};更简单更好一些 11          end 12   endmodule
 非阻塞赋值语句的功能是使得所有语句右侧变量的值都同时被赋给左侧的变量。因此,在上面的实例中,q[1]得到的是q[0]的原始值,而非sin的值(在第一条语句中,sin的值被赋给了q[0])。这正是我们期望得到的实际硬件电路。当然,我们可以把上边的四条语句合并写成一条简短的语句:q<={q[2:0],sin}。
阻塞赋值语句的功能更接近于传统的程序设计语言,但是阻塞赋值语句并不是准确的硬件工作模型。下面考虑使用阻塞赋值语句来实现同一模块可以得到什么结果。在始终clk的上升沿,verilog将会把sin的值赋给q[0],然后q[0]的新值被赋给q[1],如此继续执行下去。最终所有的四个寄存器都会得到相同的值:sin的值。
     本部分内容用意在于:讲述使用always语句块对时序逻辑电路进行建模的时候,如何使用非阻塞赋值。如果设计者能够充分的灵活应用,比如倒转上例中四条语句的顺序,那么使用阻塞赋值语句仍然能实现相应的功能,但是与使用非阻塞赋值的方法相比,这种方法并不会带来任何好处,相反还暗藏了巨大的风险。
     最后需要注意的是:每个always语句块都隐含表示一个独立的逻辑电路模块。因此,对于特定的reg类型的变量,只能在一个always语句块中对其进行赋值;否则就可能会出现两个硬件模块同时从同一个输出端口输出数据的情况,这种情况一般称为短路输出(shorted output)。

过程赋值语句多用于对reg型变量进行复制,过程赋值有阻塞复制和非阻塞赋值两种。

非阻塞赋值的符号为:<=

阻塞赋值符号为:=

 

(1)非阻塞赋值的例子:

reg c,b;

always@(posedge clk)

begin

b <= a;

c <= b;

end

verilog中的阻塞赋值与非阻塞赋值详解_第2张图片

(2)阻塞赋值的例子:

reg c,b;

always @ (posedge clk)

begin

b = a;

c = b;

end

verilog中的阻塞赋值与非阻塞赋值详解_第3张图片

上述例子中,使用非阻塞赋值方法,其中的每个<=都可以理解为一个寄存器。而在同一

个时钟下面采用的非阻塞赋值方法,模块内所有寄存器都同时随时钟跳变。这是硬件处理

的精髓,也是时序电路中大量使用非阻塞赋值的原因。

在实际书写verilogHDL代码的过程中,对于always中reg型变量,如果不是处理组合逻

辑,尽量不使用阻塞赋值的方法。这主要是基于代码的可综合性考虑的,因为在verilog

HDL代码编译的时候,对于有些从后编译的编译器,阻塞赋值会找成时序上与预想的不

一致。对于以上阻塞赋值的例子,采用非阻塞方法应该写为

 

reg c,b;

always @ (posedge clk)

begin

b <= a;

c <= a;

end

实现电路和原方法一样。

而在always用于组合逻辑中,采用阻塞赋值表明未使用寄存器。

reg a,A,B,f_a;

always @ (a or A or B)

begin

f_a = a ?A : B;

end

以下是使用阻塞和非阻塞赋值应遵循的一些基本原则,这些原则有利于防止竞态(race condition)的发生。

1)当用always块来描述组合逻辑(combinationallogic)时,应当使用阻塞赋值。

(2)对于时序逻辑(sequentiallogic)的描述和建模,应当使用非阻塞赋值。

(3)在同一个always模块中,最好不要混合使用阻塞赋值和非阻塞赋值,对同一变量

既进行阻塞赋值,又进行非阻塞赋值,在综合时会出错。所以always中要么全部使用非

阻塞赋值,要么把阻塞赋值和非阻塞赋值分在不同的always中书写。

(4)尽量不要再在多个不同的always块中对同一变量赋值。

(5)使用$strobe显示使用非阻塞赋值的变量。

Verilog HDL中,有两种过程性赋值方式,即阻塞式(blocking)和非阻塞式(non-blocking)。这两种赋值方式看似差不多,其实在某些情况下却有着根本的区别,如果使用不当,综合出来的结果和你所想得到的结果会相去甚远。


Tip:所谓过程性赋值就是指在initialalways语句内的赋值,它只能对寄存器数据类型的变量赋值。


阻塞式    blocking         的操作符为   


非阻塞式(non-blocking   的操作符为  <= 


首先,我们通过两个例子来看看这两种赋值方式的区别,这里使用的综合工具为Qt ii


1:非阻塞式赋值


module


test_non_blocking


(


input             clk,


input             testa,


input             testb,


input             testc,


input             testd,


output  reg       testout


);


reg testreg;


 


always @ (posedge clk)


begin


  testreg<= testb | testc;


  begin  


    if(testa) begin


      testout<= testreg & testd;


    end 


    elsebegin


      testout<= testd;


    end


  end


end


endmodule


1综合后的结果为

verilog中的阻塞赋值与非阻塞赋值详解_第4张图片

2:阻塞式赋值


module


test_blocking


(


input             clk,


input             testa,


input             testb,


input             testc,


input             testd,


output  reg       testout


);


reg testreg;


 


always @ (testa,testb.testc,testd)


begin


  testreg = testb |testc;


  begin  


    if(testa) begin


      testout= testreg & testd;


    end 


    elsebegin


      testout= testd;


    end


  end


end


endmodule


2综合后的结果为:

verilog中的阻塞赋值与非阻塞赋值详解_第5张图片

分析:


可以看到,例1和例2code写法完全一样,只是在always语句块中使用了不同的赋值方式,就导致综合出来的结果不同。


在例1中,是非阻塞式赋值方式,非阻塞式赋值的赋值对象总是在当前仿真时刻结束时被赋值,所以,当在对语句2中的testout赋值这一时刻, testreg 值还没有得到语句1中的新值,而是原来的值(即上一个时刻的值)。


而在例2中,使用了阻塞式赋值方式,就是在always语句块中是一句一句执行的。在执行语句2之前,语句1就已经执行完成,testreg被赋好了新值,所以语句2中的testreg值取的是新值。


建议:


1. 阻塞式赋值用于组合逻辑建模;


2. 非阻塞式赋值用于时序逻辑建模。

你可能感兴趣的:(Hardware)