Verilog assign語句完整教學|基礎語法、範例與常見錯誤解析

目次

1. Verilog的assign語句是什麼?【新手入門解說】

什麼是Verilog HDL?

Verilog HDL(硬體描述語言,Hardware Description Language)是一種用於描述數位電路的硬體描述語言。它不同於軟體開發中的程式語言,而是用來描述硬體(邏輯電路)的結構與行為,並可透過模擬與綜合轉換成FPGA或ASIC等實際電路。

在Verilog中,最常用的語法之一就是「assign語句」。特別是在描述組合邏輯電路時,assign是不可或缺的語法。

assign語句的作用是什麼?

assign語句用於對wire型信號進行「連續指定(continuous assignment)」。所謂連續,指的是當輸入信號發生變化時,輸出會立即反映變化。

例如,若要對兩個信號進行邏輯AND並輸出結果,可以這樣寫:

assign out = in1 & in2;

這一行即可實現「將in1和in2的AND結果持續輸出到out」。assign的功能就像明確定義硬體的連線一樣。

組合邏輯中使用的assign語句

數位電路主要可分為組合邏輯電路時序邏輯電路

  • 組合邏輯電路:輸入改變時,輸出立即改變(例:加法器、邏輯閘等)
  • 時序邏輯電路:使用時鐘或記憶元件來保持狀態(例:觸發器、計數器等)

其中,assign語句主要用於組合邏輯電路,因為它需要根據輸入的即時狀態持續生成輸出。

為什麼assign對新手很重要?

學習Verilog的初期,理解組合邏輯是關鍵。而描述這些電路必須掌握assign語句。從簡單的邏輯運算,到條件分支、加法器、比較器,多數功能都能用assign簡潔地實現。

同時,使用assign語句能幫助初學者理解「作為硬體的訊號流」。這種思維對於後續處理更複雜的時序電路或測試平台都非常重要。

總結:掌握assign語句的基礎

Verilog中的assign語句是描述組合邏輯的基礎。它能簡潔地表示信號連接與邏輯運算,因此對初學者而言,是最早需要掌握的語法之一。

2. Verilog中的assign語句基本語法與寫法

assign語句的基本語法

在Verilog中,assign語句的基本語法非常簡單。它主要用於對wire型信號進行邏輯運算或賦值。基本寫法如下:

assign 輸出信號 = 表達式;

這裡的「表達式」可以包含其他信號、邏輯運算、位元運算等。需要注意的是,assign語句只能用於wire型變數,不能用於reg型。

常見範例①:簡單邏輯運算

最常見的用法就是表示邏輯閘運算。以下是AND、OR、XOR的assign語句寫法:

assign and_out = a & b;   // AND閘
assign or_out  = a | b;   // OR閘
assign xor_out = a ^ b;   // XOR閘

透過運算子即可把多個輸入信號組合,並持續驅動輸出。

常見範例②:位元操作

assign語句也支援位元級操作,可以進行部分位元擷取或拼接:

assign upper_4bits = data[7:4];          // 擷取8位數據的高4位
assign lower_4bits = data[3:0];          // 擷取低4位
assign combined = {data1[3:0], data2[3:0]};  // 將兩個4位數據拼接成8位

因此,在需要重組或部分操作數據時,assign語句非常方便。

assign語句中的「連續指定」含義

在Verilog中,assign語句的賦值稱為「連續指定(continuous assignment)」,這意味著輸入一旦改變,輸出就會立即更新

這與軟體程式中的「一次性賦值」不同,在硬體設計中更接近於訊號始終保持連線狀態的概念。

assign語句中的延遲指定

Verilog允許在assign語句中指定延遲(delay),主要用於模擬,實際綜合時通常會被忽略,但對於動作驗證很有用:

assign #5 out = a & b;  // 延遲5個時間單位後,將a&b的結果輸出到out

這裡的「#5」表示基於time unit的延遲。請注意,在FPGA/ASIC合成時,這種延遲會被忽略。

使用條件運算子的assign範例

assign語句可以搭配三元運算子來實現簡單的條件分支:

assign out = sel ? data1 : data2;

這表示「如果sel=1,輸出data1;否則輸出data2」。這種寫法能簡潔地表現多路選擇器(MUX)的功能。

總結:掌握assign語句的寫法

Verilog的assign語句是一種簡單而強大的描述手段,不論是邏輯運算、位元操作、條件分支,甚至帶延遲的運算,都能輕鬆表達。

