阻塞賦值與非阻塞賦值的差異:求值時機、順序、使用場合比較

兩者都是在 Verilog 的 always 區塊中使用的賦值,但左邊被改寫的時機不同。阻塞賦值(=)在執行該行的時間點就更新左邊,下一行使用的是新的值。非阻塞賦值(<=)先求出同一時機所有右邊的值,之後再一併更新左邊。基本的準則是:以時脈驅動的循序電路使用 <=,組合電路的 always 使用 =。

項目阻塞賦值(=)非阻塞賦值(<=)
寫法a = b;a <= b;
更新左邊的時機執行該行時立即更新求出同一時機的所有右邊之後,一併更新
從下一行看到的值更新後的新值更新前的舊值
對描述順序的依賴有。調換行的順序,結果就會改變幾乎沒有。但對同一變數賦值 2 次時,會留下後面的賦值
對應的電路動作由上而下依序計算中間值的組合電路在時脈邊緣一齊擷取值的正反器
主要使用場合組合電路的 always @(*)、測試平台的程序以時脈驅動之循序電路的 always @(posedge clk)
2 級移位暫存器(以時脈驅動的區塊)q1 = d; q2 = q1; 時,q2 也會在同一個邊緣得到 d,只延遲 1 級q1 <= d; q2 <= q1; 時,q2 得到邊緣之前的 q1,成為 2 級
值的交換(以時脈驅動的區塊)a = b; b = a; 時,a 與 b 會變成相同的值a <= b; b <= a; 時,a 與 b 會互換
多個 always 區塊之間在同一個邊緣動作的區塊之間,容易發生結果隨執行順序而改變的競爭右邊的求值會先完成,因此不易發生競爭
用錯時用於循序電路時,結果會受區塊的執行順序左右,有時與合成後電路的動作不一致用於組合電路時,中間值會以舊值被讀取,模擬可能與合成後的電路不一致,或被多餘地重新執行

數值為具代表性的大致參考值,會因世代或產品而異。

適用於什麼情況

阻塞賦值(=)
在組合電路的 always @(*) 中依序計算中間值時。在測試平台中依序描述程序時。
非阻塞賦值(<=)
描述 always @(posedge clk) 等以時脈驅動的正反器時。暫存器、計數器、狀態機的狀態更新。
不要混用
不要在 1 個 always 區塊中混用 = 與 <=,也不要從多個 always 區塊對同一個變數賦值。在 SystemVerilog 中,可以用 always_ff 與 always_comb 向工具傳達意圖。

常見問題

在循序電路中使用阻塞賦值會怎樣?

依行的順序,同一個邊緣更新的值可能會直接傳給下一行,成為級數比預期少的電路。此外,與同一時脈驅動的其他 always 區塊之間,會發生結果隨執行順序而改變的競爭,成為模擬與合成後電路不一致的原因。

為什麼以非阻塞賦值寫 a <= b; b <= a; 時值會互換?

因為是先以舊值求出 2 個右邊,再一併更新左邊。動作與將 2 個正反器的輸出互相接到對方輸入的電路相同。

測試平台應該使用哪一種?

依序描述程序的部分使用阻塞賦值即可。但若在與時脈邊緣相同的時刻改變電路的輸入,擷取到舊值還是新值會隨執行順序而改變,因此應從邊緣稍微錯開,或以非阻塞賦值來改變。

理解度檢測

Verilog的非阻塞賦值(<=)的動作,下列何者正確?

  1. A賦值的瞬間左邊就被更新,下一行會使用該新值
  2. B先評估同一時間點的所有右邊,之後再一併更新左邊
  3. C捨棄賦值的結果,不做任何變更
查看答案與解析

正確答案:B. 先評估同一時間點的所有右邊,之後再一併更新左邊

在非阻塞賦值中,於時脈邊緣時,所有<=的右邊會先以舊值評估,之後再同時改寫左邊。這與以相同時脈動作的眾多正反器在邊緣之前同時取入值的實際電路動作相同。因此,對於以時脈動作的序向電路的描述,原則上應使用非阻塞賦值。

在Verilog中區分使用賦值種類時的一般準則,下列何者正確?

  1. A不論何處都只使用阻塞賦值(=)
  2. B以時脈動作的序向電路使用非阻塞賦值(<=),組合電路的always使用阻塞賦值(=)
  3. C在一個always中隨意混用兩者
查看答案與解析

正確答案:B. 以時脈動作的序向電路使用非阻塞賦值(<=),組合電路的always使用阻塞賦值(=)

以非阻塞賦值撰寫序向電路時,會與所有正反器同時取入值的電路動作一致,結果不會受到描述順序或與其他always區塊執行順序的左右。在組合電路中,由上而下依序計算中間值的寫法較為自然,因此使用阻塞賦值。此外,在同一個always區塊中不要混用兩者也很重要。

在測試平台中,若在與時脈上升相同的時刻以阻塞賦值改變對電路的輸入,有時會發生問題。主要原因與對策的組合,下列何者正確?

  1. A因為電路會取入改變前還是改變後的值,會隨執行順序而變(競爭)。將輸入從時脈邊緣稍微錯開,或以非阻塞賦值來改變
  2. B因為時脈的頻率會自動降低。將時脈加快
  3. C因為阻塞賦值在測試平台中無法使用。全部改為assign語句
查看答案與解析

正確答案:A. 因為電路會取入改變前還是改變後的值,會隨執行順序而變(競爭)。將輸入從時脈邊緣稍微錯開,或以非阻塞賦值來改變

若在同一時刻,發生測試平台改寫輸入的處理與電路的正反器取入輸入的處理,則依模擬器先執行哪一個,取入的值可能是舊值,也可能是新值。這是模擬的競爭(race),結果也可能因模擬器而異。可以透過在下降時改變輸入、稍微延遲、使用非阻塞賦值或SystemVerilog的clocking block等來防止。

  1. 在上方的描述中,時脈上升之前a=0、b=1。上升之後a與b的值會變成什麼?
  2. 原本打算做成2級位移暫存器,卻如上方般以阻塞賦值(=)撰寫。會發生的問題,下列何者正確?
  3. 原本打算做成組合電路,卻如上方般撰寫。可能發生的問題,下列何者正確?

前往硬體描述語言與驗證的練習題

相關名詞的解說

阻塞賦值(Blocking assignment)
Verilog 的「=」賦值。左側當場更新,下一行會使用該新值。用於組合電路的 always 與測試平台。
非阻塞賦值(Nonblocking assignment)
Verilog 的「<=」賦值。先對同一時間點的所有右側求值,再一併更新左側。與正反器的動作一致,因此用於序向電路。
敏感度列表(Sensitivity list)
寫在 Verilog 的 always 之後 @( ) 中,作為執行該區塊之觸發條件的訊號列表。在組合電路中若漏寫,模擬與電路會不一致。
Verilog
廣泛用於數位電路設計與驗證的硬體描述語言。以 module 為單位撰寫電路,並以 assign 與 always 表示行為。

相關的比較