如何编写testbench的总结(非常实用的总结)
1.激勵的設置
相應于被測試模塊的輸入激勵設置為reg型,輸出相應設置為wire類型,雙向端口inout在測試中需要進行處理。
方法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控制端口數據方向,并利用中間變量寄存器改變其值。等于兩個模塊之間用inout雙向口互連。往端口寫(就是往模塊里面輸入)
方法2:使用force和release語句,這種方法不能準確反映雙向端口的信號變化,但這種方法可以反映塊內信號的變化。具體如示:
module test();
wire data_inout;
reg data_reg;
reg link;
#xx;?//延時
force data_inout=1'bx;?//強制作為輸入端口
...............
#xx;
release data_inout;?//釋放輸入端口
endmodule
從文本文件中讀取和寫入向量
1)讀取文本文件:用 $readmemb系統任務從文本文件中讀取二進制向量(可以包含輸入激勵和輸出期望值)。$readmemh 用于讀取十六進制文件。例如:
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)輸出文本文件:打開輸出文件用?$fopen 例如:
integer out_file;?// out_file 是一個文件描述,需要定義為 integer類型
out_file = $fopen ( " cpu.data " ); // cpu.data 是需要打開的文件,也就是最終的輸出文本
設計中的信號值可以通過$fmonitor, $fdisplay,
2. Verilog和Ncverilog命令使用庫文件或庫目錄
ex). ncverilog -f run.f -v lib/lib.v -y lib2 +libext+.v?//一般編譯文件在run.f中, 庫文件在lib.v中,lib2目錄中的.v文件系統自動搜索
使用庫文件或庫目錄,只編譯需要的模塊而不必全部編譯
3.Verilog Testbench信號記錄的系統任務:
1). SHM數據庫可以記錄在設計仿真過程中信號的變化. 它只在probes有效的時間內記錄你set probe on的信號的變化.
ex). $shm_open("waves.shm");?//打開波形數據庫
$shm_probe(top, "AS"); // set probe on "top",
第二個參數:?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
還有一個 M ,表示當前scope的memories, 可以跟上面的結合使用, "AM" "AMS" "AMC"
什么都不加表示當前scope的ports;
$shm_close?//關閉數據庫
2). VCD數據庫也可以記錄在設計仿真過程中信號的變化. 它只記錄你選擇的信號的變化.
ex). $dumpfile("filename");?//打開數據庫
$dumpvars(1, top.u1);?//scope = top.u1, depth = 1
第一個參數表示深度, 為0時記錄所有深度; 第二個參數表示scope,省略時表當前的scope.
$dumpvars; //depth = all scope = all
$dumpvars(0); //depth = all scope = current
$dumpvars(1, top.u1); //depth = 1 scope = top.u1
$dumpoff?//暫停記錄數據改變,信號變化不寫入庫文件中
$dumpon?//重新恢復記錄
3). Debussy fsdb數據庫也可以記錄信號的變化,它的優勢是可以跟debussy結合,方便調試.
如果要在ncverilog仿真時,記錄信號, 首先要設置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系統函數,使用方法參見VCD
注意: 在用ncverilog的時候,為了正確地記錄波形,要使用參數: "+access+rw", 否則沒有讀寫權限
在記錄信號或者波形時需要指出被記錄信號的路徑,如:tb.module.u1.clk.
………………………………………………………………………………………………………
關于信號記錄的系統任務的說明:
在testbench中使用信號記錄的系統任務,就可以將自己需要的部分的結果以及波形文件記錄下來(可采用sigalscan工具查看),適用于對較大的系統進行仿真,速度快,優于全局仿真。使用簡單,在testbench中添加:initial begin
$shm_open("waves.shm");
$shm_probe("要記錄信號的路徑“,”AS“);
#10000
$shm_close; 即可。
4. ncverilog編譯的順序:?ncverilog file1 file2 ....
有時候這些文件存在依存關系,如在file2中要用到在file1中定義的變量,這時候就要注意其編譯的順序是
從后到前,就先編譯file2然后才是file2.
5. 信號的強制賦值force
首先, force語句只能在過程語句中出現,即要在initial 或者 always 中間. 去除force 用 release 語句.
initial begin?force sig1 = 1'b1; ... ; release sig1; end
force可以對wire賦值,這時整個net都被賦值; 也可以對reg賦值.
6.加載測試向量時,避免在時鐘的上下沿變化
為了模擬真實器件的行為,加載測試向量時,避免在時鐘的上下沿變化,而是在時鐘的上升沿延時一個時間單位后,加載的測試向量發生變化。如:
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
//產生隨機數,seed是種子
$random(seed);
ex: din <= $random(20);
//仿真時間,為unsigned型的64位數據
$time
ex:
...
time?condition_happen_time;
...
condition_happen_time = $time;
...
$monitor($time,"data utput = %d", dout);
...
//參數
parameter?para1 = 10,
para2 = 20,
para3 = 30;
//顯示任務
$display();
//監視任務
$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
//時間刻度
`timescale?單位時間/時間精確度
//文件I/O
1.打開文件
integer file_id;
file_id = fopen("file_path/file_name");
2.寫入文件
//$fmonitor只要有變化就一直記錄
$fmonitor(file_id, "%format_char", parameter);
eg:$fmonitor(file_id, "%m: %t in1=%d o1=%h", $time, in1, o1);
//$fwrite需要觸發條件才記錄
$fwrite(file_id, "%format_char", parameter);
//$fdisplay需要觸發條件才記錄
$fdisplay(file_id, "%format_char", parameter);
$fstrobe();
3.讀取文件
integer file_id;
file_id = $fread("file_path/file_name", "r");
4.關閉文件
$fclose(fjile_id);
5.由文件設定存儲器初值
$readmemh("file_name", memory_name"); //初始化數據為十六進制
$readmemb("file_name", memory_name"); //初始化數據為二進制
//仿真控制
$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中的最小延時min,典型延遲typ,最大延時max調整延遲參數
//generate語句,在Verilog-2001中定義.用于表達重復性動作
//必須事先聲明genvar類型變量作為generate循環的指標
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);
//上面例子使用兩個加法器和一個MUX,面積大
//下面例子使用一個加法器和兩個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;?//定義一個沒有輸入輸出的module
reg ……?//將DUT的輸入定義為reg類型
……
wire……?//將DUT的輸出定義為wire類型
……
//在這里例化DUT
initial
begin
……?//在這里添加激勵(可以有多個這樣的結構)
end
always……?//通常在這里定義時鐘信號
initial
//在這里添加比較語句(可選)
end
initial
//在這里添加輸出語句(在屏幕上顯示仿真結果)
end
endmodule
一下介紹一些書寫Testbench的技巧:
1.如果激勵中有一些重復的項目,可以考慮將這些語句編寫成一個task,這樣會給書寫和仿真帶來很大方便。例如,一個存儲器的testbench的激勵可以包含write,read等task。
2.如果DUT中包含雙向信號(inout),在編寫testbench時要注意。需要一個reg變量來表示其輸入,還需要一個wire變量表示其輸出。
3.如果initial塊語句過于復雜,可以考慮將其分為互補相干的幾個部分,用數個initial塊來描述。在仿真時,這些initial塊會并發運行。這樣方便閱讀和修改。
4.每個testbench都最好包含$stop語句,用以指明仿真何時結束。
最后提供一個簡單的示例(轉自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
?
posted @ 2009-10-28 11:24 神一樣驢子 閱讀(508) 評論(1)?編輯
(轉)verilog 中文件輸入/輸出任務
全文地址:http://hubeixunbaoren.blog.163.com/blog/static/111127004200982725032948/
系統函數$fopen用于打開一個文件,并還回一個整數指針.然后,$fdisplay就可以使用這個文件指針在文件中寫入信息,寫完后,則可以使用$fclose系統關閉這個文件
例如:
integer write_out_file;//定義一個文件指針
integer write_out_file=$fopen("write_out_file.txt");
$fdisplay(write_out_file,"@%h\n%h",addr,data);
$fclose("write_out_file");
以上語法是將addr,data分別顯示在"@%h\n%h"中的2個%h的位置,并寫入write_out_file文件指針所指向的write_out_file.txt中.
從文件中讀取數據,可以用 $readmemb?????? $readmemh??從文件中讀入數據,該文件格式是一定的.
reg[7:0] data[47:0];
$readmemh("file_name.txt",data );
就是將file_name.txt中的數據讀入到data數組中,然后就可以使用這些數據了.
????????? 還有一種方式可以把指定的數據放入指定的存儲器地址單元內,就是在存放數據的文本文件內,給相應的數據規定其內存地址,形式如下:
@address_in_hexadecimal data
@2f 20
?
兩個系統任務可以在仿真的任何時刻被執行使用,其使用格式共有以下六種:
?
???????????? 1)???? $readmemb("<數據文件名>",<存貯器名>);
???????????? 2)???? $readmemb("<數據文件名>",<存貯器名>,<起始地址>);
???????????? 3)???? $readmemb("<數據文件名>",<存貯器名>,<起始地址>,<結束地址>);
???????????? 4)???? $readmemh("<數據文件名>",<存貯器名>);
???????????? 5)???? $readmemh("<數據文件名>",<存貯器名>,<起始地址>);
???????????? 6)???? $readmemh("<數據文件名>",<存貯器名>,<起始地址>,<結束地址>);
?
在這兩個系統任務中,被讀取的數據文件的內容只能包含:空白位置(空格,換行,制表格(tab)和form-feeds),注釋行(//形式的和/*...*/形式的都允許),二進制或十六進制的數字。數字中不能包含位寬說明和格式說明,對于$readmemb系統任務,每個數字必須是二進制數字,對于$readmemh系統任務,每個數字必須是十六進制數字。數字中不定值x或X,高阻值z或Z,和下劃線(_)的使用方法及代表的意義與一般Verilog HDL程序中的用法及意義是一樣的。另外數字必須用空白位置或注釋行來分隔開。
?
在下面的討論中,地址一詞指對存貯器(memory)建模的數組的尋址指針。當數據文件被讀取時,每一個被讀取的數字都被存放到地址連續的存貯器單元中去。存貯器單元的存放地址范圍由系統任務聲明語句中的起始地址和結束地址來說明,每個數據的存放地址在數據文件中進行說明。當地址出現在數據文件中,其格式為字符“@”后跟上十六進制數。如:
?
@hh...h
?
對于這個十六進制的地址數中,允許大寫和小寫的數字。在字符“@”和數字之間不允許存在空白位置。可以在數據文件里出現多個地址。當系統任務遇到一個地址說明時,系統任務將該地址后的數據存放到存貯器中相應的地址單元中去。
?
對于上面六種系統任務格式,需補充說明以下五點:
?
1)?????? 如果系統任務聲明語句中和數據文件里都沒有進行地址說明,則缺省的存放起始地址為該存貯器定義語句中的起始地址。數據文件里的數據被連續存放到該存貯器中,直到該存貯器單元存滿為止或數據文件里的數據存完。
2)?????? 如果系統任務中說明了存放的起始地址,沒有說明存放的結束地址,則數據從起始地址開始存放,存放到該存貯器定義語句中的結束地址為止。
3)?????? 如果在系統任務聲明語句中,起始地址和結束地址都進行了說明,則數據文件里的數據按該起始地址開始存放到存貯器單元中,直到該結束地址,而不考慮該存貯器的定義語句中的起始地址和結束地址。
4)?????? 如果地址信息在系統任務和數據文件里都進行了說明,那么數據文件里的地址必須在系統任務中地址參數聲明的范圍之內。否則將提示錯誤信息,并且裝載數據到存貯器中的操作被中斷。
5)?????? 如果數據文件里的數據個數和系統任務中起始地址及結束地址暗示的數據個數不同的話,也要提示錯誤信息。
?
下面舉例說明:
先定義一個有256個地址的字節存貯器 mem:
?
reg[7:0] mem[1:256];
?
下面給出的系統任務以各自不同的方式裝載數據到存貯器mem中。
?
initial? $readmemh("mem.data",mem);
initial? $readmemh("mem.data",mem,16);
initial? $readmemh("mem.data",mem,128,1);
?
第一條語句在仿真時刻為0時,將裝載數據到以地址是1的存貯器單元為起始存放單元的存貯器中去。第二條語句將裝載數據到以單元地址是16的存貯器單元為起始存放單元的存貯器中去,一直到地址是256的單元為止。第三條語句將從地址是128的單元開始裝載數據,一直到地址為1的單元。在第三種情況中,當裝載完畢,系統要檢查在數據文件里是否有128個數據,如果沒有,系統將提示錯誤信息。
?
?
?
"引用 參考"
1.打開文件
integer file_id;
file_id = fopen("file_path/file_name");
2.寫入文件
//$fmonitor只要有變化就一直記錄
$fmonitor(file_id, "%format_char", parameter);
eg:$fmonitor(file_id, "%m: %t in1=%d o1=%h", $time, in1, o1);
//$fwrite需要觸發條件才記錄
$fwrite(file_id, "%format_char", parameter);
//$fdisplay需要觸發條件才記錄
$fdisplay(file_id, "%format_char", parameter);
$fstrobe();
3.讀取文件
integer file_id;
file_id = $fread("file_path/file_name", "r");
4.關閉文件
$fclose(fjile_id);
5.由文件設定存儲器初值
$readmemh("file_name", memory_name"); //初始化數據為十六進制
$readmemb("file_name", memory_name"); //初始化數據為二進制
?
轉:
http://zengbo.blogbus.com/logs/19569780.htmlVerilog
提供了豐富的系統函數,這為Testbench的編寫提供了方便。尤其是IEEE1364-2005,其系統級建模的能力更強。
??? 以前我一般常用到的系統函數只有幾個:$readmemb,$readmemh,$display,$fmonitor,$fwrite,$fopen,$fclose等。通常需要對文件作預處理,才能用于Testbench讀取。今天又嘗試了幾個其他的文件輸入輸出函數,不需要對文件進行預處理,直接使用需要的文件,只對需要的部分進行讀取。
??? $fseek,文件定位,可以從任意點對文件進行操作;
??? $fscanf,對文件一行進行讀寫。
??? 下面是一些常見的應用:
??? 1、讀寫文件
`timescale 1 ns/1 ns
module FileIO_tb;
integer fp_r, fp_w, cnt;
reg [7:0] reg1, reg2, reg3;
initial begin
? fp_r = $fopen("data_in.txt", "r");
? fp_w = $fopen("data_out.txt", "w");
??
? while(!$feof(fp_r)) begin
??? cnt = $fscanf(fp_r, "%d %d %d", reg1, reg2, reg3);
??? $display("%d %d %d", reg1, reg2, reg3);
??? $fwrite(fp_w, "%d %d %d\n", reg3, reg2, reg1);
? end
??
? $fclose(fp_r);
? $fclose(fp_w);
end
endmodule
??? 2、
integer file, char;
reg eof;
initial begin
? ?file = $fopenr("myfile.txt");
? ?eof = 0;
? ?while (eof == 0) begin
? ? ? ?char = $fgetc(file);
? ? ? ?eof = $feof (file);
? ? ? ?$display ("%s", char);?
???end
end
??? 3、文件處理定位
`define SEEK_SET 0
`define SEEK_CUR 1
`define SEEK_END 2
integer file, offset, position, r;
r = $fseek(file, 0, `SEEK_SET); /* Beginning */
r = $fseek(file, 0, `SEEK_CUR); /* No effect */
r = $fseek(file, 0, `SEEK_END); /* End of file */
r = $fseek(file, position, `SEEK_SET); /* Previous loc */
??? 4、
integer r, file, start, count;
reg [15:0] mem[0:10], r16;
r = $fread(file, mem[0], start, count);
r = $fread(file, r16);
??? 5、
integer file, position;
position = $ftell(file);
?? 6、
integer file, r, a, b;
reg [80*8:1] string;
file = $fopenw("output.log");
r = $sformat(string, "Formatted %d %x", a, b);
r = $sprintf(string, "Formatted %d %x", a, b);
r = $fprintf(file, "Formatted %d %x", a, b);
?? 7、
integer file, r;
file = $fopenw("output.log");
r = $fflush(file);
??? 8、
// This is a pattern file - read_pattern.pat?
// time bin dec hex
10: 001 1 1
20.0: 010 20 020
50.02: 111 5 FFF
62.345: 100 4 DEADBEEF
75.789: XXX 2 ZzZzZzZz
`timescale 1ns / 10 ps
`define EOF 32'hFFFF_FFFF
`define NULL 0
`define MAX_LINE_LENGTH 1000
module read_pattern;
integer file, c, r;
reg [3:0] bin;
reg [31:0] dec, hex;
real real_time;
reg [8*`MAX_LINE_LENGTH:0] line; /* Line of text read from file */
initial
??? begin : file_block
??? $timeformat(-9, 3, "ns", 6);
??? $display("time bin decimal hex");
??? file = $fopenr("read_pattern.pat");
??? if (file == `NULL) // If error opening file
??????? disable file_block; // Just quit
??? c = $fgetc(file);
??? while (c != `EOF)
??????? begin
??????? /* Check the first character for comment */
??????? if (c == "/")
??????????? r = $fgets(line, `MAX_LINE_LENGTH, file);
??????? else
??????????? begin
??????????? // Push the character back to the file then read the next time
??????????? r = $ungetc(c, file);
??????????? r = $fscanf(file," %f:\n", real_time);
??????????? // Wait until the absolute time in the file, then read stimulus
??????????? if ($realtime > real_time)
??????????????? $display("Error - absolute time in file is out of order - %t",
??????????????????????? real_time);
??????????????? else
??????????????????? #(real_time - $realtime)
??????????????????????? r = $fscanf(file," %b %d %h\n",bin,dec,hex);
??????????????? end // if c else
??????????? c = $fgetc(file);
??????? end // while not EOF
??? r = $fcloser(file);
??? end // initial
// Display changes to the signals
always @(bin or dec or hex)
??? $display("%t %b %d %h", $realtime, bin, dec, hex);
endmodule // read_pattern
??? 9、自動比較輸出結果
`define EOF 32'hFFFF_FFFF
`define NULL 0
`define MAX_LINE_LENGTH 1000
module compare;
integer file, r;
reg a, b, expect, clock;
wire out;
reg [`MAX_LINE_LENGTH*8:1];
parameter cycle = 20;
initial
??? begin : file_block
??? $display("Time Stim Expect Output");
??? clock = 0;
??? file = $fopenr("compare.pat");
??? if (file == `NULL)
??????? disable file_block;
??? r = $fgets(line, MAX_LINE_LENGTH, file); // Skip comments
??? r = $fgets(line, MAX_LINE_LENGTH, file);
??? while (!$feof(file))
??????? begin
??????? // Wait until rising clock, read stimulus
??????? @(posedge clock)
??????? r = $fscanf(file, " %b %b %b\n", a, b, expect);
??????? // Wait just before the end of cycle to do compare
??????? #(cycle - 1)
??????? $display("%d %b %b %b %b", $stime, a, b, expect, out);
??????? $strobe_compare(expect, out);
??????? end // while not EOF
??? r = $fcloser(file);
??? $stop;
??? end // initial
always #(cycle / 2) clock = !clock; // Clock generator
and #4 (out, a, b); // Circuit under test
endmodule // compare
??? 10、從文件中讀數據到mem(這個好像一般人用的最多了)
`define EOF 32'HFFFF_FFFF?
`define MEM_SIZE 200_000?
module load_mem;?
integer file, i;?
reg [7:0] mem[0:`MEM_SIZE];?
reg [80*8:1] file_name;?
initial ????
begin ????
file_name = "data.bin"; ????
file = $fopenr(file_name); ????
i = $fread(file, mem[0]); ????
$display("Loaded %0d entries \n", i); ????
i = $fcloser(file); ????
$stop; ????
end endmodule // load_mem
總結
以上是生活随笔為你收集整理的如何编写testbench的总结(非常实用的总结)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: testbench
- 下一篇: modelsim与modelsim_al