数字集成电路设计入门--从HDL到版图于敦山北大微电子学系第十五章VerilogTestBench使用简介学习内容:•用一个复杂的testbench复习设计的组织与仿真•建立testbench通常使用的编码风格及方法设计组织虚线表示编译时检测输入文件是否存在及可读并允许生成输出文件。testbench组织•简单的testbench向要验证的设计提供向量,人工验证输出。•复杂的testbench是自检测的,其结果自动验证。stimulus要验证的设计简单的testbench复杂的testbench激励验证结果要验证的设计并行块•fork…join块在测试文件中很常用。他们的并行特性使用户可以说明绝对时间,并且可以并行的执行复杂的过程结构,如循环或任务。moduleinline_tb;reg[7:0]data_bus;//instanceofDUTinitialforkdata_bus=8'b00;#10data_bus=8'h45;#20repeat(10)#10data_bus=data_bus+1;#25repeat(5)#20data_bus=data_bus1;#140data_bus=8'h0f;joinendmodule上面的两个repeat循环从不同时间开始,并行执行。象这样的特殊的激励集在单个的begin…end块中将很难实现。Time|data_bus0|8’b0000_000010|8’b0100_010130|8’b0100_011040|8’b0100_011145|8’b1000_111050|8’b1000_111160|8’b1001_000065|8’b0010_000070|8’b0010_000180|8’b0010_001085|8’b0100_010090|8’b0100_0101100|8’b0100_0110105|8’b1000_1100110|8’b1000_1101120|8’b1000_1110125|8’b0001_1100140|8’b0000_1111包含文件•包含文件用于读入代码的重复部分或公共数据。moduleclk_gen(clk);outputclk;regclk;`includecommon.txtinitialbeginwhile($timesim_end)beginclk=initial_clock;#(period/2)clk=!initial_clock;#(period/2);end$finish;endendmodule在上面的例子中,公共参数在一个独立的文件中定义。此文件在不同的仿真中可被不同的测试文件调用。//common.txt//clockandsimulatorconstantsparameterinitial_clock=1;parameterperiod=15;parametermax_cyc=100;parametersim_end=period*max_cyc施加激励产生激励并加到设计有很多种方法。一些常用的方法有:•从一个initial块中施加线激励•从一个循环或always块施加激励•从一个向量或整数数组施加激励•记录一个仿真过程,然后在另一个仿真中回放施加激励线性激励•线性激励有以下特性:只有变量的值改变时才列出易于定义复杂的时序关系对一个复杂的测试,测试基准(testbench)可能非常大moduleinline_tb;reg[7:0]data_bus,addr;wire[7:0]results;DUTu1(results,data_bus,addr);initialforkdata_bus=8'h00;addr=8'h3f;#10data_bus=8'h45;#15addr=8'hf0;#40data_bus=8'h0f;#60$finish;joinendmodule循环激励•从循环产生激励有以下特性:在每一次循环,修改同一组激励变量时序关系规则代码紧凑moduleloop_tb;regclk;reg[7:0]stimulus;wire[7:0]results;integeri;DUTu1(results,stimulus);alwaysbegin//clockgenerationclk=1;#5clk=0;#5endinitialbeginfor(i=0;i256;i=i+1)@(negedgeclk)stimulus=i;#20$finish;endendmodule数组激励•从数组产生激励有以下特性:在每次反复中,修改同一组激励变量激励数组可以直接从文件中读取modulearray_tb;reg[7:0]data_bus,stim_array[0:15];//数组integeri;DUTu1(results,stimulus);initialbegin//从数组读入数据#20stimulus=stim_array[0];#30stimulus=stim_array[15];//线激励#20stimulus=stim_array[1];for(i=14;i1;i=i-1)//循环#50stimulus=stim_array[i];#30$finish;endendmodule矢量采样•在仿真过程中可以对激励和响应矢量进行采样,作为其它仿真的激励和期望结果。modulecapture_tb;parameterperiod=20reg[7:0]in_vec,out_vec;integerRESULTS,STIMULUS;DUTu1(out_vec,in_vec);initialbeginSTIMULUS=$fopen(stimulus.txt);RESULTS=$fopen(results.txt);forkif(STIMULUS!=0)forever#(period/2)$fstrobeb(STIMULUS,%b,in_vec);if(RESULTS!=0)#(period/2)forever#(period/2)$fstrobeb(RESULTS,%b,out_vec);joinendendmodule矢量回放•保存在文件中的矢量反过来可以作为激励moduleread_file_tb;parameternum_vecs=256;reg[7:0]data_bus;reg[7:0]stim[num_vecs-1:0];integeri;DUTu1(results,data_bus)initialbegin//Vectorsareloaded$readmemb(vec.txt,stim);for(i=0;inum_vecs;i=i+1)#50data_bus=stim[i];endendmodule//激励文件vec.txt001110000011100100111010001111000011000000101000000110000111100010111000..•使用矢量文件输入/输出的优点:–激励修改简单–设计反复验证时直接使用工具比较矢量文件。错误及警告报告•使用文本或文件输出类的系统任务报告错误及警告always@(posedgepar_err)$display(error-busparityerrorsdetected);always@(posedgecor_err)$display(warning-correctableerrordetected);•一个更为复杂的testbench可以:–不但能报告错误,而能进行一些动作,如取消一个激励块并跳转到下一个激励。–在内部保持错误跟踪,并在每次测试结束时产生一个错误报告。强制激励•在过程块中,可以用两种持续赋值语句驱动一个值或表达式到一个信号。–过程持续赋值通常不可综合,所以它们通常用于测试基准描述。–对每一种持续赋值,都有对应的命令停止信号赋值。–不允许在赋值语句内部出现时序控制。•对一个寄存器使用assign和deassign,将覆盖所有其他在该信号上的赋值。这个寄存器可以是RTL设计中的一个节点或测试基准中在多个地方赋值的信号等。initialbegin#10assigntop.dut.fsm1.state_reg=`init_state;#20deassigntop.dut.fsm1.state_reg;end•在register和net上(例如一个门级扫描寄存器的输出)使用force和release,将覆盖该信号上的所有其他驱动。initialbegin#10forcetop.dut.counter.scan_reg.q=0;#20releasetop.dut.counter.scan_reg.q;end强制激励•可以强制(force)并释放一个信号的指定位、部分位或连接,但位的指定不能是一个变量(例如out_vec[i])•不能对register的一位或部分位使用assign和deassign•对同一个信号,force覆盖assign。•后面的assign或force语句覆盖以前相同类型的语句。•如果对一个信号先assign然后force,它将保持force值。在对其进行release后,信号为assign值。•如果在一个信号上force多个值,然后release该信号,则不出现任何force值。在上面两个例子中,在net或register上所赋的常数值,覆盖所有在时刻10和时刻20之间可能发生在该信号上的其他任何赋值或驱动。如果所赋值是一个表达式,则该表达式将被持续计算。建立时钟例1:虽然有时候在设计中给出时钟,但通常时钟是测试基准中建立。下面介绍如何产生不同的时钟波形。同时给出用门级和行为级描述方法下面是一个简单对称时钟的例子:regck;alwaysbegin#(period/2)ck=0;#(period/2)ck=1;endreggo;wireck;nand#(period/2)u1(ck,ck,go);initialbegingo=0;#(period/2)go=1;end注意:在一些仿真器中,时钟与设计使用相同的抽象级描述时,仿真性能会好一些。产生的波形(假定period为20)建立时钟例2:有启动延时的对称时钟的例子:regck;initialbeginck=0;#(period)forever#(period/2)ck=!ck;endreggo;wireck;nand#(period/2)u1(ck,ck,go);initialbegingo=0;#(period)go=1;end注意:在行为描述中,在时间0将CK初始化为0;而在结构描述中,直到period/2才影响CK值。当go信号在时间0初始化时,CK值到period/2才变化。可以使用特殊命令force和release立即影响CK值。产生的波形(假定period为20)建立时钟例3:有不规则启动延时的不对称时钟的例子:注意:在行为描述中,CK值立刻被影响;而在结构描述中,在传播延时后才输出正确波形。产生的波形(假定period为20)regck;initialbegin#(period+1)ck=1;#(period/2–1)foreverbegin#(period/4)ck=0;#(3*period/4)ck=1;endendreggo;wireck;nand#(3*period/4,period/4)u1(ck,ck,go);initialbegin#(period/4+1)go=0;#(5*period/4–1)go=1;end使用task在testbench中使用task可以压缩重复操作,提高代码效率。modulebus_ctrl_tb;reg[7:0]data;regdata_valid,data_rd;cpuu1(data_valid,data,data_rd);initialbegincpu_driver(8'b0000_0000);cpu_driver(8'b1010_1010);cpu_driver(8'b01