Verilog wait語句完整教學|模擬控制與Testbench應用解析

1. 前言

在數位電路設計與FPGA開發領域中廣泛使用的硬體描述語言 Verilog。其中的「wait 敘述」是一個重要的語法,它能在特定條件成立之前暫停處理,對於靈活的模擬控制與測試平台(testbench)的撰寫非常有幫助。

Verilog 的 wait 敘述雖然語法簡單,但具有強大的表達能力,常用於等待訊號的上升沿或特定事件的發生。然而,若使用方式或注意事項掌握不當,也可能導致意料之外的行為。正確理解與活用 wait 敘述,能直接提升設計品質與驗證效率。

本文將針對 Verilog 的 wait 敘述進行徹底解析,從基礎語法到實際使用方法,再到 testbench 中的應用範例與避免問題的技巧,並以淺顯易懂的方式介紹,讓初學者與日常從事設計與驗證的工程師都能受益。

熟練運用 Verilog 的 wait 敘述,可以讓電路模擬的效率大幅提升。透過本文,讓我們一起掌握 wait 敘述的核心與應用能力。

2. wait 敘述的基本語法與運作原理

在 Verilog 中,wait 敘述是一種控制語法,用於模擬時「希望在某個條件成立前暫停處理」的情況。最基本的寫法如下:

wait (條件式);

在這個語法中,指定的條件式在變為真(true)之前,後續處理不會被執行。一旦條件成立,程式就會繼續執行 wait 之後的敘述。

2.1 基本用法

wait 敘述主要用於 always 區塊initial 區塊 中。例如,希望在訊號 ready 變為 1 之前暫停處理,可以這樣寫:

wait (ready == 1'b1);

在這種情況下,程式會停在 wait 敘述,直到 ready 變成 1 時才繼續往下執行。條件式中也可以使用邏輯運算子或多個訊號的組合。

2.2 與其他控制敘述的差異

Verilog 中還有 if 敘述、while 敘述、forever 敘述等控制結構,但 wait 的行為有所不同:

  • if 敘述:只判斷一次條件,若成立才執行。
  • while 敘述:在條件成立期間重複執行。
  • wait 敘述:會「持續等待直到條件成立」,並且僅在條件滿足的那一刻繼續後續處理。

2.3 適用的情境

wait 敘述常用於需要等待特定事件或訊號狀態的場合。例如,輸入訊號的上升緣、模組初始化完成、testbench 中等待外部條件成立等,都是模擬中進行「時序控制」的常見技巧。

3. wait 敘述的適用與限制

wait 敘述雖然是 Verilog 中靈活的模擬控制工具,但並非所有地方都能使用。以下整理出適用場合與應避免的場合。

3.1 可以使用 wait 的情境

wait 敘述主要用於初始化或模擬控制目的的區塊中,例如:

  • initial 區塊
  • 常用於模擬開始時等待特定條件或事件發生,例如初始化程序。
  • always 區塊
  • 根據訊號變化逐步推進處理時,可以作為條件等待使用。

範例:

initial begin
  wait (reset_n == 1'b1);  // 等待 reset 解除
  // 初始處理
end
always begin
  wait (data_valid);       // 等待資料有效
  // 資料處理
end

3.2 不適合或禁止使用的情境

雖然 wait 很方便,但並非所有位置都能使用。以下情境應避免:

  • 程序區塊外(例如模組本體直下或 assign 敘述)禁止使用
    wait 必須放在 initial 區塊或 always 區塊等「程序化描述」中。
  • RTL 合成(硬體合成)設計中原則上不建議使用
    wait 是模擬專用語法,一般邏輯合成工具不支援。
  • 因此,在 FPGA 或 ASIC 的合成設計中應避免使用。

3.3 與 VHDL 的 wait 敘述差異

另一種硬體描述語言 VHDL 也有 wait 敘述,但與 Verilog 的用法不同。
在 VHDL 中,有「wait until」或「wait for」等多種形式,自由度更高;而 Verilog 僅限於 wait(條件),主要用於等待訊號狀態改變。

4. 常見的使用模式與範例

Verilog 的 wait 敘述能在「特定條件成立前暫停處理」,因此被廣泛應用於多種場景。以下介紹常見模式與代表性範例。
理解這些用法後,對 wait 敘述的印象將更為具體。

4.1 等待時脈邊緣或訊號轉換

典型的情境是「等待訊號變為 1」或「等待 reset 訊號解除」等。也就是等待訊號的上升/下降

initial begin
  // 等待 reset 解除
  wait (reset_n == 1'b1);
  // 從這裡開始寫初始化處理
end
always begin
  // 等待 data_valid 訊號
  wait (data_valid == 1'b1);
  // 當 data_valid 為 1 時進行處理
end

4.2 複數條件或邏輯運算的等待

wait 的條件式可以包含邏輯運算,因此能指定多個訊號的組合條件。

wait ((ready == 1'b1) && (start == 1'b1));

透過 AND / OR 運算,可以實現更複雜的時序控制。

4.3 等待事件發生(例:訊號變化/上升緣)

當需要「在特定訊號變化後才動作」時,wait 也很方便。
不過若僅需偵測「值的改變」,通常會與事件控制符(@)或 always 敘述搭配。

wait (enable == 1'b1);

4.4 監控旗標或狀態的等待

在 testbench 中,經常需要等待外部旗標或模組狀態變化完成後再繼續。

wait (send_done == 1'b1);

4.5 實用情境範例

  • 等待固定次數的時脈:可透過計數器搭配事件控制實現。
integer i;
for (i = 0; i < 10; i = i + 1) begin
  @(posedge clk);  // 等待時脈上升沿 10 次
end

※這不是單獨的 wait,而是與事件控制搭配的範例。

5. 在 Testbench 中活用 wait 的技巧

在 Verilog 建立 testbench 時,wait 敘述是控制模擬流程的強力工具。由於 testbench 常需要等待裝置外部輸入或特定事件,因此 wait 的應用幾乎不可或缺。以下介紹常見的使用範例。

5.1 等待初始 reset 解除

許多電路設計一開始都會先進行 reset,因此在 reset 解除後才進行下一步驗證是基本流程。利用 wait 可以簡單實現。

initial begin
  // 等待 reset 訊號解除
  wait (reset_n == 1'b1);
  // 接著投入測試資料或開始動作驗證
end

5.2 等待訊號上升/下降

在 testbench 中,常需要等待「資料有效訊號」或「旗標訊號」的上升或下降,再適時送入資料。

wait (data_valid == 1'b1);
// 驗證輸出資料
wait (busy == 1'b0);

5.3 通訊協定的握手等待

在序列通訊或 handshake 訊號等場合,常有多個事件連續發生,wait 敘述能簡潔描述。例如「等待傳送完成旗標為 1」:

wait (tx_done == 1'b1);

5.4 Testbench 中使用 wait 的注意事項

在 testbench 使用 wait 時,需留意條件永遠不成立的風險。若條件一直不滿足,模擬會卡住。建議搭配timeout 機制異常提示使用。

initial begin
  integer timeout;
  timeout = 0;
  while (reset_n != 1'b1 && timeout < 1000) begin
    #1; // 每次延遲一個時間單位
    timeout = timeout + 1;
  end
  if (timeout == 1000)
    $display("Error: reset_n 未被解除");
end

如此一來,即使條件未達成,也能安全結束模擬並輸出錯誤訊息。

6. 常見錯誤與除錯技巧

wait 敘述雖然實用,但若使用不當可能造成模擬異常。本章介紹常見問題與對策。

6.1 進入無限等待

最典型的問題是條件永遠不成立,模擬無限卡住。例如初始化錯誤、訊號未正確變化、設計失誤等。

解決方法:

  • 確認等待的訊號是否真的會在模擬中變化
  • 在 testbench 明確設定初值或切換模式
  • 加入 timeout,避免永遠停滯
integer timeout;
timeout = 0;
while (flag != 1'b1 && timeout < 1000) begin
  #1;
  timeout = timeout + 1;
end
if (timeout == 1000)
  $display("Error: flag 未變為 1");

6.2 條件式書寫錯誤

若在 wait 條件式誤寫錯誤值或邏輯,會導致行為與預期不同。特別是複數條件組合時更要小心。

解決方法:

  • 檢查括號與運算子是否正確
  • 透過 $display 印出訊號值確認狀態

6.3 競賽條件與非預期的模擬進程

當 wait 與其他事件控制(如 @ 或 always)混合使用時,可能因訊號變化時序造成執行順序非預期

解決方法:

  • 明確規劃訊號觸發點(posedge / negedge 等)
  • 必要時與事件控制或 #delay 搭配使用

6.4 除錯的小技巧

  • 使用 $display
    在 wait 前後輸出變數與時間,確認停在哪裡
  • 檢查條件成立瞬間
    在條件成立時輸出 log,確認跳出點
  • 由簡單情境開始測試
    先確認單一訊號等待是否正常,再加入複雜條件

掌握這些技巧,可提升模擬的穩定性與除錯效率。

7. 提升模擬效率的技巧

在 Verilog 模擬中,若能將 wait 與其他控制手法靈活搭配,就能大幅提升驗證效率。本章介紹幾種以 wait 為核心的效率提升技巧。

7.1 wait 與 #delay(延遲敘述)的差異與取捨

wait 與 #delay 都能「等待」,但使用情境不同:

  • wait 敘述:等待特定條件(如訊號值或狀態)成立
    範例:wait (ready == 1'b1);
  • #delay 敘述:純粹延遲指定時間
    範例:#10; // 等待 10 個時間單位

效率提升要點:

  • 條件事件驅動 → 用 wait
  • 單純時間延遲 → 用 #delay
  • 正確取捨能避免不必要的迴圈或計算,加快模擬速度。

7.2 模擬加速的實際作法

  • 避免不必要的迴圈與冗長等待
    設計時需確認條件會改變,避免無限等待。
  • 利用旗標(flag)進行有效等待
    透過狀態轉移或完成通知的旗標訊號,能讓模擬更簡潔。
wait (done_flag == 1'b1);

7.3 善用 finish_flag 與 timeout

為避免模擬意外停住,testbench 常加入 finish_flag 或 timeout 機制。

wait (finish_flag == 1'b1);
$finish;

若搭配 timeout,即使模擬超時也能自動結束,避免浪費時間。

7.4 與事件驅動搭配

wait 可以與 @ 事件控制或 fork/join 並行結構結合,形成更靈活的驗證手法。

fork
  wait (signal_a == 1'b1);
  wait (signal_b == 1'b1);
join

這樣可以同時監控多個事件,減少遺漏,提升驗證效率。

有效率的模擬設計,能直接影響專案的開發速度與品質。

8. 與 SystemVerilog 及其他語言比較

Verilog 的 wait 敘述雖然簡單,但近年來 SystemVerilog 與 VHDL 等語言提供更多強化功能。本章比較各語言差異。

8.1 SystemVerilog 的擴充

SystemVerilog 作為 Verilog 的上位相容,對 wait 加入了幾個強化功能:

  • wait fork/join:等待多個並行程序完成
    例:fork ... join wait fork;
  • wait order:可指定多個條件需依序成立(部分工具支援)
  • event / semaphore 搭配:可靈活等待使用者自訂事件或同步控制

8.2 Verilog 與 VHDL 的差異

  • VHDL 的 wait:語法多樣,如 wait until (條件);wait for 100 ns;
  • Verilog 的 wait:僅有 wait (條件);,時間等待需用 #delay@(posedge clk)

總結: VHDL 的 wait 本身涵蓋多功能,而 Verilog 則需搭配其他語法組合使用。

8.3 與其他控制流程比較

Verilog 除了 wait,還有 ifwhileforever@ 等控制結構。
wait 專注於「條件成立前暫停」,若與其他控制敘述搭配,能更精準實現時序控制。

9. 透過圖解與波形理解 wait

要直觀理解 wait,利用時序圖或波形圖非常有效。以下用範例示意。

9.1 基本動作示意

例如等待「reset_n 變為 1」:

initial begin
  wait (reset_n == 1'b1);
  // 後續處理
end
Time  | 0 | 10 | 20 | 30 | 40 | 50 | ...
reset_n  0   0    1    1    1    1
    <---wait---> |----→ 開始後續處理

9.2 訊號上升/事件偵測

always begin
  wait (data_valid == 1'b1);
  // 資料處理
end
Time        | 0 | 10 | 20 | 30 | 40 | 50 | ...
data_valid    0    0    0    1    0    1
                 <---wait---> |---開始處理

9.3 複數條件等待

wait ((ready == 1'b1) && (start == 1'b1));
Time    | ... | 40 | 50 | 60 | 70 | ...
ready         0    1    1    1
start          0    0    1    1
 <-----wait------>| 後續開始(ready 與 start 同為 1)

9.4 狀態轉移與 Testbench 範例

在 testbench 中,常會根據狀態與事件變化進行驗證。利用波形圖可直觀檢視 wait 的動作,幫助理解複雜時序。

10. 常見問題(FAQ)

以下整理了在使用 Verilog wait 敘述時,經常遇到的疑問與實務困擾,以 Q&A 形式解答。

Q1. wait 與 #delay(延遲)有何不同?
A. wait 是「等待條件成立才繼續執行」,而 #delay 是「單純等待指定時間」。
wait 適合用於事件或訊號觸發,#delay 則適合用於時間控制。

Q2. wait 可以用在 always 區塊嗎?
A. 可以。在 always 區塊中可用來等待特定訊號或事件。但注意:若是合成用的 always 區塊,原則上不建議使用,因為 wait 僅適用於模擬。

Q3. wait 可以在硬體合成(FPGA/ASIC)中使用嗎?
A. 不行。wait 是模擬專用語法,大多數合成工具不支援,因此不應出現在 RTL 合成設計中。

Q4. 為什麼 wait 沒有動作/無法跳出?
A. 最常見原因是「等待的訊號未如預期變化」。建議檢查波形或用 $display 輸出訊號值,並加入 timeout 機制避免死等。

Q5. VHDL 的 wait 與 Verilog 的 wait 有什麼差異?
A. VHDL 的 wait 形式多樣(如 wait until、wait for),控制自由度較高;Verilog 的 wait 僅限於「等待條件成立」,時間等待則需搭配 #delay 或 @ 事件控制。

Q6. 事件控制(@)與 wait 的差異是?
A. @ 是在特定事件(如正緣或負緣)發生時觸發處理;wait 則是持續判斷條件是否成立,一旦條件為真才繼續。
例:@(posedge clk)wait(clk == 1) 看似類似,但前者針對邊緣事件,後者針對邏輯條件。

Q7. 模擬停住不動怎麼辦?
A. 多半是等待條件永遠不成立。需檢查初始值與訊號變化,並加入 timeout 防護。

Q8. 可以在條件中使用多個訊號嗎?
A. 可以。可用 AND(&&)或 OR(||)等邏輯運算子組合。
例:wait((ready == 1'b1) && (start == 1'b1));

11. 總結與延伸閱讀

本文從基礎到應用,完整介紹了 Verilog wait 敘述。以下整理重點:

11.1 重點整理

  • wait 是 Verilog 中用來「等待條件成立」的控制語法,常用於 testbench 與模擬控制。
  • 基本語法: wait (條件); 可搭配單一或多重條件。
  • 常見應用: 等待 reset 解除、訊號變化、資料傳輸完成等。
  • 僅限模擬使用:不適合出現在 RTL 合成設計。
  • 需防範錯誤: 無限等待、條件式誤寫、競賽條件等問題。
  • 可與其他控制語法搭配(如 @、#delay、fork/join),打造更靈活的驗證流程。
  • 了解 SystemVerilog 與 VHDL 的差異,有助於跨語言設計與驗證。

正確理解與運用 wait 敘述,不僅能提升模擬效率,也能大幅改善設計品質。希望本文能幫助您在實務設計與驗證工作中更熟練掌握 Verilog。

本篇「Verilog wait 敘述徹底解析」到此結束,若想進一步學習,建議閱讀更多相關的測試平台設計與模擬技巧文章。