【FPGA/Verilog】 阻塞赋值与非阻塞赋值

阻塞赋值与非阻塞赋值在Verilog语言的学习中是非常重要的需要区分的概念。下面对二者的区别作出说明。

阻塞赋值:a = b;
非阻塞赋值:a <= b;

非阻塞赋值只能用于对寄存器类型变量的赋值,只能在initial块和always块中使用,非阻塞赋值不允许用于连续赋值。

下文以a代表被赋值变量或表达式,b代表赋值变量或表达式。


  • 阻塞赋值,顾名思义,指的是在赋值过程中,其他赋值是被阻塞的,直到阻塞赋值完成赋值后才可执行其他赋值,且阻塞赋值是运算后立即执行的。在此之后的其他阻塞赋值语句都将使用新赋值的结果。
    因此,假如有一阻塞赋值中被赋值变量是a,同时有一阻塞赋值中赋值变量也是a,则两个阻塞赋值的执行先后次序将会直接导致最终a被赋值的值不同。这也是为什么对于时序功能的赋值,建议使用非阻塞赋值的原因。

  • 非阻塞赋值,指的是赋值语句运算后不立即执行赋值,且执行的赋值变量b的值不是当前值,而是上一次被赋值的b值。不论改非阻塞赋值与并行的其他块执行的非阻塞赋值谁先进行运算,最终都在各自块结束时才执行赋值,且执行的先后不影响最终的结果。

上述文字表达较难理解,下面以程序为例作说明。
程序借鉴于夏宇闻等著《Verilog数字系统设计教程》


使用两种赋值方式执行同样的程序:

阻塞赋值:虽然两个always块是并行的,但实际时钟沿到达两个块的时间还是可能相差几ps。若rst已从1到0,且上面always块的时钟沿早几皮秒到达,则y1和y2都取1;若下面always块的时钟沿早几皮秒到达,则y1和y2都取0。因此这个模块是不稳定的,存在冒险和竞争现象。

module fun1(input clk, input rst, output reg y1, output reg y2);
    
    always @(posedge clk or posedge rst) begin
        if(rst) y1 = 1'b0;
        else y1 = y2;
    end
    
    always @(posedge clk or posedge rst) begin
        if(rst) y2 = 1'b1;
        else y2 = y1;
    end
    
endmodule

非阻塞赋值:复位信号回到0后,无论哪一个always块的时钟有效沿先到达,两个非阻塞赋值都在赋值开始的时候开始计算右侧表达式,在结束时刻才更新左侧表达式。无论哪个有效沿先到,y1都为0,y2都为0。y1和y2的值是由上一个周期的时钟有效沿或rst沿确定的。

module fun2(input clk, input rst, output reg y1, output reg y2);
    
    always @(posedge clk or posedge rst) begin
        if(rst) y1 <= 1'b0;
        else y1 <= y2;
    end
    
    always @(posedge clk or posedge rst) begin
        if(rst) y2 <= 1'b1;
        else y2 <= y1;
    end
    
endmodule

遵守以下8个原则,即可在综合布局布线后的仿真中避免冒险竞争现象:

  • 时序电路建模时,用非阻塞赋值
  • 锁存器电路建模时,用阻塞赋值
  • 用always块建立组合逻辑模型时,用阻塞赋值
  • 在同一个always块中建立时序和组合逻辑模型时,用非阻塞赋值
  • 在同一个always块中,不要同时使用阻塞赋值和非阻塞赋值
  • 不要在一个以上的always块中为同一个变量赋值
  • 用$strobe系统任务来显示用非阻塞赋值的变量值
  • 在赋值时不要使用#0延迟

冒险竞争现象产生的原因见 6.4 Verilog 竞争与冒险 | 菜鸟教程
建立时间和保持时间见 3.3 Verilog 建立时间和保持时间 | 菜鸟教程

你可能感兴趣的:(FPGA,fpga开发)