Verilog 中 wire 與 reg 的混淆是初學者甚至資深工程師常犯的錯誤根源。本質上,**二者區別不在「是否為暫存器」,而在「賦值語境」與「驅動機制」**。本文從語義本質、綜合映射、模組埠規則、常見錯誤四個維度徹底釐清。
| 維度 | wire (net 型) | reg (variable 型) |
|---|---|---|
| 物理隱喻 | 導線/網路,需持續驅動 | 抽象儲存單元,保持上次值 |
| 賦值方式 | 持續賦值 assign | 過程賦值 always/initial 塊內 |
| 無驅動時值 | 高阻態 z | 未知態 x (未初始化) |
| 多驅動支援 | 支援 (需解析函數 tri/wand/wor) | 禁止 (最後賦值生效,仿真報錯) |
| 綜合結果 | 組合邏輯網路 / 連線 | 觸發器 / 鎖存器 / 組合邏輯 (視敏感列表) |
// 連續賦值 → 左側必須 wire (或未聲明默認 wire)
assign wire_out = a & b; // 組合邏輯
assign bus = (oe) ? data : 'bz; // 三態輸出
// 過程賦值 → 左側必須 reg
always @(posedge clk) begin
reg_out <= din; // 時序 → 觸發器
end
always @(*) begin
comb_reg = a | b; // 組合 → 無鎖存器 (全分支覆蓋)
end
⚠️ 同一信號不可同時出現在 assign 與 always 塊中,需引入中間信號轉換。
| 埠方向 | 允許類型 | 驅動來源 | 連接目標 |
|---|---|---|---|
input | 只能 wire (net) | 外部 wire/reg 驅動 | 內部只能讀 |
output | wire 或 reg | 內部: — 過程塊賦值 → reg— assign/實例化 → wire | 外部只能接 wire |
inout | 只能 wire (三態) | 雙向,需 output_enable 控制 | 外部接 wire |
內部信號遵循同樣邏輯:過程塊賦值 → reg;持續賦值/實例化輸出 → wire。
| 錯誤現象 | 典型報錯 | 根因 | 修正 |
|---|---|---|---|
| 過程塊賦值 wire | illegal ... assignment | 左側為 net 型 | 改宣告 reg 或改用 assign |
| 實例輸出接 reg | <name> has illegal output port specification | 實例輸出只能駕馭 net | 中間加 wire 轉接 |
| input 宣告 reg | incompatible declaration | input 僅支援 net | 移除 reg 關鍵字 |
| inout 宣告 reg | 同上 | inout 必須三態 net | 宣告 wire |
// 雙向總線標準模式
inout [7:0] data_bus;
wire [7:0] data_bus;
reg [7:0] data_out;
reg oe;
assign data_bus = oe ? data_out : 8'bz; // 輸出方向
wire [7:0] data_in = data_bus; // 輸入方向
// 決策信號同時來自過程塊與 assign
reg dec_reg;
wire dec_wire;
assign dec_wire = dec_reg; // 中間轉換
// 或 SystemVerilog 直接用 logic 統一
新專案強烈建議遷移至 SystemVerilog logic 類型:
logic clk, rst_n;
logic [7:0] data_in, data_out;
logic oe;
assign data_bus = oe ? data_out : 'z; // logic 可直接 assign
always_ff @(posedge clk or negedge rst_n)
data_out <= data_in; // logic 可直接 <=
assign 左邊 wire,always 左邊 reg;
input 只能 wire,inout 三態 wire;
output 看賦值:過程塊 reg,assign wire。
同一信號雙賦值,中間轉換不可少。
徹底理解 wire/reg 本質差異,是寫出無 Lint 錯誤、仿真綜合一致 RTL 的基石。