编写testbench的一些技巧
编写高效率的testbench
编写高效率的testbench简介:由于设计的规模越来越大也越来越复杂,数字设计的验证已经成为一个日益困难和繁琐的任务。
验证工程师们依靠一些验证工具和方法来应付这个挑战。
对于几百万门的大型设计,工程师们一般使用一套形式验证(formal verification)工具。
然而对于一些小型的设计,设计工程师常常发现用带有testbench的HDL仿真器就可以很好地进行验证。
Testbench已经成为一个验证高级语言(HLL --High-Level Language) 设计的标准方法。
通常testbench完成如下的任务:1.实例化需要测试的设计(DUT);2.通过对DUT模型加载测试向量来仿真设计;3.将输出结果到终端或波形窗口中加以视觉检视;4.另外,将实际结果和预期结果进行比较。
通常testbench用工业标准的VHDL或Verilog硬件描述语言来编写。
Testbench调用功能设计,然后进行仿真。
复杂的testbench完成一些附加的功能—例如它们包含一些逻辑来选择产生合适的设计激励或比较实际结果和预期结果。
后续的章节描述了一个仔细构建的testbench的结构,并且提供了一个自动比较实际结果与预期结果的进行自我检查的testbench例子。
图1给出了一个如上所描述步骤的标准HDL验证流程。
由于testbench使用VHDL或Verilog来描述,testbench的验证过程可以根据不同的平台或不同的软件工具实现。
由于VHDL或Verilog是公开的通用标准,使用VHDL或Verilog编写的testbench以后也可以毫无困难地重用(reuse)。
图1使用Testbench的HDL验证流程构建TestbenchTestbench用VHDL或Verilog来编写。
由于testbench只用来进行仿真,它们没有那些适用于综合的RTL语言子集的语法约束限制,而是所有的行为结构都可以使用。
因而testbench可以编写的更为通用,使得它们可以更容易维护。
testbench常用语法总结
testbench常⽤语法总结1.testbench总体代码结构`timescale 1ns/1ps //时间精度`define clk_perilod 20 //时钟周期可变module test_file_tb;//==================<端⼝>==================================================reg clk ; //时钟,50Mhzreg rst_n ; //复位,低电平有效reg [XX:0] in ; //wire [XX:0] out ; ////--------------------------------------------------------------------------//-- 模块例化//--------------------------------------------------------------------------my_design u_my_design(.clk (clk ),.rst_n (rst_n ),.in (in ),.out (out ));//----------------------------------------------------------------------//-- 时钟信号和复位信号//----------------------------------------------------------------------initial beginclk = 0;forever#(`Clock/2) clk = ~clk;endinitial beginrst_n = 0; #(`Clock*20+1);rst_n = 1;end//----------------------------------------------------------------------//-- 设计输⼊信号//----------------------------------------------------------------------initial beginin = 0;#(`Clock*20+2); //初始化完成$stop;endendmodule在这⾥插⼊代码⽚2.时钟激励的编写`timescale 1ns/1ps //时间精度`define Clock 20 //时钟周期//========================================================================== //== ⽅法⼀,50%占空⽐//========================================================================== initial beginclk = 0;forever#(`Clock/2) clk = ~clk;end//========================================================================== //== ⽅法⼆,50%占空⽐//========================================================================== initial beginclk = 0;always#(`Clock/2) clk = ~clk;end//========================================================================== //== ⽅法三,产⽣固定输⼊的时钟脉冲//========================================================================== initial beginclk = 0;repeat(6)#(`Clock/2) clk = ~clk;end//========================================================================== //== ⽅法四,⾮50%占空⽐//========================================================================== initial beginclk = 0;forever begin#((`Clock/2)-2) clk = 0;#((`Clock/2)+2) clk = 1;endend3.复位信号`timescale 1ns/1ps //时间精度`define Clock 20 //时钟周期//========================================================================== //== ⽅法⼀,异步复位//========================================================================== initial beginrst_n = 0; #(`Clock*20+1);rst_n = 1;end//========================================================================== //== ⽅法⼆,同步复位//========================================================================== initial beginrst_n = 0; #(`Clock*20);rst_n = 1;end4.task使⽤//========================================================================== //== 输⼊信号任务封装//========================================================================== task i_data;input [7:0] dut_data;begin@(posedge data_en); send_data=0;@(posedge data_en); send_data=dut_data[0];@(posedge data_en); send_data=dut_data[1];@(posedge data_en); send_data=dut_data[2];@(posedge data_en); send_data=dut_data[3];@(posedge data_en); send_data=dut_data[4];@(posedge data_en); send_data=dut_data[5];@(posedge data_en); send_data=dut_data[6];@(posedge data_en); send_data=dut_data[7];@(posedge data_en); send_data=1;#100;endendtask//调⽤⽅法:i_data(8'hXX);//========================================================================== //== 多输⼊信号任务封装//========================================================================== task more_input;input [ 7:0] a;input [ 7:0] b;input [31:0] times;output [ 8:0] c;beginrepeat(times) @(posedge clk) //等待 times 个时钟上升沿c=a+b;endendtask//调⽤⽅法:more_input(x,y,t,z); //按声明顺序5.repeat ,wait函数//==========================================//== repeat重复执⾏//==========================================initial beginstart = 1;repeat(5) @(posedge clk) //等待5个时钟上升沿start = 0;endinitial beginrepeat(10)begin...//执⾏10次endend//===========================================//== wait为电平触发//==========================================initial beginstart = 1;wait(en); //等待en==1start = 0;end6.随机数产⽣$random //产⽣随机数$random % n //产⽣范围 {-n,n} 的随机数{$random} % n //产⽣范围 { 0,n} 的随机数7.⽂本输⼊输出reg [a:0] data_mem [0:b]; //定义位宽为(a+1)深度为(b+1)的存储器$readmemb/$readmemh("<读⼊⽂件名>",<存储器名>);$readmemb/$readmemh("<读⼊⽂件名>",<存储器名>,<起始地址>);$readmemb/$readmemh("<读⼊⽂件名>",<存储器名>,<起始地址>,<结束地址>);$readmemb/*------------------------------------------------------------------------*\读取⼆进制数据,读取⽂件内容只能包含:空⽩位置,注释⾏,⼆进制数数据中不能包含位宽说明和格式说明,每个数字必须是⼆进制数字。
如何编写testbench的总结(非常实用的总结)
如何编写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 定义存储器meminitial $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 scropeS -- Ports of the specified scope and below, excluding library cellsC -- Ports of the specified scope and below, including library cellsAS -- Signals of the specified scope and below, excluding library cellsAC -- 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_PLIPtrfsdb数据库⽂件的记录⽅法,是使⽤$fsdbDumpfile和$fsdbDumpvars系统函数,使⽤⽅法参见VCD注意: 在⽤ncverilog的时候,为了正确地记录波形,要使⽤参数: "+access+rw", 否则没有读写权限在记录信号或者波形时需要指出被记录信号的路径,如:tb.module.u1.clk.………………………………………………………………………………………………………关于信号记录的系统任务的说明:在testbench中使⽤信号记录的系统任务,就可以将⾃⼰需要的部分的结果以及波形⽂件记录下来(可采⽤sigalscan⼯具查看),适⽤于对较⼤的系统进⾏仿真,速度快,优于全局仿真。
如何写testbench
如何编写testbench今天,我来带领大家写一个简单的testbench ,顺便讲解如何写好一个testbench 以及写testbench 时应该注意的地方。
在讲解testbench 之前,我们先看一下前面的那个AND_2程序的仿真图,如下:如上图中所标,在1处,B 已经为低电平了,但是输出C 仍然为高电平,这样求与运算就会出错。
在2处,A 和B 都是低电平了,C 仍然为高电平,直到下次出现时钟的上升沿为止,为什么会这样呢?编译的时候并没有报错,呵呵,出了怪事了啊!其实编译器只能检查处语法错误,无法检测到逻辑错误,这个图给出的结果和我们程序所表达的结果一样,但是这并不是我们所要的求与运算,我们想要的是A 和B 同时为高电平时,C 才输出高电平。
我们把程序的敏感列表改为:always@(posedge clk or negedge rst or A or B)就可以了,把A 的电平改变和B的电平改变都加进敏感列表,激励不变,所得到的仿真图:这才是我们所要的求与运算!好了,现在开始讲如何写testbench 。
Testbench 不像RTL 级代码,可以用高级行为语句,不用考虑其可综合性,这样就能写出高效的检测代码。
在语法上,testbench 和可综合代码一样,都是类C 结构。
好了,我们开始吧!1,建立工程等,与之前的一样,但是在创建文件的时候,我们一次创建两个。
取名分别为ParallelSerial_Mult 和ParallelSerial_Mult_test。
创建完成后,如下图:这两个代码分别如下:moduleParallelSerial_Mult(Clk,Rst,MultiplicandIn,MultiplierIn,Load,Product,Out_en);parameter N=8;parameter CYCLES=3;input Clk,Rst,Load;input[N-1:0]MultiplicandIn,MultiplierIn;output[2*N-1:0]Product;output Out_en;reg[2*N-1:0]Product;wire Out_en;reg[N-1:0]Multiplicand;reg[2*N-1:0]NextProduct;reg[CYCLES:0]Count;reg Busy;wire[N-1:0]Sum;wire Carry;assign{Carry,Sum}=Multiplicand+Product[2*N-1:N];assign Out_en=Count[CYCLES];always@(posedge Clk or negedge Rst)if(!Rst)beginMultiplicand<=0;Count<=0;Product<=0;Busy<=0;endelsebeginProduct<=NextProduct;if(Load)beginMultiplicand<=MultiplicandIn;Count<=0;Busy<=1'b1;endelsebeginif(Busy)Count<=Count+1'b1;if(Count[CYCLES])beginCount<=0;Busy<=1'b0;endendendalways@(Load or MultiplierIn or Product or Count[CYCLES]or Carry or Sum) casex({Product[0],Count[CYCLES],Load})3'bxx1:NextProduct={{N{1'b0}},MultiplierIn[N-1:0]};3'b100:NextProduct={Carry,Sum,Product[N-1:1]};3'b000:NextProduct={1'b0,Product[2*N-1:1]};default:NextProduct=Product;endcaseendmodule这个代码是书中第120页的8位乘法器。
ModelSim10.1c中testbench写法及注意事项
2、开始写testbench•如右图,单击New File 弹出一个名称为Untitled-1的空文件。
这个文件就是我们要用来写testbench 的文件(现在“保存”快捷方式还是暗色的)。
单击New File灰色丆非激活状态Untitled-1•如图,在第一行输入“module AND_2_test();”后,“保存”快捷方式变亮,呈彩色。
单击保存,命名为“AND_2_test.v”,此时关键字高亮显示,如下图非高亮显示彩色丆激活态高亮显示•输入testbench代码如右图所示:–initial为初始化语句,只执行一遍,$stop()表示模拟停止;–第一个always语句产生周期为20nm的方波,后两个分别为A和B的输入;–inst是AND_2实例化的名;–initial语句和always语句是并行执行的。
注意丆输出端口是wire型6.1之前的版本模块名要写成AND_2_test;不能有()•C6.2之后的版本可有()•C也可不要()。
建议写成AND_2_test(clk,rst,A,B,C);然后在端口声明处加output clk,rst,A,B,C;这样在编译内部优化时会保留所声明的端口信号丆查看覆盖率时就可以添加波形窗口记得先要初始化实例化模块所要实例化的模块实例化的名字•注意:模块名后面要有“;”结束,endmodule后面不能再有“;”(同时注意,这些都是半角符号,否则出错了很难查错1000nm之后仿真停止)。
•这个例子很简单,所以没有写监控和比较输出部分。
•下面看如何添加•如右图,选择Project工作区,右键单击AND_2.v,弹出菜单如图,选择Add toProject\Existing File..添加我们刚刚写好的testbench(也可以从这里直接添加空白文档,然后写testbench代码),如下图:可先把空白文档添加进工程丆然后再编写文档代码。
选择文件添加该文件testbench文档•单击Compile All快捷按钮,如右图所示,下面的交互区显示“# Compile ofAND_2.v wassuccessful.•# Compile ofAND_2_test.v wassuccessful.•# 2 compiles, 0failed with noerrors. ”编译所有文档编译成功添加模拟仿真•单击Simulation快捷按钮,弹出StartSimulation选项卡,如右图。
Testbench写法总结
outer_port_tb_wire,inner_port_tb_wire);
end
else
begin
$display("\n **** time=%t ****",$time);
$display("ERROR! out_en=%d",out_en_tb);
$display("ERROR! outer_port_tb_wire != inner_port_tb_wire" );
$display("ERROR! outer_port_tb_wire=%d, inner_port_tb_wire=%d",
outer_port_tb_wire,inner_port_tb_wire);
end
end
endmodule
验证该双向端口的testbench结构如图2所示。
这是一个self-checking testbench,可以自动检查仿真结果是否正确,并在Modelsim控制台上打印出提示信息。图中Monitor完成信号采样、结果自动比较的功能。
testbench的工作过程为
1)out_en=1时,双向端口处于输出状态,testbench给inner_port_tb_reg信号赋值,然后读取outer_port_tb_wire的值,如果两者一致,双向端口工作正常。
module tb();
reg[7:0] inner_port_tb_reg;
wire[7:0] inner_port_tb_wire;
reg[7:0] outer_port_tb_reg;
wire[7:0] outer_port_tb_wire;
十大基本功之testbench
⼗⼤基本功之testbench1. 激励的产⽣对于testbench⽽⾔,端⼝应当和被测试的module⼀⼀对应。
端⼝分为input,output和inout类型产⽣激励信号的时候,input对应的端⼝应当申明为reg, output对应的端⼝申明为wire,inout端⼝⽐较特殊,下⾯专门讲解。
1)直接赋值。
⼀般⽤initial块给信号赋初值,initial块执⾏⼀次,always或者forever表⽰由事件激发反复执⾏。
举例,⼀个modulemodule exam();reg rst_n;reg clk;reg data;initialbeginclk=1'b0;rst=1'b1;#10rst=1'b0;#500rst=1'b1;endalwaysbegin#10clk=~clk;end⼤家应该注意到有个#符号,该符号的意思是指延迟相应的时间单位。
该时间单位由timscale决定.⼀般在testbench的开头定义时间单位和仿真精度,⽐如`timescale 1ns/1ps,前⾯⼀个是代表时间单位,后⾯⼀个代表仿真时间精度。
以上⾯的例⼦⽽⾔,⼀个时钟周期是20个单位,也就是20ns。
⽽仿真时间精度的概念就是,你能看到1.001ns时对应的信号值,⽽假如timescale 1ns/1ns,1.001ns时候的值就⽆法看到。
对于⼀个设计⽽⾔,时间刻度应该统⼀,如果设计⽂件和testbench⾥⾯的时间刻度不⼀致,仿真器默认以testbench为准。
⼀个较好的办法是写⼀个global.v⽂件,然后⽤include的办法,可以防⽌这个问题。
对于反复执⾏的操作,可写成task,然后调⽤,⽐如task load_count;input [3:0] load_value;begin@(negedge clk_50);$display($time, " << Loading the counter with %h >>", load_value);load_l = 1’b0;count_in = load_value;@(negedge clk_50);load_l = 1’b1;endendtask //of load_countinitialbeginload_count(4’hA); // 调⽤taskend其他像forever,for,function等等语句⽤法类似,虽然不⼀定都能综合,但是⽤在testbench⾥⾯很⽅便,⼤家可以⾃⾏查阅参考⽂档2)⽂件输⼊有时候,需要⼤量的数据输⼊,直接赋值的话⽐较繁琐,可以先⽣成数据,再将数据读⼊到寄存器中,需要时取出即可。
我的testbench书写总结
占空比为 50%的时钟`timescale 1ns/1ns //定义时间的尺度和精度,其中精度和小树部分挂钩parameter period=4’d10;reg clk; //时钟是输入给DUT的信号必须声明为reg类型initialbeginclk=1’b0; //定义clk的初始状态为低电平forever#( period/2) clk=~clk;endparameter period=4’d10;reg clk;initialbeginclk=1’b0; //定义clk的初始状态为低电平endalways #( period/2) clk=~clk;占空比非50%的时钟信号parameter HIGH_TIME=4,LOW_TIME=6;reg clk;initialbeginclk=1’b0; //定义clk的初始状态为低电平endalwaysbegin# LOW_TIME clk=1’b1;//0~LOW_TIME为低电平# HIGH_TIME clk=1’b0; //从low_time~(low_time+high_time)为高电平end固定数目的时钟信号parameter PulseCount=4,PERIOD=10;reg clk;initialbeginclk=1’b0; //定义clk的初始状态为低电平repeat(PulseCount) //相对于下面的语句重复执行4次#(PERIOD/2) clk=~clk;End//先低电平半个周期,然后再产生两个完整周期的脉冲,结束时clk为低电平parameter Phase_Shift=2;PERIOD=10;reg source_clk;wire derive_clk;//这里是wire好像没有多大用处,要是能用reg就有用了//用initial语句生成源时钟initialbeginclk=1’b0; //定义clk的初始状态为低电平forever#( PERIOD/2) source_clk=~source_clk;EndAssign #Phase_Shift derive_clk=source_clk; //生成派生时钟,要延后2ns2,对于仿真器,reg在没有赋初始值的情况下默认的值为’X’即不定值,而wire的默认值为’Z’即高阻态。
怎样写testbench
怎样写testbench本文的实际编程环境:ISE 6.2i.03ModelSim 5.8 SESynplify Pro 7.6编程语言 VHDL在ISE中调用ModelSim进行仿真一、基本概念和基础知识Testbench不仅要产生激励也就是输入,还要验证响应也就是输出。
当然也可以只产生激励,然后通过波形窗口通过人工的方法去验证波形,这种方法只能适用于小规模的设计。
在ISE环境中,当前资源操作窗显示了资源管理窗口中选中的资源文件能进行的相关操作。
在资源管理窗口选中了testbench文件后,在当前资源操作窗显示的ModelSim Simulator中显示了4种能进行的模拟操作,分别是:Simulator Behavioral Model(功能仿真)、Simulator Post-translate VHDL Model(翻译后仿真)、Simulator Post-Map VHDL Model(映射后仿真)、Simulator Post-Place & Route VHDL Model(布局布线后仿真)。
如图1所示:图1l Simulator Behavioral Model 也就是所说的功能仿真、行为仿真、前仿真。
验证功能是否正确,这是设计的第一步。
功能仿真正确的程序不一定能被正确综合,也就是硬件实现。
有的在综合时报错误,有的虽然能综合但结果并不正确。
当然,功能仿真如果都不能通过,以后的步骤也就无法进行。
这是必做的仿真。
l Simulator Post-translate VHDL Model 也就是翻译后仿真。
对源程序进行编译后首先排除了语法错误,对一些像类属命令(Generic)、生成语句(Generate)等进行了展开。
不是必做的仿真。
l Simulator Post-Map VHDL Model也就是映射后仿真。
不同的器件内部结构也不尽相同,映射的作用就是将综合后产生的网表文件对应到实际的器件上去。
vivado testbench 语法
在 Vivado中,编写 Testbench 是进行数字电路仿真和验证的重要步骤。
以下是 Vivado Testbench 的一些语法要点:1. 语言选择:Vivado支持使用 SystemVerilog 或 Verilog 作为 Testbench 的语言。
你可以根据需要选择其中之一进行编写。
2. 模块实例化:Testbench 通常包含一个顶层模块来实例化待测试的模块。
你需要创建一个模块,并使用待测试模块的端口信号进行实例化。
3. 时钟和复位:在 Testbench 中,你通常需要生成时钟信号和复位信号,并将其应用于待测试模块的输入端口。
你可以使用 `fork...join` 结构和 `repeat` 或 `forever` 循环来生成时钟信号。
4. 输入模拟:在 Testbench 中,你需要为待测试模块的输入端口提供合适的模拟数据。
你可以使用 `#` 操作符来延迟信号的更新,以模拟不同的输入情况。
5. 断言和检查:在 Testbench 中,你可以使用断言语句来验证待测试模块的行为是否符合预期。
Vivado 支持使用 `assert` 和 `assume` 等关键字来定义断言。
6. 输出比较:在仿真结束后,你可以比较待测试模块的输出信号与预期结果进行验证。
你可以使用 `$display` 或 `$monitor` 等系统任务来显示输出信号的值。
7. 仿真控制:你可以使用 `initial` 块或 `always` 块来控制 Testbench 的仿真行为。
你可以使用 `#` 操作符来延迟仿真时间或 `disable` 关键字来停止仿真。
8. 仿真时长:在 Vivado 中,你可以使用 `run` 或 `run XXns` 命令来指定仿真运行的时长。
默认情况下,仿真会一直运行直到遇到 `$finish` 或 `$stop` 系统任务。
以上是 Vivado Testbench 的一些语法要点。
vivadotestbench写法
主题:vivadotestbench编写方法内容:1. 什么是vivadotestbench?vivadotestbench是一个用于编写Verilog的测试台,用于对Verilog 模块进行仿真和验证。
它可以帮助工程师们在Verilog设计的早期阶段进行功能验证和性能评估,以确保设计的稳定性和正确性。
2. vivadotestbench的基本结构vivadotestbench通常包含以下基本结构:模块实例化、时钟和复位初始化、输入数据生成、仿真控制和输出检测。
这些基本结构构成了一个完整的测试台,可以用于对Verilog模块进行全面的验证和测试。
3. vivadotestbench的编写步骤编写vivadotestbench的步骤可以分为以下几个部分:3.1 模块实例化:首先需要实例化待测模块,并且连接时钟、复位信号和输入输出端口。
3.2 时钟和复位初始化:在测试台中需要为待测模块提供时钟信号,并对复位信号进行初始化。
3.3 输入数据生成:根据待测模块的输入端口,生成相应的测试数据,并将其输入到待测模块中。
3.4 仿真控制:控制仿真的开始、暂停和结束,以及执行仿真的时长和步长等。
3.5 输出检测:对待测模块的输出进行检测和比对,以验证其正确性和稳定性。
4. vivadotestbench的常见问题及解决方法在编写vivadotestbench的过程中,可能会遇到一些常见的问题,例如时序约束不准确、测试数据生成不完整、输出检测逻辑错误等。
针对这些问题,可以采取一些解决方法,如优化时序约束、增加测试数据生成的覆盖率、修正输出检测逻辑等。
5. vivadotestbench的优点和应用场景vivadotestbench具有易用性好、灵活性强、功能全面等优点,适用于对Verilog模块进行全面的仿真和验证。
它可以帮助工程师们提高设计的稳定性和正确性,加快设计的上线速度,降低设计的风险和成本。
结论:vivadotestbench是一个强大的Verilog测试台,可以帮助工程师们在Verilog设计的早期阶段进行全面的功能验证和性能评估。
(verilog和vhdl)Testbench编程指南
(verilog和vhdl)Testbench编程指南TestBench编程指南如今数字设计的规模变得越来越庞大,设计的复杂程度也越来越高,这就使得设计的验证变得越来越困难,而且费时费力。
为了应对这种挑战,验证工程师依靠各种验证工具和方法。
对于大型设计,如几百万门的设计,通常采用一整套正式的验证工具。
然而,对于小一些的设计,设计工程师发现往往采用带TestBench的HDL仿真工具是最好的途径。
TestBench已经变成验证高级语言设计的一种标准的方法。
通常,TestBench执行以下任务:z例化设计,使其可测试(DUT-design under test);z通过将测试向量应用到模型来仿真例化后的可测试的设计;z将结果输出到终端,或者输出波形窗口;z将真实的结果和期望的结果进行比较;一般,TestBench采用工业标准的VHDL或者Verilog硬件描述语言来编写。
TestBench调用功能设计,然后仿真。
复杂的测试文件执行附加功能――例如,他们包含逻辑以决定合适的设计激励或者比较真实的结果和期望的结果。
以下章节将讨论一个组织良好的测试文件的组成,以及例举了一个带有自检的测试文件(自动将真实的结果和预期的结果进行比较)。
下图是一个标准的HDL验证的流程。
自从测试文件可以用VHDL或者Verilog编写以来,测试验证流程就可以在平台和供应商的工具交叉进行。
同时,由于VHDL和Verilog都是标准的公用的语言,所以用VHDL或者是Verilog描述的验证可以很简单的被再使用。
图1. HDL验证流程测试文件构成:测试文件可以采用VHDL或者Verilog语言编写。
由于测试文件只是用来仿真的,他们就不被用于综合的RTL语言子集的语法所约束。
相反,所有行为结构都可以被使用。
这样,测试文件可以被写的更通用,更易于维护。
所有的测试文件都包含以下基本内容,如表1。
如上所属,测试文件经常同时包含附加功能,如结果的可视化显示和内建错误检测。
Testbench文件编写纪要(Verilog)
Testbench⽂件编写纪要(Verilog)之前在使⽤Verilog做FPGA项⽬中、以及其他⼀些不同的场合下,零散的写过⼀些练⼿性质的testbench⽂件,开始⼏次写的时候,每次都会因为⼀些基本的东西没记住、写的很不熟练,后⾯写的时候稍微熟练了⼀点、但是整体编写下来⽐较零碎不成体系,所以在这⾥简要记录⼀下⼀般情况下、针对⼩型的verilog模块进⾏测试时所需要使⽤到的testbench⽂件的编写要点。
本⽂主要参考了在⽹上找到的Lattice公司的“A Verilog HDL Test Bench Primer”⼿册中的有关内容。
谢谢!模块实例化、reg&wire声明、initial和always块的使⽤需要测试的模块(Verilog-module)被称为DUT(Design Under Test),在testbench中需要对⼀个或者多个DUT进⾏实例化。
Testbench中的顶层module不需要定义输⼊和输出。
Testbench中连接到DUT instance的输⼊的为reg类型、连接到DUT instance的输出的为wire类型。
对于DUT的inout类型变量,在testbench中需要分别使⽤reg、wire类型的变量进⾏调⽤。
例如,对于下⾯这样⼀个待测试module:module bidir_infer (DATA, READ_WRITE);input READ_WRITE ;inout [1:0] DATA ;reg [1:0] LATCH_OUT ;always @ (READ_WRITE or DATA) beginif (READ_WRITE == 1)LATCH_OUT <= DATA;endassign DATA = (READ_WRITE == 1) ? 2'bZ : LATCH_OUT;endmodule为其设计的testbench⽂件可以是:module test_bidir_ver;reg read_writet;reg [1:0] data_in;wire [1:0] datat, data_out;bidir_infer uut (datat, read_writet);assign datat = (read_writet == 1) ? data_in : 2'bZ;assign data_out = (read_writet == 0) ? datat : 2'bZ;initial beginread_writet = 1;data_in = 11;#50 read_writet = 0;endendmodule和普通的Verilog模块中⼀样、使⽤assign对wire类型的变量进⾏赋值。
VHDL——如何写简单的testbench
弄了好长时间vhdl,一直对testbench很迷惑。
前几天静下心来好好看了下资料,终于会写简单的testbench了。
六进制计数器的代码[c-sharp]view plaincopy1.library ieee;e ieee.std_logic_1164.all;e ieee.std_logic_arith.all;4.--use ieee.std_logic_unsigned.all;5.6.entity cnt6 is7. port8. (clr,en,clk :in std_logic;9. q :out std_logic_vector(2 downto 0)10. );11.end entity;12.13.architecture rtl of cnt6 is14.signal tmp :std_logic_vector(2 downto 0);15.begin16. process(clk)17.-- variable q6:integer;18. begin19.if(clk'event and clk='1') then20.if(clr='0')then21. tmp<="000";22. elsif(en='1') then23.if(tmp="101")then24. tmp<="000";25.else26. tmp<=unsigned(tmp)+'1';27. end if;28. end if;29. end if;30. q<=tmp;31.-- qa<=q(0);32. -- qb<=q(1);33. -- qc<=q(2);34. end process;35.end rtl;六进制计数器testbench的代码[c-sharp]view plaincopy1.library ieee;e ieee.std_logic_1164.all;3.4.entity cnt6_tb is5.end cnt6_tb;6.7.architecture rtl of cnt6_tb is8. component cnt69. port(10. clr,en,clk :in std_logic;11. q :out std_logic_vector(2 downto 0)12. );13. end component;14.15. signal clr :std_logic:='0';16. signal en :std_logic:='0';17. signal clk :std_logic:='0';18. signal q :std_logic_vector(2 downto 0);19.20. constant clk_period :time :=20 ns;21. begin22. instant:cnt6 port map23. (24. clk=>clk,en=>en,clr=>clr,q=>q25. );26. clk_gen:process27. begin28. wait for clk_period/2;29. clk<='1';30. wait for clk_period/2;31. clk<='0';32. end process;33.34. clr_gen:process35. begin36. clr<='0';37. wait for 30 ns;38. clr<='1';39. wait;40. end process;41.42. en_gen:process43. begin44. en<='0';45. wait for 50ns;46. en<='1';47. wait;48. end process;49.end rtl;其实testbench也有自己固定的一套格式,总结如下:[c-sharp]view plaincopy1.--测试平台文件(testbench)的基本结构2.library ieee;e ieee.std_logic_1164.all;4.5.entity test_bench is --测试平台文件的空实体(不需要端口定义)6.7.end test_bench;8.9.architecture tb_behavior of test_bench is10.11. component entity_under_test --被测试元件的声明12. port(13. list-of-ports-theri-types-and-modes14. );15. end component;16.17.begin18. instantiation:entity_under_test port map19. (20. port-associations21. );22.23. process() --产生时钟信号24.……25. end process;26.27. process() --产生激励源28.……29. end process;30.end tb_behavior;31.32.-------------------------------------------------------------------33.--简单计数程序源码34.library ieee;e ieee.std_logic_1164.all;e ieee.std_logic_unsigned.all;e ieee.std_logic_unsigned.all;38.39.entity sim_counter is40. port(41. clk :in std_logic;42. reset :in std_logic;43. count :out std_logic_vector(3 downto 0)44. );45.end entity;46.47.architecture behavioral of sim_counter is48.49.signal temp :std_logic_vector(3 downto 0);50.51.begin52. process(clk,reset)53. begin54.if reset='1' then55. temp<="0000";56. elsif clk'event and clk='1' then57. temp<=temp+1;58. end if;59. end process;60. count<=temp;61.end behavioral;62.63.-------------------------------------------------------------------64.--简单计数程序,测试文件代码(testbench)65.library ieee;e ieee.std_logic_1164.all;e ieee.std_logic_unsigned.all;e ieee.numeric_std.all;69.70.entity counter_tb_vhd is --测试平台实体71.end counter_tb_vhd;72.73.architecture behavior of counter_tb_vhd is74. --被测试元件(DUT)的声明75. component sim_counter76. port(77. clk :in std_logic;78. reset :in std_logic;79. count :out std_logic_vector(3 downto 0)80. );81. end component;82. --输入信号83. signal clk:std_logic:='0';84. signal reset :std_logic:='0';85. --输出信号86. signal count :std_logic_vector(3 downto 0);87.88. constant clk_period :time :=20 ns; --时钟周期的定义89.90.begin91. dut:sim_counter port map(92. clk=>clk,reset=>reset,counter=>counter93. );94. clk_gen:process95. begin96. clk='1';97. wait for clk_period/2;98. clk='0';99. wait for clk_period/2;100. end process;101.102. tb:process --激励信号103. begin104. wait for 20 ns;105. reset<='1';106. wait for 20 ns;107. reset<='0';108. wait for 200 ns;109. wait; --will wait forever;110. end process;111.end;112.113.114.--激励信号的产生方式115.--1.以一定的离散时间间隔产生激励信号的波形116.--2.基于实体的状态产生激励信号,也就是说基于实体的输出响应产生激励信号117.118.--两种常用的复位信号119.--1.周期性的激励信号,如时钟120.--2.时序变化的激励型号,如复位121.122.--eg.产生不对称时钟信号123. w_clk<='0' after period/4 when w_clk='1'else124.'1' after 3*period/4 when w_clk='0'else125.'0';126.127.--eg.产生堆成时钟信号,process语句128.clk_gen1:process129.constan clk_period := 40 ns;130.begin131. clk='1';132. wait for clk_period/2;133. clk='0';134. wait for clk_period/2;135.end process;如果自己不想写这些testbench的这些固定格式,可以在quartus里自动生成testbench文件的模板,然后往里面写信号就行了步骤:processing->start->start test bench template write这里需要注意的是要在仿真选项里选择一个仿真工具,然后才会生成testbench自动生成的testbench模板格式如下:[c-sharp]view plaincopy1.-- Copyright (C) 1991-2008 Altera Corporation2.-- Your use of Altera Corporation's design tools, logic functions3.-- and other software and tools, and its AMPP partner logic4.-- functions, and any output files from any of the foregoing5.-- (including device programming or simulation files), and any6.-- associated documentation or information are expressly subject7.-- to the terms and conditions of the Altera Program License8.-- Subscription Agreement, Altera MegaCore Function License9.-- Agreement, or other applicable license agreement, including,10.-- without limitation, that your use is for the sole purpose of11.-- programming logic devices manufactured by Altera and sold by12.-- Altera or its authorized distributors. Please refer to the13.-- applicable agreement for further details.14.15.-- ************************************************************************** *16.-- This file contains a Vhdl test bench template that is freely editable to17.-- suit user's needs .Comments are provided in each section to help the user18.-- fill out necessary details.19.-- ************************************************************************** *20.-- Generated on "03/13/2011 20:05:04"21.22.-- Vhdl Test Bench template for design : cnt623.--24.-- Simulation tool : ModelSim (VHDL)25.--26.27.LIBRARY ieee;E ieee.std_logic_1164.all;29.30.ENTITY cnt6_vhd_tst IS --测试平台文件的空实体(不需要端口定义)31.END cnt6_vhd_tst;32.ARCHITECTURE cnt6_arch OF cnt6_vhd_tst IS33.-- constants34.-- signals35.SIGNAL clk : STD_LOGIC; --内部信号,可与被测试元件端口一致36.SIGNAL clr : STD_LOGIC;37.SIGNAL en : STD_LOGIC;38.SIGNAL q : STD_LOGIC_VECTOR(2 DOWNTO 0);39.PONENT cnt6 --被测试元件的声明41. PORT (42. clk : IN STD_LOGIC;43. clr : IN STD_LOGIC;44. en : IN STD_LOGIC;45. q : OUT STD_LOGIC_VECTOR(2 DOWNTO 0)46. );47.END COMPONENT;48.BEGIN49. i1 : cnt6 --实例名称,i为 instance 缩写50. PORT MAP ( --端口映射,把testbench 内部信号与被测试元件端口一一对应即可51.-- list connections between master ports and signals52. clk => clk,53. clr => clr,54. en => en,55. q => q56. );57.init : PROCESS --初始化进程58.-- variable declarations59.BEGIN60. -- code that executes only once61.WAIT;62.END PROCESS init;63.always : PROCESS --激励,即主时钟、其他输入信号定制。
verilog testbench文件的编写要点
文章标题:深入探讨Verilog Testbench文件的编写要点在数字电路设计中,Verilog是一种常用的硬件描述语言,用于描述电子系统的行为。
而Testbench文件则是用来验证Verilog设计的功能和正确性的关键组成部分。
在本文中,我们将深入探讨Verilog Testbench文件的编写要点,以便读者更好地理解和掌握这一重要的技术。
一、Verilog Testbench文件的基本结构在编写Verilog Testbench文件时,需要遵循一定的基本结构,以确保测试的全面性和准确性。
Testbench文件应包括对被测试模块的实例化和初始化,并且需要定义时钟和输入信号的周期和时序关系。
Testbench文件中应包括对被测试模块输出信号的监控和比对,以验证其正确性和稳定性。
Testbench文件还应包括测试结束条件的判断和输出。
二、Verilog Testbench文件的编写要点在编写Verilog Testbench文件时,需要注意一些重要的要点,以确保测试的高效性和准确性。
需要对测试用例进行全面的设计和考虑,覆盖被测试模块的所有功能和状态。
需要对输入信号的生成和时序进行合理的设计和控制,确保测试能够完整而准确地进行。
对输出信号的监控和比对也需要有严格的设计和实现,以确保测试结果的准确性和可靠性。
三、个人观点和理解在我看来,Verilog Testbench文件的编写是Verilog验证工作中至关重要的一环。
一个好的Testbench文件可以大大提高验证的效率和准确性,而一个不好的Testbench文件则可能导致验证工作陷入困境。
我们需要将编写Testbench文件作为验证工作中的重点和难点来认真对待,不断总结和积累经验,以提高自己的测试能力和水平。
总结回顾在本文中,我们深入探讨了Verilog Testbench文件的编写要点,包括基本结构和编写要点,并共享了个人观点和理解。
通过深入理解和研究Verilog Testbench文件的编写要点,我们可以更好地应用这一技术,提高验证工作的效率和准确性。
testbench 的编写及使用方法思考与体会
【导言】1. 什么是 testbench?2. testbench 的作用和优势【testbench 编写】3. testbench 编写的基本步骤4. testbench 编写的注意事项5. testbench 编写过程中的实际问题与解决方法【testbench 使用方法】6. testbench 的使用流程7. testbench 的结果分析和验证8. testbench 使用过程中的常见错误及解决办法【结语】9. testbench 的编写与使用思考10. testbench 的未来发展预期【导言】1. 什么是 testbench?Testbench 是指在数字电路设计中对被测电路模块进行验证的仿真测试工具。
它是一种独立的模块化设计,用于测试另一个模块或芯片的功能和性能。
在数字电路设计中,testbench 是非常重要的工具,可以帮助设计人员验证电路的正确性和稳定性。
2. testbench 的作用和优势testbench 主要用于验证设计的功能和性能是否符合预期,并且检测是否有潜在的bug存在。
它可以模拟各种工作条件和异常情况,帮助设计人员有效地发现和解决问题。
testbench 的优势在于能够大大提高验证的效率和准确性,节省了大量的人力和时间成本。
【testbench 编写】3. testbench 编写的基本步骤要编写一个高质量的 testbench,需要遵循以下基本步骤:(1)分析被测电路的功能和接口,确定测试的重点和目标;(2)编写测试数据生成模块,包括设计测试用例和对输入信号进行生成;(3)编写时序控制模块,用于控制测试数据的输入和时序顺序;(4)编写仿真结果分析模块,用于对仿真结果进行验证和分析;(5)整合以上模块,编写完整的 testbench。
4. testbench 编写的注意事项在编写 testbench 的过程中,需要注意以下几点:(1)保证测试用例的全面性和充分性,覆盖各种工作条件和异常情况;(2)时序控制模块的设计要符合被测电路的时序要求,保证测试数据的输入顺序和时序正确;(3)仿真结果分析模块要设计完善,能够对仿真结果进行准确的验证和分析;(4)编写的 testbench 要符合规范,可读性强,方便他人理解和维护。
编写testbench的一些技巧
编写testbench的⼀些技巧1 Testbench的结构1) 单顶层结构⼀种结构是testbench 只有⼀个顶层,顶层再把所有的模块实例化进去。
打个⽐⽅,类似树结构,只有⼀个模块有⼦节点⽽没有⽗节点,其它模块都有⽗节点。
如下图结构所⽰:测试模块是⼀些接⼝模型,接⼝模型还可能包含了⼀些激励在内。
测试模块和DUV之间通过端⼝映射进⾏互连。
2) 多顶层结构另外⼀种结构是多顶层结构,如下图所⽰:在这种结构中,有⼀个顶层是作为测试向量模块,⼀个或多个顶层是⼀些公⽤⼦程序,这些⼦程序由于完成⼀些通⽤的功能被封装成任务、函数等被公⽤。
还有⼀个叫harness的顶层,该顶层由DUV和⼀些接⼝模型构成⼀个狭义上的测试平台,其它模块可以调⽤BFM⾥⾯的task 或event 等,向DUV施加激励。
注意这些顶层之间是没有端⼝映射的,它们之间的互相调⽤和访问是通过层次路径名的⽅式来访问,上图的虚线表⽰层次路径名的访问。
下⾯举例说明层次路径是如何访问的。
由于⼤部分⼈对C都有所认识,在这⾥作个⽐较,便于了解。
Verilog HDL的顶层类似于C的结构体,⽽实例化的模块、任务、函数、变量等就是结构体⾥的成员,可以通过句点( . )隔开的⽅式访问结构体⾥⾯的每⼀个成员。
如:顶层harness 实例化进来的模块BFM1 ⾥⾯有⼀个任务SEND_DATA , 该任务可以产⽣激励输⼊到DUV,在testcase ⾥调⽤该任务就可写为:initialbegin……harness . BFM1 . SEND_DATA (……);end多顶层结构的可扩展和重⽤性⽐单顶层结构强得多。
层次路径的访问⽅式⾮常有⽤,在下⼀节会讲述更多的应⽤。
2 如何编写Testbench1) 何时使⽤initial和alwaysinitial和always 是2个基本的过程结构语句,在仿真的⼀开始即开始相互并⾏执⾏。
通常被动的检测响应使⽤always语句,⽽主动的产⽣激励使⽤initial语句。
如何编写Testbench
…
4
4
q
qb
endmodule
?如何编写Testbench 如何编写
模块例化--位置映射的说明 模块例化 位置映射的说明
1 2 3
可以将模块的实例通过端口连接起来构成 一个大的系统或原件
在上面的例子中,REG4有模块 在上面的例子中, 有模块DFF的四个实例。 的四个实例。 有模块 的四个实例 注意,每个实例都有自己的名字( 注意,每个实例都有自己的名字(d0,d1,d2,d3), 实例名是每个对象唯一标记,通过标记可以查看 实例名是每个对象唯一标记, 每个实例的内部
REG4
d
4 d0 DFF d3 DFF
clk
clr
…
4
4
q
qb
?如何编写Testbench 如何编写
预备知识1 预备知识 模块例化方法的注意事项1 模块例化方法的注意事项
module comp(o1,o2,i1,i2); 输出端口没有连接时,通常会产生 输出端口没有连接时,通常会产生warning output o1,o2; input i1,i2; 没有连接的输入端口初始化 ...... 值为x.要特别注意可能会引起 值为 要特别注意可能会引起 芯片内电路逻辑 endmodule 出错!!! 出错!!! module test; comp c1(Q,R,J,K); //位置映射 位置映射 comp c2(.i2(K),.o1(Q),.o2(R,.i1(J));//名称映射 名称映射 comp c3(Q, ,J,K); //输出端口 没有连接 输出端口o2没有连接 输出端口 comp c4(.i1(J),.o1(Q));//输入端口 ,输出端口 没有连接 输入端口i2,输出端口o2没有连接 输入端口 endmodule
verilog testbench语法
verilog testbench语法摘要:1.Verilog Testbench 简介2.Testbench 结构3.编写Testbench 的注意事项4.Testbench 常用技巧与策略5.UVM 与Verilog Testbench 的区别正文:Verilog Testbench 是一种用于验证数字电路设计的测试工具,通过模拟和仿真来检查电路功能是否符合预期。
Testbench 主要由三个部分组成:驱动模块、监控模块和测试模块。
在本文中,我们将详细讨论Testbench 的语法、结构以及编写Testbench 时需要注意的要点。
1.Verilog Testbench 简介Verilog Testbench 是基于文本的测试工具,它使用Verilog 编程语言编写。
通过描述性文本和激励信号,Testbench 可以模拟数字电路的行为,并在仿真过程中检查电路的响应是否正确。
Testbench 主要用于验证电路设计的功能、时序和性能。
2.Testbench 结构一个典型的Testbench 结构包括以下几个部分:- 驱动模块:负责产生输入信号,用于刺激被测电路。
- 监控模块:用于捕获被测电路的输出信号,并与预期值进行比较。
- 测试模块:根据输入信号和输出信号的比较结果,判断电路设计的正确性。
3.编写Testbench 的注意事项- 确保Testbench 与被测模块的接口匹配,包括输入信号、输出信号和参数。
- 使用适当的时钟信号和复位信号,确保仿真过程中电路状态的正确切换。
- 编写清晰的注释,便于理解和维护。
- 合理使用宏定义和参数化,提高Testbench 的可复用性。
- 验证电路的边界条件,确保测试的全面性。
4.Testbench 常用技巧与策略- 使用事件驱动的仿真算法,提高仿真速度。
- 采用分阶段测试策略,逐步验证电路的各个功能模块。
- 使用随机化测试,提高测试覆盖率。
- 结合断言和检查点,确保电路的正确性。
Verilog-HDL-如何编写TESTBENCH
08:292主要内容Verilog 对验证的支持系统函数和系统任务如何编写模块的TESTBENCH08:293系统任务和系统函数是Verilog中预先定义好的,用于调试和编译预处理的任务或函数。
以$开头,用于控制和检测仿真模拟过程主要有:(1)用于获取仿真时间的系统函数(2)支持文本输出(检测信号、显示信号)的系统任务(3)用于文件输入、输出操作的系统任务(4)用于暂停和退出仿真的系统任务(5)用于产生随机数的系统任务08:294获取当前仿真时间的系统函数$time,$realtime,$stime:返回当前仿真时间。
$time返回一个64位的整数时间值,$realtime返回的结果是实数时间值,是更为精确的仿真时间 $stime返回一个32位整数时间值。
(对大于232的时间,返回模232的值。
使用它可以节省显示及打印空间。
)这些函数的返回值使用调用模块中`timescale 定义的模块仿真时间尺度为单位08:295例..\..\verilog_example\Dec2x4.v..\..\verilog_example\Dec_Test.v # At time 0, input is 0,0,0, output is,xxxx # At time 4, input is 0,0,0, output is,1111# At time 10, input is 0,0,1, output is,1111# At time 13, input is 0,0,1, output is,0111# At time 20, input is 1,0,1, output is,0111# At time 23, input is 1,0,1, output is,0101# At time 26, input is 1,0,1, output is,1101# At time 30, input is 1,1,1, output is,1101# At time 33, input is 1,1,1, output is,1100# At time 36, input is 1,1,1, output is,1110# At time 40, input is 0,1,1, output is,1110# At time 44, input is 0,1,1, output is,1011# At time 50, input is 0,0,1, output is,1011# At time 54, input is 0,0,1, output is,011108:296Verilog 支持的文本输出的系统任务显示任务:用于仿真模拟期间显示信息。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1 Testbench的结构1) 单顶层结构一种结构是testbench 只有一个顶层,顶层再把所有的模块实例化进去。
打个比方,类似树结构,只有一个模块有子节点而没有父节点,其它模块都有父节点。
如下图结构所示:测试模块是一些接口模型,接口模型还可能包含了一些激励在内。
测试模块和DUV之间通过端口映射进行互连。
2) 多顶层结构另外一种结构是多顶层结构,如下图所示:在这种结构中,有一个顶层是作为测试向量模块,一个或多个顶层是一些公用子程序,这些子程序由于完成一些通用的功能被封装成任务、函数等被公用。
还有一个叫harness的顶层,该顶层由DUV和一些接口模型构成一个狭义上的测试平台,其它模块可以调用BFM里面的task 或event 等,向DUV施加激励。
注意这些顶层之间是没有端口映射的,它们之间的互相调用和访问是通过层次路径名的方式来访问,上图的虚线表示层次路径名的访问。
下面举例说明层次路径是如何访问的。
由于大部分人对C都有所认识,在这里作个比较,便于了解。
Verilog HDL的顶层类似于C的结构体,而实例化的模块、任务、函数、变量等就是结构体里的成员,可以通过句点( . )隔开的方式访问结构体里面的每一个成员。
如:顶层harness 实例化进来的模块BFM1 里面有一个任务SEND_DATA , 该任务可以产生激励输入到DUV,在testcase 里调用该任务就可写为:initialbegin……harness . BFM1 . SEND_DATA (……);end多顶层结构的可扩展和重用性比单顶层结构强得多。
层次路径的访问方式非常有用,在下一节会讲述更多的应用。
2 如何编写Testbench1) 何时使用initial和alwaysinitial和always 是2个基本的过程结构语句,在仿真的一开始即开始相互并行执行。
通常被动的检测响应使用always语句,而主动的产生激励使用initial语句。
initial和always的区别是always 语句不断地重复执行,initial语句则只执行一次。
但是,如果希望在initial里的多次运行一个语句块,怎么办?这时可以在initial里嵌入循环语句(while,repeat,for,forever 等),如:initialbeginforever /* 无条件连续执行*/begin……endend其它循环语句请参考一些教材,这里不作赘述。
另外,如果希望在仿真的某一时刻同时启动多个任务,可以使用fork....join语句。
例如,在仿真开始的100 ns 后,希望同时启动发送和接收任务,而不是发送完毕后再进行接收,如下所示:initialbegin#100 ;fork /*并行执行*/Send_task ;Receive_task ;joinEnd2) 如何作多种工作模式的遍历测试如果设计的工作模式很多,免不了做各种模式的遍历测试,而遍历测试是需要非常大的工作量的。
我们经常遇到这样的情况:很多时候,各种模式之间仅仅是部分寄存器配置值的不同,而各模式间的测试都是雷同的。
有什么方法可以减轻这种遍历测试的工作量?不妨试试for循环语句,采用循环变量来传递各种模式的配置值,会帮助减少很多测试代码,而且不会漏掉每一种模式.initialbeginfor ( i = 0 ; i < m ; i = i + 1 ) /*遍历模式1至模式m*/for ( j = 0 ; j < n ; j = j +1 ) /*遍历子模式1至子模式n */begincase ( j ) /* 设置每种模式所需的配置值*/0 : 配置值=a ;1 : 配置值=b ;2 : 配置值=c ;……endcase/*共同的测试向量*/endend3) 如何加速问题定位过程在这部分里,通过一些实际例子,介绍在出现问题时如何借助testbench 加快问题的定位过程。
1、监测内存分配内存分配和回收示意图在这个例子里,假设总共有2K的内存块,希望在测试程序里监测内存分配和回收的块号是否正确,监测是否存在同一块号重复分配、重复回收的情况。
设置一个2K位的变量对内存的使用情况进行记录,每一位对应一个内存块,空闲的块号记为1,被占用的块号记为0。
该变量的初始值为全1,当分配一个块号出去时先判断该位是否为空闲,若是空闲则将该位设置为被占用,否则就为重复分配错误。
相反,当回收一个块号时,先判断该位是否被占用,若是被占用则将该位设置为空闲,否则就为重复回收错误。
程序如下:always @(posedge Clk or negedge Rst )beginif ( Rst == 1'b0 )Mem_status <= 2048 {1'b1} ;elsebeginif ( 层次路径 . rd ) /* 监测内存分配,block_rd 是分配的内存块号*/if ( Mem_status [ block_rd ] == 1'b1 )Mem_status [ block_rd ] <= 1'b0 ;elsebegin$display ( "Error! 重复分配同一内存块!") ;$stop ;endif ( 层次路径 . wr ) /* 监测内存回收,block_wr 是回收的内存块号*/if ( Mem_status [ block_wr ] == 1'b0 )Mem_status [ block_wr ] <= 1'b1 ;elsebegin$display ( "Error! 重复回收同一内存块!") ;$stop ;endendEnd2、监测内部接口如果你是位验证工程师,在做芯片级的仿真时,相信你会或曾遇到过这样的问题:在一个端口输入了激励数据,但另一端口却得不到正确的响应,而且这条路径涉及到很多模块和很多个不同设计者,为了定位问题,你可能很盲目地逐个找来设计人员,逐个模块地记录仿真波形,到解决问题时,可能几天已经过去了。
我们都知道,如果问题定位在越小的范围,就越便于解决问题。
所以,我们可以把模块接口间交换的数据记录到文件里,当出现问题时,就可以查看各接口的记录数据,看问题到底出现在哪个区间,简单地查看记录文件后,你就明确该找那位designer来解决问题。
3、记录有用的DEBUG信息记录有用的debug信息,输出到标准的I/O设备上(屏幕或文件),会给你的debug带来很大的便利,由上面的例子也可见一斑,在检测到有错误时也可使用$stop令仿真停下来。
值得注意的是,UNIX系统只有32个I/O,每个输出文件占用1个I/O设备号,其中第1个是屏幕显示,设备号是32'b1,其它I/O设备号由输出文件占用,一个信息可同时输出到屏幕和文件,如:initialbeginPtr_log = $fopen ("log.txt ") ; /* 创建一个文件,获得文件指针*/Ptr_log = Ptr_log | 32'b1 ; /* 指针同时指向log.txt 文件和屏幕*/endalways @(……)begin$fwrite ( Ptr_log, "useul message ",……) ; /*信息除了记录到文件同时,还显示到屏幕*/ ……end虽然记录文件会给debug带来很多便利,但文件操作会降低仿真的速度,因此应当适可而止。
另外写文件通常有2种方式,不同的仿真工具有所差异。
一种是每写一个字节打开关闭一次文件,如Verilog-XL。
另一种是先把字符暂存到内存,等累积到一定数量(如8K字节)后再通过DMA方式把字符从内存写到文件,如Verilog-NC。
因此,后一种方式就大大地降低了文件的操作次数,有利于提高仿真速度。
3 编写Testbench的一些高级技巧Verilog HDL提供很多方便和高效的建模语句,这在大多数参考书上都有介绍,在这节,只介绍一些参考教材很少介绍而较有用的建模语句。
1) force 和release望文生义,force即是可以对变量和信号强制性地赋予确定的值,而release就是解除force的作用,恢复为驱动源的值。
例如:wire a ;assign a = 1'b0 ;initialbegin#10 ;force a = 1'b1 ;#10release a ;end在10 ns时,a 的值由0变为1,在20ns时,a 的值又恢复为0 。
force 和release并不常用,有时,可以利用它们和仿真工具做简单的交互操作。
例如,Verilog-XL的图形界面可以方便的将一个信号或变量force 为0或1,在Testbench里,可以检测变量是否被force为固定的值,当被force为固定的值时就执行预定的操作,实现了简单交互操作。
2) 事件事件有些类似于任务。
首先需要定义一个事件,而事件可以作为敏感变量激活一个语句块的操作,事件可由“->”符号进行触发,如下例:event e1 ; /*定义一个事件*/always @( e1 ) /*事件e1 作为敏感变量*/begin.....endinitialbegin—> e1 ; /*创建事件e1来触发上面的always语句*/.....end事件(event )与任务(task)的区别是:执行事件触发后可以立即继续往下执行语句,只起一个触发作用,至于被触发的事件何时执行完毕并不影响程序继续执行。
而调用一个任务后,必须等待任务完成才能返回控制权。
3) 模块参数当一个模块引用另外一个模块时,高层模块可以改变低层模块用parameter定义的参数值,改变低层模块的参数值可采用以下两种方式:1)defparam 重定义参数语法:defparam path_name = value ;低层模块的参数可以通过层次路径名重新定义,如下例:module top ( .....)input....;output....;defparam U1 . Para1 = 10 ; /*修改实例U1 模块中的para1 */M1 U1 (..........);endmodulemodule M1(....);parameter para1 = 5 ;input...;output...;......endmodule在上例中,模块M1参数para1 的缺省值为5,而模块top实例了M1后将参数的值改为10。
2) 实例化时传递参数在这种方法中,实例化时把参数传递进去,如下例所示:module top ( .....)input....;output....;M1 #( 10 ) U1 (..........);endmodule在该例中,用#( 10 )修改了上例中的参数para1,当有多个参数时,用逗号隔开,如#( 10 , 5 ,3 )传递了3个参数值。