精通 Verilog 中的 $display:有效的除錯與顯示控制技巧

目次

1. 介紹:「display」在 Verilog 中的重要性與目的

「display」在 Verilog 中是什麼意思?

在 Verilog 中,$display 是一個系統任務,用來在模擬過程中「顯示」設計的內部狀態。它類似於 C 語言的 printf,可以將訊號、變數值與字串輸出到終端機或主控台——在除錯與功能驗證中扮演核心。

為什麼 $display 對 Verilog 開發至關重要?

  • 提升除錯效率 :在複雜的電路設計中,能即時觀察內部訊號是否正常運作相當關鍵。使用 $display ,即可在模擬時立即檢查感興趣的訊號值。
  • 模擬可視化 :當僅靠波形圖追蹤特定時間點的值變化可能不足時,顯示日誌提供了可靠的方式標示那些精確時刻。
  • 同時有助於文件編寫 :在向其他工程師說明設計意圖或操作規則時,加入註解式的顯示日誌可以增進程式碼的可讀性。

本文的目的與結構

在本文中,我們將系統性說明以下內容:
  1. 基本語法與使用方式 :細緻介紹 $display 的基本語法與使用方法。
  2. 與其他系統任務的比較 :整理 $write$strobe$monitor 等與顯示相關任務的差異。
  3. 格式化字元與進階使用技巧 :說明 %d%b%h%s 等格式化字元的使用,以及特殊的顯示技巧。
  4. 實務使用範例 :提供測試平台與程式碼範例,讓讀者能立即上手。
  5. 顯示控制的應用 :舉例說明硬體輸出(如 LCD、螢幕)與文字/影像顯示的情境。
透過上述結構,無論是初學者或中階使用者,都能正確理解 Verilog 中的 $display,並在實務上加以運用。接下來的每個章節,我們都會盡量以範例與圖示說明,讓概念更清晰。

2. $display 基礎:語法、使用情境與注意事項

$display 的基本語法

在 Verilog 中使用 $display 時,其基本語法如下。
$display("string or format specifiers", signal1, signal2, ...);
  • 字串部分 :寫入欲顯示的文字或格式化字元(例如: %d%b%h)。
  • 參數 :列出要依照對應格式輸出的訊號名稱或變數。
範例:顯示時脈計數與訊號值
$display("Time=%0t : clk=%b, reset=%b", $time, clk, reset);
在此範例中,會輸出模擬時間以及時脈/重置信號的值。

$display 的使用情境

  1. 掌握模擬進度 :在設計的特定位置插入 $display ,即可驗證程式碼已執行到哪一段。
  2. 訊號值驗證 :即使波形觀測器難以直觀了解條件分支或狀態轉移,文字輸出也能讓理解變得更容易。
  3. **條件訊息顯示 :結合 if 敘述,只在特定條件成立時記錄訊息。 if (reset) $display("Reset asserted at %0t", $time);

$display$write 的差異

$display 會自動在輸出末尾加上換行;相對地,$write 會持續輸出而不換行。 範例:
$display("Hello");
$display("World");
輸出:
Hello
World
$write("Hello");
$write("World");
輸出:
HelloWorld
如果需要更清晰的逐行日誌,請使用 $display;若想在同一行上格式化多項輸出,則使用 $write

注意事項

  1. 避免過度輸出:如果在每個時鐘週期都使用 $display,日誌會變得龐大且可讀性下降。→ 使用條件化來減少輸出。
  2. 利用時間顯示:輸出 $time$realtime 可讓您精確捕捉操作的時序。
  3. 僅供模擬使用的任務$display 不能用於綜合(FPGA/ASIC 實作)。它嚴格是僅供模擬的除錯工具。

3. 比較日誌輸出系統任務:$display、$write、$strobe、$monitor

Verilog 提供了除 $display 之外的系統任務來輸出訊息。它們的使用情境與時機不同,了解如何分別使用它們相當有效率。

$display:標準顯示任務

  • 特性 會自動加入換行,每次呼叫記錄一行。
  • 使用情境 最常用作為基本除錯方法;可在任何時候呼叫一次性輸出。

$write:不換行的顯示

  • 特性 不會附加換行,因而在同一行持續輸出。
  • 使用情境 當您想要將多個值並排顯示時很有用。
  • 範例 $write("A=%d, ", a); $write("B=%d\n", b); → 輸出: A=5, B=10

$strobe:在模擬週期結束時輸出

  • 特性 在當前步驟的所有模擬評估完成後才列印值。
  • 使用情境 用於避免競爭條件(多個訊號同時變化時)。
  • 範例 $strobe("Time=%0t, signal=%b", $time, sig); → 雖然 $display 可能顯示中間值,$strobe 會顯示穩定值。

