Verilog $display 教學:完整使用指南與實戰應用

目次

1. 前言:Verilog 中「display」的重要性與目的

什麼是 Verilog 中的「display」?

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("字串或格式指定符", 訊號1, 訊號2, ...);
  • 字串部分:可填入任意文字或格式指定符(例如:%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); → 可確保輸出的是穩定值。

$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 類似,只要正確選用,就能大幅提升除錯效率。

基本格式指定符

指定符內容輸出範例
%b二進位 (binary)1010
%d十進位 (decimal)10
%h十六進位 (hexadecimal)A
%o八進位 (octal)12
%c以 ASCII 字元顯示A
%s字串Hello
%t輸出模擬時間#100
%m輸出階層名稱(模組名稱)top.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("目前的模組階層是 %m"); → 輸出:目前的模組階層是 top.u1.counter
  3. 顯示模擬時間 $display("Time=%0t: clk=%b", $time, clk); → 輸出:Time=100: clk=1

特殊顯示技巧

  • 零補齊與位數指定
    %0d 可指定補零與位數,例如 $display("Count=%04d", count); → 輸出:Count=0012
  • 有號 / 無號區分
    %d 視為有號整數,%u 視為無號整數,若輸出負數不如預期,請檢查指定符。
  • 多行訊息
    可用 \n 換行:$display("Start of test\nSignal A=%b\nSignal B=%b", A, B);

注意事項

  • 注意位元寬度:不同寬度的訊號,使用 %d 可能出現截斷或符號延伸。
  • 未定義值 (X, Z):若訊號包含未知值,%b 會顯示為 xz

5. 實戰範例:在測試平台與模組中使用 $display

以下透過實際的 Verilog 程式碼範例,展示如何有效利用 $display,從基礎測試平台到條件式除錯。

基礎範例:在測試平台中輸出

在測試平台加入 $display,可於模擬過程中檢查運作狀態。

module tb_counter;
  reg clk;
  reg reset;
  wire [3:0] count;

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

  // 產生時脈
  initial begin
    clk = 0;
    forever #5 clk = ~clk;  // 每5單位反轉一次
  end

  // 測試情境
  initial begin
    reset = 1;
    #10 reset = 0;

    #50 $finish;
  end

  // 顯示狀態
  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("計數器已達 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("Time=%0t | clk=%b | reset=%b | A=%h | B=%h | SUM=%h",
         $time, clk, reset, A, B, SUM);

實戰重點整理

  • 在測試平台中顯示執行進度
  • 利用條件判斷縮小輸出範圍
  • 加入警告訊息偵測異常
  • 將多個訊號整合在單行輸出

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

前面介紹的 $display,主要用於模擬日誌與除錯訊息的「文字顯示」。
另一方面,Verilog 也廣泛應用於「顯示控制」(如 LCD、VGA、HDMI 輸出)。以下簡單介紹如何在硬體層面進行畫面顯示。

顯示控制的基本概念

要在螢幕上顯示文字或影像,不能使用 $display,而是要產生視訊訊號。典型控制訊號如下:

  • HSYNC(水平同步訊號):表示每一行的邊界。
  • VSYNC(垂直同步訊號):表示每一個畫面的邊界。
  • RGB 資料:每個像素的顏色(例如 8bit × 3 = 24bit 色彩)。

在 Verilog 中,這些訊號會透過計數器或狀態機控制,依時序輸出,進而實現「畫面顯示」。

範例1:顯示彩條

最基礎的範例是輸出彩條:

always @(posedge clk) begin
  if (h_counter < 100)       rgb <= 24'hFF0000; // 紅色
  else if (h_counter < 200)  rgb <= 24'h00FF00; // 綠色
  else if (h_counter < 300)  rgb <= 24'h0000FF; // 藍色
  else                       rgb <= 24'h000000; // 黑色
end

→ 畫面會呈現水平排列的紅、綠、藍彩條。

範例2:文字顯示

文字顯示需要字型 ROM,將每個字元的點陣轉換為像素輸出。

