Verilog常用語法

Verilog常用語法
該內容均可以在夏宇聞老師的《Verilog數字系統設計教程》第四版中找到,在此處只是便於回顧而已,沒有書的可以參考,FPGA設計常用的都已經標出來了,有部分常用,但根C語言差不多的就沒有標出來,有時間的可以仔細看一遍。

一、模塊的結構、數據類型、變量和基本運算符號
1 模塊的結構
1.1 端口定義
module 模塊名(口1,口2,口3,…);

兩種模塊例化方式:
方法一:模塊名(連接端口1信號名,連接端口2信號名,連接端口3信號名,…);
方法二:模塊名(.端口名1(連接信號1名),.端口名2(連接信號2名),…);
例化時還可以對模塊中的參數型(parameter)變量進行重新賦值;
例如:

module min(input clk,input rst_n,input a1,output a2);
parameter a = 1000;
parameter b = 2000;

endmodule

對以上模塊進行例化並修改a,b值;
min#(.a(10),b(10)) uut_min(clk(clk),rst_n(rst_n),a1(a1),a2(a2));
在模塊uut_min中a與b的值就都爲10了,所以參數化設計在例化時能很方便的對參數進行修改,而不需要修改原模塊內容;
方法一必須嚴格按照模塊定義的端口順序來連接,方法二不必嚴格按照端口順序對應,一般使用第二種例化方式,減小錯誤;

1.2 模塊內容
1.2.1 I/O說明的格式
輸入口說明:
input[信號位寬-1:0] 端口名1;
input[信號位寬-1:0] 端口名2;
輸出口說明:
output[信號位寬-1:0] 端口名1;
output [信號位寬-1:0] 端口名2;
輸入/輸出口:
inout[信號位寬-1:0] 端口名1;
inout[信號位寬-1:0] 端口名2;

1.2.2 內部信號說明
wire和reg類型變量的聲明
reg[width-1:0] R變量1,R變量2,…;
wire[width-1:0] W變量1,R變量2,…;
原則說明:由always和initial塊產生的信號,定義爲reg型,其餘均定義爲wire型;

1.2.3 功能定義
1.用assign連續賦值語句,常用來描述組合邏輯電路;如assign = a & b
2.用實例元件;如與門:and #2 u1 (q,a,b)
3.用always塊,既可用來描述組合邏輯電路也可用來描述時序邏輯電路,常用來描述時序邏輯電路;在“always”模塊內被賦值的每一個信號都必須定義成reg型,進行組合邏輯描述時,敏感列表可以直接用@(*)表示,防止敏感事件過多而寫掉;

在Verilog模塊中所有過程塊、連續賦值語句、實例引用都是並行的,只有連續賦值語句(用關鍵詞assign引用的語句)和實例引用語句可以獨立於過程塊而存在於模塊的功能定義部分;在always模塊內,邏輯是按照指定的順序執行的,always塊內的語句稱爲“順序語句”,所以always塊也稱爲“過程塊”;

2 數據類型及其常量和變量
Verilog HDL中總共有19鍾數據類型:large型,medium型,scalared型,time型,small型,tri型,trio型,tril型,triand型,trior型,trireg型,vectored型,wand型,wor型,reg型,wire型,integer型,parameter型;
常用的數據類型有:reg型、wire型、integer型、parameter型

2.1 常量
2.1.1 整數的表示形式
整數的表示形式:
<位寬>’<進制><數字>,這是一種全面的描述方式;
‘<進制>數字>採用默認位寬,由機器系統決定,至少32位;
<數字>採用默認進制(十進制);
各種進制的表示方法:
二進制:b or B;
八進制:o or O;
十進制:d or D;
十六進制:h or H;
注:位寬指的是轉換成二進制數以後的位數。

2.1.2 x和z值
x表示不定值,z代表高阻態。一個x可以用來定義十六進制數的4位二進制數的狀態,八進制數的3位,二進制數的一位;z的表示方法同x類似,此外z還可寫作“?”,在case表達式中建議這種寫法,以提高程序的可讀性。

2.1.3 負數
一個數可以被定義成負數,只須在位寬表達式前加一個減號,減號必須寫在數字定義表達式的最前面。
下劃線:下劃線可以用來分割數的表達式以提高程序的可讀性。

2.1.4 符號常量
用parameter來定義一個標識符代表一個常量,稱爲符號常量;
說明格式如下:
parameter 參數名1=表達式,參數名2=表達式,……參數名n=表達式;

2.2 變量
2.2.1 wire型(實際電路中的導線)
網絡型變量wire結構實體之間的物理連接,不能存儲值,而且必須受到驅動器的驅動,沒有驅動時該變量就是高阻的,即其值爲z。wire型數據常用來表示以assign關鍵字指定的組合邏輯信號。Verilog程序模塊中輸入、輸出信號類型默認定義爲wire型。
wire型信號的定義格式如下:
wire [n-1:0]數據名1,數據名2,……數據名i;
wire[n:1]數據名1,數據名2,……數據名i;

