# 2. Verilog 基礎語法
[參考網址:Verilog 中文詳細教程](https://www.runoob.com/w3cnote/verilog-tutorial.html)
###### tags: `verilog` `IC 設計`
---
[toc]
---
### :pushpin: 格式
Verilog 是區分大小寫的。 格式自由,可以在一行內編寫,也可跨多行編寫。 每個語句必須以分號為結束符。空白符(換行、製表、空格)都沒有實際的意義,在編譯階段可忽略。例如下面兩中編程方式都是等效的。
Example:
```verilog=
wire [1:0] results ;
assign results = (a == 1'b0) ? 2'b01 :
(b==1'b0) ? 2'b10 :
2'b11 ;
```
### :pushpin: 註釋
Verilog 中有 2 種註釋方式:
用 // 進行單行註釋:
```verilog=
reg [3:0] counter ; // A definition of counter register
```
用 /* 與 */ 進行跨行註釋:
```verilog=
wire [11:0] addr ;
/*
Next are notes with multiple lines.
Codes here cannot be compiled.
*/
assign addr = 12'b0 ;
```
### :pushpin: 標識符與關鍵字
- 標識符(identifier)可以是任意一組字母、數字、$ 符號和 _(下劃線)符號的合,但標識符的第一個字符必須是字母或者下劃線,**不能以數字或者美元符開始**。 另外,標識符是區分大小寫的。
- 關鍵字是 Verilog 中預留的用於定義語言結構的特殊標識符。 Verilog 中關鍵字全部為小寫。
# 2.2 數值種類
Verilog HDL 有下列四種基本的值來表示硬件電路中的電平邏輯:
0:邏輯 0 或 "假"
1:邏輯 1 或 "真"
x 或 X:未知
z 或 Z:高阻
- x 意味著信號數值的不確定,即在實際電路里,信號可能為 1,也可能為 0。
- z 意味著信號處於高阻狀態,常見於信號(input, reg)沒有驅動時的邏輯結果。例如一個 pad 的 input 呈現高阻狀態時,其邏輯值和上下拉的狀態有關係。上拉則邏輯值為 1,下拉則為 0 。
### :pushpin: 整數數值表示方法
數字宣告時,合法的基數格式有4種(數值可指明位寬,也可不指明位寬),包括:
- 十進制('d 或 'D)
- 十六進制('h 或 'H)
- 二進制('b 或 'B)
- 八進制('o 或 'O)
**指明位寬:**
```verilog=
4'b1011 // 4bit 数值
32'h3022_c0de // 32bit 的数值
```
其中,下劃線 _ 是為了增強代碼的可讀性。
**不指明位寬:**
一般直接寫數字時,**默認為十進製表示**,例如下面的 3 種寫法是等效的:
```verilog=
counter = 'd100 ; //一般会根据编译器自动分频位宽,常见的为32bit
counter = 100 ;
counter = 32'h64 ;
```
**負數表示**
通常在表示位寬的數字前面加一個減號來表示負數。例如:
```verilog=
-6'd15
-15
```
-15 在 5 位二進制中的形式為 5'b10001, 在 6 位二進制中的形式為 6'b11_0001。 **需要注意的是,減號放在基數和數字之間是非法的**,例如下面的表示方法是錯誤的:
```verilog=
4'd-2 //非法說明
```
**實數表示方法**
實數表示方法主要有兩種方式:
- 十進制:
```verilog
30.123
6.0
3.0
0.001
```
- 科學計數法:
```verilog
1.2e4 //大小為12000
1_0001e4 //大小為100010000
1E-3 //大小為0.001
```
**字符串表示方法**
字符串是由雙引號包起來的字符隊列。字符串不能多行書寫,即字符串中不能包含回車符。 Verilog 將字符串當做一系列的單字節 ASCII 字符隊列。例如,為存儲字符串 "www.runoob.com", 需要 14*8bit 的存儲單元。例如:
```verilog=
reg [0: 14*8-1] str ;
initial begin
str = "www.runoob.com";
end
```
# 2.3 Verilog 數據類型
Verilog 最常用的 2 種數據類型就是**線網(wire)與寄存器(reg)**,其餘類型可以理解為這兩種數據類型的擴展或輔助。
### :pushpin: 線網(wire)
wire 類型表示硬件單元之間的物理連線,由其連接的器件輸出端連續驅動。如果沒有驅動元件連接到 wire 型變量,缺省值一般為 "Z"。舉例如下:
```verilog=
wire interrupt ;
wire flag1, flag2 ;
wire gnd = 1'b0 ;
```
線網型還有其他數據類型,包括 wand,wor,wri,triand,trior,trireg 等。這些數據類型用的頻率不是很高,這裡不做介紹。
### :pushpin: 寄存器(reg)
寄存器(reg)用來表示存儲單元,它會保持數據原有的值,直到被改寫。宣告舉例如下:
```verilog=
reg clk_temp;
reg flag1, flag2 ;
```
例如在 always 塊中,寄存器可能被綜合成邊沿觸發器,在組合邏輯中可能被綜合成 wire 型變量。寄存器不需要驅動源,也不一定需要時鐘信號。在仿真時,寄存器的值可在任意時刻通過賦值操作進行改寫。例如:
```verilog=
reg rstn ;
initial begin
rstn = 1'b0 ;
#100 ;
rstn = 1'b1 ;
end
```
### :pushpin: 向量
當位寬大於 1 時,wire 或 reg 即可聲明為向量的形式。例如:
```verilog=
reg [3:0] counter ; //聲明4bit位寬的寄存器counter
wire [32-1:0] gpio_data; //聲明32bit位寬的線型變量gpio_data
wire [8:2] addr ; //聲明7bit位寬的線型變量addr,位寬範圍為8:2
reg [0:31] data ; //聲明32bit位寬的寄存器變量data, 最高有效位為0
```
對於上面的向量,我們可以指定某一位或若干相鄰位,作為其他邏輯使用。例如:
```verilog=
wire [9:0] data_low = data[0:9] ;
addr_temp[3:2] = addr[8:7] + 1'b1 ;
```
Verilog 支持可變的向量域選擇,例如:
```verilog=
reg [31:0] data1 ;
reg [7:0] byte1 [3:0];
integer j ;
always@* begin
for (j=0; j<=3;j=j+1) begin
byte1[j] = data1[(j+1)*8-1 : j*8];
//把data1[7:0]…data1[31:24]依次賦值給byte1[0][7:0]…byte[3][7:0]
end
end
```
**Verillog 還支持指定 bit 位後固定位寬的向量域選擇訪問。**
- [bit+: width] : 從起始 bit 位開始遞增,位寬為 width。
- [bit-: width] : 從起始 bit 位開始遞減,位寬為 width。
```verilog=
//下面 2 種賦值是等效的
A = data1[31-: 8] ;
A = data1[31:24] ;
//下面 2 種賦值是等效的
B = data1[0+ : 8] ;
B = data1[0:7] ;
```
**對信號重新進行組合成新的向量時,需要藉助大括號。例如:**
```verilog=
wire [31:0] temp1, temp2 ;
assign temp1 = {byte1[0][7:0], data1[31:8]}; //數據拼接
assign temp2 = {32{1'b0}}; //賦值32位的數值0
```
### :pushpin: **整數(integer)**
- 整數類型用關鍵字 integer 來聲明。聲明時不用指明位寬,位寬和編譯器有關,一般為32 bit。
- reg 型變量為無符號數,而 integer 型變量為有符號數。例如:
```verilog=
reg [31:0] data1 ;
reg [3:0] byte1 [7:0]; //數組變量,後續介紹
integer j ; //整型變量,用來輔助生成數字電路
always@* begin
for (j=0; j<=3;j=j+1) begin
byte1[j] = data1[(j+1)*8-1 : j*8];
//把data1[7:0]…data1[31:24]依次賦值給byte1[0][7:0]…byte[3][7:0]
end
end
```
此例中,integer 信號 j 作為輔助信號,將 data1 的數據依次賦值給數組 byte1。綜合後實際電路里並沒有 j 這個信號,j 只是輔助生成相應的硬件電路。
### :pushpin: **實數(real)**
實數用關鍵字 real 來聲明,可用十進製或科學計數法來表示。實數聲明不能帶有範圍,默認值為 0。如果將一個實數賦值給一個整數,則只有實數的整數部分會賦值給整數。例如:
```verilog=
real data1 ;
integer temp ;
initial begin
data1 = 2e3 ;
data1 = 3.75 ;
end
initial begin
temp = data1 ; //temp 值的大小為3
end
```
### :pushpin: **時間(time)**
Verilog 使用特殊的時間寄存器 time 型變量,對仿真時間進行保存。其寬度一般為 64 bit,通過調用系統函數 $time 獲取當前仿真時間。例如:
```verilog=
time current_time ;
initial begin
#100 ;
current_time = $time ; //current_time 的大小為100
end
```
### :pushpin: **數組**
在 Verilog 中允許聲明 reg, wire, integer, time, real 及其向量類型的數組。 數組維數沒有限制。線網數組也可以用於連接實例模塊的端口。數組中的每個元素都可以作為一個標量或者向量,以同樣的方式來使用,形如:**<數組名>[<下標>]**。對於多維數組來講,用戶需要說明其每一維的索引。例如:
```verilog
integer flag [7:0] ; //8個整數組成的數組
reg [3:0] counter [3:0] ; //由4個4bit計數器組成的數組
wire [7:0] addr_bus [3:0] ; //由4個8bit wire型變量組成的數組
wire data_bit[7:0][5:0] ; //聲明1bit wire型變量的二維數組
reg [31:0] data_4d[11:0][3:0][3:0][255:0] ; //聲明4維的32bit數據變量數組
```
下面顯示了對數組元素的賦值操作:
```verilog=
flag [1] = 32'd0 ; //將flag數組中第二個元素賦值為32bit的0值
counter[3] = 4'hF ; //將數組counter中第4個元素的值賦值為4bit 十六進制數F,等效於counter[3][3:0] = 4'hF,即可省略寬度;
assign addr_bus[0] = 8'b0 ; //將數組addr_bus中第一個元素的值賦值為0
assign data_bit[0][1] = 1'b1; //將數組data_bit的第1行第2列的元素賦值為1,這裡不能省略第二個訪問標號,即 assign data_bit[0] = 1'b1; 是非法的。
data_4d[0][0][0][0][15:0] = 15'd3 ; //將數組data_4d中標號為[0][0][0][0]的寄存器單元的15~0bit賦值為3
```
雖然數組與向量的訪問方式在一定程度上類似,但不要將向量和數組混淆。向量是一個單獨的元件,位寬為 n;數組由多個元件組成,其中每個元件的位寬為 n 或 1。它們在結構的定義上就有所區別。
### :pushpin: **存儲器**
存儲器變量就是一種寄存器數組,可用來描述 RAM 或 ROM 的行為。例如:
```verilog=
reg membit[0:255] ; //256bit的1bit存儲器
reg [7:0] mem[0:1023] ; //1Kbyte存儲器,位寬8bit
mem[511] = 8'b0 ; //令第512個8bit的存儲單元值為0
```
### :pushpin: **參數**
參數用來表示常量,用關鍵字 parameter 聲明,只能賦值一次。例如:
```verilog=
parameter data_width = 10'd32 ;
parameter i=1, j=2, k=3 ;
parameter mem_size = data_width * 10 ;
```
但是,通過實例化的方式,可以更改參數在模塊中的值。此部分以後會介紹。 局部參數用 localparam 來聲明,其作用和用法與 parameter 相同,區別在於它的值不能被改變。所以當參數只在本模塊中調用時,可用 localparam 來說明。
### :pushpin: **字符串**
字符串保存在 reg 類型的變量中,每個字符佔用一個字節(8bit)。因此寄存器變量的寬度應該足夠大,以保證不會溢出。 字符串不能多行書寫,即字符串中不能包含回車符。如果寄存器變量的寬度大於字符串的大小,則使用 0 來填充左邊的空餘位;如果寄存器變量的寬度小於字符串大小,則會截去字符串左邊多餘的數據。例如,為存儲字符串 "run.runoob.com", 需要 14*8bit 的存儲單元:
```verilog=
reg [0: 14*8-1] str ;
initial begin
str = "run.runoob.com";
end
```
有一些特殊字符在顯示字符串中有特殊意義,例如換行符,製表符等。如果需要在字符串中顯示這些特殊的字符,則需要在前面加前綴轉義字符 \ 。例如下表所示:

其實,在 SystemVerilog(主要用於 Verilog 仿真的編程語言)語言中,已經可以直接用關鍵字 string 來表示字符串變量類型,這為 Verilog 的仿真帶來了極大的便利。
# 2.4 Verilog 表達式
### :pushpin: 表達式
表達式由操作符和操作數構成,其目的是根據操作符的意義得到一個計算結果。表達式可以在出現數值的任何地方使用。例如:
```verilog=
a^b ; //a與b進行異或操作
address[9:0] + 10'b1 ; //地址累加
flag1 && flag2 ; //邏輯與操作
```
### :pushpin: 操作數
操作數可以是任意的數據類型,只是某些特定的語法結構要求使用特定類型的操作數。 操作數可以為常數,整數,實數,線網,寄存器,時間,位選,域選,存儲器及函數調用等。
```verilog=
module test;
//實數
real a, b, c;
c = a + b ;
//寄存器
reg [3:0] cprmu_1, cprmu_2 ;
always @(posedge clk) begin
cprmu_2 = cprmu_1 ^ cprmu_2 ;
end
//函數
reg flag1 ;
flag = calculate_result(A, B);
//非法操作數
reg [3:0] res;
wire [3:0] temp;
always@ (*)begin
res = cprmu_2 – cprmu_1 ;
//temp = cprmu_2 – cprmu_1 ; //不合法,always塊裡賦值對像不能是wire型
end
endmodule
```
### :pushpin: 操作符
Verilog 中提供了大約 9 種操作符,分別是算術、關係、等價、邏輯、按位、歸約、移位、拼接、條件操作符。 大部分操作符與 C 語言中類似。同類型操作符之間,除條件操作符從右往左關聯,其餘操作符都是自左向右關聯。圓括號內表達式優先執行。例如下面每組的 2 種寫法都是等價的。
```verilog=
//自右向左關聯,兩種寫法等價
A+B-C ;
(A+B)-C ;
//自右向左關聯,兩種寫法等價,結果為 B、D 或 F
A ? B : C ? D : F ;
A ? B : (C ? D : F) ;
//自右向左關聯,兩種寫法不等價
(A ? B : C) ? D : F ; //結果 D 或 F
A ? B : C ? D : F ; //結果為 B、D 或 F
```
不同操作符之間,優先級是不同的。下表列出了操作符優先級從高至低的排列順序。當沒有圓括號時,Verilog 會根據操作符優先級對錶達式進行計算。為了避免由操作符優先級導致的計算混亂,在不確定優先級時,建議用圓括號將表達式區分開來。

### :pushpin: 算術操作符
算術操作符包括單目操作符和雙目操作符。 雙目操作符對 2 個操作數進行算術運算,包括乘(*)、除(/)、加(+)、減(-)、求冪(**)、取模(%)。
```verilog=
reg [3:0] a, b;
reg [4:0] c ;
a = 4'b0010 ;
b = 4'b1001 ;
c = a+b; //結果為c=b'b1011
c = a/b; //結果為c=4,取整
```
如果操作數某一位為 X,則計算結果也會全部出現 X。例如:
```verilog=
b = 4'b100x ;
c = a+b ; //结果为c=4'bxxxx
```
對變量進行聲明時,要根據變量的操作符對變量的位寬進行合理聲明,不要讓結果溢出。上述例子中,相加的 2 個變量位寬為 4bit,那麼結果寄存器變量位寬最少為 5bit。否則,高位將被截斷,導致結果高位丟失。無符號數乘法時,結果變量位寬應該為 2 個操作數位寬之和。
```verilog=
reg [3:0] mula ;
reg [1:0] mulb;
reg [5:0] res ;
mula = 4'he ;
mulb = 2'h3 ;
res = mula * mulb ; //結果為res=6'h2a, 數據結果沒有丟失位數
```
+ 和 - 也可以作為單目操作符來使用,表示操作數的正負性。此類操作符優先級最高。
```verilog=
-4 //表示負4
+3 //表示正3
```
負數表示時,可以直接在十進制數字前面增加一個減號 -,也可以指定位寬。因為負數使用二進制補碼來表示,不指定位寬來表示負數,編譯器在轉換時,會自動分配位寬,從而導致意想不到的結果。例如:
```verilog=
mula = -4'd4 ;
mulb = 2 ;
res = mula * mulb ; //計算結果為res=-6'd8, 即res=6'h38,正常
res = mula * (-'d4) ; //(4的32次冪-4) * 2, 結果異常
```
### :pushpin: 關係操作符
關係操作符有大於(>),小於(<),大於等於(>=),小於等於(<=)。 關係操作符的正常結果有 2 種,真(1)或假(0)。 如果操作數中有一位為 x 或 z,則關係表達式的結果為 x。
```verilog=
A = 4 ;
B = 3 ;
X = 3'b1xx ;
A > B //为真
A <= B //为假
A >= Z //为X,不确定
```
### :pushpin: 等價操作符
等價操作符包括邏輯相等(==),邏輯不等(!=),全等(===),非全等(!==)。 等價操作符的正常結果有 2 種:為真(1)或假(0)。 邏輯相等/不等操作符不能比較 x 或 z,當操作數包含一個 x 或 z,則結果為不確定值。 全等比較時,如果按位比較有相同的 x 或 z,返回結果也可以為 1,即全等比較可比較 x 或 z。所以,全等比較的結果一定不包含 x。舉例如下:
```verilog=
A = 4 ;
B = 8'h04 ;
C = 4'bxxxx ;
D = 4'hx ;
A == B //为真
A == (B + 1) //为假
A == C //为X,不确定
A === C //为假,返回值为0
C === D //为真,返回值为1
```
### :pushpin: 邏輯操作符
邏輯操作符主要有 3 個:&&(邏輯與), ||(邏輯或),!(邏輯非)。 邏輯操作符的計算結果是一個 1bit 的值,0 表示假,1 表示真,x 表示不確定。 如果一個操作數不為 0,它等價於邏輯 1;如果一個操作數等於 0,它等價於邏輯 0。如果它任意一位為 x 或 z,它等價於 x。 如果任意一個操作數包含 x,邏輯操作符運算結果不一定為 x。 邏輯操作符的操作數可以為變量,也可以為表達式。例如:
```verilog=
A = 3;
B = 0;
C = 2'b1x ;
A && B // 为假
A || B // 为真
! A // 为假
! B // 为真
A && C // 为X,不确定
A || C // 为真,因为A为真
(A==2) && (! B) //为真,此时第一个操作数为表达式
```
### :pushpin: 按位操作符
按位操作符包括:取反(~),與(&),或(|),異或(^),同或(~^)。 按位操作符對 2 個操作數的每 1bit 數據進行按位操作。 如果 2 個操作數位寬不相等,則用 0 向左擴展補充較短的操作數。 取反操作符只有一個操作數,它對操作數的每 1bit 數據進行取反操作。 下圖給出了按位操作符的邏輯規則。

```verilog=
A = 4'b0101 ;
B = 4'b1001 ;
C = 4'bx010 ;
~A //4'b1010
A & B //4'b0001
A | B //4'b1101
A^B //4'b1100
A ~^ B //4'b0011
B | C //4'b1011
B&C //4'bx000
```
### :pushpin: 歸約操作符
歸約操作符包括:歸約與(&),歸約與非(~&),歸約或(|),歸約或非(~|),歸約異或(^),歸約同或(~^)。 歸約操作符只有一個操作數,它對這個向量操作數逐位進行操作,最終產生一個 1bit 結果。 邏輯操作符、按位操作符和歸約操作符都使用相同的符號表示,因此有時候容易混淆。區分這些操作符的關鍵是分清操作數的數目,和計算結果的規則。
```verilog=
A = 4'b1010 ;
&A ; //結果為 1 & 0 & 1 & 0 = 1'b0,可用來判斷變量A是否全1
~|A ; //結果為 ~(1 | 0 | 1 | 0) = 1'b0, 可用來判斷變量A是否為全0
^A ; //结果为 1 ^ 0 ^ 1 ^ 0 = 1'b0
```
### :pushpin: 移位操作符
移位操作符包括左移(<<),右移(>>),算術左移(<<<),算術右移(>>>)。 移位操作符是雙目操作符,兩個操作數分別表示要進行移位的向量信號(操作符左側)與移動的位數(操作符右側)。 算術左移和邏輯左移時,右邊低位會補 0。 邏輯右移時,左邊高位會補 0;而算術右移時,左邊高位會補充符號位,以保證數據縮小後值的正確性。
```verilog=
A = 4'b1100 ;
B = 4'b0010 ;
A = A >> 2 ; //结果为 4'b0011
A = A << 1; //结果为 4'b1000
A = A <<< 1 ; //结果为 4'b1000
C = B + (A>>>2); //结果为 2 + (-4/4) = 1, 4'b0001
```
### :pushpin: 拼接操作符
拼接操作符用大括號 {,} 來表示,用於將多個操作數(向量)拼接成新的操作數(向量),信號間用逗號隔開。 拼接符操作數必須指定位寬,常數的話也需要指定位寬。例如:
```verilog=
A = 4'b1010 ;
B = 1'b1 ;
Y1 = {B, A[3:2], A[0], 4'h3 }; //结果为Y1='b1100_0011
Y2 = {4{B}, 3'd4}; //结果为 Y2=7'b111_1100
Y3 = {32{1'b0}}; //结果为 Y3=32h0,常用作寄存器初始化时匹配位宽的赋初值
```
### :pushpin: 條件操作符
條件表達式有 3 個操作符,結構描述如下:
```verilog=
condition_expression ? true_expression : false_expression
```
- 如果 condition_expression 為真(邏輯值為 1),則運算結果為 true_expression;
- 如果 condition_expression 為假(邏輯值為 0),則計算結果為 false_expression。
```verilog=
assign hsel = (addr[9:8] == 2'b0) ? hsel_p1 : hsel_p2 ;
//當信號 addr 高 2bit 為 0 時,hsel 賦值為 hsel_p1; 否則,將 hsel_p2 賦值給 hsel。
```
其實,條件表達式類似於 2 路(或多路)選擇器,其描述方式完全可以用 if-else 語句代替。 當然條件操作符也能進行嵌套,完成一個多次選擇的邏輯。例如:
```verilog=
assign hsel = (addr[9:8] == 2'b00) ? hsel_p1 :
(addr[9:8] == 2'b01) ? hsel_p2 :
(addr[9:8] == 2'b10) ? hsel_p3 :
(addr[9:8] == 2'b11) ? hsel_p4 ;
```
# 2.5 Verilog 編譯指令
以反引號 開始的某些標識符是 Verilog 系統編譯指令。 編譯指令為 Verilog 代碼的撰寫、編譯、調試等提供了極大的便利。 下面介紹下完整的 8 種編譯指令,其中前 4 種使用頻率較高。
`define, `undef
在編譯階段,`define 用於文本替換,類似於 C 語言中的 #define。 一旦 `define 指令被編譯,其在整個編譯過程中都會有效。例如,在一個文件中定義:
```verilog=
`define DATA_DW 32
```
則在另一個文件中也可以直接使用 DATA_DW。
```verilog=
`define S $stop;
//用`S來代替系統函數$stop; (包括分號)
`define WORD_DEF reg [31:0]
//可以用`WORD_DEF來聲明32bit寄存器變量
```
`undef 用來取消之前的宏定義,例如:
```verilog=
`define DATA_DW 32
……
reg [DATA_DW-1:0] data_in ;
……
`undef DATA_DW
`ifdef, `ifndef, `elsif, `else, `endif
```
這些屬於條件編譯指令。例如下面的例子中,如果定義了 MCU51,則使用第一種參數說明;如果沒有定義 MCU、定義了 WINDOW,則使用第二種參數說明;如果 2 個都沒有定義,則使用第三種參數說明。
```verilog=
`ifdef MCU51
parameter DATA_DW = 8 ;
`elsif WINDOW
parameter DATA_DW = 64 ;
`else
parameter DATA_DW = 32 ;
`endif
```
`elsif, `else 編譯指令對於 `ifdef 指令是可選的,即可以只有 `ifdef 和 `endif 組成一次條件編譯指令塊。 當然,也可用 `ifndef 來設置條件編譯,表示如果沒有相關的宏定義,則執行相關語句。 下面例子中,如果定義了 WINDOW,則使用第二種參數說明。如果沒有定義 WINDOW,則使用第一種參數說明。
```verilog=
`ifndef WINDOW
parameter DATA_DW = 32 ;
`else
parameter DATA_DW = 64 ;
`endif
```
### :pushpin: `include
使用 `include 可以在編譯時將一個 Verilog 文件內嵌到另一個 Verilog 文件中,作用類似於 C 語言中的 #include 結構。該指令通常用於將全局或公用的頭文件包含在設計文件裡。 文件路徑既可以使用相對路徑,也可以使用絕對路徑。
```verilog=
`include "../../param.v"
`include "header.v"
```
### :pushpin: `timescale
在 Verilog 模型中,時延有具體的單位時間表述,並用 `timescale 編譯指令將時間單位與實際時間相關聯。 該指令用於定義時延、仿真的單位和精度,格式為:
```verilog=
`timescale time_unit / time_precision
```
time_unit 表示時間單位,time_precision 表示時間精度,它們均是由數字以及單位 s(秒),ms(毫秒),us(微妙),ns(納秒),ps(皮秒)和 fs(飛秒)組成。時間精度可以和時間單位一樣,但是時間精度大小不能超過時間單位大小,例如下面例子中,輸出端 Z 會延遲 5.21ns 輸出 A&B 的結果。
```verilog=
`timescale 1ns/100ps //时间单位为1ns,精度为100ps,合法
//`timescale 100ps/1ns //不合法
module AndFunc(Z, A, B);
output Z;
input A, B ;
assign #5.207 Z = A & B
endmodule
```
- 在編譯過程中,`timescale 指令會影響後面所有模塊中的時延值,直至遇到另一個 `timescale 指令或 `resetall 指令。 由於在 Verilog 中沒有默認的 `timescale,如果沒有指定 `timescale,Verilog 模塊就有會繼承前面編譯模塊的 `timescale 參數。有可能導致設計出錯。
- 如果一個設計中的多個模塊都帶有 `timescale 時,模擬器總是定位在所有模塊的最小時延精度上,並且所有時延都相應地換算為最小時延精度,時延單位並不受影響。例如:
```verilog=
`timescale 10ns/1ns
module test;
reg A, B ;
wire OUTZ ;
initial begin
A = 1;
B = 0;
# 1.28 B = 1;
# 3.1 A = 0;
end
AndFunc u_and(OUTZ, A, B) ;
endmodule
```
- 在模塊 AndFunc 中,5.207 對應 5.21ns。
- 在模塊 test 中,1.28 對應 13ns,3.1 對應 31ns。
- 但是,當仿真 test 時,由於 AndFunc 中的最小精度為 100ps,因此 test 中的時延精度將進行重新調整。 13ns 將對應 130*100ps,31ns 將對應 310*100ps。
- 仿真時,時延精度也會使用 100ps。仿真時間單位大小沒有影響。
- 如果有並行子模塊,子模塊間的 `timescale 並不會相互影響。 例如在模塊 test 中再例化一個子模塊 OrFunc。
- 仿真 test 時,OrFunc 中的 #5.207 延時依然對應 52ns。
```verilog=
//子模块:
`timescale 10ns/1ns //时间单位为1ns,精度为100ps,合法
module OrFunc(Z, A, B);
output Z;
input A, B ;
assign #5.207 Z = A | B
endmodule
//顶层模块:
`timescale 10ns/1ns
module test;
reg A, B ;
wire OUTZ ;
wire OUTX ;
initial begin
A = 1;
B = 0;
# 1.28 B = 1;
# 3.1 A = 0;
end
AndFunc u_and(OUTZ, A, B) ;
OrFunc u_and(OUTX, A, B) ;
endmodule
```
此例中,仿真 test 時,OrFunc 中的 #5.207 延時依然對應 52ns。
`timescale 的時間精度設置是會影響仿真時間的。時間精度越小,仿真時佔用內存越多,實際使用的仿真時間就越長。所以如果沒有必要,應盡量將時間精度設置的大一些。
### :pushpin: `default_nettype
該指令用於為隱式的線網變量指定為線網類型,即將沒有被聲明的連線定義為線網類型。
```verilog=
`default_nettype wand
```
該實例定義的缺省的線網為線與類型。因此,如果在此指令後面的任何模塊中的連線沒有說明,那麼該線網被假定為線與類型。
```verilog=
`default_nettype none
```
該實例定義後,將不再自動產生 wire 型變量。 例如下面第一種寫法編譯時不會報 Error,第二種寫法編譯將不會通過。
```verilog=
//Z1 無定義就使用,系統默認Z1為wire型變量,有 Warning 無 Error
module test_and(
input A,
input B,
output Z);
assign Z1 = A & B ;
endmodule
```
```verilog=
//Z1 無定義就使用,由於編譯指令的存在,系統會報Error,從而檢查出書寫錯誤
`default_nettype none
module test_and(
input A,
input B,
output Z);
assign Z1 = A & B ;
endmodule
```
### :pushpin: `resetall
該編譯器指令將所有的編譯指令重新設置為缺省值。
`resetall 可以使得缺省連線類型為線網類型。
當 `resetall 加到模塊最後時,可以將當前的 `timescale 取消防止進一步傳遞,只保證當前的 `timescale 在局部有效,避免 `timescale 的錯誤繼承。
### :pushpin: `celldefine, `endcelldefine
這兩個程序指令用於將模塊標記為單元模塊,他們包含模塊的定義。例如一些與、或、非門,一些 PLL 單元,PAD 模型,以及一些 Analog IP 等。
```verilog=
`celldefine
module (
input clk,
input rst,
output clk_pll,
output flag);
……
endmodule
`endcelldefine
```
### :pushpin: `unconnected_drive, `nounconnected_drive
在模塊實例化中,出現在這兩個編譯指令間的任何未連接的輸入端口,為正偏電路狀態或者為反偏電路狀態。
```verilog=
`unconnected_drive pull1
. . .
/ *在這兩個程序指令間的所有未連接的輸入端口為正偏電路狀態(連接到高電平) * /
`nounconnected_drive
```
```verilog=
`unconnected_drive pull0
. . .
/ *在這兩個程序指令間的所有未連接的輸入端口為反偏電路狀態(連接到低電平) * /
`nounconnected_drive
```