關鍵詞:阻塞賦值,非阻塞賦值,並行

過程性賦值是在 initial 或 always 語句塊裏的賦值,賦值對象是寄存器、整數、實數等類型。

這些變量在被賦值後,其值將保持不變,直到重新被賦予新值。

連續性賦值總是處於激活狀態,任何操作數的改變都會影響表達式的結果;過程賦值隻有在語句執行的時候,才會起作用。這是連續性賦值與過程性賦值的區別。

Verilog 過程賦值包括 2 種語句:阻塞賦值與非阻塞賦值。

阻塞賦值

阻塞賦值屬於順序執行,即下一條語句執行前,當前語句一定會執行完畢。

阻塞賦值語句使用等號 = 作為賦值符。

前麵的仿真中,initial 裏麵的賦值語句都是用的阻塞賦值。

非阻塞賦值

非阻塞賦值屬於並行執行語句,即下一條語句的執行和當前語句的執行是同時進行的,它不會阻塞位於同一個語句塊中後麵語句的執行。

非阻塞賦值語句使用小於等於號 <= 作為賦值符。

利用下麵代碼,對阻塞、非阻塞賦值進行仿真,來說明 2 種過程賦值的區別。

實例

`timescale 1ns/1ns
 
module test ;
    reg [3:0]   ai, bi ;
    reg [3:0]   ai2, bi2 ;
    reg [3:0]   value_blk ;
    reg [3:0]   value_non ;
    reg [3:0]   value_non2 ;
 
    initial begin
        ai            = 4'd1 ;   //(1)
        bi            = 4'd2 ;   //(2)
        ai2           = 4'd7 ;   //(3)
        bi2           = 4'd8 ;   //(4)
        #20 ;                    //(5)
 
        //non-block-assigment with block-assignment
        ai            = 4'd3 ;     //(6)
        bi            = 4'd4 ;     //(7)
        value_blk     = ai + bi ;  //(8)
        value_non     <= ai + bi ; //(9)
 
        //non-block-assigment itself
        ai2           <= 4'd5 ;           //(10)
        bi2           <= 4'd6 ;           //(11)
        value_non2    <= ai2 + bi2 ;      //(12)
    end
 
   //stop the simulation
    always begin
        #10 ;
        if ($time >= 1000) $finish ;
    end
 
endmodule

仿真結果如下:

語句(1)-(8)都是阻塞賦值,按照順序執行。

20ns 之前,信號 ai,bi 值改變。由於過程賦值的特點,value_blk = ai + bi 並沒有執行到,所以 20ns 之前,value_blk 值為 X(不確定狀態)。

20ns 之後,信號 ai,bi 值再次改變。執行到 value_blk = ai + bi,信號 value_blk 利用信號 ai,bi 的新值得到計算結果 7。

語句(9)-(12)都是非阻塞賦值,並行執行。

首先,(9)-(12)雖然都是並發執行,但是執行順序也是在(8)之後,所以信號 value_non = ai + bi 計算是也會使用信號 ai,bi 的新值,結果為 7。

其次,(10)-(12)是並發執行,所以 value_non2 = ai2 + bi2 計算時,並不關心信號 ai2,bi2 的最新非阻塞賦值結果。即 value_non2 計算時使用的是信號 ai2,bi2 的舊值,結果為 4'hF。

使用非阻塞賦值避免競爭冒險

上述仿真代碼隻是為了讓讀者更好的理解阻塞賦值與非阻塞賦值的區別。實際 Verilog 代碼設計時,切記不要在一個過程結構中混合使用阻塞賦值與非阻塞賦值。兩種賦值方式混用時,時序不容易控製,很容易得到意外的結果。

更多時候,在設計電路時,always 時序邏輯塊中多用非阻塞賦值,always 組合邏輯塊中多用阻塞賦值;在仿真電路時,initial 塊中一般多用阻塞賦值。

如下所示,為實現在時鍾上升沿交換 2 個寄存器值的功能,在 2 個 always 塊中使用阻塞賦值。

因為 2 個 always 塊中的語句是同時進行的,但是 a=b 與 b=a 是無法判定執行順序的,這就造成了競爭的局麵。

但不管哪個先執行(和編譯器等有關係),不考慮 timing 問題時,他們執行順序總有先後,最後 a 與 b 的值總是相等的。沒有達到交換 2 個寄存器值的效果。

實例

always @(posedge clk) begin
    a = b ;
end
 
always @(posedge clk) begin
    b = a;
end

但是,如果在 always 塊中使用非阻塞賦值,則可以避免上述競爭冒險的情況。

如下所示,2 個 always 塊中語句並行執行,賦值操作右端操作數使用的是上一個時鍾周期的舊值,此時 a<=b 與 b<=a 就可以相互不幹擾的執行,達到交換寄存器值的目的。

實例

always @(posedge clk) begin
    a <= b ;
end
 
always @(posedge clk) begin
    b <= a;
end

當然,利用下麵代碼也可以實現交換寄存器值的功能,但是顯然不如在 always 塊中直接用非阻塞賦值簡單直觀。

實例

always @(posedge clk) begin
    temp    = a ;
    a       = b ;
    b       = temp ;
end

源碼下載

Download