對初學者來說,熟悉assign語句的基本寫法,並能靈活描述組合邏輯,是掌握Verilog設計的第一步。

3. assign與wire的關係|從宣告到使用

assign語句與wire的基本關係

在Verilog中,使用assign語句時最重要的一點是:assign語句只能用於wire型變數。如果忽略這個規則來寫程式,會立刻遇到語法錯誤。

assign語句所定義的賦值屬於「連續指定(continuous assignment)」,而這種連續驅動僅允許針對wire型信號。

什麼是wire型?── 模擬實體「配線」的概念

在Verilog中,wire型顧名思義就是模擬電路中配線(wire)的資料型態。它不會自行保存值,而是用來接收並傳遞其他輸出的值

因此,wire型信號的特徵是「自己不記錄數值,只是把assign語句或模組輸出的值傳遞下去」。

範例:

wire a, b, out;

assign out = a & b;  // out持續輸出a與b的AND運算

在這裡,out必須是wire型。如果誤寫成reg型,就會編譯錯誤。

為什麼reg型不能用assign?

reg型則主要用於暫存值的時序電路。它通常在always區塊中,依據條件或時脈觸發來更新值,而不是由assign持續驅動。

錯誤範例:

reg out;
assign out = a & b;  // 錯誤!reg型不能用assign語句

因此,規則是:assign只能搭配wire,always區塊則搭配reg

wire型的宣告與匯流排(多位元)處理

wire型不僅支援單一位元,也能處理多位元匯流排(bus)。

wire [3:0] a, b;
wire [3:0] out;

assign out = a & b;  // 逐位進行AND運算

在處理多位元信號時,只需在宣告時指定位元數即可,寫法仍然一致。

模組連接時wire的角色

在Verilog中,模組之間傳遞信號時通常使用wire。例如,當兩個模組相互連接時,可以透過wire型來承接訊號:

wire result;

module1 u1 (.a(a), .b(b), .out(result));
module2 u2 (.in(result), .y(y));

這顯示了wire不僅在assign語句中重要,也是整個Verilog設計的基本連線元素。

總結:理解wire才能正確使用assign

在Verilog中,若要正確使用assign語句,必須理解wire的特性wire是配線概念,只能接收與傳遞值;而reg則用於保存狀態。

掌握這些差異後,便能正確區分assign與always的使用場景,進而撰寫出更高效、正確的硬體描述程式。

4. assign與always的差異是什麼?【初學者容易混淆的重點】

為什麼「assign」與「always」容易讓人混淆?

對剛開始學習Verilog的新手來說,最常感到困惑的就是assign語句與always區塊的使用差異。兩者都能對信號進行賦值,但用途與可搭配的型態完全不同。

本章將深入解釋兩者的本質差異,以及在實務上如何正確區分使用。

assign語句的特點與使用場景

先來回顧一下assign語句的主要特徵:

  • 用途:用於組合邏輯電路
  • 型態:只能搭配wire
  • 賦值方式:連續指定(持續驅動信號)
  • 關鍵字assign

範例:二輸入AND閘(assign語句)

wire a, b;
wire out;

assign out = a & b;

這表示只要輸入a或b發生改變,輸出就會立即更新。這正是組合邏輯的典型行為

always區塊的特點與使用場景

always區塊則更具彈性,主要用於時序邏輯、條件分支與時脈同步處理。

  • 用途:時序邏輯或複雜控制
  • 型態:搭配reg
  • 賦值方式:條件式賦值(根據觸發條件執行)
  • 關鍵字always

範例:時脈觸發的暫存器(always區塊)

reg out;

always @(posedge clk) begin
  out <= a & b;
end

這裡的out會在時脈上升沿時被更新。這是具有時間概念的處理,必須用always區塊。

wire與reg的比較

特徵wire型reg型
使用位置assign語句always區塊內
是否保存數值否(僅傳遞)是(可暫存)
是否可設初始值不可可(模擬時)
賦值方式連續指定阻塞式或非阻塞式賦值

因此,assignalways成套理解,分別對應不同的型態與用途。

不同情境下的使用建議

目的語法型態
邏輯運算(組合邏輯)assignwire
時脈同步的暫存處理(時序邏輯)alwaysreg
條件控制輸出alwaysreg
簡單的訊號連線或運算結果輸出assignwire

範例:if條件必須使用always

reg y;
always @(a or b) begin
  if (a == 1) y = b;
  else        y = 0;
end

這種包含條件分支的程式無法用assign實現。如果需要流程或狀態控制,就必須用always。

assign與always可以同時用在同一個信號嗎?

