Verilog function完整教學|從基礎概念到範例、應用與注意事項

目次

1. Verilog function是什麼?(基本概念與角色)

Verilog HDL(Hardware Description Language,硬體描述語言)是一種用於數位電路設計與模擬的語言。其中的 function(函式),是一種將特定處理模組化並方便重複利用的機制。

理解Verilog function不僅能提升程式碼的可讀性與維護性,也能帶來更高效的電路設計。本文將說明Verilog function的基本概念,並解釋function在實務上的使用方式。

什麼是function?

Verilog function 是一個 執行特定計算或處理,並回傳單一數值的區塊。使用function可以減少冗長的程式碼,使電路設計更加簡潔。

function的特點

  • 輸入可指定一個以上(僅能使用 input
  • 輸出僅限一個(函式的回傳值)
  • 不可包含時間延遲(如 #10
  • 函式內必須描述組合邏輯(combinational logic)
  • function定義在always區塊外,與task不同,會立即被評估

Verilog function的使用場景

Verilog function主要應用於以下情境:

1. 描述組合邏輯

function會針對輸入立即回傳結果,因此常用於組合邏輯(Combinational Logic)。
範例:加法、減法、編碼器(encoder)、解碼器(decoder)等運算。

2. 提升程式碼的重用性

透過將重複的運算封裝為函式,可以簡化程式碼並提高模組的可讀性。
範例:含有條件判斷的複雜運算式可寫成函式,讓模組更清晰。

3. 減少設計錯誤

將計算或邏輯集中於一個function中,可降低修改時出現錯誤的風險。
範例:CRC(循環冗餘檢查)運算或奇偶校驗。

function與task的差異

在Verilog中,除了function,還有 task(任務)。兩者雖然相似,但存在以下差異:

項目functiontask
輸出僅限1個可多個
輸入
內部變數可使用可使用
延遲(#10不可
always內使用不可
呼叫方式函式名(參數)task名(參數);

適合使用function的情況

  • 需要立即取得計算結果
  • 不包含延遲(delay)的邏輯處理
  • 僅需回傳一個值的簡單處理

適合使用task的情況

  • 包含時間延遲(如 #10)的處理
  • 需要多個輸出的處理
  • 模擬時的除錯用途(如日誌輸出)

總結

  • Verilog function接收輸入並回傳單一數值的函式
  • 適用於組合邏輯的描述,且不可包含延遲
  • 能減少冗長程式碼並提升可讀性
  • function與task不同,需根據用途選擇

2. Verilog function的寫法【含新手範例】

在上一節我們已經學習了Verilog function的基本概念。接下來將更詳細地介紹Verilog function的具體寫法。

function的基本語法

Verilog function可依照以下基本語法來撰寫:

function [輸出的位元寬度] 函式名稱;
    input [輸入的位元寬度] 輸入1, 輸入2, ...;
    begin
        函式名稱 = 計算式;
    end
endfunction

重點

  • 使用 function 關鍵字宣告
  • 與函式名稱相同的變數即為回傳值
  • 輸入需以 input 宣告(不可使用 outputinout
  • 計算處理需寫在 begin ... end
  • 必須定義在 always 區塊之外

簡單的Verilog function範例

以下範例示範一個執行8位元加法的function:

module example;
    function [7:0] add_function;
        input [7:0] a, b;
        begin
            add_function = a + b;
        end
    endfunction

    reg [7:0] x, y, sum;

    initial begin
        x = 8'b00001100; // 12
        y = 8'b00000101; // 5
        sum = add_function(x, y);
        $display("Sum: %d", sum); // Sum: 17
    end
endmodule

解說

  • add_function 是一個 接收兩個8位元輸入(a, b),並回傳其和 的函式
  • 透過 sum = add_function(x, y); 呼叫函式,並將結果指定給變數 sum
  • initial 區塊內使用 $display 輸出結果

Verilog function的輸入與輸出宣告方式

輸入宣告

Verilog function的參數 僅能使用 input

function [7:0] my_function;
    input [7:0] in1, in2;
    begin
        my_function = in1 & in2; // AND運算
    end
endfunction

注意: 不可指定 output,函式的回傳值由 與函式名稱相同的變數 承載。

含條件分支的Verilog function

在function內可以使用 ifcase 進行條件判斷。

function [3:0] max_function;
    input [3:0] a, b;
    begin
        if (a > b)
            max_function = a;
        else
            max_function = b;
    end
endfunction

此函式會 回傳a與b之中較大的值

小結

  • Verilog function以 function 關鍵字定義,並回傳單一值
  • 僅能使用 input 作為參數(不可使用 output
  • 透過將計算結果指定給函式名稱來回傳值
  • 可以在function內使用 ifcase 進行條件分支

3. Verilog function的用法【附實際程式碼】

在上一節中,我們學習了Verilog function的基本語法與寫法。
本節將透過範例,說明function在實際設計中如何應用

function的呼叫方式

Verilog function的呼叫方式與一般變數相似,使用 函式名(參數1, 參數2, ...) 的形式。
以下範例定義了一個8位元的XOR函式,並在模組中使用:

module function_example;
    function [7:0] xor_function;
        input [7:0] a, b;
        begin
            xor_function = a ^ b;
        end
    endfunction

    reg [7:0] x, y, result;

    initial begin
        x = 8'b11001100;
        y = 8'b10101010;
        result = xor_function(x, y); // 呼叫function
        $display("XOR Result: %b", result); // XOR Result: 01100110
    end
endmodule

重點

  • 呼叫方式: 變數 = function(參數);
  • 可在always區塊或initial區塊內使用
  • 以組合邏輯(combinational logic)的方式運作

在組合邏輯中使用function

由於Verilog function 總是立即被評估,因此在撰寫組合邏輯時非常方便。
以下範例示範如何用function實作一個2-to-4解碼器:

module decoder_example;
    function [3:0] decoder;
        input [1:0] sel;
        begin
            case (sel)
                2'b00: decoder = 4'b0001;
                2'b01: decoder = 4'b0010;
                2'b10: decoder = 4'b0100;
                2'b11: decoder = 4'b1000;
                default: decoder = 4'b0000;
            endcase
        end
    endfunction

    reg [1:0] select;
    wire [3:0] decoded_output;

    assign decoded_output = decoder(select); // 使用function

    initial begin
        select = 2'b01;
        #10; // 加入延遲以觀察模擬變化
        $display("Decoded Output: %b", decoded_output); // Decoded Output: 0010
    end
endmodule

解說

  • decoder function會將 2位元輸入轉換為4位元輸出
  • 透過 case 敘述決定輸出的結果
  • 使用 assign 敘述將 function的輸出指定給 decoded_output
    → function能作為組合邏輯的一部分

always語句與function的差異【比較表】

Verilog function與 always 語句都能描述邏輯,但用途與限制不同。

項目functionalways語句
撰寫位置always 區塊之外always 區塊之內
輸入僅限 inputregwire 均可
輸出僅能回傳單一數值可更新多個變數
延遲 (#10)不可
狀態保持不可(總是立即運算)可(可作為觸發器或暫存器)
主要用途組合邏輯序向邏輯或事件驅動處理

使用上的重點

  • function:適合簡單的組合邏輯運算
  • always語句:適合設計具狀態的序向邏輯(如觸發器、計數器)
  • 若需要延遲(如 #10),應使用always而非function

Verilog function使用總結

✅ 使用 函式名(參數) 的形式呼叫
適用於組合邏輯設計,功能與always不同
✅ 可搭配 caseif 撰寫彈性的邏輯
常見應用:解碼器、運算處理

4. Verilog function的應用範例(解碼器與ALU設計)

到目前為止,我們已經學習了Verilog function的基本語法與使用方式。
本節將說明在實際數位電路設計中,如何運用function,並以解碼器(Decoder)與ALU(算術邏輯單元)為例做詳細解說。

解碼器的function實作(2-to-4 Decoder)

解碼器是一種 將少量位元輸入轉換為多位元輸出的電路
例如:2位元輸入轉換成4位元輸出的2-to-4解碼器。以下使用function來實作:

module decoder_example;
    function [3:0] decoder;
        input [1:0] sel;
        begin
            case (sel)
                2'b00: decoder = 4'b0001;
                2'b01: decoder = 4'b0010;
                2'b10: decoder = 4'b0100;
                2'b11: decoder = 4'b1000;
                default: decoder = 4'b0000;
            endcase
        end
    endfunction

    reg [1:0] select;
    wire [3:0] decoded_output;

    assign decoded_output = decoder(select); // 使用function

    initial begin
        select = 2'b00; #10;
        $display("Decoded Output: %b", decoded_output);
        select = 2'b01; #10;
        $display("Decoded Output: %b", decoded_output);
        select = 2'b10; #10;
        $display("Decoded Output: %b", decoded_output);
        select = 2'b11; #10;
        $display("Decoded Output: %b", decoded_output);
    end
endmodule

ALU的function實作(加法、減法、AND、OR)

ALU(Arithmetic Logic Unit,算術邏輯單元) 是CPU的核心運算單元,主要負責加法、減法與邏輯運算(AND、OR等)。
以下範例示範如何用Verilog function設計一個簡單的8位元ALU:

module alu_example;
    function [7:0] alu;
        input [7:0] a, b;
        input [1:0] op; // 2位元控制訊號
        begin
            case (op)
                2'b00: alu = a + b; // 加法
                2'b01: alu = a - b; // 減法
                2'b10: alu = a & b; // AND運算
                2'b11: alu = a | b; // OR運算
                default: alu = 8'b00000000;
            endcase
        end
    endfunction

    reg [7:0] x, y;
    reg [1:0] opcode;
    wire [7:0] result;

    assign result = alu(x, y, opcode); // 使用function

    initial begin
        x = 8'b00001100; // 12
        y = 8'b00000101; // 5

        opcode = 2'b00; #10;
        $display("Addition Result: %d", result); // 12 + 5 = 17

        opcode = 2'b01; #10;
        $display("Subtraction Result: %d", result); // 12 - 5 = 7

        opcode = 2'b10; #10;
        $display("AND Result: %b", result); // AND運算

        opcode = 2'b11; #10;
        $display("OR Result: %b", result); // OR運算
    end
endmodule

小結

function可應用於解碼器與ALU等組合邏輯電路
搭配case語句能實現多樣化的運算
提升程式碼可讀性並方便重複使用
function適合組合邏輯,但不適用於序向邏輯(不可包含延遲)

5. 使用Verilog function時的注意事項

Verilog function能提升程式碼的可讀性與重用性,但也存在一些限制。本節將詳細說明在使用function時需要注意的要點。

不可進行遞迴呼叫

Verilog function禁止函式的遞迴呼叫(recursive call)
換句話說,function內不能再呼叫自己。

❌ 錯誤範例:含遞迴呼叫的函式

function [3:0] factorial;
    input [3:0] n;
    begin
        if (n == 0)
            factorial = 1;
        else
            factorial = n * factorial(n - 1); // ❌ 遞迴呼叫不可使用
    end
endfunction

此程式碼會造成 模擬錯誤

✅ 解決方法:使用迴圈

若需要類似遞迴的處理,可在 always區塊內使用迴圈,或改用task

task factorial_task;
    input [3:0] n;
    output [15:0] result;
    integer i;
    begin
        result = 1;
        for (i = 1; i <= n; i = i + 1)
            result = result * i;
    end
endtask

這樣可透過迴圈來避免遞迴。

function內不可使用時間延遲(#10)

由於Verilog function 屬於即時運算(combinational logic)
因此不可包含任何時間延遲(例如 #10)。

❌ 錯誤範例:在function內使用延遲

function [7:0] delay_function;
    input [7:0] in;
    begin
        #10; // ❌ function內不能使用延遲
        delay_function = in + 1;
    end
endfunction

此程式會產生 編譯錯誤

✅ 解決方法:使用always或task

若需要延遲,應改用 always區塊或task

task delay_task;
    input [7:0] in;
    output [7:0] out;
    begin
        #10;
        out = in + 1;
    end
endtask

👉 適合含有延遲的處理請使用task。

function與task的正確區分

除了function,Verilog還有 task。雖然類似,但用途不同,因此必須正確選用。

項目functiontask
輸出僅限1個(透過函式名稱回傳)可多個(透過output變數)
輸入僅能使用input可使用input / output
內部變數可使用可使用
延遲 (#10)不可
always內使用不可
呼叫方式函式名(參數)task名(參數);

適合使用function的情境

✅ 需要立即取得計算結果(如加法、減法、邏輯運算)
✅ 適合無延遲的組合邏輯
✅ 僅需單一回傳值

適合使用task的情境

✅ 需要包含延遲(如 #10
✅ 需要多個輸出
✅ 適用於模擬除錯(例如輸出log或監控訊號)

function不能在always區塊內定義

Verilog function不可在always區塊內宣告
必須定義在模組層級,並於always區塊內呼叫

❌ 錯誤範例:在always內定義function

always @(a or b) begin
    function [7:0] my_function; // ❌ 禁止在always內定義
        input [7:0] x, y;
        begin
            my_function = x + y;
        end
    endfunction
end

這段程式會造成編譯錯誤。

✅ 正確範例

應將function定義在always區塊之外,並在always中呼叫

function [7:0] add_function;
    input [7:0] x, y;
    begin
        add_function = x + y;
    end
endfunction

always @(a or b) begin
    result = add_function(a, b); // ✅ 呼叫function
end

小結

Verilog function有一些限制
不可遞迴呼叫(需用迴圈或task替代)
不可使用時間延遲(#10)(需用always或task)
不可在always內定義(需定義於模組層級)
僅能有一個回傳值(若需要多輸出,應使用task)
必須正確區分function與task的用途

6. 【FAQ】關於Verilog function的常見問題

前面章節已經詳細介紹了Verilog function的基礎、應用與注意事項。
本節整理常見問題與解答,方便快速理解。

function與task的差異?

Q. Verilog function與task有什麼不同?該如何選擇?

A. function用於「立即回傳單一數值的處理」,task則用於「可輸出多個值或包含延遲的處理」。

項目functiontask
輸出僅限1個(回傳函式名)可多個(透過output變數)
輸入僅限inputinput / output 皆可
內部變數可使用可使用
延遲 (#10)不可
always中使用不可
呼叫方式函式名(參數)task名(參數);

適合使用function的情境

✅ 需要立即獲得計算結果(例如加法、減法、邏輯運算)
✅ 適合無延遲的組合邏輯
✅ 僅需單一回傳值

適合使用task的情境

✅ 需要包含延遲(例如 #10
✅ 需要多輸出
✅ 模擬除錯用途(例如訊號監控、日誌輸出)

function內可以使用reg嗎?

Q. 在function中可以宣告 reg 嗎?

A. 不行。但可以使用 integer 作為計算變數。

在Verilog function內不允許宣告 reg,但可以透過 integer 來進行運算。

✅ 正確範例(使用integer)

function [7:0] multiply;
    input [3:0] a, b;
    integer temp;
    begin
        temp = a * b;
        multiply = temp;
    end
endfunction

什麼時候該使用function?

Q. 在哪些情況下使用function最合適?

A. 適合用於「單純運算處理」與「組合邏輯的描述」。

常見應用範例:

  • 運算處理(加法、減法、邏輯運算)
  • 解碼器、編碼器
  • 比較運算(例如最大值/最小值判斷)
  • 錯誤檢查(例如奇偶檢查、CRC)

⚠️ 但不適用於序向邏輯(例如觸發器)

function內可以呼叫其他function嗎?

Q. Verilog function內可以呼叫其他function嗎?

A. 可以。但需注意函式間的依賴關係。

function [7:0] add;
    input [7:0] a, b;
    begin
        add = a + b;
    end
endfunction

function [7:0] double_add;
    input [7:0] x, y;
    begin
        double_add = add(x, y) * 2; // 呼叫另一個function
    end
endfunction

function與always應該如何區分使用?

Q. function與always語句要如何正確使用?

A. function適合用於「組合邏輯」,always則適合「序向邏輯」。

項目functionalways
延遲 (#10)不可
狀態保持不可(立即計算)(可作為暫存器或計數器)
主要用途組合邏輯(即時運算)序向邏輯(時脈觸發)

例如,加法運算:

✅ function(組合邏輯)

function [7:0] add;
    input [7:0] a, b;
    begin
        add = a + b;
    end
endfunction

✅ always(序向邏輯)

always @(posedge clk) begin
    sum <= a + b; // 作為觸發器(暫存器)
end

小結

function適合單純計算與組合邏輯
需理解與task的差異,並正確選擇
與always語句分工:function負責組合邏輯,always負責序向邏輯
不可使用延遲或陣列(需要時改用task或module)

7. 總結

透過本篇文章,我們完整介紹了Verilog function 的概念、寫法、應用、限制以及常見問題。以下為重點整理:

  • Verilog function 是一種 接收輸入並立即回傳單一值的函式,適合用於組合邏輯。
  • 不可包含時間延遲(#10等),也不可進行遞迴呼叫
  • function僅能使用 input 作為參數,且回傳值必須透過與函式同名的變數給出。
  • 能大幅提升程式碼的可讀性與重用性,適合用於加法、減法、編碼器、解碼器等運算。
  • taskfunction 的用途不同:
      → function 適合簡單、立即回傳結果的組合邏輯。
      → task 適合含延遲、多輸出或除錯用的處理。
  • always語句 則適用於序向邏輯(例如暫存器、計數器),與function互補。

✅ 在設計數位電路時,正確區分 function、task、always 的用途,才能寫出高效、可維護的Verilog程式。