1.激勵的設置 相應于被測試模塊的輸入激勵設置為reg型,輸出相應設置為wire類(lèi)型,雙向端口inout在測試中需要進(jìn)行處理。 方法1:為雙向端口設置中間變量inout_reg作為該inout的輸出寄存,inout口在testbench中要定義為wire型變量,然后用輸出使能控制傳輸方向。 eg: inout [0:0] bi_dir_port; wire [0:0] bi_dir_port; reg [0:0] bi_dir_port_reg; reg bi_dir_port_oe; assign bi_dir_port=bi_dir_port_oe?bi_dir_port_reg:1'bz; 用bi_dir_port_oe控制端口數據方向,并利用中間變量寄存器改變其值。等于兩個(gè)模塊之間用inout雙向口互連。往端口寫(xiě)(就是往模塊里面輸入) 方法2:使用force和release語(yǔ)句,這種方法不能準確反映雙向端口的信號變化,但這種方法可以反映塊內信號的變化。具體如示: module test(); wire data_inout; reg data_reg; reg link; #xx; //延時(shí) force data_inout=1'bx; //強制作為輸入端口 ............... #xx; release data_inout; //釋放輸入端口 endmodule 從文本文件中讀取和寫(xiě)入向量 1)讀取文本文件:用 $readmemb系統任務(wù)從文本文件中讀取二進(jìn)制向量(可以包含輸入激勵和輸出期望值)。$readmemh 用于讀取十六進(jìn)制文件。例如: reg [7:0] mem[1:256] // a 8-bit, 256-word 定義存儲器mem initial $readmemh ( "mem.data", mem ) // 將.dat文件讀入寄存器mem中 initial $readmemh ( "mem.data", mem, 128, 1 ) // 參數為寄存器加載數據的地址始終 2)輸出文本文件:打開(kāi)輸出文件用?$fopen 例如: integer out_file; // out_file 是一個(gè)文件描述,需要定義為 integer類(lèi)型 out_file = $fopen ( " cpu.data " ); // cpu.data 是需要打開(kāi)的文件,也就是最終的輸出文本 設計中的信號值可以通過(guò)$fmonitor, $fdisplay, 2. Verilog和Ncverilog命令使用庫文件或庫目錄 ex). ncverilog -f run.f -v lib/lib.v -y lib2 +libext+.v //一般編譯文件在run.f中, 庫文件在lib.v中,lib2目錄中的.v文件系統自動(dòng)搜索 使用庫文件或庫目錄,只編譯需要的模塊而不必全部編譯 3.Verilog Testbench信號記錄的系統任務(wù): 1). SHM數據庫可以記錄在設計仿真過(guò)程中信號的變化. 它只在probes有效的時(shí)間內記錄你set probe on的信號的變化. ex). $shm_open("waves.shm"); //打開(kāi)波形數據庫 $shm_probe(top, "AS"); // set probe on "top", 第二個(gè)參數: A -- signals of the specific scrope S -- Ports of the specified scope and below, excluding library cells C -- Ports of the specified scope and below, including library cells AS -- Signals of the specified scope and below, excluding library cells AC -- Signals of the specified scope and below, including library cells 還有一個(gè) M ,表示當前scope的memories, 可以跟上面的結合使用, "AM" "AMS" "AMC",什么都不加表示當前scope的ports; $shm_close //關(guān)閉數據庫 2). VCD數據庫也可以記錄在設計仿真過(guò)程中信號的變化. 它只記錄你選擇的信號的變化. ex). $dumpfile("filename"); //打開(kāi)數據庫 $dumpvars(1, top.u1); //scope = top.u1, depth = 1 第一個(gè)參數表示深度, 為0時(shí)記錄所有深度; 第二個(gè)參數表示scope,省略時(shí)表當前的scope. $dumpvars; //depth = all scope = all $dumpvars(0); //depth = all scope = current $dumpvars(1, top.u1); //depth = 1 scope = top.u1 $dumpoff //暫停記錄數據改變,信號變化不寫(xiě)入庫文件中 $dumpon //重新恢復記錄 3). Debussy fsdb數據庫也可以記錄信號的變化,它的優(yōu)勢是可以跟debussy結合,方便調試. 如果要在ncVerilog仿真時(shí),記錄信號, 首先要設置debussy: a. setenv LD_LIBRARY_PATH :$LD_LIBRARY_PATH (path for debpli.so file (/share/PLI/nc_xl//nc_loadpli1)) b. while invoking ncverilog use the +ncloadpli1 option. ncverilog -f run.f +debug +ncloadpli1=debpli:deb_PLIPtr fsdb數據庫文件的記錄方法,是使用$fsdbDumpfile和$fsdbDumpvars系統函數,使用方法參見(jiàn)VCD 注意: 在用ncverilog的時(shí)候,為了正確地記錄波形,要使用參數: "+access+rw", 否則沒(méi)有讀寫(xiě)權限 在記錄信號或者波形時(shí)需要指出被記錄信號的路徑,如:tb.module.u1.clk. ……………………………………………………………………………………………………… 關(guān)于信號記錄的系統任務(wù)的說(shuō)明: 在testbench中使用信號記錄的系統任務(wù),就可以將自己需要的部分的結果以及波形文件記錄下來(lái)(可采用sigalscan工具查看),適用于對較大的系統進(jìn)行仿真,速度快,優(yōu)于全局仿真。使用簡(jiǎn)單,在testbench中添加:initial begin $shm_open("waves.shm"); $shm_probe("要記錄信號的路徑“,”AS“); #10000 $shm_close; 即可。 4. ncverilog編譯的順序: ncverilog file1 file2 .... 有時(shí)候這些文件存在依存關(guān)系,如在file2中要用到在file1中定義的變量,這時(shí)候就要注意其編譯的順序是 從后到前,就先編譯file2然后才是file2. 5. 信號的強制賦值force 首先, force語(yǔ)句只能在過(guò)程語(yǔ)句中出現,即要在initial 或者 always 中間. 去除force 用 release 語(yǔ)句. initial begin force sig1 = 1'b1; ... ; release sig1; end force可以對wire賦值,這時(shí)整個(gè)net都被賦值; 也可以對reg賦值. 6.加載測試向量時(shí),避免在時(shí)鐘的上下沿變化 為了模擬真實(shí)器件的行為,加載測試向量時(shí),避免在時(shí)鐘的上下沿變化,而是在時(shí)鐘的上升沿延時(shí)一個(gè)時(shí)間單位后,加載的測試向量發(fā)生變化。如: assign #5 c="a"^b …… @(posedge clk) #(0.1*`cycle) A="1"; ****************************************************************************** //testbench的波形輸出 module top; ... initial begin $dumpfile("./top.vcd"); //存儲波形的文件名和路徑,一般是.vcd格式. $dumpvars(1,top); //存儲top這一層的所有信號數據 $dumpvars(2,top.u1); //存儲top.u1之下兩層的所有數據信號(包含top.u1這一層) $dumpvars(3,top.u2); //存儲top.u2之下三層的所有數據信號(包含top.u2這一層) $dumpvars(0,top.u3); //存儲top.u3之下所有層的所有數據信號 end endmodule //產(chǎn)生隨機數,seed是種子 $random(seed); ex: din <= $random(20); //仿真時(shí)間,為unsigned型的64位數據 $time ex: ... time condition_happen_time; ... condition_happen_time = $time; ... $monitor($time,"data output = %d", dout); ... //參數 parameter para1 = 10, para2 = 20, para3 = 30; //顯示任務(wù) $display(); //監視任務(wù) $monitor(); //延遲模型 specify ... //describ pin-to-pin delay endspecify ex: module nand_or(Y,A,B,C); input A,B,C; output Y; AND2 #0.2 (N,A,B); OR2 #0.1 (Y,C,N); specify (A*->Y) = 0.2; (B*->Y) = 0.3; (C*->Y) = 0.1; endspecify endmodule //時(shí)間刻度 `timescale 單位時(shí)間/時(shí)間精確度 //文件I/O 1.打開(kāi)文件 integer file_id; file_id = fopen("file_path/file_name"); 2.寫(xiě)入文件 //$fmonitor只要有變化就一直記錄 $fmonitor(file_id, "%format_char", parameter); eg:$fmonitor(file_id, "%m: %t in1=%d o1=%h", $time, in1, o1); //$fwrite需要觸發(fā)條件才記錄 $fwrite(file_id, "%format_char", parameter); //$fdisplay需要觸發(fā)條件才記錄 $fdisplay(file_id, "%format_char", parameter); $fstrobe(); 3.讀取文件 integer file_id; file_id = $fread("file_path/file_name", "r"); 4.關(guān)閉文件 $fclose(fjile_id); 5.由文件設定存儲器初值 $readmemh("file_name", memory_name"); //初始化數據為十六進(jìn)制 $readmemb("file_name", memory_name"); //初始化數據為二進(jìn)制 //仿真控制 $finish(parameter); //parameter = 0,1,2 $stop(parameter); //讀入SDF文件 $sdf_annotate("sdf_file_name", module_instance, "scale_factors"); //module_instance: sdf文件所對應的instance名. //scale_factors:針對timming delay中的最小延時(shí)min,典型延遲typ,最大延時(shí)max調整延遲參數 //generate語(yǔ)句,在Verilog-2001中定義.用于表達重復性動(dòng)作 //必須事先聲明genvar類(lèi)型變量作為generate循環(huán)的指標 eg: genvar i; generate for(i = 0; i < 4; i = i + 1) begin assign = din[ i] = i % 2; end endgenerate //資源共享 always @(A or B or C or D) sum = sel ? (A+B):(C+D); //上面例子使用兩個(gè)加法器和一個(gè)MUX,面積大 //下面例子使用一個(gè)加法器和兩個(gè)MUX,面積小 always @(A or B or C or D) begin tmp1 = sel ? A:C; tmp2 = sel ? B:D; end always @(tmp1 or tmp2) sum = tmp1 + tmp2; 模板: module testbench; //定義一個(gè)沒(méi)有輸入輸出的module reg …… //將DUT的輸入定義為reg類(lèi)型 …… wire…… //將DUT的輸出定義為wire類(lèi)型 …… //在這里例化DUT initial begin …… //在這里添加激勵(可以有多個(gè)這樣的結構) end always…… //通常在這里定義時(shí)鐘信號 initial //在這里添加比較語(yǔ)句(可選) end initial //在這里添加輸出語(yǔ)句(在屏幕上顯示仿真結果) end endmodule 一下介紹一些書(shū)寫(xiě)Testbench的技巧: 1.如果激勵中有一些重復的項目,可以考慮將這些語(yǔ)句編寫(xiě)成一個(gè)task,這樣會(huì )給書(shū)寫(xiě)和仿真帶來(lái)很大方便。例如,一個(gè)存儲器的testbench的激勵可以包含write,read等task。 2.如果DUT中包含雙向信號(inout),在編寫(xiě)testbench時(shí)要注意。需要一個(gè)reg變量來(lái)表示其輸入,還需要一個(gè)wire變量表示其輸出。 3.如果initial塊語(yǔ)句過(guò)于復雜,可以考慮將其分為互補相干的幾個(gè)部分,用數個(gè)initial塊來(lái)描述。在仿真時(shí),這些initial塊會(huì )并發(fā)運行。這樣方便閱讀和修改。 4.每個(gè)testbench都最好包含$stop語(yǔ)句,用以指明仿真何時(shí)結束。 最后提供一個(gè)簡(jiǎn)單的示例(轉自Xilinx文檔): DUT: module shift_reg (clock, reset, load, sel, data, shiftreg); input clock; input reset; input load; input [1:0] sel; input [4:0] data; output [4:0] shiftreg; reg [4:0] shiftreg; always @ (posedge clock) begin if (reset) shiftreg = 0; else if (load) shiftreg = data; else case (sel) 2’b00 : shiftreg = shiftreg; 2’b01 : shiftreg = shiftreg << 1; 2’b10 : shiftreg = shiftreg >> 1; default : shiftreg = shiftreg; endcase end endmodule Testbench: module testbench; // declare testbench name reg clock; reg load; reg reset; // declaration of signals wire [4:0] shiftreg; reg [4:0] data; reg [1:0] sel; // instantiation of the shift_reg design below shift_reg dut(.clock (clock), .load (load), .reset (reset), .shiftreg (shiftreg), .data (data), .sel (sel)); //this process block sets up the free running clock initial begin clock = 0; forever #50 clock = "clock; end initial begin// this process block specifies the stimulus. reset = 1; data = 5’b00000; load = 0; sel = 2’b00; #200 reset = 0; load = 1; #200 data = 5’b00001; #100 sel = 2’b01; load = 0; #200 sel = 2’b10; #1000 $stop; end initial begin// this process block pipes the ASCII results to the //terminal or text editor $timeformat(-9,1,"ns",12); $display(" Time Clk Rst Ld SftRg Data Sel"); $monitor("%t %b %b %b %b %b %b", $realtime, clock, reset, load, shiftreg, data, sel); end endmodule |