Verilog 陣列完整教學:從基本語法到SystemVerilog進階應用

1. 前言

Verilog(韋理ログ)作為一種硬體描述語言,被廣泛應用於FPGA與ASIC等電路設計,是不可或缺的工具。要利用Verilog進行高效率的設計,對陣列的理解極為重要。

透過陣列,可以更簡潔直觀地處理資料集合,並提升電路描述的可讀性與維護性。特別是在將多個訊號分組,或是表示RAM等記憶體結構時,陣列的效果非常顯著。

本文將以「Verilog 陣列」為核心關鍵字,從基礎的陣列定義方式,到實務上有用的應用技巧進行全面解析。同時涵蓋陣列的種類、SystemVerilog的擴充功能,以及常見錯誤與FAQ,幫助讀者建立完整的理解。

文章內容淺顯易懂,並搭配實用的程式範例,即使是初學者也能輕鬆上手。建議閱讀到最後,確保完整掌握。

2. Verilog的基本資料型態

在Verilog中使用陣列之前,必須先理解基本資料型態。Verilog提供了數種主要型態,用來處理電路設計中必須的邏輯訊號。

reg型與wire型的差異

Verilog中最常用的型態是「reg(暫存器)」與「wire(導線)」。需要根據邏輯訊號的行為來選擇適當型態。

  • wire型
    wire用作連接線,需由其他模組或電路驅動,必須透過assign語句進行賦值,適合組合邏輯電路的輸出。例:
  wire a;
  assign a = b & c;
  • reg型
    reg作為暫存變數,能在程序區塊(如always)中賦值,用於表示鎖存器或正反器。例:
  reg q;
  always @(posedge clk) begin
      q <= d;
  end

可用於陣列的資料型態

在Verilog中,陣列通常以reg型態定義,但wire在部分情境下也可使用。不過,早期的Verilog不支援多維陣列,這一點在SystemVerilog中獲得了改善。

以下為簡單的陣列宣告範例:

reg [7:0] data_array [0:15];  // 儲存16筆8位元資料的陣列

掌握這些基本概念能避免在宣告或使用陣列時的混淆。特別是reg與wire的誤用,容易導致模擬錯誤或綜合錯誤。

3. 陣列的基本概念

在Verilog中,當需要一次處理多個相同型態的訊號時,就會使用「陣列(array)」。利用陣列能夠更好地整理訊號,並提升程式碼的可讀性與可重用性。

陣列的宣告方式

Verilog主要支援一維陣列,基本語法如下:

reg [位元寬度] 陣列名稱 [索引範圍];

具體範例:

reg [7:0] data_array [0:15];  // 儲存16筆8位元資料的陣列

此範例中,data_array擁有0到15共16個元素,每個元素皆為8位元(1位元組)。

陣列元素的存取方式

陣列的每個元素可透過索引號碼存取,與C語言相同,索引從0開始。

data_array[0] = 8'hFF;   // 將十六進位FF指定給第1個元素
data_array[1] = 8'd12;   // 將十進位12指定給第2個元素

同時,也可在always區塊中使用迴圈進行初始化或操作。

integer i;
always @(posedge clk) begin
    for (i = 0; i < 16; i = i + 1) begin
        data_array[i] <= 8'd0;
    end
end

4. 多維陣列的活用

在Verilog中,陣列能簡化設計並整理電路結構,而透過多維陣列,則可更有效率地處理複雜的資料結構。

需要注意的是,早期的Verilog(IEEE 1364-1995)並不支援多維陣列,而Verilog 2001之後才正式引入。若要進行更靈活的陣列操作,建議使用SystemVerilog。

多維陣列的宣告

自Verilog 2001起,可以對單一變數指定多個索引來定義多維陣列。基本語法如下:

reg [7:0] matrix [0:3][0:3];  // 定義4×4的8位元矩陣

此宣告使matrix成為一個擁有16個8位元元素的二維陣列。

元素的存取與賦值

在多維陣列中,也可透過索引存取特定元素並進行賦值:

matrix[0][0] = 8'hA5;
matrix[2][3] = 8'd255;

搭配for迴圈

透過巢狀for迴圈,可以靈活操作多維陣列。以下範例為初始化操作:

integer i, j;
always @(posedge clk) begin
    for (i = 0; i < 4; i = i + 1) begin
        for (j = 0; j < 4; j = j + 1) begin
            matrix[i][j] <= 8'd0;
        end
    end
end

多維陣列的應用範例

  • 矩陣運算與濾波處理:適用於需要複雜資料結構的電路設計。
  • 影像處理或數位訊號處理(DSP):可用於逐像素資料操作。
  • ROM/RAM區塊化,以及位址與資料配對整理

注意事項與限制

  • 使用多維陣列時,需確認綜合工具的支援情況,部分工具可能不完全支援。
  • 與模組實例化或介面結合時,可能產生額外限制。
  • 掌握與SystemVerilog的差異,有助於避免相容性問題(後續章節將詳述)。