2.2.2 reg型
寄存器是數據存儲單元的抽象,寄存器數據類型的關鍵字是reg。reg數據類型的默認初始值爲不定值x。reg型數據常用來表示“always”模塊內的指定信號,常代表觸發器,“always”模塊內每一個被賦值的信號必須被定義成reg型。
reg型數據可以賦正值也可以賦負值,但當一個reg型數據是一個表達式中的操作數時,它的值被當做是無符號數,即正值。

2.2.3 memory型
verilog HDL通過對reg型變量建立數組來對存儲器建模,格式如下:
reg[n-1:0]存儲器名[m-1:0];
reg[n-1:0]定義了每一個存儲單元的大小,該存儲單元是一個n位的寄存器,[m-1:0] 則定義了該存儲器有多少個這樣的存儲單元。
注:在同一個數據類型聲明語句裏,可以同時定義存儲類型數據和reg型數據。
注:對存儲器進行地址索引的表達式必須是常數表達式

3 運算符及表達式
Verilog HDL語言運算符按其功能可以分爲以下幾類:
算數運算符(+,-,*,/,%);
賦值運算符(=,<=);
等式運算符(!=,==)
關係運算符(<,>,=>,<=);
邏輯運算符(&&,||,!);
條件運算符(?:);
位運算符(,|,^,&,^);
拼接運算符({ })。
除法運算結果略去小數,只取整數部分;取模運算(%,也稱求餘運算符)時,結果的符號位採用模運算式裏的第一個操作數的符號位。
注:在進行算術運算操作時,如果有一個操作數有不確定的值x,則整個結果也爲不定值x。
位運算:不同長度的數據進行位運算,系統自動按右端對齊,位數少的操作數會在相應的高位用0填滿。
注:算數運算符,關係運算符,邏輯運算符,條件運算符的規則都與C語言中相同。只是位運算符(除按位非運算符外,其餘既可爲單目也可爲雙目運算符)和拼接運算符與C語言有區別;

3.3 等式運算符
(1)==(等於);

(2)!=(不等於);

(3)===(等於);

(4)!==(不等於)。

注意:求反號、雙等號、三個等號之間不能有空格
(1)和(2)又稱邏輯等式運算符,當操作數中的某些位是不定值x或高阻值z時,結果爲不定值x;而(3)和(4)對不定值和高阻值也進行比較,另兩個操作數必須完全一樣,結果才爲1,(3)和“!==”常用與case表達式的判別,所以又稱爲“case等式表達式”。

3.2 縮減運算符(位運算符的單目形式)
&是單目運算符,如:
reg [3:0] B ;
reg C;
C = &B;
相當於:
C=((B[0]&B[1])&B[2])&B[3];
具體運算過程是這樣的:第一步先將操作數的第1位與第2位進與、或、非運算;第二步將運算結果與第三位進行與、或、非運算,直至最後一位。

3.3 拼接運算符
{信號1的某幾位,信號2的某幾位,…,…,信號n的某幾位},中間用逗號隔開,最後用大括號括起來表示一個信號整體。
位拼接表達式中不允許出現沒有指明位數的信號;
位拼接可以用重複法來簡化表達式:{4{w}}等同於{w,w,w,w};
位拼接還可以用嵌套的方式來表達:{b,{3{a,b}}}等同於{b,a,b,a,b,a,b}。

二、賦值語句和結構說明語句
1 賦值語句
爲解釋問題方便,下面定義兩個縮寫字:
RHS——賦值等號右邊的表達式或變量可分別縮寫爲RHS表達式或RHS變量;
LHS——賦值等號左邊的表達式或變量可分別縮寫爲LHS表達式或LHS變量;

1.1 非阻塞(Non_Blocking)賦值("<=")
(1)爲什麼稱這種賦值爲非阻塞賦值呢?這是因爲在賦值操作開始時計算非阻塞賦值符的RHS表達式,賦值操作結束時刻才更新LHS。在計算非阻塞賦值的RHS表達式和更新LHS期間,其它Verilog語句,包括其它的Verilog非阻塞賦值語句都能同時計算RHS表達式和更新LHS。非阻塞賦值允許其它的Verilog語句同時進行操作。
(2)非阻塞賦值的操作過程可以看作兩個步驟:
1.在賦值開始時刻,計算非阻塞賦值RHS表達式;
2.在賦值結束時刻,更新非阻塞賦值LHS表達式。
(3)非阻塞賦值操作只能用於對寄存器類型變量進行賦值,因此只能用在“initial”塊和“always”塊等過程塊中,而非阻塞賦值不允許用於連續賦值。