不可以。同一個信號不能同時由assign與always驅動。若同一信號同時被多處指定,會導致衝突(多重驅動),產生錯誤或未定義行為。

錯誤範例:

assign y = a & b;

always @(posedge clk)
  y <= a | b;  // 錯誤!同一信號被assign與always同時驅動

因此,必須明確規劃每個信號的唯一驅動來源

總結:如何區分assign與always

在Verilog設計中,「在什麼時機、如何控制信號」決定了應使用assign還是always:

  • 輸入與輸出直接相連 → assign(wire)
  • 需要時間或狀態概念 → always(reg)

只要掌握這個規則,新手就能避免assign與always的常見混淆。

5. 使用assign語句的組合邏輯電路實例【附圖解】

什麼是組合邏輯電路?

我們先從基礎概念開始。組合邏輯電路指的是輸出會隨輸入即時改變的電路。由於沒有記憶元件,它不依賴過去狀態,而只取決於當前輸入

在Verilog中,用來描述這類電路最合適的語法就是assign語句。

基本邏輯閘的實作(AND、OR、XOR)

以下範例展示如何用assign語句來描述多個基本邏輯閘:

module logic_gates(
  input  wire a,
  input  wire b,
  output wire and_out,
  output wire or_out,
  output wire xor_out
);

  assign and_out = a & b;
  assign or_out  = a | b;
  assign xor_out = a ^ b;

endmodule

這個模組會對輸入ab進行AND、OR、XOR運算,並分別輸出結果。由於這些運算不需要條件判斷或時脈,完全可以透過assign實現。

半加器(Half Adder)的實作

組合邏輯的經典教材之一是半加器(Half Adder)。它能將兩個位元的二進位數相加,輸出「和(Sum)」與「進位(Carry)」。

邏輯公式

  • Sum(和)= A ⊕ B(XOR)
  • Carry(進位)= A・B(AND)

Verilog實作範例

module half_adder(
  input  wire a,
  input  wire b,
  output wire sum,
  output wire carry
);

  assign sum   = a ^ b;
  assign carry = a & b;

endmodule

半加器僅需兩行assign即可完成,非常適合初學者熟悉assign的用法。

全加器(Full Adder)的實作

另一個常見的例子是全加器(Full Adder),它能將三個位元(A、B、Cin)相加,並輸出「Sum」與「Carry」。

邏輯公式

  • Sum(和)= A ⊕ B ⊕ Cin
  • Carry(進位)= (A・B) + (Cin・(A ⊕ B))

Verilog實作範例

module full_adder(
  input  wire a,
  input  wire b,
  input  wire cin,
  output wire sum,
  output wire cout
);

  wire ab_xor;

  assign ab_xor = a ^ b;
  assign sum    = ab_xor ^ cin;
  assign cout   = (a & b) | (cin & ab_xor);

endmodule

這裡我們利用中間信號ab_xor,分步完成計算。這展示了wire + assign能靈活應對多步驟運算

選擇器(MUX)的實作

另一個常見電路是多工器(Multiplexer, MUX),它能依條件切換輸出:

module mux2to1(
  input  wire a,
  input  wire b,
  input  wire sel,
  output wire y
);

  assign y = sel ? b : a;

endmodule

sel為1時輸出b,否則輸出a。這利用了assign中的三元運算子,非常簡潔。

設計重點與注意事項

  • 輸出信號必須是wire:assign不能用於reg。
  • 多個輸出應分開寫assign:避免將過多邏輯擠在一行,保持可讀性。
  • 使用中間信號提升可讀性:當運算較複雜時,先分解成多個wire。

總結:基本組合邏輯可完全用assign實現

如同範例所示,大部分基本組合邏輯電路都能用assign輕鬆完成。從邏輯閘到加法器、MUX,都能以簡潔的語法描述。

對初學者來說,建議先多練習這類簡單電路,熟悉assign的寫法,自然而然就能掌握信號流與電路結構。

6. 使用assign語句時的注意事項與常見錯誤

初學者常遇到的assign陷阱

assign語句是Verilog中最簡單的語法之一,但正因為「看似容易上手」,許多初學者在未完全理解規則時使用,常會導致編譯錯誤或非預期行為

以下整理了新手到中級使用者在使用assign時常見的錯誤與解決方法。

1. 嘗試對reg型使用assign

✅ 常見錯誤範例:

reg out;
assign out = a & b;  // 錯誤!reg不能用assign指定

💡 原因與解法:

assign語句只能用於wirereg則需在always區塊內更新。