5. 使用陣列建構記憶體模型

在Verilog中,可以利用陣列建構簡易的記憶體模型。這樣能夠簡潔且靈活地描述RAM或ROM等儲存電路,並用於模擬與設計。

其中,一維陣列的記憶體模型特別常見,應用於CPU設計或通訊系統。

記憶體模型的基本語法

以下範例表示一個每字32位元、1024個位址(0〜1023)的簡單RAM:

reg [31:0] memory [0:1023];  // 32位元 × 1024字的記憶體

此宣告建立了一個名為memory的陣列,每個索引(位址)可存放32位元資料。

記憶體的寫入與讀出

透過陣列來進行記憶體的讀寫操作,可如下撰寫:

// 寫入
always @(posedge clk) begin
    if (we) begin
        memory[addr] <= data_in;
    end
end

// 讀出
assign data_out = memory[addr];

重點說明:

  • 寫入於posedge clk下同步執行,並受we(write enable)控制。
  • 讀出通常以組合邏輯方式(assign)實現,屬於非同步讀取。

記憶體初始化方式

在測試平台或初始狀態設定中,可以使用initial區塊初始化陣列:

integer i;
initial begin
    for (i = 0; i < 1024; i = i + 1) begin
        memory[i] = 32'd0;
    end
end

此外,利用$readmemh$readmemb可從外部檔案讀入初始值:

initial begin
    $readmemh("rom_init.hex", memory);  // 以16進位格式初始化
end

實務應用範例

  • CPU或微控制器的暫存器檔案
  • FPGA內部區塊RAM(BRAM)的行為模擬
  • 快取記憶體(Cache)的動作驗證
  • ROM資料的讀取模擬

注意事項

  • 當陣列規模過大時,會增加模擬時間與綜合資源
  • 讀取方式(同步或非同步)需依設計規格與工具支援審慎選擇。
  • 若考慮綜合,應遵循記憶體推論規則,以確保合成工具能正確識別為實體RAM。

6. SystemVerilog中的陣列擴充

在Verilog 2001以前,陣列功能有限,設計常顯得繁瑣。為此,後繼的SystemVerilog大幅擴充了陣列功能,提供更靈活且強大的描述方式。

本章將介紹SystemVerilog中的三種代表性陣列:「動態陣列」、「關聯陣列」、「佇列(Queue)」,並說明其特點與應用情境。

動態陣列(Dynamic Arrays)

特點

  • 可在執行時改變大小。
  • 適合大小不固定或需彈性變動的資料集合。

宣告與使用範例

int dyn_array[];             // 宣告動態陣列
dyn_array = new[10];         // 初始化為10個元素
dyn_array[0] = 100;

應用情境

  • 測試平台中暫存資料。
  • 管理大小可變的緩衝區。

關聯陣列(Associative Arrays)

特點

  • 索引可使用任意值(整數、字串等)
  • 可扮演雜湊表(hash table)的角色。

宣告與使用範例

int assoc_array[string];     // 使用字串作為索引的關聯陣列
assoc_array["id_001"] = 42;

應用情境

  • 儲存不同參數的設定值。
  • 以ID或名稱檢索資料。

佇列(Queues)

特點

  • 接近FIFO(先進先出)結構。
  • 元素可動態新增或刪除,適合變動頻繁的資料序列。

宣告與使用範例

int queue_array[$];          // 宣告佇列型陣列

queue_array.push_back(10);   // 在尾端新增
queue_array.push_front(5);   // 在前端新增
int val = queue_array.pop_front();  // 從前端取出

應用情境

  • 資料暫存(FIFO緩衝區)。
  • 事件處理或交易管理。

比較與使用策略

陣列類型大小變更索引型態適用情境
動態陣列可變整數大小不固定時
關聯陣列可變任意(int, string等)需要雜湊結構時
佇列可變自動(前端/尾端)頻繁新增刪除資料時

注意事項

  • 這些擴充功能屬於SystemVerilog在Verilog中不可用
  • 可綜合範圍依工具而異,但多數情況僅用於測試平台
  • 若目標為FPGA或ASIC,必須事先確認是否支援。

7. 陣列操作的最佳實踐

在Verilog或SystemVerilog中使用陣列時,若能兼顧效率與可讀性,將直接影響硬體設計品質。本章將介紹一些安全且有效的使用技巧。

透過註解與命名明確表達意圖

雖然陣列具備高可擴展性,但元素代表意義可能不直觀,因此建議:

  • 陣列名稱使用具意義的單詞:reg [7:0] sensor_data [0:7];
  • 透過註解標明用途與單位:
  // 儲存來自8個感測器的8位元資料
  reg [7:0] sensor_data [0:7];

for迴圈需注意邊界條件