1.2 阻塞賦值("=")
(1)爲什麼稱這種賦值爲阻塞賦值呢?這是因爲在賦值時刻先計算等號右手方向(RHS)部分的值,這時賦值語句不允許任何別的Verilog語句的干擾,直到現行的賦值完成時刻,即把RHS賦值給LHS的時刻,它才允許別的賦值語句的執行。 一般可綜合的阻塞賦值操作在RHS不能設定有延遲(即使是零延遲也不允許)。從理論上講,他與後面的賦值語句只有概念上的先後,而無實質上的延遲。若在RHS上加延遲,則在延遲期間會阻止賦值語句的執行,延遲後才執行賦值,這種賦值語句是不可綜合的,在需要綜合的模塊設計中不可使用這種風格的代碼。
(2)阻塞賦值的執行可以認爲是隻有一個步驟的操作,即計算RHS並更新LHS,此時不能允許有來自任何其它Verilog語句干擾。阻塞的概念:指在同一個always塊中,其後面的賦值語句從概念上(即使不設定延遲)是在前一句賦值語句結束後再開始賦值的。
(3)如果在一個過程塊中阻塞賦值的RHS變量正好是另一個過程塊中阻塞賦值的LSH變量,這兩個過程塊又用同一時鐘沿觸發,這時阻塞賦值操作會出現問題,即如果阻塞賦值的順序安排不好,就會出現競爭。若這兩個阻塞賦值操作用同一個時鐘沿觸發,則執行的順序是無法確定的。

1.3 Verilog模塊編程要點
(1)時序電路建模時,用非阻塞賦值。
(2)鎖存器電路建模時,用非阻塞賦值。
(3)用always塊建立組合邏輯模型時,用阻塞賦值。
(4)在同一個always塊中建立時序和組合邏輯電路時,用非阻塞賦值。
(5)在同一個always塊中不要既用非阻塞賦值有用阻塞賦值。
(6)不要在一個以上的always塊中爲同一個變量賦值。
(7)用$strobe系統函數來顯示用非阻塞賦值的變量值。
(8)在賦值時不要使用 #0 延時。
(9)如果always塊中只有一條賦值語句,使用阻塞賦值或非阻塞賦值語句都可以。

2 塊語句
2.1 順序塊(begin_end)與C語言的{}類似
1.塊內語句是按順序執行的,即只有上面一條語句執行完後下面的語句才能執行。
2.每條語句的延遲時間是相對於前一條語句的仿真時間而言的。
3.直到下一條語句執行完,程序流程控制才跳出該語句塊。
begin :塊名 語句1;語句2;語句n; end
begin 後可加塊名,塊內聲明語句可以是參數聲明語句,reg型變量聲明語句,integer型聲明語句和real型變量聲明語句。

2.2 並行塊(fork_join)
1.塊內語句是同時執行的,即程序流程一進入到該並行塊,塊內語句則開始同時並行地執行。
2.塊內每條語句的延遲時間是相對於程序流程控制進入到塊內的仿真時間。
3.延遲時間是用來給賦值語句提供執行時序的。
4.當按時間順序排序在最後的語句執行完後或一個disable語句執行時,程序流程控制跳出該程序塊。
格式如下:

fork:塊名

語句1;

語句2;

語句n;
1
2
3
4
5
end
fork後可加塊名,語句塊內的說明語句可以是參數說明語句、reg型變量聲明語句、integer型變量聲明語句、real型變量聲明語句、time型變量聲明語句和事件(event)說明語句。如果兩條語句在同一時刻對同一個變量產生影響,那麼將會引起隱含的競爭。
注意:順序塊與並行塊之間的根本區別在於:當控制轉移到塊語句的時刻,並行塊中所有的語句同時開始執行,語句之間的先後順序是無關緊要的。

2.3 塊語句的特點
2.3.1 嵌套塊
塊可以嵌套使用,順序快和嵌套塊能夠混合在一起使用;

2.3.2 命名塊(塊可以具有名字)
特點:
(1)命名塊中可以聲明局部變量;
(2)命名塊是設計層次的一部分,命名塊中聲明的變量可以通過層次引用進行訪問;
(3)命名塊可以禁用(用關鍵字disable);

2.3.3 命名塊的禁用
disable 可以用來從循環中退出,處理錯誤條件以及根據控制信號來控制某些代碼段是否被執行,與C語言的break類似,但兩者區別在於break只能退出當前所在循環,而使用disable則可以禁用設計中的任意一個命令塊;
格式:disable 塊名;

3 塊名
1、可以在塊內定義局部變量,即只在塊內使用的變量;
2、可以通過塊名被其他塊調用,如disable語句。
3、在Verilog語言中,所有的變量都是靜態的,即所有的變量都只有一個唯一的存儲地址,因此進入或跳出塊並不影響存儲在變量的值。

4 起始時間和結束時間
對於順序塊,起始時間就是第一條語句開始被執行的時間,結束時間就是最後一條語句執行完的時間;對於並行快,起始時間對於塊內所有的語句都是相同的,即程序流程控制進入該塊的時間,其結束時間是按時間排序在最後的語句執行結束的時間。
當把一個塊嵌入到另一個塊時,塊的起始時間和結束時間是很重要的。至於跟在塊後面的語句只有在該塊的結束時間到了纔開始執行。也就是說,只有該塊完全執行完後,跟在後面的語句纔可以執行。