解法:將out改為wire,或改用always區塊處理。

2. 同一信號被多個assign驅動

✅ 錯誤範例:

assign y = a & b;
assign y = a | b;  // 錯誤,y有多個驅動來源

💡 原因與解法:

Verilog規定一個信號只能有唯一驅動來源。多個assign對同一信號賦值會造成衝突。

解法:使用always區塊或中間信號分解運算。

3. 錯把assign當初始化使用

✅ 錯誤寫法:

assign a = 1'b0;  // 不是初始化,而是「永遠為0」

💡 原因與解法:

assign表示持續驅動,而非一次性的初始化。硬體設計中「初始值」的概念與軟體不同。

解法:模擬時可用initial,實際電路則需透過reset控制。

4. 忘記宣告信號型態

✅ 錯誤範例:

assign result = a & b;  // result未宣告 → 錯誤

💡 原因與解法:

Verilog要求所有信號必須先宣告。若未宣告,會導致模擬器警告或合成錯誤。

解法:明確將信號宣告為wirereg

5. 在assign中使用不支援的運算

部分運算如除法(/)或取餘數(%)在FPGA或ASIC合成時可能不支援:

assign out = a / 3;  // 部分工具無法合成,可能報錯

解法:確認是否可合成,必要時改用邏輯運算或always區塊實現。

6. 過度使用巢狀三元運算子

assign out = sel1 ? a : (sel2 ? b : (sel3 ? c : d));  // 可讀性差!

巢狀條件太多會讓程式難以閱讀。

解法:將條件拆分成中間信號,或用always區塊實現。

assign除錯小技巧

  • 明確區分wire與reg
  • 注意警告訊息(常提示潛在問題)
  • 確認語法是否可合成(模擬能跑≠能實現硬體)

總結:assign雖簡單,但仍需小心

assign語句是Verilog中最基礎、最常用的結構,但仍有一些限制:只能用於wire不可多重驅動不能當初始化。遵守這些規則,才能避免設計上的陷阱,寫出穩定可維護的程式。

7. 常見問題(FAQ)

關於Verilog的assign語句,從初學者到中級使用者都經常提出疑問。本章整理了實際常被搜尋、也常在設計現場遇到的問題,以Q&A方式解答。

Q1:assign與always,哪一個對初學者比較容易?

A:建議先從assign開始學習。

assign能簡單表達組合邏輯,因此對初學者最合適。而always主要用於時序電路或條件控制,相對更複雜。

  • 簡單邏輯運算 → assign
  • 需要時間或狀態控制 → always

Q2:如果想在reg型上使用assign,該怎麼辦?

A:不行。reg必須透過always區塊來指定。

assign是專屬於wire的。若要對reg賦值,必須使用always

// 正確寫法(reg在always區塊中更新)
reg out;
always @(a or b)
  out = a & b;
// 錯誤寫法(assign不能用於reg)
reg out;
assign out = a & b;  // 錯誤

Q3:可以用多個assign語句同時指定同一個信號嗎?

A:不可以,這會造成衝突。

在Verilog中,一個信號只能有唯一驅動來源。多個assign同時對同一個信號賦值,會導致競爭或未定義行為

如果需要多條件控制,應該用always區塊來實現。