使用for迴圈進行批次操作時,必須正確設定索引範圍:

  • 若上限錯誤 → 可能造成陣列越界。
  • 注意<<=的差別。

範例:

integer i;
always @(posedge clk) begin
    for (i = 0; i < 8; i = i + 1) begin
        sensor_data[i] <= 8'd0;
    end
end

明確初始化

若陣列未初始化就使用,可能影響模擬結果。尤其在RAM或暫存器組的情境,建議明確初始化:

initial begin
    for (i = 0; i < 256; i = i + 1)
        mem[i] = 32'd0;
end

在SystemVerilog中,也可透過建構子或foreach迴圈更簡潔地初始化。

設計具備可重用性的模組

利用陣列可讓設計更具彈性,例如元素數量或位元寬度變化時仍能適應:

  • 使用parameter控制陣列大小:
  parameter DEPTH = 16;
  reg [7:0] buffer [0:DEPTH-1];
  • 允許從外部指定大小,可提升模組的可重用性。

考慮可綜合性

設計時需留意合成工具支援

  • Verilog的一維reg陣列:一般可安全綜合。
  • SystemVerilog的動態/關聯/佇列:不可綜合(僅限模擬)

陣列與模組分割的取捨

雖然陣列可減少程式碼量,但過於複雜時,建議以模組分割設計

  • 處理內容單純且數量不大 → 陣列 + for迴圈。
  • 功能差異大或規模龐大 → 採模組化與階層設計。

8. 常見問題(FAQ)

在使用Verilog或SystemVerilog的陣列時,初學者到中階工程師都常會遇到一些共通的問題。以下整理了有關「Verilog 陣列」的三個常見問題,並結合實務經驗加以解釋。

Q1. 在Verilog中使用多維陣列會出錯,為什麼?

A1.

在Verilog 1995或Verilog 2001之前的版本,多維陣列要嘛不支援,要嘛僅在有限情況下支援,因此會出現編譯錯誤。

例如,下列程式在Verilog 1995會發生錯誤:

reg [7:0] matrix [0:3][0:3];  // 僅Verilog 2001之後支援

解決方式:

  • 確認所使用的工具是否支援Verilog 2001或更新版本
  • 若受限於工具,可改以一維陣列模擬,透過運算公式存取索引:
reg [7:0] matrix_1d [0:15];  
// 存取時以 (i*4 + j) 代表二維索引

Q2. 若用陣列描述RAM,是否能在實際硬體中運作?

A2.

可以,多數綜合工具已經支援使用Verilog陣列來描述RAM。例如:

reg [31:0] mem [0:255];  // 32位元 × 256字的RAM

但需要注意:

  • 綜合工具必須能推論為Block RAM,條件需符合該工具的模板。
  • 讀寫方式(同步/非同步)若不符合規範,可能無法正確映射為實體RAM。

建議:

  • 參考各晶片廠商的「記憶體推論指南」。
  • 若模擬與實機行為不同,建議檢查Log並依照官方模板修改。

Q3. SystemVerilog的動態陣列、關聯陣列、佇列能否在實機中使用?

A3.

基本上,動態陣列、關聯陣列、佇列都不可綜合(僅限模擬)。這些結構雖然靈活,但無法直接映射為硬體電路。

因此,常見用途為:

  • 測試平台的暫存資料。
  • 驗證環境中的隨機化、scoreboard實作。
  • 描述複雜交易或事件。

實作上的提醒:

  • 若設計中使用動態/關聯陣列,綜合工具會忽略或直接報錯。
  • 要實際部署於硬體,必須改寫為固定長度的一維reg陣列。

9. 總結

本文以「Verilog 陣列」為主題,從基礎到應用進行全面解析。正確使用陣列,能有效提升設計效率、程式可讀性、維護性

本文回顧重點

  • 理解Verilog基本資料型態(reg與wire),可避免陣列使用錯誤。
  • 一維陣列是資料集合與記憶體模型的重要結構。
  • 多維陣列在Verilog 2001後支援,可處理矩陣等進階結構。
  • 使用SystemVerilog可引入動態陣列、關聯陣列、佇列(主要用於模擬)。
  • 遵循最佳實踐(初始化、命名、可重用性、合成考量),能撰寫更高品質程式。

實務上的提示

雖然陣列非常方便,但並非所有情況都適合用陣列。若要考慮綜合與協作,需配合設計規範與團隊風格。

SystemVerilog的進階陣列功能,若將其定位於模擬用途,才能發揮最大效益。根據目標靈活切換,才是優秀設計者的條件。

延伸閱讀建議

若已掌握本文內容,建議進一步學習以下主題:

  • 利用generate語句進行動態電路生成。
  • 結合interface與陣列的匯流排設計。
  • 利用陣列最佳化FIFO、環形緩衝區、ROM設計。

熟練掌握陣列,是成為專業Verilog設計工程師的重要第一步。