在Verilog中有兩種類型的賦值語句:阻塞賦值語句(“=”)和非阻塞賦值語句(“<=”)。正確地使用這兩種賦值語句對于Verilog的設計和仿真非常重要。
Verilog語言中講的阻塞賦值與非阻塞賦值,但從字面意思來看,阻塞就是執行的時候在某個地方卡住了,等這個操作執行完在繼續執行下面的語句,而非阻塞就是不管執行完沒有,我不管執行的結果是什么,反正我繼續下面的事情。而Verilog中的阻塞賦值與非阻塞賦值正好也是這個意思,通過執行一個例子,就可以簡單地明白了:
1、阻塞賦值可以理解為語句的順序執行,因此語句的執行順序很重要
2、非阻塞賦值可以理解為語句的并行執行,所以語句的執行不考慮順序
3、在assign的結構中,必須使用的是阻塞賦值
也就是說:
阻塞:在本語句中“右式計算”和“左式更新”完全完成之后,才開始執行下一條語句;
非阻塞:當前語句的執行不會阻塞下一語句的執行。
下面給出實例來說明:
給出相應的案例來幫助理解:
module prj1(in,b,c,d,clk,rst_n);
input in;
input clk;
input rst_n;
output b,c,d;
reg b,c,d;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
b <=0;
c <=0;
d <=0;
end
else begin
b <=in;
c <=b;
d <=c;
end
end
endmodule
這個目的是為了展示非阻塞賦值過程中的時序變化,對應的RTL電路圖和仿真波形如下圖:
從仿真圖可以看書,b,c,d是在每個時鐘后依次傳遞的,如果采用阻塞賦值,如果in改變,那么b,c,d立刻改變,這個就在這里不給出仿真。
阻塞賦值和非阻塞賦值的另外一個區別在于綜合的時候,如果輸出只有d,bc作為中間變量,阻塞賦值在綜合的過程中會自動省略掉中間過程。給出如下仿真,理解更為清楚
module prj1(in,b,c,clk,rst_n);
input in;
input clk;
input rst_n;
output b,c;
reg b,c, e,f, m,n;
/* <= */
always @(posedge clk or negedge rst_n) begin
if(!rst_n) b <=0;
else begin
e <=in;
f <=e;
b <=f;
end
end
/* = */
always @(posedge clk or negedge rst_n) begin
if(!rst_n) c=0;
else begin
m = in;
n = m;
c = n;
end
end
endmodule
綜合后結果如圖,可以看出,采用阻塞賦值,綜合后的邏輯單元只有一個,中間變量m,n直接省去了。
下面我們來看看兩者代碼之間到底是怎么運行的。
(1)對于阻塞賦值的情況:
always @(posedge clk or negedge rst_n) begin
if(!rst_n) c=0;
else begin
m = in;
n = m;
c = n;
end
end
always語句塊對Clk的上升沿敏感,當發生Clk 0~1的跳變時,執行該always語句。
在begin...end語句塊中所有語句是順序執行的,而且最關鍵的是,阻塞賦值是在本語句中“右式計算”和“左式更新”完全完成之后,才開始執行下一條語句的。
在本例中,in的值賦給m以后,再執行n = m;同樣在n的值更新以后,才執行c = n。這樣,最終的計算結果就是in = c。也就是說時鐘上升沿到來的時候,整個語句塊執行完后,in,m,n,c的值都是一樣的,這也就是我們前面說的,in變化之后,m,n,c都跟著變化。所有的語句執行完以后,該always語句等待Clk的上升沿,從而再一次觸發begin...end語句。
總結
完成阻塞賦值的過程為:首先計算等號右邊表達式的結果;接著這個結果存入仿真系統的內部臨時寄存器中,這個寄存器也稱為賦值事件隊列和調度的臨時寄存器。如果賦值時沒有延遲信息,則這個事件立即被調度執行。
(2)對于非阻塞賦值的情況
always @(posedge clk or negedge rst_n) begin
if(!rst_n) b <=0;
else begin
e <=in;
f <=e;
b <=f;
end
end
首先執行e <= in,產生一個更新事件,將in的當前值賦給e,但是這個賦值過程并沒有立刻執行,而是在事件隊列中處于等待狀態。
然后執行f <= e,同樣產生一個更新事件,將e的當前值(注意上一語句中將in值賦給e的過程并沒有完成,e還是舊值)賦給f,這個賦值事件也將在事件隊列中處于等待狀態。
再執行b <= f,產生一個更新事件,將f的當前值賦給b,這個賦值事件也將在事件隊列中等待執行。
這時always語句塊執行完成,開始對下一個Clk上升沿敏感。也就是說,使用非阻塞賦值方式進行賦值時,各個賦值語句同步執行;因此,通常在一個時鐘沿對臨時變量進行賦值,而在另一個時鐘沿對其進行采樣。
那么什么時候才執行那3個在事件隊列中等待的事件呢?只有當當前仿真時間內的所有活躍事件和非活躍事件都執行完成后,才開始執行這些非阻塞賦值的更新事件。這樣就相當于將in、e和f的值同時賦給了e、f和b。
注:
*仿真器首先按照仿真時間對事件進行排序,然后再在當前仿真時間里按照事件的優先級順序進行排序。
*活躍事件是優先級最高的事件。在活躍事件之間,它們的執行順序是隨機的。阻塞賦值(=)、連續賦值(assign)以及非阻塞賦值的右式計算等都屬于活躍事件。
總結 :
非阻塞語句的執行過程為:首先,它會把非阻塞賦值放入調度隊列中;接著,仿真工具開始執行下一條語句而不等待當前這條語句執行完畢。也就是說,先計算出等號右邊表達式的結果,再把這個結果的賦值操作保存在事件隊列中,等輪到事件被調度的時候,把這個結果賦值給等號左邊。如果沒有指定等號右邊的延遲,賦值的操作會發生在當前時間單位的最后時刻。
知道了阻塞賦值和非阻塞賦值的區別之后,大家肯定就會關心什么時候該用阻塞賦值什么時候該用非阻塞賦值,下面我簡單的說幾句:
賦值的類型的選擇取決于建模的邏輯類型。一般情況是這樣的(也有特殊情況):
(1)在時序邏輯電路中一般使用非阻塞賦值。
非阻塞賦值在塊結束后才完成賦值操作,此賦值方式可以避免在仿真出現冒險和競爭現象。
(2)在組合邏輯電路中一般使用阻塞賦值。
使用阻塞方式對一個變量進行賦值時,此變量的值在在賦值語句執行完后就立即改變。
(3)在assign語句中必須使用阻塞賦值語句
希望大家在懂得了阻塞和非阻塞語句的區別之后,能夠很好的在自己的項目中靈活地運用,這也是大家面試的時候,必須會面對的一個問題,希望大家能夠掌握
編輯:jq
-
Verilog
+關注
關注
28文章
1351瀏覽量
110390 -
HDL
+關注
關注
8文章
328瀏覽量
47468 -
阻塞
+關注
關注
0文章
24瀏覽量
8140
原文標題:Verilog HDL中阻塞語句和非阻塞語句的區別
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
socket編程中的阻塞與非阻塞
socket阻塞和非阻塞的區別是什么
求助,關于CyU3PUsbHostEpWaitForCompletion函數中的阻塞問題求解
是否可以使用Aurix中的CCU6模塊,以非阻塞方式在檢測到GPIO下降沿時觸發中斷?
FPGA學習筆記---基本語法
關于Verilog的一些基本語法
鴻蒙TypeScript入門學習第6天:【條件語句】
![鴻蒙TypeScript入門學習第6天:【條件<b class='flag-5'>語句</b>】](https://file1.elecfans.com/web2/M00/C6/84/wKgZomYKR_aAbRPhAABW3F6g280594.png)
評論