深入分析 verilog 阻塞和非阻塞赋值

1、学verilog 一个月了,在开发板上面写了很多代码,但是始终对一些问题理解的不够透彻,这里我们来写几个例子仿真出阻塞和非阻塞的区别,我们先上代码

 

module LED  
(
    CLK, RSTn,
    scan,
    flag ,
    c,
    ,one,two,three,four
);
     input CLK;
     input RSTn;
     input scan;
     output flag,c;
     output [3:0] one,two,three,four;
/***********************************************************/
    
    reg F1,F2;
    reg a,b;
    reg [3:0] one,two,three,four;
 /********************信号传递之间的非阻塞赋值***************************************/  
  always @ ( posedge CLK or negedge RSTn )   //
      if( !RSTn )
         begin
           F1 <= 1'b1;
           F2 <= 1'b1;
      end
      else
  begin
   F1 <= scan;
   F2 <= F1;
      end
 /*******************信号传递之间的阻塞赋值****************************************/  
  always @ ( posedge CLK or negedge RSTn )   //
      if( !RSTn )
         begin
           a = 1'b1;
           b = 1'b1;
      end
      else
  begin
   a = scan;
   b = a;
      end
 /******************数据加  非阻塞赋值  先判断后计数*****************************************/     
 always @ ( posedge CLK or negedge RSTn )   //one <=
  if( !RSTn )
   begin
    one<=0;
   end
  else
  begin
   if(one==14)   
    one<=0;
   else      
    one<=one+1;
  end
/***************数据加  非阻塞赋值  先计数后判断********************************************/   
 always @ ( posedge CLK or negedge RSTn )    //  two<=
  if( !RSTn )
   begin
    two<=0;
   end
  else
  begin
   two<=two+1;
   if(two==14)
    two<=0;
  end
 /**************数据加  阻塞赋值  先判断后计数*********************************************/
 always @ ( posedge CLK or negedge RSTn )  //three =
  if( !RSTn )
   begin
    three=0;
   end
  else
  begin
   if(three==14)
    three=0;
   else
    three=three+1;
  end
/*************数据加  阻塞赋值  先计数后判断**********************************************/
 always @ ( posedge CLK or negedge RSTn )  //four =
  if( !RSTn )
   begin
    four=0;
   end
  else
  begin
   four=four+1;
   if(four==14)
    four=0;
  end
            
/****************信号之间传递***********************/
 
assign flag = F2 & !F1;
assign c  = b  & !a;
/***************************************/
 
endmodule    

 

2、我使用modesim 仿真,下面为我的  test bench

 


