testbench框架基础进阶

上传人:仙*** 文档编号:34997116 上传时间:2021-10-25 格式:DOC 页数:23 大小:177.50KB
返回 下载 相关 举报
testbench框架基础进阶_第1页
第1页 / 共23页
testbench框架基础进阶_第2页
第2页 / 共23页
testbench框架基础进阶_第3页
第3页 / 共23页
点击查看更多>>
资源描述
-project-此模块为yuv444转yuv422模块,由输入24bit数转8bit输出,本笔记都以此工程为基础,都仿真测试过module yuv444to422( input ccd_clk, input sys_clk, input resetn, input HD, input VD, input 23:0 yuv, input yuv_trans_done, output clk_out, output reg 7:0 data_out, output HD_out, output VD_out, output reg 11:0 v_cnt );assign clk_out = ccd_clk;assign HD_out = HD;/;hd11assign VD_out = VD;/v_cnt_out;/;vd11/1280*1024-1280*720/reg 1:0 hs_edge;always(posedge ccd_clk)beginif(!resetn)hs_edge = 2b00;else hs_edge= hs_edge0,HD;endalways(posedge ccd_clk)beginif(!resetn)v_cnt = 12b0;else if(!VD) v_cnt =0;else if(hs_edge=2b01) v_cnt = v_cnt +1; end/*reg v_cnt_out;always(posedge sys_clk)beginif(!resetn) v_cnt_out = 12d179) & (v_cnt = 12d899) v_cnt_out = 1d1;else v_cnt_out = 1d0;end*/reg 1:0cnt1;always(posedge ccd_clk)begin if(!resetn) cnt1=0; else if(yuv_trans_done) cnt1=cnt1+1;endalways(posedge ccd_clk)begin if(!resetn) begin data_out = 8b0; end else if (HD) begin case(cnt1) 2b0: data_out = yuv23:16; 2b1: data_out = yuv15:8; /8h80;/ 2d2: data_out = yuv23:16; 2d3: data_out = yuv7:0; /8h80;/ endcase end endendmodule -testbench-timescale 1 ns/ 1 psinclude D:/tangate_quartus/yuv444to422/yuv444to422.v module yuv444to422_vlg_tst();reg HD;reg VD;reg ccd_clk;reg resetn;reg sys_clk;reg 23:0 yuv;reg yuv_trans_done;/ wires wire HD_out;wire VD_out;wire clk_out;wire 7:0 data_out;wire 11:0 v_cnt; yuv444to422 i1 (.HD(HD),.HD_out(HD_out),.VD(VD),.VD_out(VD_out),.ccd_clk(ccd_clk),.clk_out(clk_out),.data_out(data_out),.resetn(resetn),.sys_clk(sys_clk),.v_cnt(v_cnt),.yuv(yuv),.yuv_trans_done(yuv_trans_done);/- parameter n = 10;/- initial begin HD = 1b0; / time 0 时刻 VD = 1b0; repeat(n) begin # 200 VD = 1b1; / time 200 repeat(10*n) begin #100 HD = 1b1; /time 300 #100 HD = 1b0; /time 700 end #100 VD = 1b0; end end /-initial begin yuv_trans_done = 1b0; #30 yuv_trans_done = 1b1; end/-/*initial begin resetn = 1b0; #30 resetn = 1b1; end */- initial begin sys_rst (100);/复位100个时间单位 endtask sys_rst ; input 10:0 rst_time; /调用task的时候,将参数赋值给rst_time begin resetn = 0; #rst_time; resetn = 1; endendtask/- always begin #10 ccd_clk = 1b0; #10 ccd_clk = 1b1; end initial begin yuv = 24b0; forever (posedge ccd_clk) #20 yuv = $random; end /- initial begin #10000 $stop(2); end/-ok- /*initial begin #3000 begin $display(current scope is %m); $display(the signal is %s,data_out); $display(the time is %t,$time) ; $display(data_out has %c ascii character value,data_out); $display(data_out=%h hex %d decimal, data_out, data_out); $display(data_out=%o otal %b binary, data_out, data_out); end end*/-ok-/always (data_out) $display(Output changed at %t to %b,$time,data_out);/-ok-/always (data_out) $strobe(Output changed at %t to %b,$time,data_out);/-ok-/initial /$monitor (at time is %t and the signal is %bn,$time , VD_out) ;/-ok-/initial / $monitor (%d is changed at %t,data_out,$time);/yuv444to422_vlg_tst./-ok- /* reg 23:0 buffer;initial buffer = 24b0000_1110_0001_1011_0101_1100; always ( posedge ccd_clk) #10 yuv,buffer = buffer,yuv;*/-ok-/*always (ccd_clk) begin $timeformat(-9,1,ns,0); $display (time clk HD VD v_cnt data_out ); $monitor(%t %b %b %b %b %b , $realtime, clk_out, HD_out, VD_out, v_cnt, data_out); end*/-write ok-/*initial begin : blockinteger file_id; file_id = $fopen(D:/tangate_quartus/yuv444to422/data_out.txt);/1.打开文件/$fmonitor只要有变化就一直记录 自动换行无需加n /格式:$fmonitor(file_id, %format_char, parameter);$fmonitor(file_id, %m: %t data_out=%d v_cnt=%h_tangate, $time, data_out, v_cnt);/$fwrite需要触发条件才记录 ,只记录一行数据,换行需加n,注意反斜杠的方向$fwrite(file_id, %m: time is %t*data_out=%d*v_cnt=%hn, $time, data_out, v_cnt);/2.写入文件/$fdisplay需要触发条件才记录,只记录一行数据,自动换行无需加n/格式:$fdisplay(file_id, %format_char, parameter);$fdisplay (file_id, %m: %t data_out=%d v_cnt=%h_great man, $time, data_out, v_cnt);$fclose(D:/tangate_quartus/yuv444to422/data_out.txt); /3.关闭文件end*/ /-read ok-initial begin : blockinteger file_id; file_id = $fopen(D:/tangate_quartus/yuv444to422/data_out.txt);/1.打开文件$fread( D:/tangate_quartus/yuv444to422/data_out.txt, $time, data_out, v_cnt);/2.读取文件$fclose(D:/tangate_quartus/yuv444to422/data_out.txt); /3.关闭文件endendmodule Testbench 学习笔记(框架)为什么C不能取代verilog和VHDL作为硬件描述语言?因为C缺少了硬件描述最基本的三个思想:连通性(Connectivity),时间性(Time)和并行性(Concurrency)。连通性是指使用一个简单并相互连接的模块来描述设计的能力,原理图设计工具就是连通性完美的支持工具。时间性是指表现设计状态演进的时间变化的能力,这个能力不同于衡量一个代码执行运行多久的时间。并行性是指描述同时发生相互独立的行为的能力。Testbench读写紊乱状态在同一时刻对同一个寄存器进行读写容易发生紊乱状态。以下的例子,第一个always块对count操作(写),第二个always却要显示它。那么会出现什么状态呢?module rw_race(clk);input clk;integer count;always (posedge clk)begincount = count + 1;endalways (posedge clk)begin$write(Count is equal to %0dn, count);endendmodule由于testbench基于计算机,那么处理的时候也是分时复用的,从而这两个always块会先后执行。也就是说会出现两种情况,这里假设count在执行前为10,若先执行第一个块,那么后第二个块执行的结果显示count=11;若先执行第二个块再执行第一个块,显示的结果count=10。往往这样的紊乱状态不是我们希望看到的,很可能会给我们的测试工作带来许多不必要的麻烦。那么,有什么解决办法呢?module rw_race(clk);input clk;integer count;always (posedge clk)begincount = count + 1;endalways (posedge clk)begin$write(“Count is equal to %0dn, count);endendmodule采用非阻塞赋值语句后,这个紊乱的状态就会得到解决。在第一个always块count增加的同时第二个always块也在执行,那么最后显示的count值是count增1之前的数值。再看下面的例子module rw_race;wire 7:0 out;assign out = count + 1;integer count;initialbegincount = 0;$write(Out = %bn, out);endendmodule会得到什么结果呢?这取决于你所使用的仿真器和命令行。一般的,Verilog-XL会输出”xxxxxxxx”,而VCS则会认为是”00000001”。那么如何改进呢?module rw_race;wire 7:0 out, tmp;assign #1 out = tmp - 1;assign #3 tmp = count + 1;integer count;initialbegincount = 0;#4;/ out will be 0 or xs.$write(Out = %bn, out);endendmodule这些都是一个好的testbench应该注意的细节。testbench的实现方法多样,而且还不断涌现出新方法,这些都是人们在为更好的验证设计做的努力。如VHDL,verilog,systemC,systemverilog均可以,但是真正的实际应用中绝对不是单独应用,而是将他们结合起来,使你的验证更方便,更全面。由于系统验证的庞大,我们还是从最简单的,最熟悉的上手,就先单独用VHDL语言来写仿真语言,当然VHDL语言我们已经比较熟悉了,但是有个比较大的区别是,我们以前都是尽量学习能够综合的语言,但是在仿真中经常会用到一些行为级描述的语言,他们是不能被综合成逻辑的,但是却绝对可以让我们的验证更加高效方便,但是我们也不单独来讨论VHDL中哪些语言是能够综合,哪些语言是不能综合的,重点将会放在testbench的一般结构,编写testbench的一般思想,以及更多的是实际接触,以实际的例子与前面的可综合逻辑相结合达到完整的系统设计的上的!testbench的几种思路:一、只在testbench中实例化DUT(design under test),激励输入是在testbench中临时产生的,只能用于简单逻辑。优点:简单,易操作。缺点:复用性差 ,效率低 模型如图1所示二、DUT的输入由单独的一个文件产生,在testbench中实例化两上entity,可以复杂输入,简单输出的模块。模型如图2所示三、DUT的输入与测试输出各由单独文件产生,在testbench由三个实例化模块产生,用于具有复杂输入以及输出的模块,模型如图3所示。四、可以根据仿真输出来修改输入激励的,可以自动通过输出来修改输入,使验证更加准确。如图4所示的模型五、有文件做为testbench的输入,输出的模型。仿真中需要顺序的输入大量数据,以及接收相应的数据,可以通过从文件中读入数据,然后将产生的数据存入文件,使复杂系统验证更加方便。模型如图5所示。六、可以将激励同时输入自己设计的模块和已经验证了相同模块,比较二者输出。模型如图6所示。图1图2图3图4图5图6 Testbench 学习笔记(基础)书写testbench是数字电路设计中不可或缺的一项设计方法,主要是提供的是激励。尽管现在各种开发工具都通过绘制波形图的方法生成测试激励,测试书写的代码,但是其不可移植性,不可通用性,还有有些功能无法是实现,如监视变量的值的变化,显示数据的状态等。一个完整的testbench包含下列几个部分:(1)module的定义,一般无输入输出端口。(2)信号的定义,定义哪些是你要输入,输入的定义为reg类型,输出的定义为wire型(3)实例化待测试的模块(4)提供测试激励1.如何书写测试激励;(1)时钟信号的产生Initialbeginclk=0;forever#clk_period/2clk=clk;end或者是alwaysbegin#clk_period/2clk=0;#clk_period/2clk=1;End产生时钟的原理,是利用always或者是initial+forever产生不断重复的信号,构成时钟信号,其中initial语句必须要有初始值clk = 0。(2)复位信号的产生复位信号就是在复位电平下延时一段时间,然后再将复位电平信号取反即可。如:initialbeginrst=0;#100;rst=1;end在实际应用将其封装为task,使用时掉调用即可。调用如下:initialbeginsys_rst(100);/复位100个时间单位.end任务的定义如下:tasksys_rst;input10:0rst_time;/调用task的时候,将参数赋值给rst_timebeginrst=0;#rst_time; Rst =1;endendtask(3)产生一种复杂的信号。如下面的实例产生一个vs和hs信号/-parametern=10;/-initialbegin cmos_hs=1b0;/time0时刻 cmos_vs=1b0;repeat(n)/重复n次begin #200cmos_vs=1b1;/time200repeat(10*n) begin#100cmos_hs=1b1;/time300#100cmos_hs=1b0;/time700 end#100cmos_vs=1b0;endend该程序段可以生成10个vs信号,每个vs信号下有100个hs信号。由于initial信号只能执行一次,所以为了得到有限的重复信号,可以采用repeat关键词得到。这样基本上就可以完成一些简单的测试testbench了。2.如何将我们的测试尽可能的简单明了化用Modelsim对VerilogHDL进行仿真的人都会知道,看一大堆波形会很麻烦,如果代码变量很多,很复杂,出了问题都不知道问你在哪里,或者看了半天,发现图形是个错的。运用合适的方法将自己需要的变量以文本方式显示,监视变量的变化不很好嘛?下面介绍几种常用的系统函数(1)$time作用:返回所在模块的仿真时间,可以查看信号的出现的时间,用来把握信号的时序。如:$display(thetimeis%t,$time);/显示当时的时间(2)$display作用:将需要显示的内容在命令栏显示出来,会自动换行,无需加n如:$display(thesignalis%d,ad);/将ad信号以十进制的方式显示出来例如:timescale 1 ns/ 1 psinitial begin #3000 begin $display(the signal is %d,data_out); $display(the time is %t,$time) ; end end第一个系统函数表示程序运行3000ns后,以十进制的方式显示data_out值,第二个表示显示时间,也就是3000ns,用initial运行一次,用always语句反复运行。运行结果如下: (3)$monitor如:initial $monitor(attimeis%tandthesignalis%bn,$time,VD_out);此系统函数表示监视变量VD_out的值,一旦变量变化,则将变量显示出来,会自动换行,用initial,不用always,显示如下所示: 同步显示:l initial $monitor (“%d is changed at %t”,MUT.current,$time);/一般在 initial中调用,采用$monitor显示模块MUT内部current的值以及发生变化的时间,其中MUT是testbench顶层模块名,current为textbench中变量的值,$monitor是一个后台运行任务函数,多个模块下,任意时间只能有一个$monitor起作用,可用$monitoron $monitoroff来控制,例如:Initial $monitor (%d is changed at %t,yuv444to422_vlg_tst.data_out,$time);(4)文件操作类$fopen作用:打开一个文件面,对文件的操作$fdisplay作用:在打开的文件里,写入显示的内容$fmonitor作用:在打开的文件里,写入监视的变量变化时的内容$fclose作用:关闭当前的内容如下段程序,其中文件data_out.txt可以自己建一个空的文本文件,如果不手动建,它也会自动生成。写文件:initial begin : blockinteger file_id; file_id = $fopen(D:/tangate_quartus/yuv444to422/data_out.txt);/1.打开文件/$fmonitor只要有变化就一直记录 自动换行无需加n /格式:$fmonitor(file_id, %format_char, parameter);$fmonitor(file_id, %m: %t data_out=%d v_cnt=%h_tangate, $time, data_out, v_cnt);/$fwrite需要触发条件才记录 ,只记录一行数据,换行需加n,注意反斜杠的方向$fwrite(file_id, %m: time is %t*data_out=%d*v_cnt=%hn, $time, data_out, v_cnt);/2.写入文件/$fdisplay需要触发条件才记录,只记录一行数据,自动换行无需加n/格式:$fdisplay(file_id, %format_char, parameter);$fdisplay (file_id, %m: %t data_out=%d v_cnt=%h_great man, $time, data_out, v_cnt);$fclose(D:/tangate_quartus/yuv444to422/data_out.txt); /3.关闭文件End读文件:initial begin : blockinteger file_id; file_id = $fopen(D:/tangate_quartus/yuv444to422/data_out.txt);/1.打开文件$fread( D:/tangate_quartus/yuv444to422/data_out.txt, $time, data_out, v_cnt);/2.读取文件$fclose(D:/tangate_quartus/yuv444to422/data_out.txt); /3.关闭文件End注意在读写文件中要加入“:block”,否则会不正确(不知道为什么)。(5)$display与$strobe任务lalways (data_out) $display(“Output changed at %t to %b”,$time,data_out);当z发生变化输出z值以及变化时间,自动换行。 显示结果如下: lalways (data_out) $strobe(“Output changed at %t to %b”,$time,data_out);/仿真结束后显示输出,查看非阻塞赋值变量的值。 显示结果如下: 3、产生随机数和随机时间间隔随机数据initial repeat(5) #7 x = $random;a = $random%60; /产生-5959之间随机数a = $random%60; /产生059之间随机数产生随机时间间隔always begin t= $random;#(t) x = $random;end4、数据缓存Reg 15:0 buffer;initial buffer = 16b1110_0001_1011_0101;/将测试数据进行初始化always ( posedge clk) #1 x,buffer = buffer,x;/可以在控制的数据下输入信号xTestbench 学习笔记(进阶)如何编写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:1bz;用bi_dir_port_oe控制端口数据方向,并利用中间变量寄存器改变其值。等于两个模块之间用inout双向口互连。往端口写(就是往模块里面输入)方法2:使用force和release语句,这种方法不能准确反映双向端口的信号变化,但这种方法可以反映块内信号的变化。具体如示:module test();wire data_inout;reg data_reg;reg link;#xx; /延时force data_inout=1bx; /强制作为输入端口.#xx;release data_inout; /释放输入端口endmodule从文本文件中读取和写入向量1)读取文本文件:用 $readmemb系统任务从文本文件中读取二进制向量(可以包含输入激励和输出期望值)。$readmemh 用于读取十六进制文件。下面举例说明:先定义一个有256个地址的字节存贮器 mem:reg7:0 mem1:256;下面给出的系统任务以各自不同的方式装载数据到存贮器mem中。initial $readmemh(mem.data,mem); / 将mem.data文件读入寄存器mem中initial $readmemh(mem.data,mem,16);initial $readmemh(mem.data,mem,128,1);第一条语句在仿真时刻为0时,将装载数据到以地址是1的存贮器单元为起始存放单元的存贮器中去。第二条语句将装载数据到以单元地址是16的存贮器单元为起始存放单元的存贮器中去,一直到地址是256的单元为止。第三条语句将从地址是128的单元开始装载数据,一直到地址为1的单元。在第三种情况中,当装载完毕,系统要检查在数据文件里是否有128个数据,如果没有,系统将提示错误信息。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文件系统自动搜索使用库文件或库目录,只编译需要的模块而不必全部编译3Verilog 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 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中添加: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 = 1b1; . ; release sig1; endforce可以对wire赋值,这时整个net都被赋值; 也可以对reg赋值.6加载测试向量时,避免在时钟的上下沿变化为了模拟真实器件的行为,加载测试向量时,避免在时钟的上下沿变化,而是在时钟的上升沿延时一个时间单位后,加载的测试向量发生变化。如:assign #5 c=ab(posedge clk) #(0.1*cycle) A=1;*/testbench的波形输出module top;.initialbegin$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之下所有层的所有数据信号endendmodule/产生随机数,seed是种子$random(seed);ex: din = $random(20);/仿真时间,为unsigned型的64位数据$timeex:.time condition_happen_time;.condition_happen_tim
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 商业管理 > 销售管理


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!