1. 前言
1-1. 什麼是 Verilog 的 if-else 敘述?
Verilog 是一種硬體描述語言(HDL),廣泛用於設計 FPGA 與 ASIC 等數位電路。其中,if-else 敘述 是依據條件控制程式流程的關鍵語法。
Verilog 中 if-else 敘述的主要用途如下:
- 組合電路 的條件分支
- 時序電路(如觸發器) 的操作控制
- 動態訊號控制(例如:選擇器或條件運算)
例如,透過 if-else 敘述,可以根據訊號狀態產生不同的輸出。這在電路設計中非常實用,但若使用不當,可能會意外產生鎖存器(記憶元件)。
1-2. 不正確使用 if-else 敘述可能造成的問題
若未正確使用 Verilog 的 if-else 敘述,可能會產生以下問題:
- 意外生成鎖存器
- 若條件分支未明確涵蓋所有情況,綜合工具可能會自動插入鎖存器(記憶元件)。
- 這會造成非預期的保持行為,使電路無法如預期運作。
- 模擬結果與綜合結果不一致
- 在模擬中可能表現正常,但實際實現在 FPGA 或 ASIC 時行為卻不同。
- 這是因為 if-else 的寫法可能導致綜合工具進行錯誤的最佳化。
- 程式可讀性降低
- 過度巢狀的 if-else 敘述會降低可讀性。
- 必要時可改用
case
敘述來提升程式結構的清晰度。
1-3. 本文的目的
本文將深入介紹 Verilog 中 if-else 敘述的基礎語法、應用範例、最佳實踐,以及與 case 敘述的使用差異。
透過閱讀本文,你將能學到:
- if-else 敘述的正確用法
- 如何避免產生鎖存器的 Verilog 程式寫法
- if-else 與 case 敘述的正確區分與應用
- Verilog 設計中的最佳實務
文章將搭配具體範例程式碼,讓初學者也能輕鬆理解,建議完整閱讀!
2. Verilog if-else 敘述的基本語法
2-1. if-else 敘述的寫法
Verilog 的 if-else 敘述與軟體語言(如 C、Python)的 if-else 相似,但必須考慮到硬體描述語言的特性。
基本的 if-else 語法如下:
always_comb begin
if (條件)
處理1;
else
處理2;
end
另外,也可以使用 else if
來實現多重條件分支:
always_comb begin
if (條件1)
處理1;
else if (條件2)
處理2;
else
處理3;
end
此語法經常用於設計組合電路,讓電路依據不同條件輸出不同的行為。
2-2. if-else 敘述的基本範例程式
接下來我們用一個具體的例子,建立一個簡單的選擇器電路。
範例:依據輸入 a
來決定輸出 y
的值
module if_else_example(input logic a, b, output logic y);
always_comb begin
if (a == 1'b1)
y = b;
else
y = ~b;
end
endmodule
解說
- 當
a
為1
時,y
會輸出與b
相同的值。 - 當
a
為0
時,y
會輸出b
的反相值。
由此可見,if-else 敘述能輕鬆描述依據條件改變訊號的控制。
2-3. if-else 敘述的運作原理
Verilog 的 if-else 敘述可用於以下兩種不同的電路設計:
- 組合電路(使用 always_comb)
- 輸出會依輸入的變化即時更新。
- 不會生成鎖存器,因此可避免非預期行為。
- 建議使用
always_comb
,而非always @(*)
。
- 時序電路(使用 always_ff)
- 輸出會依據時脈訊號更新。
- 常用於D 觸發器等電路。
接下來我們分別介紹這兩種情境下 if-else 的實際用法。
2-4. 組合電路中的 if-else 敘述
在組合電路中,輸出會立即反映輸入的變化。
因此必須使用 always_comb
,避免生成不必要的鎖存器。
module combination_logic(input logic a, b, output logic y);
always_comb begin
if (a == 1'b1)
y = b;
else
y = ~b;
end
endmodule
此程式會依據輸入 a
來改變輸出 y
:
- 當
a == 1
時:y = b
- 當
a == 0
時:y = ~b
注意事項
- 務必使用
always_comb
來避免產生鎖存器。 - 必須在所有條件下對輸出變數指定值(若省略
else
,可能會產生鎖存器)。
2-5. 時序電路中的 if-else 敘述
在時序電路中,必須使用 always_ff
,讓輸出隨時脈訊號更新。
範例:D 觸發器
module d_flipflop(input logic clk, reset, d, output logic q);
always_ff @(posedge clk or posedge reset) begin
if (reset)
q <= 1'b0;
else
q <= d;
end
endmodule
這個程式代表一個 D 觸發器:
- 當
reset
為1
時,q
被重設為0
。 - 當
reset
為0
且時脈clk
上升沿觸發時,d
的值會被存入q
。
注意事項
- 時序電路建議使用
always_ff
,而非always @(*)
。 - 應使用
<=
(非阻塞指派)來避免競爭與衝突。
2-6. if-else 敘述的實際應用範例
Verilog 的 if-else 敘述在以下情境中常被使用:
- LED 控制
- 依據開關的狀態來控制 LED 的開啟與關閉。
- ALU(算術邏輯單元)
- 用於加法、減法與邏輯運算的控制。
- 狀態轉移
- 有限狀態機(FSM)的設計(將於後續章節詳細介紹)。
總結
- if-else 敘述用於 Verilog 的條件分支控制。
- 組合電路(always_comb)與時序電路(always_ff)必須正確區分使用。
- 若未在所有條件下指定輸出值,可能會產生鎖存器。
- 在實際電路設計中,if-else 敘述常用於狀態控制。

3. if-else 敘述的應用
if-else 敘述是 Verilog 條件分支的基礎,不僅能處理簡單控制,還能應用於組合電路與時序電路的設計。本章將示範 if-else 敘述的應用,例如4 位元加法器與有限狀態機(FSM)。
3-1. 組合電路的設計
組合電路的輸出會隨輸入即時變化。
設計組合電路時,應使用 always_comb
並避免生成不必要的鎖存器。
範例1: 4 位元加法器
此電路將兩個 4 位元輸入 (a
與 b
) 相加,並輸出含進位 (cout
) 的結果 (sum
)。
module adder(
input logic [3:0] a, b,
input logic cin,
output logic [3:0] sum,
output logic cout
);
always_comb begin
if (cin == 1'b0)
{cout, sum} = a + b; // 無進位
else
{cout, sum} = a + b + 1; // 有進位
end
endmodule
解說
- 當
cin = 0
時,計算a + b
。 - 當
cin = 1
時,計算a + b + 1
(包含進位)。 - 使用
always_comb
確保其為組合電路,並避免生成鎖存器。
3-2. 在時序電路(暫存器)中的應用
時序電路會依據時脈訊號 (clk) 更新資料。
if-else 敘述可用於狀態轉移或暫存器控制。
範例2: D 觸發器
D 觸發器會在時脈訊號的上升沿(posedge clk)將輸入 d
儲存到輸出 q
。
module d_flipflop(
input logic clk, reset, d,
output logic q
);
always_ff @(posedge clk or posedge reset) begin
if (reset)
q <= 1'b0; // reset 時歸零
else
q <= d; // 在 clk 上升沿更新 q
end
endmodule
解說
- 當
reset = 1
時,q
被重設為0
。 - 在
clk
上升沿時,d
的值被存入q
。 - 使用
always_ff
讓其正確作為暫存器(觸發器)運作。
3-3. 在狀態轉移(FSM)中的 if-else 應用
if-else 敘述也可用於有限狀態機(FSM: Finite State Machine) 的設計。
FSM 是一種具有多個狀態並依據條件進行轉換的電路。
範例3: 簡單的狀態轉移電路
依據按鈕輸入 (btn
),切換 LED 狀態 (led_state
) 的 FSM。
module fsm_toggle(
input logic clk, reset, btn,
output logic led_state
);
typedef enum logic {OFF, ON} state_t;
state_t state, next_state;
always_ff @(posedge clk or posedge reset) begin
if (reset)
state <= OFF; // 初始狀態
else
state <= next_state;
end
always_comb begin
case (state)
OFF: if (btn) next_state = ON;
else next_state = OFF;
ON: if (btn) next_state = OFF;
else next_state = ON;
default: next_state = OFF;
endcase
end
assign led_state = (state == ON);
endmodule
解說
state
用來記錄 LED 狀態(ON 或 OFF)。- 當
reset = 1
時,LED 回到 OFF 狀態。 - 每次
btn
被按下時,LED 狀態會在 ON 與 OFF 之間切換。 - 使用
case
敘述來描述狀態轉換,提升程式可讀性。
3-4. if-else 敘述的進階技巧
① 避免過度巢狀的 if-else
過度巢狀的 if-else 敘述會降低可讀性,並容易導致錯誤。
不良範例(巢狀過深)
always_comb begin
if (a == 1) begin
if (b == 1) begin
if (c == 1) begin
y = 1;
end else begin
y = 0;
end
end else begin
y = 0;
end
end else begin
y = 0;
end
end
改進範例(使用 case 敘述)
always_comb begin
case ({a, b, c})
3'b111: y = 1;
default: y = 0;
endcase
end
- 透過將條件組合成位元向量並使用
case
敘述,可以減少巢狀結構並提升可讀性。
總結
- if-else 敘述可用於組合電路與時序電路。
- 組合電路建議使用
always_comb
,時序電路則建議使用always_ff
。 - FSM 狀態機可透過 if-else 或 case 敘述實現。
- 當 if-else 巢狀過深時,應使用 case 敘述或位元向量化條件改善程式結構。
4. if-else 敘述與 case 敘述的差異
在 Verilog 中,用於條件分支的有 if-else 敘述 與 case 敘述。
這兩者都是常見的控制結構,但適用的場合不同,正確區分使用非常重要。
4-1. 什麼是 case 敘述?
case 敘述的基本語法
case
敘述用於針對多個不同的值執行對應的處理。
當條件為特定值時,使用 case 會更合適。
always_comb begin
case (條件變數)
值1: 處理1;
值2: 處理2;
值3: 處理3;
default: 處理4; // 其他情況
endcase
end
case 敘述範例程式
以下範例依據輸入 sel
的值切換輸出 y
:
module case_example(input logic [1:0] sel, input logic a, b, c, d, output logic y);
always_comb begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
2'b11: y = d;
default: y = 0; // 預設值,避免未定義
endcase
end
endmodule
解說
- 根據
sel
的值,y
將輸出a, b, c, d
其中之一。 - 當條件為多個固定值時,使用 case 會讓程式更簡潔。
- 設定
default
可以避免未定義的輸入造成誤動作。
4-2. if-else 與 case 的差異
if-else 與 case 都能做條件分支,但有以下關鍵差異:
比較項目 | if-else 敘述 | case 敘述 |
---|---|---|
適用情境 | 適合範圍條件或連續判斷 | 適合針對特定固定值判斷 |
可讀性 | 巢狀過深會降低可讀性 | 條件清晰,結構簡單 |
綜合結果 | if-else 由工具最佳化 | case 常被轉換為多工器(Multiplexer) |
鎖存器發生可能性 | 若條件未完全指定,可能產生鎖存器 | 若未加 default ,可能導致未定義行為 |
4-3. if-else 與 case 的使用時機
① 適合使用 if-else 的情境
✅ 條件為範圍判斷
always_comb begin
if (value >= 10 && value <= 20)
output_signal = 1;
else
output_signal = 0;
end
- 若條件涉及範圍(例如
10~20
),使用 if-else 更適合。 - case 無法直接描述範圍條件。
✅ 條件具有優先順序
always_comb begin
if (x == 1)
y = 10;
else if (x == 2)
y = 20;
else if (x == 3)
y = 30;
else
y = 40;
end
- 當條件具優先性時,使用 if-else 更直觀。
- 上層條件成立後,後續條件將不再檢查。
② 適合使用 case 的情境
✅ 針對特定值進行分支
always_comb begin
case (state)
2'b00: next_state = 2'b01;
2'b01: next_state = 2'b10;
2'b10: next_state = 2'b00;
default: next_state = 2'b00;
endcase
end
- 依據
state
的值決定下一狀態。 - FSM 狀態轉移設計中,case 是常用寫法。
✅ 條件種類較多
always_comb begin
case (opcode)
4'b0000: instruction = ADD;
4'b0001: instruction = SUB;
4'b0010: instruction = AND;
4'b0011: instruction = OR;
default: instruction = NOP;
endcase
end
- 在指令解碼器等情境下,當條件數量眾多時,case 的可讀性更佳。
總結
✅ if-else 適合範圍條件或具有優先順序的處理
✅ case 適合固定值判斷與狀態轉移(FSM)
✅ 當條件數量龐大時,建議使用 case 提升可讀性
✅ 判斷使用何者時,應依「條件型態」與「是否需要優先順序」來決定
5. Verilog if-else 敘述的最佳實踐
if-else 敘述雖然是 Verilog 中常見的條件分支方法,但若未正確撰寫,可能導致鎖存器生成或非預期行為。本章將說明撰寫 if-else 的最佳實踐。
5-1. 避免生成鎖存器的寫法
在 Verilog 的組合電路設計中,若 if-else 區塊未涵蓋所有條件,工具會推斷需要保留先前的值,進而自動產生鎖存器。
① 鎖存器產生的錯誤範例
always_comb begin
if (a == 1'b1)
y = b; // 當 a == 0 時,y 沒有被指定 → 保持前一個值
end
為什麼會生成鎖存器?
- 當
a == 1
時,y
被指定為b
。 - 但當
a == 0
時,y
未被指定 → 工具認為需要保留先前的值。 - 結果:隱含生成一個鎖存器,導致非預期的電路行為。
② 正確的寫法(使用 else)
應該在所有條件下明確指定輸出。
always_comb begin
if (a == 1'b1)
y = b;
else
y = 1'b0; // 明確指定輸出
end
③ 使用 default 值
在 if 區塊前,先給輸出一個初始值。
always_comb begin
y = 1'b0; // 預設值
if (a == 1'b1)
y = b;
end
✅ 重點:只要在所有情況下都有給定值,就不會產生鎖存器!
5-2. 善用 always_comb
與 always_ff
自 Verilog 2001 起,建議區分組合電路與時序電路:
① 組合電路(always_comb)
always_comb begin
if (a == 1'b1)
y = b;
else
y = 1'b0;
end
always_comb
會自動建立感測清單((*)
),無需手動維護。- 設計意圖更清楚,且利於工具最佳化。
② 時序電路(always_ff)
always_ff @(posedge clk or posedge reset) begin
if (reset)
q <= 1'b0;
else
q <= d;
end
always_ff
明確表示此區塊為時脈驅動的暫存器。- 比傳統的
always @(posedge clk ...)
可讀性更高,且能減少設計錯誤。
5-3. 提升 if-else 可讀性的技巧
if-else 敘述雖然直觀,但過多的巢狀結構會讓程式難以閱讀。以下方法可改善:
① 減少巢狀層級
不良範例(巢狀過深)
always_comb begin
if (mode == 2'b00) begin
if (enable) begin
y = a;
end else begin
y = b;
end
end else begin
y = c;
end
end
改良範例(使用 case 或三元運算子)
always_comb begin
case (mode)
2'b00: y = enable ? a : b;
default: y = c;
endcase
end
- 使用
case
敘述能讓條件更清楚。 - 使用
?:
三元運算子,可進一步簡化程式。
總結
✅ if-else 必須涵蓋所有條件,否則會生成鎖存器。
✅ 組合電路應使用 always_comb
,時序電路應使用 always_ff
。
✅ 巢狀過深時應改用 case 或三元運算子,提升可讀性。
✅ 變數命名應具體明確,以增加程式可讀性。
6. 常見問題(FAQ)
Verilog 的 if-else
敘述是基礎的條件分支結構,無論是初學者或進階工程師,都常會遇到一些常見疑問。
本章將以 Q&A 形式回答,包括 鎖存器產生問題、與 case 敘述的差異、對速度的影響 等。
Q1: 為什麼使用 if-else 會生成鎖存器?如何避免?
A1: 鎖存器生成的原因
在 Verilog 中,若 if-else 敘述未對所有條件明確指定輸出值,綜合工具會推斷需保留舊值,因此自動生成鎖存器(Latch)。
錯誤範例(會生成鎖存器)
always_comb begin
if (a == 1'b1)
y = b; // 當 a == 0 時,y 沒有被指定 → 保持舊值
end
解決方法
① 使用 else
always_comb begin
if (a == 1'b1)
y = b;
else
y = 1'b0; // 明確指定
end
② 設定預設值
always_comb begin
y = 1'b0; // 預設值
if (a == 1'b1)
y = b;
end
✅ 重點:在所有情況下給定輸出值,就不會產生鎖存器!
—
Q2: if-else 與 case 有什麼不同?何時應該使用?
A2: 使用時機
條件特性 | 適合使用的語法 |
---|---|
範圍判斷(例: 10 <= x <= 20 ) | if-else |
針對特定值分支 | case |
有優先順序 | if-else |
分支數量多 | case |
—
Q3: if-else 會影響電路速度嗎?
A3: 取決於綜合後的硬體結構
- Verilog 是硬體描述語言,速度取決於綜合後的電路,而非語法本身。
- 若 if-else 巢狀過深,可能增加邏輯延遲。
- 但綜合工具通常會最佳化,讓等效電路的延遲差異不大。
✅ 最佳化建議
盡量減少巢狀,改用 case
或簡單條件運算。
—
Q4: 在 if-else 中應該用 =
還是 <=
?
A4: 差異在於「阻塞」與「非阻塞」指派
指派方式 | 適用情境 |
---|---|
= (阻塞指派) | 組合電路(always_comb) |
<= (非阻塞指派) | 時序電路(always_ff) |
✅ 組合電路 → 使用 =
always_comb begin
if (a == 1)
y = b; // 阻塞指派
end
✅ 時序電路 → 使用 <=
always_ff @(posedge clk) begin
if (reset)
y <= 0; // 非阻塞指派
else
y <= d;
end
—
Q5: 如何減少 if-else 的巢狀結構?
A5: 使用 case 或條件運算子
不良範例(巢狀過深)
always_comb begin
if (mode == 2'b00) begin
if (enable) begin
y = a;
end else begin
y = b;
end
end else begin
y = c;
end
end
改良範例(使用 case 與三元運算子)
always_comb begin
case (mode)
2'b00: y = enable ? a : b;
default: y = c;
endcase
end
✅ 重點:使用 ? :
條件運算子可簡化 if-else
—
總結
✅ if-else 未涵蓋所有條件時會產生鎖存器 → 解法是加 else 或預設值。
✅ 範圍或優先順序 → 用 if-else;多個固定值 → 用 case。
✅ 組合電路用 =,時序電路用 <=。
✅ 巢狀過深時,應改用 case 或條件運算子,提升可讀性。
7. 總結
Verilog 的 if-else
敘述是數位電路設計中非常重要的條件分支方法。本文已經從基礎語法、應用範例、最佳實踐到常見疑問做了詳細解說。
以下整理出在使用 if-else 時需要注意的重點:
7-1. Verilog if-else 的基本要點
✅ 基本語法
if-else
是條件分支的基本結構。- 在組合電路中應使用
always_comb
,並確保所有條件皆有指定值。
always_comb begin
if (a == 1'b1)
y = b;
else
y = 1'b0; // 避免產生鎖存器
end
- 在時序電路(時脈驅動)中應使用
always_ff
,並採用非阻塞指派<=
。
always_ff @(posedge clk or posedge reset) begin
if (reset)
q <= 1'b0;
else
q <= d;
end
✅ 重點:組合電路用 =
,時序電路用 <=
!
—
7-2. if-else 的正確使用方式
✅ 在組合電路中
- 使用
always_comb
。 - 確保所有條件下都有輸出值,避免鎖存器。
- 可設定預設值以防未定義情況。
✅ 在時序電路中
- 使用
always_ff
。 - 利用
<=
更新暫存器狀態。 - 適合設計觸發器、狀態機等。
✅ if-else 適用場景
條件特性 | 建議使用 |
---|---|
範圍判斷(例: 10 <= x <= 20 ) | if-else |
具有優先順序(例: if (x==1) → else if (x==2) ) | if-else |
條件較少(2~3 個) | if-else |
—
7-3. 與 case 的區分
if-else 適合連續範圍與優先判斷,
case 適合多個固定值或狀態轉移。
✅ case 更適合以下情況:
條件特性 | 建議使用 |
---|---|
針對特定值分支(例: 狀態 = IDLE, RUN, STOP) | case |
分支數量眾多(例: 8 種以上) | case |
有限狀態機(FSM) | case |
—
7-4. if-else 的最佳實踐
✅ 所有條件皆需指定輸出,避免鎖存器
always_comb begin
if (a == 1'b1)
y = b;
else
y = 1'b0; // 必須處理所有情況
end
✅ 正確區分 always_comb 與 always_ff
always_comb begin // 組合電路
if (a)
y = b;
else
y = 0;
end
always_ff @(posedge clk) begin // 時序電路
if (reset)
y <= 0;
else
y <= d;
end
✅ 巢狀過深時,應改用 case
always_comb begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = d;
endcase
end
—
7-5. 常見錯誤與修正
錯誤 | 正確寫法 |
---|---|
省略 else → 產生鎖存器 | 必須使用 else 或指定預設值 |
在時序電路使用 = | 應使用 <= (非阻塞指派) |
if-else 巢狀過深 | 改用 case 提升可讀性 |
—
7-6. 總結重點
✅ if-else 可用於組合與時序電路,但必須正確撰寫。
✅ 若未覆蓋所有條件,將導致鎖存器。
✅ 多個固定值或狀態轉移 → 使用 case。
✅ 時序電路用 <=,組合電路用 =。
✅ 避免巢狀過深,使用 case 或三元運算子提升可讀性。
—
7-7. 下一步
本文完整介紹了 Verilog if-else 敘述的基礎、應用、最佳實踐與常見問題。接下來建議學習以下進階主題:
✅ FSM(有限狀態機)的設計技巧
✅ 使用 case 進行高效控制設計
✅ 在流水線設計中的 if-else 應用
✅ 時脈同步設計的最佳化方法
透過深入學習,將能提升對 Verilog 的掌握度,並設計出更高效與可靠的數位電路!🚀