三、條件語句,循環語句,塊語句和生成語句
1 條件語句
1.1 if_else語句
三種形式:

if(表達式)語句;
if(表達式) 語句1;
else 語句2;
if(表達式1) 語句1;
else if(表達式2)語句2;
else if(表達式3)語句3;

else if(表達式n)語句n;
else 語句n+1;
注:
(1)條件語句必須在過程塊中使用,所謂過程塊是指initial和always語句所引導的執行語句集合。表達式爲0或者x視爲假。if語句支持嵌套。
(2)if語句的語法與C語言中的語法基本一致;
1.2 case語句
三種形式:
(1)case(表達式) <case分支項> endcase
(2)casex(表達式) <case分支項> endcase
(3)casez(表達式) <case分支項> endcase
分支項的一般格式如下:
分支表達式: 語句;
default: 語句;
注:
1.default可有可無,但一般加上,防止生成鎖存器以及死鎖現象;
2.每個分支項必須不同;
3.所有表達式位寬必須相同,常犯錯誤:用’bx,’bz代替n’bx,n’bz;
4.casez用來處理不考慮高阻值z的比較過程;
5.casex用來處理將高阻值z和不定值x都視爲不關心的過程;
6.分支項可以爲begin…end塊;
7.case語句支持嵌套;

注意:爲了避免Verilog代碼綜合後生成鎖存器,如果用到if語句,最好寫上else項,如果用到case語句最好寫上default項。並且要使得這個else以及default是有效的,不然還是會生成鎖存器;無效的組合邏輯else例如else a = a;

2 循環語句
2.1 forever
forever語句:連續執行的語句;
格式爲:forever 語句;或 forever begin多條語句end
注意:forever 循環語句常用於產生週期性的波形,用來作爲仿真測試信號。他與 always 語句不同之處在於不能獨立寫在程序中,而必須寫在 initial 塊中;

2.2 repeat
repeat語句:連續執行一條語句n次;
格式爲:repeat(表達式)語句;或 repeat(表達式)begin 多條語句 end

2.3 while
while語句:執行一條語句直到某個條件不滿足;
格式爲:while(表達式)語句;或while(表達式)begin 多條語句 end

2.4 for
for語句:通過以下三個步驟決定語句的循環執行:

先給控制循環次數的變量賦初值
判定控制循環的表達式的值,如爲假,則跳出循環語句;如爲真,則執行指定的語句後,轉到第3步
執行一條語句賦值語句來修正控制循環變量次數的變量值,然後返回第二步
格式爲:for(表達式1;表達式2;表達式3)
注意:在for語句中,循環變量增值表達式可以不必是一般的常規加法或者減法表達式,比如可以使用右移表達式。for循環執行方法與C語言一致,但是所表達的意思卻有很大區別;
作用可看以下圖片中:

 

 


3 生成塊
四、結構語句、系統任務、函數語句和顯示系統任務
1 結構說明語句
1.1 initial語句
initial語句格式:

initial

begin

語句1;

語句2;

……

語句n;

End
1
2
3
4
5
6
7
8
9
10
11
12
13
注意:一個模塊可以有多個initial塊,它們都是並行運行的,initial常用與測試文件和虛擬模塊的編寫,用來產生仿真測試信號和設置信號記錄等仿真環境;

1.2 always語句
always語句格式:always <時序控制> <語句>
如果一個always語句沒有時序控制,則這個語句將會使仿真器產生死鎖。always的時序控制可以是沿觸發也可以是電平觸發。沿觸發的always塊常常用來描述時序行爲,通過綜合工具轉換爲表示寄存器組合門級組合的組合邏輯結構;而電平觸發的always塊產長用來描述組合邏輯行爲,通過綜合工具轉換爲表示組合邏輯的門級邏輯和帶鎖存器的組合邏輯結構。
always塊的OR事件控制(敏感列表):
由關鍵詞“or”連接的多個事件名或者信號名組成的列表稱爲敏感列表,關鍵詞“or”被用來表示這種關係,或者使用“,”來代替。此外,如果組合邏輯塊的輸入變量很多,Verilog提供另外兩個特殊的符號:@*和@(*),它們都表示對其後面語句塊中所有輸入變量的變化是敏感列表;
wait關鍵字表示的電平敏感時序控制:
Verilog同時也允許使用另外一種形式表示的電平敏感時序控制(即後面的語句和語句塊需要等待某個條件爲真才能執行);
例:always
wait (count_enable) #20 count=count+1;

注意:一個程序模塊可以有多個initial和always過程塊,每個initial和always說明語句在仿真的一開始同時立即開始執行,initial語句只執行一次,而always語句則不斷重複活動着,直到仿真過程結束;

1.3 task任務說明語句
1.3.1 任務的的定義 任務的定義語法如下:
task <任務名>;
<端口及數據類型聲明語句>

<語句1>

<語句2>

……

<語句n>