Q4:assign的延遲指定(#)有什麼用途?

A:僅在模擬時有意義,實際合成時會被忽略。

例如:

assign #5 out = a & b;

這表示輸出延遲5個時間單位。但在FPGA或ASIC合成時通常無效。

  • 模擬 → 可用於驗證延遲
  • 合成 → 基本上會被忽略

Q5:要在assign語句中寫條件分支,要怎麼做?

A:可以使用三元運算子(條件運算子)。

assign out = sel ? a : b;

這代表「若sel為1,輸出a;否則輸出b」。

若條件過於複雜,則建議使用always區塊。

Q6:模擬時,為什麼assign的輸出沒有變化?

A:請確認輸入是否有正確改變。

assign輸出的更新依賴輸入。如果輸入沒有變化,輸出自然不會改變。

  • 輸入信號是否正確驅動?
  • 是否有初始化?
  • 波形檢查時輸入真的有切換嗎?

Q7:assign描述的電路可以合成嗎?

A:大部分情況可以,但要注意運算種類。

一般邏輯運算(AND、OR、XOR等)幾乎都能合成。但部分運算如除法(/)、實數或浮點數處理,可能會被工具拒絕。

  • AND / OR / XOR → ✅ 可合成
  • 除法、浮點數 → ⚠️ 可能無法合成

8. 術語集:Verilog初學者必備基礎名詞

在學習Verilog HDL時,有一些基礎術語必須先理解。本章整理了與assign以及組合邏輯相關的重要關鍵字,方便新手快速掌握。

wire(導線)

定義:
模擬電路中實體「導線」的信號型態。用來接收並傳遞其他信號的值

特徵:

  • 可透過assign指定值
  • 本身不會保存數值
  • 主要用於組合邏輯

範例:

wire a, b, out;
assign out = a & b;

reg(暫存器)

定義:
可以暫時保存數值的信號型態。主要在always區塊中使用

特徵:

  • 不能用assign驅動
  • 可作為記憶元素
  • 常用於時序邏輯

範例:

reg out;
always @(posedge clk) out <= a;

assign(指定)

定義:
對wire型信號進行連續指定(continuous assignment)的語法

特徵:

  • 用於組合邏輯
  • 輸入改變時,輸出即時更新
  • 可包含邏輯、條件與常數運算

範例:

assign y = a & b;

always(永遠區塊)

定義:
根據特定條件(事件)來執行的程式區塊

特徵:

  • 用於reg
  • 可描述時脈或條件控制
  • 可搭配if、case等流程控制語句

範例:

always @(posedge clk) begin
  out <= a + b;
end

組合邏輯電路(Combinational Circuit)

定義:
輸出僅依賴當前輸入的電路。

特徵:

  • 沒有記憶元素
  • 範例:邏輯閘、加法器、多工器(MUX)
  • 在Verilog中可用assignalways @(*)描述

時序邏輯電路(Sequential Circuit)

定義:
輸出取決於輸入與過去的狀態的電路。

特徵:

  • 包含記憶元素(暫存器、觸發器)
  • 依據時脈運作
  • 常用always @(posedge clk)描述

三元運算子(條件運算子)

定義:
以「條件 ? 真值 : 假值」形式撰寫的條件語法。

特徵:

  • 常用於assign
  • 比if語句更簡潔

範例:

assign y = sel ? a : b;

模組(module)

定義:
Verilog設計的基本單位,就像電路的元件。

特徵:

  • 擁有輸入與輸出埠
  • 可以階層式組合形成大型電路

範例:

module adder(input a, input b, output sum);
  assign sum = a + b;
endmodule

初始化(initial)

定義:
模擬時僅執行一次的程式區塊。

特徵:

  • 不會合成到實體電路
  • 常用於測試平台(testbench)

範例:

initial begin
  a = 0;
  b = 1;
end

非阻塞賦值(<=)

定義:
always區塊中使用的賦值方式,可同時更新多個變數。

特徵:

  • 常用於時脈同步設計
  • 適合平行更新多個寄存器

範例:

always @(posedge clk) begin
  out1 <= in1;
  out2 <= in2;
end

總結:理解術語是學好Verilog的第一步

以上術語屬於Verilog最基礎的核心概念。只要理解它們的用途與差異,就能快速定位錯誤來源,加速開發效率。

9. 總結|掌握assign語句的關鍵

本文針對Verilog HDL中的assign語句,從基礎到進階進行了逐步說明。assign是初學者必須最先掌握的語法之一,雖然簡單卻非常重要,因為它是組合邏輯設計的核心工具

回顧assign語句的重點

以下整理了本文所涵蓋的關鍵要點:

✅ assign語句的角色

  • 用於wire型信號的連續指定
  • 輸入變化會立即反映到輸出
  • 最適合用來描述組合邏輯電路

✅ 使用規則

  • assign不能用於reg
  • 同一個信號不能被多個assign驅動
  • 不能作為初始化使用(僅能持續驅動)

✅ 使用技巧

  • 明確區分assign與always的使用場景
  • 靈活運用三元運算子,簡潔描述條件分支
  • 複雜邏輯時可分解為中間信號,提高可讀性

下一步學習方向

在掌握assign之後,建議進一步挑戰以下主題:

  • 使用always區塊編寫時序邏輯
  • 熟悉caseif條件控制設計
  • 學習撰寫測試平台(testbench)並進行模擬驗證
  • 多模組整合與階層式設計

Verilog的學習重點在於「實作經驗」。從小型電路開始,多動手練習,透過assign建立基礎,將能快速提升設計能力。

最後的話

只要能正確理解並靈活運用assign,就等於跨過了Verilog學習的一大門檻。

希望本文能成為你隨時回顧的「assign實用指南」,幫助你在數位電路設計的學習道路上更加順利。