$monitor:自動蹤輸出

  • 特性 當任何被監控的訊號變化時自動輸出。
  • 使用情境 想要持續監控一組訊號時相當方便。
  • 範例 $monitor("At %0t a=%b, b=%b", $time, a, b); → 當 ab 任一變化時記錄。

總結表格

任務換行輸出時機主要使用情境
$display呼叫時基本日誌輸出
$write呼叫時並排格式化
$strobe模擬週期結束後 檢查穩定值
$monitor訊號變化時自動持續監控

有效使用技巧

  • 預設使用 $display :可讀性佳,對初學者友好。
  • 需要單行合併輸出時使用 $write
  • 在變化後需要穩定值時使用 $strobe
  • 需要持續訊號監控時使用 $monitor

4. 格式說明子與進階顯示技巧

使用 $display$write 等任務時,您可以在字串中加入「格式說明子」以所需的格式輸出訊號或變數。因為它類似 C 語言的 printf,依需求正確使用可大幅提升除錯效率。

基本格式說明子

說明子說明輸出範例
%bbinary1010
%ddecimal10
%hhexadecimalA
%ooctal12
%cASCII characterA
%sstringHello
%tsimulation time#100
%mhierarchy nametop.u1.u2

實用範例

  1. 以多種格式顯示訊號 reg [7:0] data = 8'b10101010; $display("data = %b (bin), %d (dec), %h (hex)", data, data, data); → 範例輸出: data = 10101010 (bin), 170 (dec), AA (hex)
  2. 檢查模組層級 $display("Current module hierarchy is %m"); → 範例輸出: Current module hierarchy is top.u1.counter
  3. 顯示模擬時間 $display("Time=%0t: clk=%b", $time, clk); → 範例輸出: Time=100: clk=1

進階顯示技巧

  • Zero‑padding 與欄位寬度 您可以使用 %0d 之類的格式指定零填充或欄位寬度。範例: $display("Count=%04d", count); → 輸出: Count=0012
  • 有號與無號的差別 %d 以有號解讀數值, %u 以無號方式解讀。若負數未如預期顯示,請檢查並調整格式說明子。
  • 多行訊息格式化 使用 \n 斷行以提升可讀性。範例: $display("Start of test\nSignal A=%b\nSignal B=%b", A, B);

注意事項

  • 留意位元寬度 :Verilog 訊號的寬度可能不同,使用 %d 可能導致截斷或符號延伸的問題。
  • 處理未定義值 (X, Z) :若包含未定義位元,使用 %b 會直接顯示 xz

5. 實作範例:在 Test Bench 與 Module 中使用 $display

以下將透過真實的 Verilog 程式碼範例,說明 $display 的有效用法,涵蓋測試平台的基本應用到條件式除錯訊息。

基本範例:在 Test Bench 中輸出

在測試平台中插入 $display,即可在模擬期間觀察行為。
module tb_counter;
  reg clk;
  reg reset;
  wire [3:0] count;

  // DUT (Device Under Test)
  counter uut (
    .clk(clk),
    .reset(reset),
    .count(count)
  );

  // Clock generation
  initial begin
    clk = 0;
    forever #5 clk = ~clk;  // invert every 5 units
  end

  // Test scenario
  initial begin
    reset = 1;
    #10 reset = 0;

    #50 $finish;
  end

  // Display state
  always @(posedge clk) begin
    $display("Time=%0t | reset=%b | count=%d", $time, reset, count);
  end
endmodule
在此範例中,每個上升時都會輸出 resetcount。過文字日誌與波形同時檢視,可更容易追蹤行為。

條件式顯示範例

