過程性賦值是在 ?initial
?或 ?always
?語句塊里的賦值,賦值對象是寄存器、整數(shù)、實數(shù)等類型。
這些變量在被賦值后,其值將保持不變,直到重新被賦予新值。
連續(xù)性賦值總是處于激活狀態(tài),任何操作數(shù)的改變都會影響表達式的結(jié)果;過程賦值只有在語句執(zhí)行的時候,才會起作用。這是連續(xù)性賦值與過程性賦值的區(qū)別。
Verilog 過程賦值包括 2 種語句:阻塞賦值與非阻塞賦值。
阻塞賦值屬于順序執(zhí)行,即下一條語句執(zhí)行前,當前語句一定會執(zhí)行完畢。
阻塞賦值語句使用等號 ?=
? 作為賦值符。
前面的仿真中,?initial
? 里面的賦值語句都是用的阻塞賦值。
非阻塞賦值屬于并行執(zhí)行語句,即下一條語句的執(zhí)行和當前語句的執(zhí)行是同時進行的,它不會阻塞位于同一個語句塊中后面語句的執(zhí)行。
非阻塞賦值語句使用小于等于號 ?<=
? 作為賦值符。
利用下面代碼,對阻塞、非阻塞賦值進行仿真,來說明 2 種過程賦值的區(qū)別。
`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
仿真結(jié)果如下:
語句(1)-(8)都是阻塞賦值,按照順序執(zhí)行。
20ns 之前,信號 ai,bi 值改變。由于過程賦值的特點,value_blk = ai + bi 并沒有執(zhí)行到,所以 20ns 之前,?value_blk
?值為 X(不確定狀態(tài))。
20ns 之后,信號 ai,bi 值再次改變。執(zhí)行到 value_blk = ai + bi,信號 ?value_blk
?利用信號 ai,bi 的新值得到計算結(jié)果 7。
語句(9)-(12)都是非阻塞賦值,并行執(zhí)行。
首先,(9)-(12)雖然都是并發(fā)執(zhí)行,但是執(zhí)行順序也是在(8)之后,所以信號 ?value_non
?= ai + bi 計算是也會使用信號 ai,bi 的新值,結(jié)果為 7。
其次,(10)-(12)是并發(fā)執(zhí)行,所以 ?value_non2
?= ai2 + bi2 計算時,并不關(guān)心信號 ai2,bi2 的最新非阻塞賦值結(jié)果。即 ?value_non2
?計算時使用的是信號 ai2,bi2 的舊值,結(jié)果為 4'hF。
上述仿真代碼只是為了讓讀者更好的理解阻塞賦值與非阻塞賦值的區(qū)別。實際 Verilog 代碼設(shè)計時,切記不要在一個過程結(jié)構(gòu)中混合使用阻塞賦值與非阻塞賦值。兩種賦值方式混用時,時序不容易控制,很容易得到意外的結(jié)果。
更多時候,在設(shè)計電路時,?always
?時序邏輯塊中多用非阻塞賦值,?always
?組合邏輯塊中多用阻塞賦值;在仿真電路時,?initial
?塊中一般多用阻塞賦值。
如下所示,為實現(xiàn)在時鐘上升沿交換 2 個寄存器值的功能,在 2 個 ?always
?塊中使用阻塞賦值。
因為 2 個 ?always
?塊中的語句是同時進行的,但是 a=b 與 b=a 是無法判定執(zhí)行順序的,這就造成了競爭的局面。
但不管哪個先執(zhí)行(和編譯器等有關(guān)系),不考慮 ?timing
?問題時,他們執(zhí)行順序總有先后,最后 a 與 b 的值總是相等的。沒有達到交換 2 個寄存器值的效果。
always @(posedge clk) begin
a = b ;
end
always @(posedge clk) begin
b = a;
end
但是,如果在 ?always
?塊中使用非阻塞賦值,則可以避免上述競爭冒險的情況。
如下所示,2 個 ?always
?塊中語句并行執(zhí)行,賦值操作右端操作數(shù)使用的是上一個時鐘周期的舊值,此時 a<=b 與 b<=a 就可以相互不干擾的執(zhí)行,達到交換寄存器值的目的。
always @(posedge clk) begin
a <= b ;
end
always @(posedge clk) begin
b <= a;
end
當然,利用下面代碼也可以實現(xiàn)交換寄存器值的功能,但是顯然不如在 ?always
?塊中直接用非阻塞賦值簡單直觀。
always @(posedge clk) begin
temp = a ;
a = b ;
b = temp ;
end
點擊這里下載源碼
更多建議: