$monitor
- 任務$monitor提供了監控和輸出參數列表中的表達式或變量值的功能。
- 格式: $monitor(p1,p2,...,pn); $monitor; $monitoron; $monitoroff;
- 當monitor的參數列表(monitor后面接的括號里面的參數)中的參數或表達式的值發生變化時,整個參數列表中變量或表達式的值都將輸出顯示;
- 在$monitor中,參數可以是$time系統函數。這樣參數列表中變量或表達式的值同時發生變化的時刻可以通過標明同一時刻的多行輸出來顯示。eg:
$monitor($time,,"rxd=%b txd=%b",rxd,txd);
上面"$time,,"的兩個逗號代表中間是一個空參數。空參數在輸出時顯示為空格。
- $monitor的打開和關閉分別用$monitoron和$monitoroff表示,用來控制任務的啟動和停止,使得很容易控制任務何時發生,何時結束。
- 通常在通過調用$monitoron來啟動$monitor時,不管$monitor參數列表中的值是否發生變化,總是立刻輸出顯示當前時刻參數列表中的值,這用於在監控的初始時刻設定初始比較值;
- $monitor往往在initial塊中調用,只要不調用$monitoroff,$monitor便不斷地對所設定的信號進行監視。
-
`timescale 1ns/1ns module moni_test(); reg[3:0]counter; reg clk; reg rst; initial begin rst=0; #10 rst=1; $monitor ($time,,"counter=%d",counter); $monitoron; end initial begin clk=0; forever #5 clk=~clk; end always@(negedge rst or posedge clk) if(!rst) counter<=4'd0; else counter<=counter+1; endmodule
注意1. 僅監測$time是不會每次都輸出的,必須是除了時間之外的參量變化才可以做到參量變化就輸出
2 . ,,符號輸出的是一個空格符,輸出結果中得到了驗證。
`timescale會在后面介紹。基本的:`timescale<時間單位>/<時間精度>
時間精度就是模塊仿真時間和延時的精確程序,比如:定義時間精度為10ns, 那么時序中所有的延時至多能精確到10ns,而8ns或者18ns是不可能做到的。
這里面的#p的p是以時間單位為基准的。
- 不需要,也不能在always過程塊中調用$monitor。
$time
- $time 可以返回一個64位的整數來表示的當前仿真時刻值。該時刻是以模塊的仿真時間尺度為基准的。
- $time常用在$monitor中,用來做時間標志。
-
1 `timescale 10ns/1ns 2 module time_test(); 3 reg set; 4 parameter p=1.6; 5 initial 6 begin 7 $monitor($time,,"set=",set); 8 $monitoron; 9 #p set=0; 10 #p set=1; 11 end 12 endmodule
輸出:run 64ns
# 0 set=x
# 2 set=0
# 3 set=1
本來,預設的輸出應該為1.6和3.2的。但是由於$time返回一個64位整數。所以要先進行取整。1.6與3.2取整后分別是2和3 。
$realtime
- $realtime和$time的作用是一樣的。總是$realtime返回的時間數字是一個實數(包含小數)型,該數字也是以時間尺度為基准的。
- 在上面的例子中,將parameter p=1.6;中的p的值改為1.55,然后將$time,,改為$realtime,, 。最后的輸出結果為 :
0 set=x
1.6 set=0
3.2 set=1
為什么不是1.55 和3.10???? <——見本節后面的 `timescale。
$finish
-
系統任務finish的作用是退出仿真器,結束仿真過程。
- 格式: (1)$finish;
(2)$finish(n);
當$finish帶參數時,如(2),根據不同的參數值,系統輸出的特征信息:
-
-
-
-
-
-
-
-
-
- 0:不輸出任何信息;
- 1:輸出當前仿真時刻和位置;
- 2:輸出當前仿真時刻、位置和仿真過程中所用的memory及CPU時間的統計。
-
-
-
-
-
-
-
-
當$finish后面不帶參數時,默認參數為1。
$stop
-
$stop任務得作用是把EDA工具(例如仿真器)置為暫停模式,在仿真環境下給出一個交互式的命令提示符,將控制權交給用戶。
-
格式: 1). $stop; 2). $stop(n);
-
帶參數時,根據參數值(0,1或2)的不同,輸出不同的信息。參數值越大,輸出的信息越多。
$stop與$finish
1. $stop:用於在仿真時,暫停仿真。運行到$stop的時候,仿真會暫停;此時可以在命令行輸入run繼續運行仿真。
2. $finish:仿真停止。運行到$finish的時候,仿真停止退出,此時不可以再繼續運行。
3.$stop和$finish常用在測試模塊的initial模塊中,配合時間延遲用來控制仿真的持續時間。
$readmemb和$readmemh
- $readmemb和$readmemh用來從文件中讀取數據到存儲器中。
- 使用格式:
-
- $readmemb("<數據文件名>",<存儲器名>);
- $readmemb("<數據文件名>",<存儲器名>,<起始地址>);
- $readmemb("<數據文件名>",<存儲器名>,<起始地址>,<結束地址>);
- $readmemh("<數據文件名>",<存儲器名>);
- $readmemh("<數據文件名>",<存儲器名>,<起始地址>);
- $readmemh("<數據文件名>",<存儲器名>,<起始地址>,<結束地址>);
- 在這兩個系統任務中,被讀取的數據文件的內容只能包含:空白位置(空格,換行,制表格(tab)和“換頁符”(form feed)),注釋行(//形式的和/*...*/形式的都允許),二進制或十六進制的數字。數字中不能包含位寬說明和格式說明,對於$readmemb系統任務,每個數字必須是二進制數字,對於$readmemh系統任務,每個數字必須是十六進制數字。數字中不定值x或X,高阻值z或Z,和下划線(_)的使用方法及代表的意義與一般Verilog HDL程序中的用法及意義是一樣的。另外數字必須用空白位置或注釋行來分隔開。
- 存儲器單元的存放地址范圍由系統任務聲明語句中的起始地址和結束地址來說明。每個數據的存放地址在數據文件中進行說明。當地址出現在數據文件中,其格式為字符"@"后跟上十六進制數。如:@hhh...h(允許大小寫,在@和數字之間不能有空格。
-
1 module readmemb_test; 2 reg [7:0] memory [7:0]; 3 integer i; 4 5 initial 6 begin 7 $readmemb("init.dat",memory); 8 for(i=0;i<8;i=i+1) 9 $display("Memory[%0d] = %b",i,memory[i]); // 是%0d 10 end 11 endmodule
當修改文件的@006位@005時,后面的會將前面的覆蓋。
-
對於上面六種系統任務格式,需補充說明以下五點:
1) 如果系統任務聲明語句中和數據文件里都沒有進行地址說明,(1)、(4).則缺省的存放起始地址為該
存貯器定義語句中的起始地址。數據文件里的數據被連續存放到該存貯器中,直到該存貯
器單元存滿為止或數據文件里的數據存完。
2) 如果系統任務中說明了存放的起始地址,沒有說明存放的結束地址,(2)、(5)。則數據從起始地址開
始存放,存放到該存貯器定義語句中的結束地址為止。
3) 如果在系統任務聲明語句中,起始地址和結束地址都進行了說明,則數據文件里的數據按
該起始地址開始存放到存貯器單元中,直到該結束地址,而不考慮該存貯器的定義語句中
的起始地址和結束地址。
4) 如果地址信息在系統任務和數據文件里都進行了說明,那么數據文件里的地址必須在系統
任務中地址參數聲明的范圍之內。否則將提示錯誤信息,並且裝載數據到存貯器中的操作
被中斷。
5) 如果數據文件里的數據個數和系統任務中起始地址及結束地址暗示的數據個數不同的話,
也要提示錯誤信息。
$random
- $random提供了一個產生隨機數的手段。當函數被調用時返回一個32位的帶符號的整數型隨機數。
- $random的一般用法:$random % b;用來產生一個范圍在(-b+1)~(b-1)中的隨機數。
// 第一個例子:產生一個范圍在-59~59之間的隨機數。 reg [23:0] rand; rand = $rand % 60; // 第二個例子;通過位並接操作產生一個值在0~59之間的數。 reg [23:0] rand; rand = {$random}%60;
預編譯處理
- 介紹`define、`include、`timescale
宏定義:`define
- 用一個指定的標識符(即名字)來代表一個字符串。
- 一般形式: `define 標識符(宏名) 字符串(宏內容) eg:
`define signal string
- 在編譯預處理時將宏名替換成字符串的過程稱為“宏展開”。
- `define可以寫在模塊定義里面,也可以寫在外面。宏名的有效范圍為定義命名后到原文件結束。通常:`define命令寫在模塊定義的外面,作為程序的一部分,在此程序內有效。
- 在引用已定義的宏名時,必須在宏名的前面加上符號“`”,表示該名字是一個經過宏定義的名字。
- 如果行末加了分號,會連分號一起替換。
- 在進行宏定義時,可以引用已定義的宏名。
- 宏名和宏內容必須在同一行中進行聲明。如果在宏內容中包含注釋行,注釋行不會作為被置換的內容。
“文件包含”處理 `include
- verilog使用`include命令來實現‘文件包含’的操作。
- 形式為: `include "文件名"
- 一個`include命令只能指定一個 被包含的文件,如果要包含n個文件,要用n個`include命令。
- `include可以出現在源程序的任何地方,被包含的文件名可以是相對路徑,也可以是絕對路徑。
- 可以將多個`include命令寫在同一行,在`inlucde命令行,可以出現空格和注釋行。
- 如果文件1包含文件2,而文件2要用到文件3的內容,則可以在文件1用兩個`include分別包含文件2和文件3。而且文件3要在文件2之前。(。。。。。。最好直接在寫文件2的時候再文件2中包含文件3.)
- 許多verilog編譯器支持多模塊編譯,也就是說只要把需要用`include包含的所有文件都放置在一個項目中,建立存放編譯結果的庫,用模塊名就可以把所有有關的模塊聯系在一起,此時在程序模塊中就不必使用`include編譯預處理指令。
時間尺度`timescale
- 意義:`timescale命令用來說明跟在改命令后的模塊的時間單位和時間精度。
- 作用:使用`include命令可以在同一個設計里包含采用了不同的時間單位的模塊。
- `timescale命令的格式如下: `timescale<時間單位>/<時間精度>
- 時間單位參量用來定義模塊中仿真時間和延遲時間的基准單位。 時間精度參量用來聲明該模塊的仿真時間的精確程度。該參量被用來對延遲時間值進行取整操作(仿真前),因此該參量又被稱為取整精度。
- 如果在同一個程序設計里,存在多個`timescale命令,則用最小的時間進度來決定仿真的時間單位。
- 時間精度的值不能大於時間單位值。
- 在`timescale命令中,用於說明時間單位和時間精度參量值的數字必須是整數,其有效數字為1,10,100,單位為s, ms, μs, ns,ps, fs 。
- `timescale 1ns/1ps:表示模塊中的所有的時間值都是1ns的整數倍。模塊中的延遲時間可表示成帶3位小數的實型數
-
`timescale 10ns/1ns module test; reg set; parameter d=1.55; initial begin #d set=0; #d set=1; end endmodule
由於`timescale 1ns/1ns。所以所有的時間值都為10ns的整數倍,並且以1ns位時間精度,所以當d=1.55時,由於時間單位為10ns,所以時間d表示的延遲為1.55x10ns=15.5ns;但是由於該模塊的時間精度為1ns。而15.5ns的時間精度不是1ns而是0.1ns.所以系統將15.5按4舍5入改為16ns。
- 注意:如果在同一個設計里,多個模塊中用到的時間單位不同,需要用到以下的時間結構。
1) 用`timescale命令來聲明本模塊中所用到的時間單位和時間精度。
2) 用系統任務$printtimescale來輸出顯示一個模塊的時間單位和時間精度。
3) 用系統函數$time和$realtime及%t格式聲明來輸出顯示EDA工具記錄的時間信息 。 - 在用`timescale時需要注意的是,當多個帶不同`timescale定義的模塊包含在一起的時候,只有最后一個才起作用。 所以屬於一個項目,但`timescale定義不同的多個模塊做好分開編譯,不要包含在一起編譯,一面吧時間單位搞混。(?)
條件編譯命令 `ifdef、`else、`endif
- 條件編譯:有時希望對程序中的一部分內容只有在滿足條件時才進行編譯,也就是對一部分內容指定編譯的條件。
- 形式:
-
- `ifdef 宏名(標識符)
程序段1
`else
程序段2
`endif
第一種的作用是當宏名已經被定義過(用`define命名定義),則對程序段1進行編譯,程序段2將被忽略;否則編譯程序段2,程序段1被忽略。其中`else部分可以沒有。如果沒有,就變為:形式2:
2. `ifdef 宏名(標識符)
程序段1
`endif
- 通常在Verilog HDL程序中用到`ifdef、`else、`endif編譯命令的情況有以下幾種:
- 選擇一個模塊的不同代表部分;
- 選擇不同的時序或結構信息;
- 對不同的EDA工具,選擇不同的激勵。
- 最常用的情況是:Verilog代碼中的一部分可能適用於某個編譯環境,但不適用與另一個環境。還有一種方法就是所謂的條件編譯。即設計者在代碼中指定其中某一部分只有在設置了某個標識后,這一段代碼才能被編譯(類似generate---endgenerate ? )。
- 條件編譯可以用編譯指令`ifdef、`ifndef、`else、`elsif和`endif來實現。
-
/* 摘自書上的例子 */ // 條件編譯 // 例1 `ifdef TEST // 若設置 TEST 標志,則編譯 test 模塊。 module test; initial $display("module %m complied"); endmodule `else // 在默認情況下,則編譯stimulus模塊 module stimulus; initial $display("Module %m complied"); endmodule `endif // `ifdef 語句的結束 // 例2 module top; bus_master b1(); // 無條件的調用模塊 `ifdef ADD_B2 bus_master b2(); // 若定義了ADD_B2文本宏標志,則有條件地調用b2 `ifdef ADD_B3 bus_master b3(); // 若定義了ADD_B2文本宏標志,則有條件地調用b2 `else bus_master b4(); // 在默認情況下,則有條件的調用b4 `endif `ifndef IGNORE_B5 bus_master b5(); // 若沒有定義 IGNORE_B5文本宏標志。則有條件的調用b5 endmodule
注意review "generate---endgenerate"帶if的情況。
-
`ifdef語句中不容許使用布爾表達式,例如:TEST && ADD_B2 來表示編譯條件是不允許的。
條件執行
- 條件執行標識允許設計者在運行時控制語句執行的流程。所有語句都被編譯,但是有條件的執行他們。條件語句僅能用於行為語句。
- 條件執行系統任務關鍵字:$test$plusargs(一塊的,中間沒空格)
/* 帶$test$plusargs的條件執行 */ // 條件執行 module test; reg a,b,c; initial begin a=1'b1; b=1'b0; c=1'b1; if($test$plusargs("DISPLAY_VAR")) $display("Disolay=%b",{a,b,c}); // 只有當標志設置時才能顯示 else $display{"No Display"}; // 其他情況下不顯示 end endmodule
僅當在運行設置了標志DISPLAY_VAR時才顯示變量。可以指定+DISPLAY_VAR選項在程序運行時設置標志。
- 可以使用系統任務關鍵字$value$plusargs來進一步控制條件執行。該系統任務用語測試調用選項的參數值。如果沒有找到匹配的調用選項,那么$value$plusargs返回0;如果找到匹配的選項,那么$value$plusargs返回非0值。