位於begin/end塊內的多條阻塞賦值語句是串行執行的,這一點同標準的程序設計語言是相同的。但是多條非阻塞賦值語句卻是並行執行的,這些非阻塞賦值語句都會在其中任何一條語句執行完成之前開始執行。這正是硬件電路的特點,因爲實際的邏輯門電路都是獨立運轉的,而不是等到其他門電路運轉結束之後自己纔開始運轉。
下面我們以描述移位寄存器的兩種方法爲例來講述兩種賦值類型的區別。在下面的這種描述中,第一個觸發器中的數據被移到第二個觸發器中,第二個觸發器中的數據被移到第三個觸發器中……如此繼續下去,直到最後一個觸發器中的數據被移出該寄存器爲止。
1moduleshiftreg (inputclk,
2inputsin,
3outoutreg[3:0]q);//這是正確使用非阻塞賦值的實例
4always@(posedgeclk)
5begin
6q[0]<=sin;//非阻塞賦值:<=
7q[1]<=q[0];
8q[2]<=q[1]
9q[3]<=q[2];
10//這裏寫作q<= {q[2:0],sin};更簡單更好一些
11end
12endmodule
本部分內容用意在於:講述使用always語句塊對時序邏輯電路進行建模的時候,如何使用非阻塞賦值。如果設計者能夠充分的靈活應用,比如倒轉上例中四條語句的順序,那麼使用阻塞賦值語句仍然能實現相應的功能,但是與使用非阻塞賦值的方法相比,這種方法並不會帶來任何好處,相反還暗藏了巨大的風險。
最後需要注意的是:每個always語句塊都隱含表示一個獨立的邏輯電路模塊。因此,對於特定的reg類型的變量,只能在一個always語句塊中對其進行賦值;否則就可能會出現兩個硬件模塊同時從同一個輸出端口輸出數據的情況,這種情況一般稱爲短路輸出(shorted output)。
過程賦值語句多用於對reg型變量進行復制,過程賦值有阻塞複製和非阻塞賦值兩種。
非阻塞賦值的符號爲:<=
阻塞賦值符號爲:=
(1)非阻塞賦值的例子:
reg c,b;
always@(posedge clk)
begin
b <= a;
c <= b;
end
(2)阻塞賦值的例子:
reg c,b;
always @ (posedge clk)
begin
b = a;
c = b;
end
上述例子中,使用非阻塞賦值方法,其中的每個<=都可以理解爲一個寄存器。而在同一
個時鐘下面採用的非阻塞賦值方法,模塊內所有寄存器都同時隨時鐘跳變。這是硬件處理
的精髓,也是時序電路中大量使用非阻塞賦值的原因。
在實際書寫verilog HDL代碼的過程中,對於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塊來描述組合邏輯(combinational logic)時,應當使用阻塞賦值。
(2)對於時序邏輯(sequential logic)的描述和建模,應當使用非阻塞賦值。
(3)在同一個always模塊中,最好不要混合使用阻塞賦值和非阻塞賦值,對同一變量
既進行阻塞賦值,又進行非阻塞賦值,在綜合時會出錯。所以always中要麼全部使用非
阻塞賦值,要麼把阻塞賦值和非阻塞賦值分在不同的always中書寫。
(4)儘量不要再在多個不同的always塊中對同一變量賦值。
(5)使用$strobe顯示使用非阻塞賦值的變量。
在Verilog HDL中,有兩種過程性賦值方式,即阻塞式(blocking)和非阻塞式(non-blocking)。這兩種賦值方式看似差不多,其實在某些情況下卻有着根本的區別,如果使用不當,綜合出來的結果和你所想得到的結果會相去甚遠。
Tip:所謂過程性賦值就是指在initial或always語句內的賦值,它只能對寄存器數據類型的變量賦值。
阻塞式(blocking)的操作符爲“= ”
非阻塞式(non-blocking)的操作符爲 “<= ”
首先,我們通過兩個例子來看看這兩種賦值方式的區別,這裏使用的綜合工具爲Qt ii。
例1:非阻塞式賦值
module
test_non_blocking
(
input clk,
input testa,
input testb,
input testc,
input testd,
output regtestout
);
reg testreg;
always @ (posedge clk)
begin
testreg <= testb | testc;
begin
if (testa) begin
testout <= testreg & testd;
end
else begin
testout <= testd;
end
end
end
endmodule
例1綜合後的結果爲
例2:阻塞式賦值
module
test_blocking
(
input clk,
input testa,
input testb,
input testc,
input testd,
output regtestout
);
reg testreg;
always @ (testa,testb.testc,testd)
begin
testreg = testb | testc;
begin
if (testa) begin
testout = testreg & testd;
end
else begin
testout = testd;
end
end
end
endmodule
例2綜合後的結果爲:
分析:
可以看到,例1和例2的code寫法完全一樣,只是在always語句塊中使用了不同的賦值方式,就導致綜合出來的結果不同。
在例1中,是非阻塞式賦值方式,非阻塞式賦值的賦值對象總是在當前仿真時刻結束時被賦值,所以,當在對語句2中的testout賦值這一時刻,testreg 值還沒有得到語句1中的新值,而是原來的值(即上一個時刻的值)。
而在例2中,使用了阻塞式賦值方式,就是在always語句塊中是一句一句執行的。在執行語句2之前,語句1就已經執行完成,testreg被賦好了新值,所以語句2中的testreg值取的是新值。
建議:
1. 阻塞式賦值用於組合邏輯建模;
2. 非阻塞式賦值用於時序邏輯建模。