Clifford E. Cummings
摘要
Verilog 語言中最令人困惑的概念之一是什么時候變量是reg,什么時候變成wire?雖然聲明reg 和wire 的規則非常簡單,但大多數新的和自學成才的Verilog用戶不明白何時以及為什么需要這種聲明。
目的
目的:
刪除聲明標量寄存器數據類型的要求,并用線網類型替換寄存器類型
無論何時對由連續賦值或者實例端口也被驅動為值的變量進行過程分配時,都要報告語法錯誤。
原因:
- 去除Verilog語言令人討厭和混淆的聲明要求
- 減少和簡化所需的Verilog聲明的數量。
介紹
Verilog 中寄存器和線網變量的概念在很大程度上被誤解。
VHDL的過程大致等同于Verilog 中的always 塊,并且VHDL并行信號賦值大致等同于Verilog連續賦值。但VHDL不需要對過程和當前信號分配不同數據類型聲明。在VHDL中,信號通常用于代替Verilog 寄存器和線網數據類型。
為什么Verilog用戶對這兩種不同的數據類型有負擔?
寄存器和線網聲明的簡單規則
在Verilog中,寄存器數據類型包括:reg, integer, time, real 和realtime。線網數據類型包括: wire, tri, vor, trior, wand, triand, tri0, tril1, supply0, supply1 和trireg。讓我們看看兩個簡單的Verilog實例來幫助理解寄存器和線網數據類型的聲明。
module and2a(y, a, b);
output y;
input a, b;
assign y = a & b;
endmodule
Example 1
module and2b (y, a, b);
output y;
input a, b;
wire y;
assign y = a & b;
endmodule
Example 2
在實例1中, 使用連續賦值語句對2輸入和門進行建模。y輸出不必聲明,因為他是一位線網型。實例2是完全相同的2輸入和門,可選‘wire y’聲明。
在實例3中, 我們決定用always 塊替換連續賦值,但編譯此代碼時,Verilog編譯器會報告‘非法左側賦值’形式的語法錯誤,因為我們忘記更改‘wire’聲明為‘reg’,如果把‘wire’改為‘reg’,該模型正確編譯和模擬。
module and2c (y, a, b);
output y;
input a, b;
wire y;
always @(a or b) y = a & b;
endmodule
Example 3
現在如果實例3中的2輸入和門的always塊被更改回連續賦值,如實例4所示,Verilog編譯器將再次報告語法錯誤,但是這次該消息的格式變為‘非法轉讓給線網’,因為我們忘記了改變‘reg’為‘wire’,很煩人。
module and2d (y, a, b);
output y;
input a, b;
reg y;
assign y = a & b;
endmodule
Example 4
簡單規則:在Verilog中,程序分配左側的任何內容都必須聲明為寄存器數據類型。Verilog中的其他一切都是線網數據類型,沒有例外。
為什么區分線網型和寄存器型
為什么在Verilog中區分線網型和寄存器型數據類型?這個問題的答案似乎是,數據類型檢查是一種簡單的方法,可以識別連續和過程賦值中的同一個變量的錯誤分配。
連續賦值驅動線網型數據,如例5所示,多驅動能驅動相同的線網數據。
module drivers1 (y, a1, en1, a2, en2);
output y;
input a1, en1, a2, en2;
assign y = en1 ? a1 : 1'bz;
assign y = en2 ? a2 : 1'bz;
endmodule
Example 5
過程賦值,例如always塊賦值會導致對單個行為變量的更改。實例6的多個always塊賦值只是賦值給相同的行為變量,而且不設置多個驅動過程,在這個例子中,最后的賦值勝出。
module drivers2 (y, a1, en1, a2, en2);
output y;
input a1, en1, a2, en2;
reg y;
always @(a1 or en1)
if (en1) y = a1;
else y = 1'bz;
always @(a2 or en2)
if (en2) y = a2;
else y = 1'bz;
endmodule
Example 6
如果嘗試為統一變量設置驅動和行為級賦值,則驅動過程需要線網聲明,而always塊賦值需要reg聲明,這兩個語句都是相同的變量,這是語法錯誤。這種語法錯誤是維持Verilog設計人員試圖對相同變量進行兩種基本不同類型的賦值的一種方法。
module drivers3 (y, a1, en1, a2, en2);
output y;
input a1, en1, a2, en2;
wire?/reg? y;
always @(a1 or en1)
if (en1) y = a1;
else y = 1'bz;
assign y = en2 ? a2 : 1'bz;
endmodule
Example 7
例7中的代碼不能合法地將y變量聲明為線網類型或寄存器類型。圖1從概念上表明,例7的代碼證嘗試更改行為變量并通過連續賦值來驅動相同的變量。
如果一位設計師真的想對同一個變量做一個線網驅動變量的過程賦值,那么可以將always塊中的LHS聲明為通常被稱為‘影子’寄存器的那個,這是一個臨時寄存器,然后通過連續賦值被驅動到線網型變量中,如例8和圖2所示。但是如果你打算這樣做,那么你完全可以跳過always塊的賦值,只需使用第二個連續賦值語句來進行分配。
module drivers4 (y, a1, en1, a2, en2);
output y;
input a1, en1, a2, en2;
wire y;
reg y_tmp;
always @(a1 or en1)
if (en1) y_tmp = a1;
else y_tmp = 1'bz;
assign y = y_tmp;
assign y = en2 ? a2 : 1'bz;
endmodule
Example 8
條件編譯
如果設計人員想要包含條件編譯,選擇always塊或連續賦值,如示例9所示,該怎么辦?條件編譯的1位連續賦值不需要數據類型聲明或可以包含可選的線網聲明。
另一個有條件編譯的分支,即1位始終塊分配,需要注冊數據類型聲明。
一些公司的編碼準則要求在所有I / O聲明之后立即將所有數據類型聲明放置在模塊的頂部。 條件編譯的always-block代碼將違反本指南,除非單獨的條件編譯的聲明部分添加到模塊代碼頂部附近的分組聲明中(未顯示)。
module inva (y, a);
output y;
input a;
` ifdef ASSIGN
assign #(1:2:3,4:5:6) y = ~a;
`else
// mid-code reg declaration
reg y;
always @(a) #(1:2:3) y = ~a;
`endif
endmodule
Example 9
Verilog-2000端口增強
在Verilog-1995中,所有寄存器類型的輸出端口必須聲明三次:
在模塊頭部
與端口聲明,和
作為單獨的寄存器數據類型。
module and2ora (y, a, b, c);
output y;
input a, b, c;
reg y;
reg tmp;
always @(a or b)
tmp = a & b;
always @(tmp or a)
y = tmp | c;
endmodule
Example 10
Verilog-1995的另一個要求是,連續任務的LHS的任何網絡變量都必須聲明為不連接到端口,包括1bit線網。Verilog-1995 的這個不一致的要求在Verilog-2000中得到了修正。在廣泛實施Verilog-2000之前,如果例10的always塊分配被替換為如例11所示的等效連續賦值,則需要tmp的線網聲明。
module and2orb (y, a, b, c);
output y;
input a, b, c;
reg y;
wire tmp;
assign tmp = a & b;
always @(tmp or a)
y = tmp | c;
endmodule
Example 11
從Verilog-2000開始,可以使用端口聲明簡化增強功能。
為了本文的目的,使用以下端口聲明樣式定義:
- 樣式1 端口聲明聲明端口方向和數據類型,包括所有可選的數據類型聲明。
- 樣式2 端口聲明聲明所有端口方向,但僅聲明所需的數據類型。 所有可選的數據類型都被省略。
也可以做一個風格#1和風格2#的混合體,但本文沒有一個例子顯示這種組合。
Verilog-2000中的第一個端口聲明增強功能包括組合端口和類型聲明的功能。 示例12顯示了所有用數據類型聲明的端口(以上稱為樣式#1)。 y輸出不需要單獨的寄存器聲明。
module and2orc (y, a, b, c);
output reg y;
input wire a, b, c;
reg tmp;
always @(a or b)
tmp = a & b;
always @(tmp or a)
y = tmp | c;
endmodule
Example 12
示例13還顯示了合法的Verilog-2000聲明,其中只有reg類型端口聲明包含數據類型,而所有net類型端口聲明省略數據類型(以前稱為樣式#2)。
module and2ord (y, a, b, c);
output reg y;
input a, b, c;
reg tmp;
always @(a or b)
tmp = a & b;
always @(tmp or a)
y = tmp | c;
endmodule
Example 13
Verilog-2000的另一個端口增強功能是在模塊頭本身允許使用端口方向和數據類型,從而可以將所有端口聲明為一次。 預期的模塊頭端口聲明的方法是使用左括號開頭編碼模塊頭,然后是在單獨的后續行中聲明的每個端口,并在獨立行上以結束括號和分號結尾, 如圖所示在例14中。
module and2ore (
output reg y;
input a, b, c;
);
reg tmp;
always @(a or b)
tmp = a & b;
always @(tmp or a)
y = tmp | c;
endmodule
Example 14
使模塊頭部中的所有端口聲明都能保證所有端口都將被聲明在模塊的頂部,Verilog標準組織(VSG)預期在Verilog編譯期間將允許增強優化和加速。 在Verilog-1995中,端口聲明可以出現在模塊中的任何地方,這意味著在讀取endmodule語句之前,編譯器無法識別并報告缺失的端口。
在示例14和示例15中所示的單聲明,增強端口編碼樣式中,如果將tmp變量分配給always,則仍然需要將tmp變量聲明為reg
塊(示例14),或者如果從連續賦值(示例15)中指定,tmp變量可以省略或聲明為連線。 在這兩個示例中,y輸出還需要reg聲明。
module and2orf (
output reg y;
input a, b, c;
);
wire tmp;
assign tmp = a & b;
always @(tmp or a)
y = tmp | c;
endmodule
Example 15
所有Verilog-2000端口聲明增強仍然存在的問題是, 將輸出端口或內部變量分配從連續賦值更改為always 塊仍需要改變增強型端口聲明以反映變量的數據類型被修改,與Verilog-1995數據類型聲明相同。
如果單獨的寄存器和線網數據類型要求被消除,則示例16和示例17中所示的相同的增強型端口聲明將是縮寫和合法的。 請注意,在這兩個示例中,聲明都是相同的,不需要線網聲明或寄存器聲明。
module and2org (
output y;
input a, b, c;
);
always @(a or b)
tmp = a & b;
always @(tmp or a)
y = tmp | c;
endmodule
Example 16
module and2orh (
output y;
input a, b, c;
);
assign tmp = a & b;
assign y = tmp | c;
endmodule
Example 17
module and2ori (
output y;
input a, b, c;
);
assign tmp = a & b;
always @(tmp or c)
y = tmp | c;
endmodule
Example 18
當然,在Verilog-2005中也可以簡化和/或模型,方法是將前面例子中看到的單獨賦值合并成例19中所示的單個連續賦值,或者合并成單個always塊, 如例20所示。例20還顯示了組合的靈敏度列表運算符“@* ”,用于將所有的RHS變量,if-表達式變量(不在本例中)和case-expression變量(本例中不是這個例子)收集到靈敏度列表中。“@* ”操作符是Verilog-2000的新增功能。
module and2orj (
output y;
input a, b, c;
);
assign y = (a & b) | c;
endmodule
Example 19
module and2ork (
output y;
input a, b, c;
);
always @*
y = (a & b) | c;
endmodule
Example 20
在例19和例20中,代碼都已經簡化了等效的Verilog-1995模塊,如例21所示。
module and2orl (y, a, b, c);
output y;
input a, b, c;
reg y;
always @(a or b or c)
y = (a & b) | c;
endmodule
Example 21
聲明線網型的優點和缺點
一些Verilog設計人員認為,在每個模塊中都存在線網的情況下,聲明所有線網(包括1位線網)是一種很好的做法。 做出所有聲明的明顯原因是(1)記錄所有連線的存在,以及(2)Verilog對所有聲明變量進行全面大小檢查的錯誤概念。
聲明所有連線也是一些習慣,這些習慣是由一些以前使用VHDL設計的工程師開發的,VHDL是一種需要所有信號聲明的語言。 在VHDL中,編譯器檢查聲明的信號,信號大小和實際VHDL模型中使用的信號大小。
在Verilog中,不存在同樣嚴格的大小檢查。 盡管會發生一些大小檢查,除非代碼體內的變量(不僅僅是聲明中)包含位范圍,否則大部分大小檢查都可能 很容易被忽略。
許多變量聲明為1位線網,然后用作總線,而不參考Verilog代碼中的總線范圍,將被轉換為1位線網,其中所有前導位位置的分配都用0填充。
module invbad1 (y, a);
output [7:0] y;
input [7:0] a;
wire tmp;
assign tmp = ~a;
assign y = tmp;
endmodule
Example 22
例22中的模型是一個人為的但簡單的8位反相器的例子,其中內部tmp變量被錯誤地聲明為1位線網。編譯時沒有語法錯誤或警告,并且使用例23中的testbench進行模擬時,1位tmp填充了前導零,導致模塊輸出的高7位始終為零。
module tb;
reg [7:0] a;
wire [7:0] y;
inv_module u1 (.y(y), .a(a));
initial begin
$monitor ("y=%h a=%h", y, a);
a = 8'h00;
#10 a = 8'h55;
#10 a = 8'hCC;
#10 $finish;
end
endmodule
Example 23
使用示例24中所示的1位reg變量的相同模型具有與示例22的1位Wire代碼完全相同的問題。在任何情況下,wire或reg聲明的存在都有助于定位編碼錯誤; 事實上,可以認為聲明的存在可能掩蓋了變量被錯誤地聲明的事實。
module invbad2 (y, a);
output [7:0] y;
input [7:0] a;
reg tmp;
always @(a) tmp = ~a;
assign y = tmp;
endmodule
Example 24
作為一個側面說明,即使VHDL執行了前面提到的所 有大小檢查,作者完成的一個非常大的VHDL設計,作 者注意到他花了幾乎同樣多的時間調試頂部所需信號 聲明的頁面他花費在調試實際設計問題上的設計水平。
作者認為,將聲明限制為總線聲明有助于簡明地顯示哪些標識符應該具有多位寬度,同時消除往往會占用空間并掩蓋內部總線存在的不必要和冗長的1位聲明。 作者認為,短絨工具最適合檢查尺寸并報告潛在問題。 作者承認,其他熟練的設計師持相反的觀點, 即所有變量都應該聲明。
在示例25和示例26中,已經做出了適當的內部總線聲明,并且兩種模型都可以正確模擬。
module invgood1 (y, a);
output [7:0] y;
input [7:0] a;
wire [7:0] tmp;
assign tmp = ~a;
assign y = tmp;
endmodule
Example 25
module invgood2 (y, a);
output [7:0] y;
input [7:0] a;
reg [7:0] tmp;
always @(a) tmp = ~a;
assign y = tmp;
endmodule
Example 26
線網型和寄存器型差異
Verilog網絡和寄存器數據類型之間有一些顯著差異。 圖3顯示了一個列出網絡和寄存器數據類型之間重要區別的表格。
線網型 | 寄存器型 | |
---|---|---|
Verilog優勢 | 是 | 否 |
初始值 | 高阻 | x |
多賦值 | 所有驅動的值 | 上次賦的值 |
用于聲明的范圍 | wire, tri, wor, trior, wand, triand, tri0, tri1, supply0, supply1, trireg | reg |
處理現有的寄存器類型
為了實施注冊禁令的增強,必須制定一項計劃,使這種增強與現有的寄存器類型向后兼容聲明。 所有Verilog-1995和Verilog-2000寄存器類型聲明的現有模型都需要正確讀取和模擬。
為了說明Verilog編譯器如何將不同的寄存器數據類型視為向后兼容,圖4顯示了一個可能的實現表。
寄存器類型 | 線網型實現 |
---|---|
reg | wire |
reg [msb:lsb] | wire [msb:lsb] |
integer | wire signed[31:0] |
time | wire[63:0 |
real | 只在過程塊中賦值 |
realtime | 只在過程塊中賦值 |
上述integer,time,realtime和實時關鍵字仍然可以用來暗示某些范圍內的某些數據類型。 整數聲明還可以推斷添加到Verilog-2000的簽名線網數據類型。 實際和實時數據類型在渲染時可能沒有意義,因此對這些變量的分配可能會繼續限制為程序塊,Verilog-AMS可能例外。
在內部,Verilog編譯器可以繼續像現在一樣處理所有數據類型。 區別在于Verilog應該能夠從代碼的上下文中推斷適當的內部數據類型。 如果未指定,則在always模塊內部分賦值的1位線網變量在模擬開始時仍可能未知,并且在程序塊內部賦值的線網變量仍然可以在沒有Verilog強度的情況下實施。
上述實施建議可能存在一些小問題,但作者認為這些是IEEE Verilog標準組可以制定的小細節。
現有的寄存器數據類型聲明可能大部分被忽略,除非它們涉及變量是否是帶符號變量(整數),隱含的總線寬度(整數 - 32位寬/時間 - 64位寬)或顯式總線寬度(reg [msb:lsb])。
一種新的語法檢查
與其強迫用戶區分過程塊中使用的數據類型和過程塊外使用的數據類型之間的區別,為什么不在過程塊內部和外部賦值同一變量時定義語法錯誤。
提出的reg-removal增強存在的一個潛在問題是, 消除所需的net和register數據類型將允許設計人員進行驅動程序類型賦值和行為類型賦值給同一個變量。 這不應該被允許。
寄存器移除提案的實施應該伴隨著一種新的語法檢查,一種可以確定哪些變量是從程序塊中分配的,哪些不是。從程序性指配和非程序性指派中指定同一個變量應是非法的。這實際上是Verilog編譯器在當前的網絡和注冊聲明要求下執行的。
其他賦值限制可能包括:
- 不得允許對內部端口進行程序性任務。
- 不允許對封閉模塊的輸入端口進行程序分配。
- 不允許對由實例化模塊輸出端口驅動的網絡進行過程分配。
結論
總之,目前的凈數據和寄存器數據類型要求令人困惑和煩人。 執行這些聲明規則的唯一明顯理由是不讓程序員對由非程序性分配源驅動的變量進行程序分配。
為了消除上述問題并簡化Verilog建模,作者提出了以下建議:
- 刪除為程序賦值聲明寄存器數據類型的要求。
- 允許賦值程序塊內的凈數據類型。
- 為了向后兼容性和用于簡短聲明,允許可選的寄存器類型聲明
- 如果從過程塊內部和外部對同一變量進行賦值,則需要符合標準的仿真器標記語法錯誤。