endtask
1
2
3
4
5
6
7
8
9
10
11
12
1.3.2 任務的調用及變量的傳遞
任務的調用:
<任務名>(端口1,端口2,端口3,……端口n);
1
2
1.4 function函數說明語句
函數的目的是返回一個用於表達式的值。

1.4.1 定義函數的語法:
function <返回值的類型或範圍> (函數名);

<端口說明語句>

<變量類型說明語句>

begin

<語句>

……

end
1
2
3
4
5
6
7
endfuction
注:<返回值的類型或範圍>這一項是可選項,如默認則返回值爲一位寄存器類型數據。

1.4.2 從函數返回的值
函數的定義蘊含聲明瞭與函數同名的、函數內部的寄存器,函數的定義把函數返回值所賦值寄存器的名稱初始化爲與函數同名的內部變量。

1.4.3 函數的調用:
函數的調用時通過將函數作爲表達式中的操作數來實現的。
調用格式如下:
<函數名>(<表達式>,…<表達式>)

1.4.4 函數的使用規則:
1.函數的定義不能包含任何的時間控制語句,即用任何用#、@或wait來標識的語句;
2.函數不能啓動任務,但可以調用其他函數;
3.定義函數時至少要有一個輸入變量;
4.在函數定義中必須有一條賦值語句給函數中的一個內部變量賦以函數的結果值,該內部變量具有和函數名相同的名字。

1.4.5 自動(遞歸)函數
Verilog中的函數不能夠進行遞歸調用的。設計模塊中若某函數在兩個不同的地方被同時併發調用,由於這兩個調用同時對同一塊地址空間進行操作,那麼計算結果將是不確定的。
若在函數聲明時使用了關鍵字automatic,那麼該函數將成爲自動的或可遞歸的,即仿真器爲每一次函數調用動態地分配新的地址空間,每一個函數調用對各自的地址空間進行操作。因此,自動函數中聲明的局部變量不能通過層次名進行訪問。而自動函數本身可以通過層次名進行調用。
在定義時將automatic插入到function即可。
格式 :function automatic <返回值的類型或範圍> (函數名);

1.4.6 常量函數
參數是常量的函數,這種函數能夠用來引用複雜的值;
例如:在工程中,參數化設計是非常常見的。模塊接口的位寬,常見的有8位、16位、32位、64位和128位等;雖然功能相同,僅因爲位寬不同,就要另外寫一個模塊,那設計工作就很繁複了。爲此,我們可以採用參數化來實現,即用parameter來定義常數。但是參數化會遇到一個問題,就是某些信號的位寬跟此參數有着密切的關係。例如,我們可以使用parameter來定義FIFO的深度,但是表示FIFO深度的信號usedw,其位寬是跟參數相關的。如果深度爲512,usedw位寬是9位,如果深度爲1024,其位寬是10位。這時如果此模塊可以自己計算位寬那就再好不過了。
下面設計一個自動計算位寬的函數;

module ram(… … …);
parameter RAM_DEPTH = 256;
input [clogb2(RAM_DEPTH)-1:0] addr_bus;


//
function integer clogb2(input integer depth)

begin

if(depth==0)

clogb2 = 1;
else if(depth!=0)
for(clogb2==0;clogb2>0;clogb2=clogb2+1)
depth = depth + 1;
end
endfunction

1.5 關於使用任務和函數的小結
1.任務和函數都是用來對設計中多處使用的公共代碼進行定義;使用任務和函數可以將模塊分割成許多個可獨立管理的子單元,增加了模塊的可讀性和可維護性;它們和C語言中的子程序起相同作用。
2.任務可以具有任意多個輸入、輸出和輸入\輸出(inout)變量;在任務中可以使用延遲、事件和時序控制結構,在任務中可以調用其它的任務和函數;
3.可重入任務使用關鍵字automatic進行定義,它的每一次調用都對不同的地址空間進行操作。因此在被多次併發調用時,它仍然可以獲得正確的結果;
4.函數只能有一個返回值,並且至少要有一個輸入變量;在函數中不能使用延遲、事件和時序控制結構,但可以調用其它函數,不能調用任務。
5.當聲明函數時,Verilog仿真器都會隱含的聲明一個同名的寄存器變量,函數的返回值通過這個寄存器傳遞迴調用處;
6.遞歸函數使用關鍵詞automatic進行定義,遞歸函數的每一次調用都擁有不同的地址空間,因此對這種函數的遞歸調用和併發調用可以得到正確的結果;
7.任務和函數都包含在設計層次之中,可以通過層次名對它們進行調用;

1.6常用的系統任務
1.6.1 $display 和 $write任務
1.作用:將參數p2到pn按參數p1給定的格式輸出。
2.格式:
$ display (p1,p2,……pn);
$ write(p1,p2,……pn);
其中參數p1稱爲“格式控制”,參數p2到pn通常稱爲“輸出列表”;
3. $ display自動地在輸出後進行換行,$ write則不是這樣,如果想在一行裏輸出多個信息,可以使用$ write。在$ display和$write中,其輸出格式控制是用雙引號括起來的字符串,它包括以下兩種信息:
格式說明,由“%”格式字符組成;