// 依照 'A' 的字型點陣輸出
if (font_rom[char_code][y][x] == 1'b1)
    rgb <= 24'hFFFFFF;  // 白色
else
    rgb <= 24'h000000;  // 黑色背景

→ 可在畫面上繪製特定字元(如「A」)。

範例3:影像顯示

顯示影像時,需預先將位圖資料存入 ROM 或外部記憶體,再轉換為像素輸出。

rgb <= image_rom[addr];  // 從 ROM 取出顏色資料

在 FPGA 的嵌入式系統中,這種方法常用於顯示圖示或 Logo。

與除錯用 $display 的差異

  • $display文字輸出(僅模擬用)
  • 顯示控制:視訊訊號產生(可於硬體實作)

兩者用途完全不同,但常被初學者混淆。

  • 「模擬中檢查運作」 → 使用 $display
  • 「FPGA 輸出到螢幕」 → 設計視訊訊號控制電路

應用延伸

  • 學習用 FPGA 板常使用七段顯示器小型 LCD
  • 進一步可實作VGA/HDMI 輸出,應用於遊戲或 GUI。
  • 若同時掌握 $display 與顯示控制,能在模擬與實機中靈活處理「顯示」問題。

7. 根據應用場景的使用方式與技巧

在 Verilog 中,「顯示」可分為兩種:模擬用的 $display 系統任務硬體上的顯示控制。依場景正確選用,能提升開發與除錯效率。

模擬中的應用

  1. $display 作為除錯日誌
    • 輸出重要變數或訊號,確認設計是否正確。
    • 特別適合檢查「特定條件」或「計數器到達點」。
  2. 避免過量輸出
    • 若每個時脈都輸出,日誌會爆量。建議加條件。
    • 例如:if (state == ERROR) $display("錯誤發生 at %0t", $time);
  3. 搭配其他系統任務
    • $monitor → 持續監視訊號
    • $strobe → 輸出穩定值
    • $write → 排版整齊的單行輸出

實機顯示控制的應用

  1. 七段顯示器學習
    • FPGA 入門常用計數器數值顯示。
    • 搭配 $display 模擬,可雙重驗證。
  2. LCD / VGA 顯示
    • 透過字型 ROM 或影像 ROM 顯示文字與影像。
    • 可與 $display 搭配,用於驗證訊號正確性。
  3. 除錯疊加層
    • 將「計數值」「座標」「訊息」疊加在影像上。
    • FPGA 開發常利用「畫面」作為除錯工具。

實務技巧

  • 注意「模擬 → 實機」的流程
    先用 $display 確認,再實作顯示控制。
  • 結合日誌與波形
    $display 適合檢查事件時刻,波形適合檢查細節轉換。
  • 團隊開發時統一訊息格式
    如加前綴或時間,方便多人解析日誌。

小結

  • $display:模擬用觀察工具
  • 顯示控制:硬體實現的輸出方式
  • 兩者結合,能大幅提升開發效率

8. FAQ(常見問題與回答)

Q1. $display$monitor 有何不同?

A. $display 是「呼叫當下只輸出一次」,而 $monitor 則是在指定訊號發生變化時「自動持續輸出」。

  • 單次除錯 → $display
  • 持續監控 → $monitor

Q2. 什麼時候需要使用 $strobe

A. $strobe 會在模擬週期結束時輸出,確保顯示的是「最終穩定值」。
例如在時脈上升沿後有多個訊號同時變化時,$display 可能輸出暫時值,此時用 $strobe 能得到穩定結果。

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

A. %m 可輸出當前模組階層名稱。在大型設計中,可快速確認訊息來自哪個階層。

$display("目前模組: %m");

輸出範例:

目前模組: top.u1.counter

Q4. 如果寫了太多 $display,日誌變得非常龐大怎麼辦?

A. 可採取以下方法:

  • 使用 if 條件限制輸出
  • 只輸出錯誤或關鍵事件
  • $monitor 監控必要訊號
  • 將輸出導入檔案,再過濾分析

Q5. $display 可以在合成(FPGA/ASIC)中使用嗎?

A. 不行。$display 只在模擬環境下有效,合成工具會忽略它,無法用於實機。
若要在實機輸出,需要設計「七段顯示器」「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. 實戰範例
    • 在測試平台中輸出進度
    • 搭配條件判斷提升除錯效率
  5. 顯示控制的應用
    • $display 僅限模擬,但可在 FPGA 上實作 HSYNC、VSYNC、RGB 以輸出文字或影像。
    • 從七段顯示器到 VGA/HDMI,皆可逐步延伸。

下一步

  • 學習 SystemVerilog
    → SystemVerilog 提供更進階的除錯工具(如 assertion、加強版 $display)。
  • 結合波形檢視
    → 將 $display 輸出與波形檢視結合,可從數值與時序兩面向分析。
  • 實機顯示專案
    → 在 FPGA 上嘗試七段顯示器或 LCD 專案,體驗「模擬輸出」與「實機顯示」的差異。
  • 團隊開發應用
    → 統一 $display 日誌格式,方便多人協作分析。

結語

$display 不只是單純的「文字輸出」,更是強大的模擬除錯工具。若再進一步學習顯示控制,還能利用 FPGA 將圖形實際輸出到螢幕。
希望本文能幫助學習 Verilog 的讀者,掌握「模擬除錯」與「實機顯示」的完整技巧。