如何编写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命令使用库文件或库目录 3.Verilog Testbench信号记录的系统任务: 1). SHM数据库可以记录在设计仿真过程中信号的变化. 它只在probes有效的时间内记录你set probe on的信号的变化. 在记录信号或者波形时需要指出被记录信号的路径,如:tb.module.u1.clk. ……………………………………………………………………………………………………… 关于信号记录的系统任务的说明: 在testbench中使用信号记录的系统任务,就可以将自己需要的部分的结果以及波形文件记录下来(可采用sigalscan工具查看),适用于对较大的系统进行仿真,速度快,优于全局仿真。使用简单,在testbench中添加:initial begin $shm_open("waves.shm"); $shm_probe("要记录信号的路径“,”AS“); #10000 $shm_close; 即可。 4. ncverilog编译的顺序: ncverilog file1 file2 .... 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 output = %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 % 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 |
文章评论(0条评论)
登录后参与讨论