Clifford E. Cummings
摘要
Verilog 語言中最令人困惑的概念之一是什么時候變量是reg,什么時候變成wire魁莉?雖然聲明reg 和wire 的規(guī)則非常簡單庶诡,但大多數(shù)新的和自學成才的Verilog用戶不明白何時以及為什么需要這種聲明。
目的
目的:
刪除聲明標量寄存器數(shù)據(jù)類型的要求躺同,并用線網(wǎng)類型替換寄存器類型
無論何時對由連續(xù)賦值或者實例端口也被驅(qū)動為值的變量進行過程分配時趣倾,都要報告語法錯誤聘惦。
原因:
- 去除Verilog語言令人討厭和混淆的聲明要求
- 減少和簡化所需的Verilog聲明的數(shù)量。
介紹
Verilog 中寄存器和線網(wǎng)變量的概念在很大程度上被誤解儒恋。
VHDL的過程大致等同于Verilog 中的always 塊善绎,并且VHDL并行信號賦值大致等同于Verilog連續(xù)賦值。但VHDL不需要對過程和當前信號分配不同數(shù)據(jù)類型聲明碧浊。在VHDL中涂邀,信號通常用于代替Verilog 寄存器和線網(wǎng)數(shù)據(jù)類型。
為什么Verilog用戶對這兩種不同的數(shù)據(jù)類型有負擔箱锐?
寄存器和線網(wǎng)聲明的簡單規(guī)則
在Verilog中比勉,寄存器數(shù)據(jù)類型包括:reg, integer, time, real 和realtime。線網(wǎng)數(shù)據(jù)類型包括: wire, tri, vor, trior, wand, triand, tri0, tril1, supply0, supply1 和trireg驹止。讓我們看看兩個簡單的Verilog實例來幫助理解寄存器和線網(wǎng)數(shù)據(jù)類型的聲明浩聋。
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中, 使用連續(xù)賦值語句對2輸入和門進行建模臊恋。y輸出不必聲明衣洁,因為他是一位線網(wǎng)型。實例2是完全相同的2輸入和門抖仅,可選‘wire y’聲明坊夫。
在實例3中砖第, 我們決定用always 塊替換連續(xù)賦值,但編譯此代碼時环凿,Verilog編譯器會報告‘非法左側(cè)賦值’形式的語法錯誤梧兼,因為我們忘記更改‘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
現(xiàn)在如果實例3中的2輸入和門的always塊被更改回連續(xù)賦值,如實例4所示到推,Verilog編譯器將再次報告語法錯誤考赛,但是這次該消息的格式變?yōu)椤欠ㄞD(zhuǎn)讓給線網(wǎng)’,因為我們忘記了改變‘reg’為‘wire’莉测,很煩人颜骤。
module and2d (y, a, b);
output y;
input a, b;
reg y;
assign y = a & b;
endmodule
Example 4
簡單規(guī)則:在Verilog中,程序分配左側(cè)的任何內(nèi)容都必須聲明為寄存器數(shù)據(jù)類型捣卤。Verilog中的其他一切都是線網(wǎng)數(shù)據(jù)類型复哆,沒有例外。
為什么區(qū)分線網(wǎng)型和寄存器型
為什么在Verilog中區(qū)分線網(wǎng)型和寄存器型數(shù)據(jù)類型腌零?這個問題的答案似乎是,數(shù)據(jù)類型檢查是一種簡單的方法唆阿,可以識別連續(xù)和過程賦值中的同一個變量的錯誤分配益涧。
連續(xù)賦值驅(qū)動線網(wǎng)型數(shù)據(jù),如例5所示驯鳖,多驅(qū)動能驅(qū)動相同的線網(wǎng)數(shù)據(jù)闲询。
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塊賦值會導(dǎo)致對單個行為變量的更改浅辙。實例6的多個always塊賦值只是賦值給相同的行為變量扭弧,而且不設(shè)置多個驅(qū)動過程,在這個例子中记舆,最后的賦值勝出鸽捻。
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
如果嘗試為統(tǒng)一變量設(shè)置驅(qū)動和行為級賦值,則驅(qū)動過程需要線網(wǎng)聲明泽腮,而always塊賦值需要reg聲明御蒲,這兩個語句都是相同的變量,這是語法錯誤诊赊。這種語法錯誤是維持Verilog設(shè)計人員試圖對相同變量進行兩種基本不同類型的賦值的一種方法厚满。
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變量聲明為線網(wǎng)類型或寄存器類型。圖1從概念上表明碧磅,例7的代碼證嘗試更改行為變量并通過連續(xù)賦值來驅(qū)動相同的變量碘箍。
如果一位設(shè)計師真的想對同一個變量做一個線網(wǎng)驅(qū)動變量的過程賦值遵馆,那么可以將always塊中的LHS聲明為通常被稱為‘影子’寄存器的那個,這是一個臨時寄存器丰榴,然后通過連續(xù)賦值被驅(qū)動到線網(wǎng)型變量中货邓,如例8和圖2所示。但是如果你打算這樣做多艇,那么你完全可以跳過always塊的賦值逻恐,只需使用第二個連續(xù)賦值語句來進行分配。
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
條件編譯
如果設(shè)計人員想要包含條件編譯峻黍,選擇always塊或連續(xù)賦值复隆,如示例9所示,該怎么辦姆涩?條件編譯的1位連續(xù)賦值不需要數(shù)據(jù)類型聲明或可以包含可選的線網(wǎng)聲明挽拂。
另一個有條件編譯的分支,即1位始終塊分配骨饿,需要注冊數(shù)據(jù)類型聲明亏栈。
一些公司的編碼準則要求在所有I / O聲明之后立即將所有數(shù)據(jù)類型聲明放置在模塊的頂部。 條件編譯的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中,所有寄存器類型的輸出端口必須聲明三次:
在模塊頭部
與端口聲明察署,和
作為單獨的寄存器數(shù)據(jù)類型闷游。
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的另一個要求是,連續(xù)任務(wù)的LHS的任何網(wǎng)絡(luò)變量都必須聲明為不連接到端口贴汪,包括1bit線網(wǎng)脐往。Verilog-1995 的這個不一致的要求在Verilog-2000中得到了修正。在廣泛實施Verilog-2000之前扳埂,如果例10的always塊分配被替換為如例11所示的等效連續(xù)賦值业簿,則需要tmp的線網(wǎng)聲明。
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 端口聲明聲明端口方向和數(shù)據(jù)類型希太,包括所有可選的數(shù)據(jù)類型聲明克饶。
- 樣式2 端口聲明聲明所有端口方向,但僅聲明所需的數(shù)據(jù)類型誊辉。 所有可選的數(shù)據(jù)類型都被省略矾湃。
也可以做一個風格#1和風格2#的混合體,但本文沒有一個例子顯示這種組合堕澄。
Verilog-2000中的第一個端口聲明增強功能包括組合端口和類型聲明的功能邀跃。 示例12顯示了所有用數(shù)據(jù)類型聲明的端口(以上稱為樣式#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類型端口聲明包含數(shù)據(jù)類型途戒,而所有net類型端口聲明省略數(shù)據(jù)類型(以前稱為樣式#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的另一個端口增強功能是在模塊頭本身允許使用端口方向和數(shù)據(jù)類型僵驰,從而可以將所有端口聲明為一次喷斋。 預(yù)期的模塊頭端口聲明的方法是使用左括號開頭編碼模塊頭,然后是在單獨的后續(xù)行中聲明的每個端口蒜茴,并在獨立行上以結(jié)束括號和分號結(jié)尾星爪, 如圖所示在例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)預(yù)期在Verilog編譯期間將允許增強優(yōu)化和加速顽腾。 在Verilog-1995中,端口聲明可以出現(xiàn)在模塊中的任何地方诺核,這意味著在讀取endmodule語句之前抄肖,編譯器無法識別并報告缺失的端口。
在示例14和示例15中所示的單聲明窖杀,增強端口編碼樣式中漓摩,如果將tmp變量分配給always,則仍然需要將tmp變量聲明為reg
塊(示例14)入客,或者如果從連續(xù)賦值(示例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端口聲明增強仍然存在的問題是酥诽, 將輸出端口或內(nèi)部變量分配從連續(xù)賦值更改為always 塊仍需要改變增強型端口聲明以反映變量的數(shù)據(jù)類型被修改鞍泉,與Verilog-1995數(shù)據(jù)類型聲明相同。
如果單獨的寄存器和線網(wǎng)數(shù)據(jù)類型要求被消除肮帐,則示例16和示例17中所示的相同的增強型端口聲明將是縮寫和合法的咖驮。 請注意,在這兩個示例中训枢,聲明都是相同的托修,不需要線網(wǎng)聲明或寄存器聲明。
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中所示的單個連續(xù)賦值,或者合并成單個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中搓彻,代碼都已經(jīng)簡化了等效的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
聲明線網(wǎng)型的優(yōu)點和缺點
一些Verilog設(shè)計人員認為旭贬,在每個模塊中都存在線網(wǎng)的情況下怔接,聲明所有線網(wǎng)(包括1位線網(wǎng))是一種很好的做法。 做出所有聲明的明顯原因是(1)記錄所有連線的存在骑篙,以及(2)Verilog對所有聲明變量進行全面大小檢查的錯誤概念蜕提。
聲明所有連線也是一些習慣,這些習慣是由一些以前使用VHDL設(shè)計的工程師開發(fā)的靶端,VHDL是一種需要所有信號聲明的語言谎势。 在VHDL中,編譯器檢查聲明的信號杨名,信號大小和實際VHDL模型中使用的信號大小脏榆。
在Verilog中,不存在同樣嚴格的大小檢查台谍。 盡管會發(fā)生一些大小檢查须喂,除非代碼體內(nèi)的變量(不僅僅是聲明中)包含位范圍,否則大部分大小檢查都可能 很容易被忽略趁蕊。
許多變量聲明為1位線網(wǎng)坞生,然后用作總線,而不參考Verilog代碼中的總線范圍掷伙,將被轉(zhuǎn)換為1位線網(wǎng)是己,其中所有前導(dǎo)位位置的分配都用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位反相器的例子任柜,其中內(nèi)部tmp變量被錯誤地聲明為1位線網(wǎng)卒废。編譯時沒有語法錯誤或警告,并且使用例23中的testbench進行模擬時宙地,1位tmp填充了前導(dǎo)零摔认,導(dǎo)致模塊輸出的高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
作為一個側(cè)面說明蓖柔,即使VHDL執(zhí)行了前面提到的所 有大小檢查辰企,作者完成的一個非常大的VHDL設(shè)計,作 者注意到他花了幾乎同樣多的時間調(diào)試頂部所需信號 聲明的頁面他花費在調(diào)試實際設(shè)計問題上的設(shè)計水平况鸣。
作者認為牢贸,將聲明限制為總線聲明有助于簡明地顯示哪些標識符應(yīng)該具有多位寬度,同時消除往往會占用空間并掩蓋內(nèi)部總線存在的不必要和冗長的1位聲明镐捧。 作者認為潜索,短絨工具最適合檢查尺寸并報告潛在問題。 作者承認懂酱,其他熟練的設(shè)計師持相反的觀點竹习, 即所有變量都應(yīng)該聲明。
在示例25和示例26中列牺,已經(jīng)做出了適當?shù)膬?nèi)部總線聲明整陌,并且兩種模型都可以正確模擬。
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
線網(wǎng)型和寄存器型差異
Verilog網(wǎng)絡(luò)和寄存器數(shù)據(jù)類型之間有一些顯著差異瞎领。 圖3顯示了一個列出網(wǎng)絡(luò)和寄存器數(shù)據(jù)類型之間重要區(qū)別的表格泌辫。
線網(wǎng)型 | 寄存器型 | |
---|---|---|
Verilog優(yōu)勢 | 是 | 否 |
初始值 | 高阻 | x |
多賦值 | 所有驅(qū)動的值 | 上次賦的值 |
用于聲明的范圍 | wire, tri, wor, trior, wand, triand, tri0, tri1, supply0, supply1, trireg | reg |
處理現(xiàn)有的寄存器類型
為了實施注冊禁令的增強,必須制定一項計劃九默,使這種增強與現(xiàn)有的寄存器類型向后兼容聲明震放。 所有Verilog-1995和Verilog-2000寄存器類型聲明的現(xiàn)有模型都需要正確讀取和模擬。
為了說明Verilog編譯器如何將不同的寄存器數(shù)據(jù)類型視為向后兼容驼修,圖4顯示了一個可能的實現(xiàn)表殿遂。
寄存器類型 | 線網(wǎng)型實現(xiàn) |
---|---|
reg | wire |
reg [msb:lsb] | wire [msb:lsb] |
integer | wire signed[31:0] |
time | wire[63:0 |
real | 只在過程塊中賦值 |
realtime | 只在過程塊中賦值 |
上述integer,time乙各,realtime和實時關(guān)鍵字仍然可以用來暗示某些范圍內(nèi)的某些數(shù)據(jù)類型墨礁。 整數(shù)聲明還可以推斷添加到Verilog-2000的簽名線網(wǎng)數(shù)據(jù)類型。 實際和實時數(shù)據(jù)類型在渲染時可能沒有意義耳峦,因此對這些變量的分配可能會繼續(xù)限制為程序塊饵溅,Verilog-AMS可能例外。
在內(nèi)部妇萄,Verilog編譯器可以繼續(xù)像現(xiàn)在一樣處理所有數(shù)據(jù)類型。 區(qū)別在于Verilog應(yīng)該能夠從代碼的上下文中推斷適當?shù)膬?nèi)部數(shù)據(jù)類型咬荷。 如果未指定冠句,則在always模塊內(nèi)部分賦值的1位線網(wǎng)變量在模擬開始時仍可能未知,并且在程序塊內(nèi)部賦值的線網(wǎng)變量仍然可以在沒有Verilog強度的情況下實施幸乒。
上述實施建議可能存在一些小問題懦底,但作者認為這些是IEEE Verilog標準組可以制定的小細節(jié)。
現(xiàn)有的寄存器數(shù)據(jù)類型聲明可能大部分被忽略,除非它們涉及變量是否是帶符號變量(整數(shù))聚唐,隱含的總線寬度(整數(shù) - 32位寬/時間 - 64位寬)或顯式總線寬度(reg [msb:lsb])丐重。
一種新的語法檢查
與其強迫用戶區(qū)分過程塊中使用的數(shù)據(jù)類型和過程塊外使用的數(shù)據(jù)類型之間的區(qū)別,為什么不在過程塊內(nèi)部和外部賦值同一變量時定義語法錯誤杆查。
提出的reg-removal增強存在的一個潛在問題是扮惦, 消除所需的net和register數(shù)據(jù)類型將允許設(shè)計人員進行驅(qū)動程序類型賦值和行為類型賦值給同一個變量。 這不應(yīng)該被允許亲桦。
寄存器移除提案的實施應(yīng)該伴隨著一種新的語法檢查崖蜜,一種可以確定哪些變量是從程序塊中分配的,哪些不是客峭。從程序性指配和非程序性指派中指定同一個變量應(yīng)是非法的豫领。這實際上是Verilog編譯器在當前的網(wǎng)絡(luò)和注冊聲明要求下執(zhí)行的。
其他賦值限制可能包括:
- 不得允許對內(nèi)部端口進行程序性任務(wù)舔琅。
- 不允許對封閉模塊的輸入端口進行程序分配等恐。
- 不允許對由實例化模塊輸出端口驅(qū)動的網(wǎng)絡(luò)進行過程分配。
結(jié)論
總之备蚓,目前的凈數(shù)據(jù)和寄存器數(shù)據(jù)類型要求令人困惑和煩人课蔬。 執(zhí)行這些聲明規(guī)則的唯一明顯理由是不讓程序員對由非程序性分配源驅(qū)動的變量進行程序分配。
為了消除上述問題并簡化Verilog建模星著,作者提出了以下建議:
- 刪除為程序賦值聲明寄存器數(shù)據(jù)類型的要求购笆。
- 允許賦值程序塊內(nèi)的凈數(shù)據(jù)類型。
- 為了向后兼容性和用于簡短聲明虚循,允許可選的寄存器類型聲明
- 如果從過程塊內(nèi)部和外部對同一變量進行賦值同欠,則需要符合標準的仿真器標記語法錯誤。