輸出格式 說明
%h或%H 以十六進制數的形式輸出
%d或%D 以十進制數的形式輸出
%0或%O 以八進制數的形式輸出
%b或%B 以二進制數的形式輸出
%c或%C 以ASCII碼字符的形式輸出
%v或%V 輸出網絡型數據信號強度
%m或%M 輸出等級層次的名字
%s或%S 以字符串的形式輸出
%t或%T 以當前的時間格式輸出
%e或%E 以指數的形式輸出實型數
%f或%F 以十進制數形式輸出實型數
%g或%G 以指數或十進制數的形式輸出實型數,無論何種格式都以較短的結果輸出
普通字符,即需要原樣輸出的字符。

換碼序列 功能
\n 換行
\t 橫向跳格(即跳到下一個輸出區)
\ 反斜槓字符\
‘’ 雙引號字符"
\o 1~3位八進制數代表的字符
%% 百分符號%
注:
1.可以通過在%和表示進制的字符中間插入一個0自動調整顯示輸出數據寬度的方式。
例如:$displaay("d=%0h a=%0h",data,addr);
2.在輸出格式爲十進制的情況下:若部分位爲不確定位,則輸出結果爲大寫的X,若輸出部分位爲高阻值,則輸出結果爲大寫的Z;
例如·:$display("%d",1'bx); 輸出結果:x
3.在輸出格式爲十六進制或八進制的情況下:若部分位爲不定值,則該位進制數輸出結果爲大寫的X;若部分位爲高阻值,則輸出結果爲大寫Z;
例如:$display("%h",14'bx0_1010); 輸出結果:xxXa

1.6.2 文件輸出
1.打開文件
用法:$ fopen(“<文件名>”);
用法:<文件句柄>=$ fopen(“<文件名>”);
2.寫文件
系統任務 $ fdisplay、$ fmonitor、$ fwire、$ fstrobe都用於寫文件;
格式:$ fdisplay(<文件描述符>,p1,p2,……,pn);
格式:$ fmonitor(<文件描述符>,p1,p2,……,pn);
3.關閉文件
用法:$fclose(<文件描述符>);

1.6.3 顯示層次
通過任何顯示任務,比如$ display、$ write、$ monitor或者$strobe任務中的%m選項的方式可以顯示任何級別的層次。

1.6.4 選通顯示
$ strobe與$ display任務除了在執行順序上有區別外,其餘用法相同:$ strobe總是在同時刻的其他賦值語句執行完成之後才執行的,而$ display的執行順序則是不確定的。$strobe的這種同步機制可以確保所有在同一時鐘沿賦值的其他語句在執行完畢之後才顯示數據。

1.6.5 值變轉儲文件
值變存儲文件(VCD)是一個ASCII文件,它包含仿真時間、範圍與信號的定義以及仿真運行過程中信號值得變化信息。設計中的所有信號或者選定的信號集合在仿真過程中都可以被寫入VCD文件。
Verilog提供了系統任務來選擇要轉儲的模塊實例或者模塊實例信號($ dumpvars),選擇VCD文件的名稱($ dumpfile),選擇轉儲過程的起點和終點($ dumpon,$ dumpoff),選擇生成檢測點($dumpall)。具體使用見教材P97。

五、調試用系統任務和常用編譯預處理語句
1 系統任務$monitor
格式:

$monitor(p1,p2,……pn);
$monitor;
$monitoron;
$monitoroff;

$ monitor提供了監控和輸出參數列表中的表達式或變量值的功能。當啓動一個帶有一個或多個參數的$ monitor任務時,仿真器則建立一個處理機制,使得每當參數列表中變量或表達式的值發送變化時,整個參數列表中的變量或表達式的值都將輸出顯示。如果同一時刻。兩個或多個參數的值發生變化時,則在該時刻輸出只顯示一次。但在$ monitor中,參數可以是$ time系統函數。這樣參數列表中變量或者表達式的值同時發生變化的時刻可以通過標明同一時刻的多行輸出來顯示。例如:
$ monitor($ time, ,"rxd=%b,txd=%b",rxd,txd);
“,,”代表一個空參數,在輸出時顯示爲空格。
$ monitoron和$ monitoroff任務的作用是通過打開或關閉監控標誌來控制監控任務$ mmonitor的啓動和停止,這樣使得使得程序員可以很容易的控制$ monitor何時發生。其中$ monitoroff任務用於關閉監控標誌,停止監控任務$ monitor,$ monitoron則用於打開監控標誌,啓動監控任務$ monitor。通常在通過調用$ monitoron來啓動$ monitor時,不管$ monitor參數列表中的值是否發生變化,總是立刻輸出顯示當前時刻參數列表中的值,這用於在監控的初始時刻設定初始比較值。在默認情況下,控制標誌在仿真的起始時刻就已經打開了。在多模塊調試的情況下,許多模塊都調用了$ monitor,因爲任何時刻只能有一個$ monitor起作用,因此需配合$ monitoron和$ monitoroff使用,把需要監視的模塊用$ monitoron打開,在監視完畢後及時用$ monitoroff關閉,以便把$ monitor讓給其它模塊使用。$ monitor與$ display的不同處還在於$ monitor往往在initial塊中調用,只要不調用$ monitoroff,$ monitor便不間斷地對所設定的信號進行監視。
不需要、也不能在always過程塊中調用$ monitor

2 時間度量系統函數
2.1 $time系統函數
$ time返回一個64位的整數來表示當前的仿真時刻值。該時刻是以模塊的仿真時間尺度位基準的。$ time輸出的總是時間尺度的倍數,且總是輸出整數(在將經過尺度比例變換的數字輸出時,要先進行取整)。
例如:

`timescale 10ns/1ns//時間尺度/時間間隔
module test;

reg set;
parameter p = 1.6;
initial

begin
$ monitor($time,“set=”,set);
#p set=0;
#p set=1;
end
endmodule

輸出結果·:
0 set=x
2 set=0
3 set=1

2.2 $realtime系統函數
$ realtime和$ time的作用是一樣的,只是$realtime返回的數字是一個實型數,該數字也是以時間尺度位基準的。
例如:

`timescale 10ns/1ns//時間尺度/時間間隔
module test;

reg set;
parameter p = 1.6;
initial

begin
$ monitor($realtime,“set=”,set);
#p set=0;
#p set=1;
end
endmodule

輸出結果·:
0 set=x
1.6 set=0
3.2 set=1

3 系統任務$finish
格式:
$ finish;
$ finish(n);
系統任務$ finish的作用是退出仿真器,返回主操作系統。如果不帶參數,默認$finish的參數值爲1。
各種參數代表的含義:
0 不輸出任何信息;
1 輸出當前仿真時刻和位置;
2 輸出當前仿真時刻、位置和在仿真過程中所用的memory及cpu時間統計。

4 系統停止任務$stop
格式:
$stop;
$stop(n);
$stop任務的作用是是把EDA工具(例如仿真器)置成暫停模式,在仿真環境下給出一個交互式的命令提示符,將控制權交給用戶·。根據參數值的不同,輸出不同的信息,參數越大,輸出的信息越多。

5 系統任務$ readmemb和$readmemh
$ readmemb和$ readmemh用來從文件中讀數據到存儲器中,這兩個系統任務可以在仿真的任意時刻被執行使用,其格式共有以下六種:
1.readmemb(“<數據文件名>,<存儲器名>”);
2.readmemb(“<數據文件名>,<存儲器名>,<起始地址>”);
3.readmemb(“<數據文件名>,<存儲器名>,<起始地址>,<結束地址>”);
4.readmemh(“<數據文件名>,<存儲器名>”);
5.readmemh(“<數據文件名>,<存儲器名>,<起始地址>”);
6.readmemh(“<數據文件名>,<存儲器名>,<起始地址>,<結束地址>”);
在這兩個系統任務中,被讀取的數據文件的內容只能包含:空白位置(空格、換行、製表(tab)和from-feeds),註釋行(//形式的和/…/形式的都允許)、二進制和十六進制數字。數字中不能包含位寬說明和格式說明,對於r e a d m e m b 系 統 任 務 , 每 個 數 字 必 須 是 二 進 制 數 字 , 對 於 readmemb系統任務,每個數字必須是二進制數字,對於readmemb系統任務,每個數字必須是二進制數字,對於readmemh系統任務,每個數字必須是十六進制數字。每個數字的存放地址值可以在數據文件中進行說明,@後跟上十六進制數。如:@hh…h
補充說明:
(1)如果系統任務聲明語句和數據文件裏都沒有進行地址說明,則默認的存放起始地址爲該存儲器定義語句中的起始地址,數據文件裏的數據被連續存放到該存儲器中,直到該存儲器單元存滿爲止或數據文件裏的數據存完。
(2)如果系統任務中說明了存放的起始地址,沒有說明存放的結束地址,則數據從起始地址開始存放,存放到該存儲器定義語句中的結束地址爲止;
(3)如果在系統任務聲明語句中,起始地址和結束地址都進行了說明,則數據文件裏的數據按起始地址開始存放到存儲器單元中,直到該結束地址,而不考慮該存儲器的定義語句中的起始地址和結束地址;
(4)如果地址信息在系統任務和數據文件裏都進行了說明,那麼數據文件裏的地址必須在系統任務中地址參數聲明的範圍之內。否則將提示錯誤信息,並且裝載數據到存儲器中的操作被中斷。
(5)如果數據文件裏的數據個數和系統任務中起始地址及結束地址暗示的數據個數不同的話,也要提示錯誤信息。

6 系統任務$random
這個系統函數提供了一個產生隨機數的手段。當函數被調用時返回一個32位隨機數。$ random的一般用法是:$ random%b,其中b>0。它給出了一個在(-b+1):(b-1)中的隨機數。
例:
reg[23:0] rand;
rand=$ random%60;
上面的例子給出了一個範圍在-59到59之間的隨機數,下面的例子通過位並接操作產生一個值在0~59之間的數:
例:
reg[23:0] rand;
rand={$random}%60;
利用這個系統函數可以產生隨機脈衝序列或寬度隨機的脈衝序列,以用於電路的測試。

7 編譯預處理
預處理命令以符號“`”開頭,這些預處理命令的有效作用範圍爲定義命令之後到文本結束或其它命令定義替代該命令之處。

7.1 宏定義`define
用一個指定的標識符(即名字)來代表一個字符串(宏內容),它的一般形式爲:
`define標識符(宏名)字符串(宏內容);
例 :`define signal string
在編譯預處理時將宏名替換成字符串的過程稱爲“宏展開”,
說明:
(1)建議使用大寫字母,以與變量名相區別;
(2)`define命令可以出現在模塊定義裏面,也可以出現在模塊定義外面,宏名有效範圍爲定義命令之後到原文件結束。通常,`define命令寫在模塊定義的外面,作爲程序的一部分,在此程序內有效。
(3)在引用已定義的宏名時,必須在宏名的前面加上符號“`”,表示該名字是一個經過宏定義的名字;
(4)宏定義是用宏名代替一個字符串,也就是做簡單的置換,不做語法檢查。預處理時照樣帶入,不管含義是否正確,只有在編譯已被展開後的源程序時纔會報錯。
(5)宏定義不是Verilog HDL語句,不必在行末加分號,如果加了分號會連分號一起進行置換。
(6)在進行宏定義時,可以引用已定義的宏名,可以層層置換。
(7)宏名和宏內容必須在同一行中進行聲明,如果在宏內容中包含有註釋行,註釋行不會作爲被置換的內容。
注意:
組成宏內容的字符串不能被註釋行、數字、字符串、確認符、關鍵詞、雙目和三目運算符分隔開的;

7.2 “文件包含”處理`include
所謂“文件包含”處理是一個源文件可以將另一源文件的全部內容包含進來,即將另外的文件包含到本文件之中。其一般形式爲:
`include“文件名”
說明:
(1)一個`include命令之內指定一個被包含的文件,如果要包含n個文件,要用n個`include命令;注意後面的寫法是錯誤的;\`include "aaa.v""bbb.v";
(2)`include命令可以出現在Verilog HDL源程序的任何地方,被包含文件名可以是相對路徑名,也可以是絕對路徑名。例如:\`include "parts/count.v";
(3)可以將多個`include命令寫在一行,在`include命令行,可以出現空格和註釋行。
(4) 如果文件1包含文件2,而文件2要用到文件3的內容,則可以在文件1用兩個`include命令分別包含文件2和文件3,而且文件3應出現在文件2之前。
(5)在一個被包含文件中又可以包含另一個被包含文件,即文件包含是可以嵌套的。

7.3 時間尺度`timescale
`timescale命令的格式如下:
`timescale<時間單位>/<時間精度>
時間單位參量是用來定義模塊中仿真時間和延遲時間的基準單位,時間精度參量是用來聲明該模塊的仿真時間精確程度的。時間精度不能大於時間單位值。如果在同一個程序設計中,存在多個`timescale命令,則用最小的時間精度值來決定仿真的時間單位;
用於說明時間單位和時間精度參量值的數字必須時整數;當多個帶不同`timescale定義的模塊包含在一起時只有最後一個起作用。

時間單位 定義
s(秒) 1s
ms(毫秒) 千分之一秒(10-3s)
us(微秒) 百萬分之一秒(10-6s)
ns(納秒) 十億分之一秒(10-9s)
ps(皮秒) 萬億分之一秒(10-12s)
fs(飛秒) 千萬億分之一秒(10-15s)
注意:如果在同一個設計裏,多個模塊中用到的時間單位不同,需要用到以下的時間結構:
(1)用`timescale命令來聲明本模塊中所用到的時間單位和時間精度;
(2)用系統函數$ printtimescale來輸出顯示一個模塊的時間單位和時間精度;
(3)用系統函數$ time和$realtime及%t格式聲明來輸出顯示EDA工具記錄的時間。

7.4 條件編譯命令`ifdef、`else、`endif
條件編譯:對一部分內容指定編譯的條件
條件編譯命令有以下幾種格式:
(1)`ifdef(標識符)
程序段1
`else
程序段2
`endif
(2)`ifdef(標識符)
程序段1
`endif
條件編譯命令可以用編譯命令:`ifdef、`ifndef、`else、`endif實現,`ifdef語句中不允許使用布爾表達式(指的是不允許在(標識符)位置使用布爾表達式)。

7.5 條件執行
條件執行標誌允許設計者在允許時控制語句執行的流程,所有語句都被編譯,但是有條件的執行它們,條件執行標誌位僅能用於行爲語句,系統任務關鍵字$ test和$ plusargs用於條件執行;

原文鏈接:https://blog.csdn.net/weixin_50810761/article/details/113091498

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章