在Verilog中有兩種類型的賦值語句:阻塞賦值語句(「=」)和非阻塞賦值語句(「<=」)。正確地使用這兩種賦值語句對於Verilog的設計和仿真很是重要。html
Verilog語言中講的阻塞賦值與非阻塞賦值,但從字面意思來看,阻塞就是執行的時候在某個地方卡住了,等這個操做執行完在繼續執行下面的語句,而非阻塞就是無論執行完沒有,我無論執行的結果是什麼,反正我繼續下面的事情。而Verilog中的阻塞賦值與非阻塞賦值正好也是這個意思,經過執行一個例子,就能夠簡單地明白了:
一、阻塞賦值能夠理解爲語句的順序執行,所以語句的執行順序很重要
二、非阻塞賦值能夠理解爲語句的並行執行,因此語句的執行不考慮順序
三、在assign的結構中,必須使用的是阻塞賦值面試
也就是說:工具
阻塞:在本語句中「右式計算」和「左式更新」徹底完成以後,纔開始執行下一條語句;
非阻塞:當前語句的執行不會阻塞下一語句的執行。spa
下面給出實例來講明:設計
給出相應的案例來幫助理解:code
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電路圖和仿真波形以下圖:htm
從仿真圖能夠看書,b,c,d是在每一個時鐘後依次傳遞的,若是採用阻塞賦值,若是in改變,那麼b,c,d馬上改變,這個就在這裏不給出仿真。blog
阻塞賦值和非阻塞賦值的另一個區別在於綜合的時候,若是輸出只有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語句中必須使用阻塞賦值語句
但願你們在懂得了阻塞和非阻塞語句的區別以後,可以很好的在本身的項目中靈活地運用,這也是你們面試的時候,必須會面對的一個問題,但願你們可以掌握!