`timescale 1 ps/ 1 ps
module LED_vlg_tst();
// constants                                          
// general purpose registers
reg eachvec;
// test vector input registers
reg CLK;
reg RSTn;
reg scan;
// wires                                              
wire c;
wire flag;
wire [3:0]  four;
wire [3:0]  one;
wire [3:0]  three;
wire [3:0]  two;

// assign statements (if any)                         
LED i1 (
// port map - connection between master ports and signals/registers  
 .CLK(CLK),
 .RSTn(RSTn),
 .c(c),
 .flag(flag),
 .four(four),
 .one(one),
 .scan(scan),
 .three(three),
 .two(two)
);

/*
initial                                               
begin                                                 
// code that executes only once                       
// insert code here --> begin                         
                                                      
// --> end                                            
$display("Running testbench");                      
end                                                   
always                                                
// optional sensitivity list                          
// @(event1 or event2 or .... eventn)                 
begin                                                 
// code executes for every event on sensitivity list  
// insert code here --> begin                         
                                                      
@eachvec;                                             
// --> end                                            
end                                                   
endmodule
*/

initial begin

CLK = 0;

forever

#10 CLK = ~CLK; 

end

initial begin

scan = 0;

forever

#100 scan = ~scan; 

end


initial begin

RSTn = 0;

#1000 RSTn =  1;

#1000;
#1000;
#1000;
#1000;
#1000;
#1000;
#1000;
#1000;
$stop;

end

endmodule

 

主要就是初始化一个CLK 和scan的信号,然后就是初始化一下复位,最后就是设置仿真时间,这样modesim 就不会一直处于仿真状态,消耗资源,也可以方便仿真。

 

其中quartus  与modesim  互相调用调试,可以关注我的博客,这里我就不具体讲解了!

 

3、modesim 波形图

 

 

大家注意到红线框内的数据变化,就能很清楚的理解 阻塞与非阻塞了!

 

 

微观分析 阻塞与非阻塞

1、上代码,具体观察,a,b,c与F1,F2,flage 的变化

 

module LED  
(
    CLK, RSTn,
    scan,
    flag ,
    a,b,c,F1,F2,
);
     input CLK;
     input RSTn;
     input scan;
     output flag,a,b,c;
     output F1,F2;
/***********************************************************/
    
    reg F1,F2;
    reg a,b;

 /***********************************************************/  
  always @ ( posedge CLK or negedge RSTn )   //
      if( !RSTn )
         begin
           F1 <= 1'b1;
           F2 <= 1'b1;
      end
      else
  begin
   F1 <= scan;
   F2 <= F1;
      end
 /***********************************************************/  
  always @ ( posedge CLK or negedge RSTn )   //
      if( !RSTn )
         begin
           a = 1'b1;
           b = 1'b1;
      end
      else
  begin
   a = scan;
   b = a;
      end
 /***********************************************************/      
assign flag = F2 & !F1;
assign c  = b  & !a;
/***************************************/
 
endmodule    

 

 

代码涵义就不讲解了

 

2、test bench 代码,与上面相同,这里不重复了

 

3、上图   看波形

 

 

 

 

 

 

 下面是一位 仁兄的博客,写的不错,转过来,也可以供大家参考

 

越是看似简单、经常接触的。我们越是不知其所以然。这就是我写本文的原因。
        阻塞和非阻塞赋值一般使用在进程中,包括always和initial进程、assign赋值等操作中。
        在Verilog HDL中,描述进程的基本语句是always和initial。always过程反复执行其中的块语句,而initial过程语句只执行一次。此外,一个assign赋值语句,一个实例元件的调用也都是一个独立的进程。
        进程只有两种状态,即执行状态和等待状态,一旦满足特定的条件,如敏感变量发送变化,进程即进入执行状态,执行完毕或遇到停止语句后,即停止执行,自动返回到起始语句,进入等待状态。

在进行数字系统设计的时候,应该注意以下几点:
        将硬件电路的行为以合理的方式映射为一些进程,对每个进程,以最合理的方式描述并实现。
        将组合逻辑实现的电路和用时序逻辑实现的电路尽量分配到不同的进程中。
        进程中输出的信号在各种可能的情况下都应该被赋值,且赋值号右端不再出现该信号。
        多个进程之间通过信号线进行通信,在设计中,为了达到多个进程协调运行,可以设置一些握手信号,在进程中检测这些握手信号的状态,以决定是否进行必要的操作。在有的设计中,这种握手信号的协调是必不可少的。
        一个进程中一般只描述对应于一个时钟信号的同步时序逻辑。
        进程必须由敏感信号的变化来启动,因此必须精心选择进程敏感表达式中的敏感变量。
——

阻塞赋值与非阻塞赋值

always @(event-expression)

    begin     <LHS1=RHS1>

                  <LHS2=RHS2>

      ......   

    end

同样可将采用非阻塞赋值方式的always进程块写成下面的形式:

always @(event-expression)

    begin     <LHS1<=RHS1>

                  <LHS2<=RHS2>

   ......

    end

        阻塞赋值“=”与非阻塞赋值“<=”的本质区别在于:非阻塞赋值语句右端表达式计算完后并不立即赋值给左端,而是同时启动下一条语句继续执行,我们 可以将其理解为所有的右端表达式RHS1、RHS2等在进程开始时同时计算,计算完后 ,等进程结束时同时分别赋给左端变量LHS1、LHS2等。
        而阻塞赋值语句在每个右端表达式计算完后立即赋给左端变量,即赋值语句LHS1=RHS1执行完后LHS1是立即更新的,同时只有LHS1=RHS1执行 完后才可执行语句LHS1=RHS2,依次类推。前一条语句的执行结果直接影响到后面语句的执行结果。
        非阻塞赋值不能用于“assign”持续赋值中。阻塞赋值则既能用于“assign”持续赋值,也能用于“initial”和“always”等过程块 中,阻塞赋值则技能用于“assign”持续赋值,也嗯那个用于“initial”和“always”等过程赋值中。
对于时许逻辑描述和建模,应尽量使用非阻塞赋值方式。

——
用阻塞赋值方式描述了一个移位寄存器:

EX1:
module weise1(Q0,Q1,Q2,Q3,din,clk);
output Q0,Q1,Q2,Q3;
input clk,din;
reg Q0,Q1,Q2,Q3;
always @(posedge clk)
    begin
    Q3=Q2;
    Q2=Q1;
    Q1=Q0;
    Q0=din;
    end
endmodule

其RTL综合结果:

——


——

将上面例子中的阻塞赋值语句保持不变,仅仅将其两条语句的排列顺序改一下的话,则综合器综合的结果就会大不同。

EX2:
module weise1(Q0,Q1,Q2,Q3,din,clk);
output Q0,Q1,Q2,Q3;
input clk,din;
reg Q0,Q1,Q2,Q3;
always @(posedge clk)
    begin
    Q3=Q2;
    Q1=Q0;
    Q2=Q1;
    Q0=din;
    end
endmodule

其RTL综合结果:

——

将四条阻塞赋值语句的顺序完全颠倒的话,则综合器实际上等效成一个触发器。

EX3:
module weise1(Q0,Q1,Q2,Q3,din,clk);
output Q0,Q1,Q2,Q3;
input clk,din;
reg Q0,Q1,Q2,Q3;
always @(posedge clk)
    begin
    Q0=din;
    Q1=Q0;
    Q2=Q1;
    Q3=Q2;
    end
endmodule

其RTL综合结果:

——

        而用非阻塞赋值方式描述的移位寄存器,无论将其“always”过程块中四条赋值语句的顺序怎么变动,均不会影响其综合几个,其结果与第一个例子的结果相 同。对于时许逻辑描述和建模,应尽量使用非阻塞赋值方式。此外,若在同一个“always”过程块中描述时许和组合逻辑混合电路时,也最好使用非阻塞赋值 方式。

——

        非阻塞赋值不能用于“assign”持续赋值中,一般只出现在“initial”和“always”等过程块中,对reg型变量进行赋值。像assign out<=a+b;这样的语句是错误的。
        当用“always”块来描述组合逻辑时,既可以用阻塞赋值,也可以采用非阻塞赋值。但在同一个过程块中,最好不要同时用阻塞赋值和非阻塞赋值,虽然同时使用这两种赋值方式在综合时并不一定会出错。
在向函数(function)的返回值赋值时,应使用阻塞赋值“=”。
        不能在一个以上的“always”过程块中对同一个变量赋值,这样会引起冲突,在综合时会报错。
        在一个模块中,严禁对同一个变量既进行阻塞赋值,又进行非阻塞赋值,这样在综合时会报错。
        对时序逻辑描述和建模,应尽量使用非阻塞赋值方式,此外,若在同一个“always”过程块中描述时序和组合逻辑混合电路时,也最好使用非阻塞赋值方式。

——

 

阅读更多
个人分类: FPGA学习
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