15 / 37 · 概念
Nonblocking 指定與同步狀態更新
分開舊值與下一值,正確解讀管線與計數器 RTL。
單元可免費閱讀,註冊課程即可儲存學習進度。
右側算式以舊狀態求值
時脈驅動的循序區塊中,<= 先計算右側,再於 nonblocking非阻塞賦值(nonblocking)在循序 RTL 中以 <= 表示。它先求右側運算式的值,再排程更新,讓同一邊緣的暫存器都能依據先前狀態計算。 更新階段反映結果。同一邊緣更新多個暫存器暫存器儲存多個位元的狀態。本課程中的同步暫存器會在時脈邊緣儲存指定的輸入。 詳細內容時,可避免新值因程式行序而一路穿透。
always_ff @(posedge clk) begin
if (rst) begin
a <= 8'd0;
b <= 8'd0;
end else begin
a <= d;
b <= a;
end
end初始 a=b=0、輸入依序 5、9、2,邊緣後 (a,b) 為 (5,0)、(9,5)、(2,9)。若認為第二行讀到已更新的 a,就等於刪掉一級管線管線以暫存器將計算分為多個階段,讓多筆輸入重疊處理。資料、valid 與控制訊號必須在各階段對齊。 詳細內容。
每欄是一次上升緣上升緣是時脈由 0 變成 1 的瞬間。它與表示整段 CLK=1 期間的高準位不同。 詳細內容的觀察紀錄。輸入為邊緣前值,名稱含「後」的狀態為更新後值。欄位對齊表示取樣順序,並未繪出實體傳播延遲傳播延遲是輸入改變後,輸出穩定至正確值所需的時間。邏輯式是否等價與時間特性是兩個不同問題。 詳細內容。
由 a=b=0 開始。a 儲存新 d 的同時,b 儲存舊 a,因此兩列相差一級。
查看波形資料
| 訊號 | 波形 | 匯流排值 |
|---|---|---|
| 邊緣 | 234 | E0 → E1 → E2 |
| d | 234 | 5 → 9 → 2 |
| a 後 | 234 | 5 → 9 → 2 |
| b 後 | 234 | 0 → 5 → 9 |
改成 blocking 為何危險
若同一區塊改為 a=d; b=a;,b 的計算可能讀到區塊內已改變的 a。與其他循序區塊的排程也可能產生競爭。基本原則是組合計算用 blocking、狀態儲存用 nonblocking;只有能解釋事件語意時才採用例外。
避免對同一暫存器多次指定
同一程序若對 q 執行多次 nonblocking 指定,最後執行者可能決定結果。不要用多個獨立 if 隱藏優先權,應用 if / else if / else 或明確的 next-state 變數表達功能,也應避免多個區塊驅動同一暫存器。
RTL 審查時,為每個暫存器寫一條下一狀態式很有幫助。若出現兩條式子,或某條件下未定義,就是重新檢查控制優先權的訊號。
自己試試看
初始 a=1、b=4,在同一時脈區塊執行 a<=b; b<=a;,一個與兩個邊緣後的狀態各為何?
查看解說
一個邊緣後 (a,b)=(4,1),兩個後為 (1,4)。各指定都讀取同一份前次狀態,因此互換。若寫 a=b; b=a;,依順序可能都成為舊 b,並非相同功能。