結合 if 敘述,只在特定條件成立時記錄訊息。
always @(posedge clk) begin
  if (count == 4'd10) begin
    $display("Count has reached 10 (Time=%0t)", $time);
  end
end
→ 這樣可以避免過多的日誌,同時精確定位您關心的事件。

除錯訊息範例

在設計除錯時,捕捉訊號進入「非預期狀態」非常有效。
always @(posedge clk) begin
  if (count > 4'd12) begin
    $display("WARNING: count overflow detected! Time=%0t, value=%d", $time, count);
  end
end
→ 您可以快速發現設計缺陷或模擬行為異常。

同時監控多個訊號

當需要同時輸出多個訊號時,使用 $display 將它們合併成一行,可提升日誌的可讀性。
$display("Time=%0t | clk=%b | reset=%b | A=%h | B=%h | SUM=%h",
         $time, clk, reset, A, B, SUM);

實用技巧小結

  • $display 放在測試平台中以視覺化進度
  • 使用條件分支精緻化日誌
  • 產生警告訊息以偵測異常
  • 將多個訊號合併同一行以提升可讀性

6. 顯示控制應用(像素/文字/影像顯示)

到目前為止,我們已介紹 $display 用於模擬日誌。另一方面,Verilog 也廣泛應用於硬體層面的「顯示控制」(LCD、VGA、HDMI 輸出)。本節將簡要說明硬體層面實作螢幕顯示。

顯示控制的基本概念

要在螢幕上顯示文字或影像,必須產生視訊訊號,而非僅使用 $display。 常見的控制訊號包括:
  • HSYNC(水平同步) :表示每一行結束的訊號
  • VSYNC(垂直同步) :表示每一影格結束的訊號
  • RGB 資料 :代表每個像素顏色的訊號(例如 8 位元 × 3 = 24 位元顏色)
在 Verilog 中,您會透過計數器與來控制這些訊號,並依時序輸出,以實現「螢幕顯示」。

範例 1:顯示彩條

… 最基本的範例是水平輸彩條於顯示器上。
always @(posedge clk) begin
  if (h_counter < 100)       rgb <= 24'hFF0000; // red
  else if (h_counter < 200)  rgb <= 24'h00FF00; // green
  else if (h_counter < 300)  rgb <= 24'h0000FF; // blue
  else                       rgb <= 24'h000000; // black
end
會在螢幕上水平排列出紅、綠、藍三色彩條。

範例 2:文字顯示

要顯示文字,您需要準備 字型 ROM,並將每個字元的點陣圖樣轉換成像素。
// Referencing the pattern of 'A' from the font ROM and displaying
if (font_rom[char_code][y][x] == 1'b1)
    rgb <= 24'hFFFFFF;  // white for display
else
    rgb <= 24'h000000;  // black background
→ 這會在螢幕上繪製特定字元例如「A」)。

範例 3:影像顯示

要顯示影像,您需要讀取預先儲存的 點陣資料(ROM 或外部記憶體),並將其轉換為像素輸出。
rgb <= image_rom[addr];  // Retrieve color data from ROM
在使用 FPGA 的嵌入式系統中,此方法可讓您顯示簡單的圖示或標誌。

與除錯 $display 的差異

  • $display文字輸出(僅限模擬)
  • 顯示控制則是 視訊訊號產生(可硬體實作)
雖然兩者的目的不同,Verilog 學習者常常會混淆。
  • 「我想在模擬期間驗證行為」→ 使用 $display
  • 「我想在 FPGA 上輸出到實際螢幕」→ 設計視訊訊號邏輯

應用擴展

  • 在學習 FPGA 開發板時,Verilog 常用於 七段 LED 顯示小型 LCD 顯示
  • 進一步發展,您可以構建支援 VGA/HDMI 輸出 的系統,用於遊戲開發或 GUI 顯示。
  • 結合 $display 與顯示控制邏輯的知識,您即可在模擬與實體硬體中同時處理「顯示」功能。

7. 依據應用情境的適當使用與技巧

當我們談到 Verilog 中的「顯示」時,分為兩個層面:僅限模擬的 $display 任務硬體實作的顯示控制。適當使用兩者可提升開發與除錯效率。

模擬中的使用

  1. $display 作為除錯日誌
    • 使用 $display 輸出關鍵變數或訊號,以檢查設計是否如預期運作。
    • 透過日誌驗證「特定條件下的值」或「計數器達到某點」相當有效率。
  2. 避免過度輸出
    • 每個時脈週期都輸出會淹沒日誌並降低可讀性。請縮小條件範圍。
    • 範例: if (state == ERROR) $display("Error occured at %0t", $time);
  3. 區分不同任務
    • $monitor → 用於持監看訊號
    • $strobe → 當需要輸出穩定值時 * $write → 用於格式化的水平輸出

硬體顯示控制的使用

  1. 學習用的七段顯示
    • 在 FPGA 初學者專案中,將計數器值顯示於七段 LED 是常見做法。
    • 結合 $display 的模擬輸出,可加深對顯示與模擬差異的理解。
  2. LCD 或 VGA 顯示器控制
    • 使用字型 ROM 或影像 ROM 來顯示文字或圖像。
    • 同時在模擬中使用 $display,可雙重驗證視訊訊號產生是否正確。
  3. 硬體除錯覆疊 您可以在實際的視訊輸出上覆疊「計數器值」、「座標」或「除錯訊息」。
    • 在 FPGA 開發中,常會將「螢幕本身」作為除錯工具。

實用技巧

  • 先模擬 → 硬體流程 先使用 $display 於模擬中驗證行為,之後再移至硬體實作的顯示控制邏輯。
  • 同時使用日誌與波形 來自 $display 的文字日誌會指出「事件發生時間」,而波形則顯示「詳細轉換」。兩者結合可提升除錯精度。
  • 統一團隊開發的訊息格式 標準化 $display 訊息格式(前綴、時間顯示等)可讓多人協作時的日誌分析更為容易。

摘要

  • $display 類型的任務是僅供模擬使用的「觀測工具」
  • 顯示控制則是硬體實作的「顯示方法」
  • 依需求適當使用並結合兩者,可提升開發效率

8. 常見問題 (FAQ)

Q1. $display$monitor 有何差異?

A. $display 於被呼叫的那一刻輸出一次;相較之下,$monitor 會在註冊的訊號變化時自動輸出。
  • 單次除錯 → $display
  • 持續監控 → $monitor

Q2. 何時該使用 $strobe

A. $strobe 會在模擬週期結束時輸出穩定值。例如在同一個時脈上升沿同時改變多個訊號時,$display 可能會顯示中間值;此時 $strobe 可方便地顯示最終值。

Q3. 格式指定子 %m 有什麼用途?

A. %m 會輸出目前的模組階層名稱。在大型設計中,記錄「訊息來源的階層」能大幅簡化分析。
$display("Current module: %m");
範例輸出:
Current module: top.u1.counter

Q4. 我的日誌因使用大量 $display 而變得龐大,該怎麼辦?

A. 以下措施相當有效:
  • 使用 if 敘述過濾輸出
  • 僅輸出錯誤偵測或特定事件
  • 使用 $monitor 只監看最少必要的訊號
  • 輸出至日誌檔,於分析時再套用過濾工具

Q5. $display 可以用於綜合 (FPGA/ASIC) 嗎?

A. 不能。$display 嚴格是 僅供模擬的任務,綜合工具會忽略它,實際硬體中不會出現。如果想在實體硬體上顯示輸出,必須以 Verilog 設計「七段 LED」、「LCD」或「VGA 控制邏輯」等方式。

Q6. 如何在實體硬體上顯示文字或影像?

A. 不是透過 $display,而是產生視訊訊號。
  • 七段顯示器 → 用於簡單的數字或字元顯示
  • VGA / LCD → 產生 HSYNC、VSYNC、RGB 訊號並加以控制
  • 文字 → 使用字型 ROM 輸出點陣圖案
  • 影像 → 將點陣圖存於 ROM 或外部記憶體,依序輸出像素

9. 結論與後續步驟

本文摘要

本文從基礎到應用,全面說明 Verilog 中的「display」相關概念,重點包括:
  1. $display 基礎
    • 一個模擬使用的任務,可類比 C 語言 printf 來顯示訊號或變數。
  2. 與相關任務的差異
    • $write → 顯示但不換行
    • $strobe → 在模擬週期結束時輸出穩定值
    • $monitor → 自動監看訊號變化並輸出
  3. 使用格式指定子
    • 透過 %b%d%h%m%t 等,可產生更清晰、實用的日誌。
  4. 實作範例
    • 在測試平台中插入 $display 以監控執行進度。
    • 使用條件訊息提升除錯效率。
  5. 顯示控制的應用
    • $display 僅限模擬;硬體實作則以 HSYNC、VSYNC、RGB 輸出文字/影像。
    • 從學習七段顯示器到進階 VGA/HDMI 控制,皆有延伸可能。

後續步驟

  • 進階至 SystemVerilog → 在繼任語言 SystemVerilog 中,你可以使用更進階的除錯功能(斷言、增強的 $display 等)。
  • 結合波形檢視器 → 結合 $display 日誌與波形資料,你可以同時分析數值與變化,提升除錯精度。
  • 學習輸出 → 在小型 FPGA 開發板專案中嘗試將資訊輸出至 7 段顯示器或 LCD,體驗「模擬顯示」與「硬體顯示控制」之間的差異。 應用於團隊開發* → 透過統一 $display 訊息格式,可提升多人開發時的日誌分析效率。

結語

$display 不只是單純的「文字輸出」。它是模擬除錯的強大工具。當你踏入顯示控制的領域時,亦能體驗在 FPGA 上於真實螢幕顯示圖形的樂趣。 希望本篇文章能協助 Verilog 學習者清楚了解「模擬除錯」與「硬體顯示輸出」兩者的差異。