您好,欢迎来到华佗小知识。
搜索
您的当前位置:首页testbench的学习笔记

testbench的学习笔记

来源:华佗小知识
Testbench学习笔记

书写testbench是数字电路设计中不可或缺的一项设计方法,主要是提供的是激励。尽管现在各种开发工具都通过绘制波形图的方法生成测试激励,测试书写的代码,但是其不可移植性,不可通用性,还有有些功能无法是实现,如监视变量的值的变化,显示数据的状态等。 一个完整的testbench包含下列几个部分: (1)module的定义,一般无输入输出端口。

(2)信号的定义,定义哪些是你要输入,输入的定义为reg类型,输出的定义为wire型

(3)实例化待测试的模块 (4)提供测试激励 1.如何书写测试激励; (1)时钟信号的产生 initial begin clk = 0; forever

#clk_period/2 clk = ~clk; end 或者是 always begin

#clk_period/2 clk = 0; #clk_period/2 clk = 1; end

产生时钟的原理,是利用always或者是initial + forever产生不断重复的信号,构成时钟信号。 (2)复位信号的产生

复位信号就是在复位电平下延时一段时间,然后再将复位电平信号取反即可。如: initial begin rst = 0; # 100; rst = 1; end

在实际应用将其封装为task,使用时掉调用即可。 调用如下: initial begin

sys_time(100); //复位100个时间单位 ..... end

任务的定义如下: task sys_rst ;

input [10:0] rst_time; //调用task的时候,将参数赋值给rst_time begin rst = 0; #rst_time; rst_time = 1;

end endtask

(3)产生一种复杂的信号。如下面的实例产生一个vs和hs信号

//------------------------------------------------------------------------------------- parameter n = 10;

//------------------------------------------------------------------------------------- initial begin

cmos_hs = 1'b0; // time 0 时刻 cmos_vs = 1'b0; repeat(n) //重复n次 begin

# 200 cmos_vs = 1'b1; // time 200 repeat(10*n) begin

#100 cmos_hs = 1'b1; //time 300 #100 cmos_hs = 1'b0; //time 700 end

#100 cmos_vs = 1'b0; end end

该程序段可以生成10个vs信号,每个vs信号下有100个hs信号。由于initial信号只能执行一次,所以为了得到有限的重复信号,可以采用repeat关键词得到。

这样基本上就可以完成一些简单的测试testbench了。 2.如何将我们的测试尽可能的简单明了化

用Modelsim对Verilog HDL进行仿真的人都会知道,看一大堆波形会很麻烦,如果代码变量很多,很复杂,出了问题都不知道问你在哪里,或者看了半天,发现图形是个错的。运用合适的方法将自己需要的变量以文本方式显示,监视变量的变化不很好嘛?

下面介绍几种常用的系统函数 (1)$time

作用:返回所在模块的仿真时间,可以查看信号的出现的时间,用来把握信号的时序。

如:$display(''the time is %t'',$time) ;//显示当时的时间 (2)$display

作用: 将需要显示的内容在命令栏显示出来

如: $display(\"the signal is % d\将ad信号以十进制的方式显示出来

(3)$monitor

作用:监视变量的变化,一旦变量变化,则将变量显示出来

如:$monitor (\"at time is %t and the signal is %b\\n\ (3) 文件操作类 $fopen

作用:打开一个文件面,对文件的操作 $fdisplay

作用:在打开的文件里,写入显示的内容 $fmonitor

作用:在打开的文件里,写入监视的变量变化时的内容 $fclose

作用:关闭当前的内容 如:initial

Begin :block //可以在内部声明局部变量 Integer out_file;

out_file = $fopen(\"data.out\打开data.out这个文件后,从第一行开始写,如果该文件没有,则首先创建该文件,然后再写。打开文件后返回out_file这个文件整形指针

$fdisplay(out_file,\"at the time is %t \",$time); .....

$fmonitor(out_file,\"at the time is %t and the signal is %b\\n \",$time ,signal); ......

$fclose(out_file); End

标签:

FPGA CPLD SOPC NIOS VHDL verilog ZRtech modelsim testbench

为什么要写testbench?

我们为什么要写testbench?

经常看到论坛里有人问我们为什么要写testbench,总是觉得不好回答,下面是整理出来的一些理由供大家参考。

与写testbench相对应的功能手段还有画波形图,两者相比,画波形图的方法更加直观和易于入门,那为什么我们还要写Testbench呢?原因

有以下五点:

第一,画波形图只能提供极低的功能覆盖。

画波形无法产生复杂的激励,因此它只产生极其有限的输入,从而只能对电路的极少数功能进行测试;而testbench以语言的方式描述激励源,容易进行高层次的抽象,可以产生各种激励源,轻松地实现远高于画波形图所能提供的功能覆盖率。以PCI转以太网电路设计为例,该设计并不复杂,但却已经需要考虑多种情况:PCI的配置读写、存储器读写等操作;以太网的短包、长包等。如果这些激励都用画波形图完成,其工作量是难以想象的;用testbench则可以轻松完成这些工作。 第二,画波形无法实现验证自动化。

对于规模设计来说,仿真时间很长,如果一个需要仿真一天设计在检错时仅通过画波形图来观测,将几乎不能检查出任何错误;而testbench是以语言的方式进行描述的,能够很方便地实现对仿真结果的自动比较,并以文字的方式报告仿真结果。我们甚至可以在下班时开始仿真,然后第二天早上上班时再查看验证结果。 第三,画波形图难于定位错误。

用画波形图进行仿真是一种原始的墨盒验证法,无法使用新的验证技术;而testbench可以通过在内部设置观测点,或者使用断言等技术,快速地定位问题。

第四,画波形的可重用性和平台移植性极差。

如果将一个PCI转100Mb以太网的设计升级到PCI转1000Mb以太网,这时原来画的波形图将不得不重新设计,耗费大量的人力物力及

时间;但若使用testbench,只需要进行一些小的修改就可以完成一个新的测试平台,极大地提高了验证效率。 第五,通过画波形的验证速度极慢。

Testbench的仿真速度比画波形图的方式快几个数量级,在Quartus下画波形需半个小时才能跑出来的仿真结果,在ModelSim下使用testbench可能只需几秒钟就可以完成。

所以,在设计中除了那些极简单的设计(如调用厂商提供的MegaCore),推荐通过写testbench的方法来做功能验证。

标签: FPGA CPLD SOPC NIOS VHDL verilog ZRtech

Altera产品命名规则

ALTERA产品型号命名

XXX XX XX X XX X X

1 2 3 4 5 6 7

工艺 + 型号 + LE数量 + 封装 + 管脚数目+ 温度范围 + 器件速度。

1.前缀: EP 典型器件

EPC 组成的EPROM 器件

EPF FLEX 10K 或FLFX 6000 系列、FLFX 8000 系列 EPM MAX5000 系列、MAX7000 系列、MAX9000 系列 EPX 快闪逻辑器件 2.器件型号

3. LE数量: XX(k) 4.封装形式:

D 陶瓷双列直插

Q 塑料四面引线扁平封装 P 塑料双列直插

R 功率四面引线扁平封装 S 塑料微型封装

T 薄型J 形引线芯片载体 J 陶瓷J 形引线芯片载体

W 陶瓷四面引线扁平封装 L 塑料J 形引线芯片载体 B 球阵列 5.管脚

6.温度范围:

C ℃至70℃, I -40℃至85℃, M -55℃至125℃ 7.速度:

数字越小速度越快。

举例:

EP2C20F484C6 EP 工艺

2C cyclone2 (S代表stratix。A代表arria) 20 2wLE数量

F484 FBGA484pin 封装 C6 八速

标签: testbench

Testbench巧解!

=================================概念=========================

testbench是一种验证的手段。

首先,任何设计都是会有输入输出的。但是在软环境中没有激励输入,也不会对你设计的输出正确性进行评估。那么此时便有一种,模拟实际环境的输入激励和输出校验的一种“虚拟平台”的产生。在这个平台上你可以对你的设计从软件层面上进行分析和校验,这个就是testbench的含义。

==============================================================

======================初步认识=================================

就初学而言,testbench更像一个激励的产生器。

举例:一个ram,可能有几个input和output。分别列在下面。

clk,时钟输入 addr,地址输入 wen,写使能 data,数据输入

然后还有一个dataout的数据输出。

那么你可以写一个文件,给clk,addr,wen,data送入你预想的一些信号,然后观察q的输出,看看ram是否工作正常。那么这个文件从一定意义上可以叫做\"testbench\"。

联想(帮助理解):从quartus里面你仿真,你可能对着那个画图一样的东西画上输入,然后编译以后看他的输出。对吧。那么在modelsim里面,我告诉你,可以不用画图了~,你只需要按照一定规则写一个.v或者.vhd的文件,这个文件可以给你的设计提供你预想的输入。这个就是testbench的文件。然后在modelsim这个特定的软件环境下,这个软件能根据你的代码给你的设计提供输入,又可以把你设计的输出在屏幕上显示出来给你debug。那么这个时候,一个在modelsim上的testbench就完成了。

狭义的总结一下:FPGA的testbench就是一个.v(verilog)或者.vhd(vhdl)的文件。这个文件能给你的设计提供激励,并能在一些专用的软件中提供良好的debug接口。这个就是一个testbench。

==============================================================

=====================高级应用================================

关于testbench的高级应用。

刚才说了初步的testbench。其实testbench是verification(验证)中的一个手段。

验证是什么呢?举例:做鱼了,你往里面加了调料,然后再尝尝味道,这个就是验证的过程。同样你可以分成几个部分,一条鱼,好比你的设计,然后你给他一定的激励,也就是调料啦。然后你再尝一尝,看看鱼是不是达到你想要的味道了。那就是一种验证的手段,如果淡了。那么加点盐,再尝尝,这个就是反复验证。

testbench图解一下比较清楚。

=========================Testbench=============================

| | | |

| | ================== | | | 激励生成 |====》 | | | 输出校验 | | |预想输入 | 设计 |==》 | | | | | | 设计 | |

================================= 输出 ===================

testbench里面包含了三个东西:

1、激励生成。也就是我们刚才初级时候说的所谓的“testbench”。英文么就是simulator,这个只用来生成输出,他自己没有输入,只是按照一定的规律去给你的设计激励,激励通过设计的输入端口送到你的设计中。其余的事情不管。这里的激励,都是预先设想好的,比如根据某个协议,或者某种通信方式传递。

2、你的设计。英文可以叫做DUT:design under testbench或者DUV:design under verification。当然咯。这个是你主要目标。

3、输出校验。校验你的输出。英文叫markerboard,他所管的事情就是,接收你设计的输入,然后通过校验,找出对应的问题。然后报错,或者统计错误。等等。通俗的讲,你设计它就是把你自己解脱出来,让他来帮你找错误。他输出给你的可能就是通过打印啊,通知啊,等等方法了解你设计的正确性。

那么你有可能问了,这个东西用verilog或者VHDL能写么,modelsim里能用么?的确是可以的,有写甚至可以用c的代码通过程序接口来转换到modelsim里面来帮助验证。

========================高级应用结束==========================

最后小说两句:testbench是一个平台,帮助你从软件方面验证的。对于这个概念不需强求,等你自己的验证写多了,自然而然就会了解其中深刻的含义。先开始慢慢的写一些激励,然后再写写校验。到时候你收获的东西自然而然的能帮助你理解testbench和verification 。

标签: Testbench 编写技巧

Testbench编写技巧

测试平台是个没有输入输出端口的模块。仿真在一个模块设计中是很关键的步骤,而testbench是仿真的很好工具。

与待测模块接口

与输入端口相连接的变量定义为reg 与输出端口相连的定义为wire

initial块中初始化变量,必须的。 用$stop或$finish暂停或结束仿真

wait(z==1’b1);//等待变量值改变,变量可以是待测试模块的输出或者内部变量

时钟产生:

always # 10 clk =~clk;产生时钟

initial repeat(13) #5 clk =~clk; //控制只产生13个时钟。

同步数据:

initial forever @ (posedge clk) #3 x = $random;

为了降低多个输入同时翻转的概率,对时序电路的输入一般采用素数作为时间间隔。

同步显示:

 initial $monitor (“%d is changed at %t”,MUT.current,$time);//

一般在 initial中调用,采用$monitor显示模块MUT内部current的值以及发生变化的时间,$monitor是一个后台运行任务函数,多个模块下,任意时间只能有一个$monitor起作用,可用$monitoron $monitoroff来控制。

 always @(z) $display(“Output changed at %t to %b”,$time,z);当z发生变化输出z值

以及变化时间,自动换行。

 always @(z) $strobe(“Output changed at %t to %b”,$time,z);//仿真结束后显示输出,

查看非阻塞赋值变量的值。

随机数据

initial repeat(5) #7 x = $random; a = $random%60; //产生-59~59之间随机数 a = {$random}%60; //产生0~59之间随机数

产生随机时间间隔

always begin

t= $random; #(t) x = $random; end

数据缓存

initial buffer = 16’b1110_0001_1011_0101;//将测试数据进行初始化

always @ ( posedge clk) #1 {x,buffer} = {buffer,x};//可以在控制的数据下输入信号x

读取数据文件

reg [7:0] mem1[0:1024]; initial begin

$readmemh( “data1.dat” , mem1);

模板:

`timescale 1ns/100ps `include “*.v” module t; 参数定义

输入测试模块的测试信号定义 reg,wire 内部变量定义 initial begin 初始化变量

$monitor (“%d is changed at %t”,MUT.current,$time);//监控数据 forever @ (posedge clk) #3 x = $random;//同步数据 repeat(5) #7 x = $random; end

always @(z) $display(“Output changed at %t to %b”,$time,z); always @(z) $strobe(“Output changed at %t to %b”,$time,z); always # 10 clk =~clk;//产生时钟 always begin//

t= $random; #(t) x = $random;

end 模块实例化

defparam module. WIDTH = 32; endmodule

标签: ModelSim TestBench

初学TestBench

2009.5.23

初学TestBench

`timescale 1ns/10ps //单位时间/精度 `include \"adder.v\"

module adder_testbench; reg a,b; wire sum,cout;

adder adder_t( //调用待测模块 .sum(sum), .c(count), .a(a), .b(b) );

initial begin

a = 0; //初始值a=0

forever #20 a = ~a; //每经过20个单位时间,a取反 end

initial begin

b = 0; //初始值b=0

forever #10 b = ~b; //每经过10个单位时间,b取反 end

initial begin

$monitor ($time,,,\"%d + %d = {%d,%d}\//监控输出

#40 $stop; end

小结:

$monitor 输出打印显示 $stop停止当前仿真

$finish结束仿真,询问是否退出ModelSim

时钟产生方法: 1.用intial语句 reg clock; initial begin clock = 0;

forever #10 clock = ~clock; end

2.用always语句 reg clock; initial clock = 0; always

#10 clock = ~clock;

以上写法产生的时钟如下:

标签: modelsim Testbench FPGA仿真

FPGA调试篇(二)在Modelsim下使用TestBench对设计进行仿真

Signal Tap II是物理调试,它捕捉的是系统在运行过程中实实在在的信号,是最真实的。但是每次调试时,都需要综合、布局、布线、并将生成的SOF文件下载至FPGA;而这一过程往往需要一定的时间。显然,这种调试方式在需要重复次数较多的情况下,效率是很低的。

前面提到,调试FPGA需要多种方式相结合,那么本文将介绍在Modelsim下使用Testbench对设计进行逻辑仿真。

如同 FPGA调试篇(一),本文仍然是以总线时序逻辑为例。

首先,编写激励文件,即产生单片机总线的读写时序;(仍然是在0x2345地址入写0x43,0x5812地址处写0x25)。

parameter ADDR1 = 16'h2345, ADDR2 = 16'h5812; parameter DAT1 = 8'h43, DAT2 = 8'h25;

ebiWriteDat(ADDR1, DAT1); ebiWriteDat(ADDR2, DAT2); 详细代码见符件

然后,新建modelsim工程,将激励文件和源文件加入工程,并编译;

启动仿真 Simulate-> Start Simulateion,弹出如下界面

添加信号到WAVE窗口

运行 run 100us 查看结果

Transcript窗口的打印信息

Wave窗口

结果分析:

仿真文件中,首先对0x2345地址处写0x43,然后在0x5812地址处写0x25,20ns后读出这两个地址处的值,比较,如果跟写入的值一样就显示Success,否则显示Error。

Transcript窗口是仿真文件运行中的打印信息,很直观的表达了仿真的结果。

同样,从波形图的时序也可以来分析整个逻辑是否正确。

Modelsim编译速度很快,不需要像quartus那样去综合,布局布线,大大提高了调试的效率。

另:

本文中的CODE和Testbench是在网友Adeko的代码基础上修改得来的,特向他表示感谢,有兴趣的朋友可以参考他的一篇设计:

UART的FPGA实现过程 - 附完整的FPGA,ModelSim,MCU代码和工程,以及实现文档

本文PDF和代码

使用FPGA完成了UART的设计,包含设计文档,FPGA源代码和工程文件,ModelSim的testbench和工程,硬件测试的AVR源代码和IAR工程等,有参考价值,

以此借阿莫的地盘作新网站(创造力电子开发网-http://www.edaok.net)的宣传,欢迎大家访问,申请加“酷”,不通过请阿莫反馈建议

================【以下为文档和源代码工程的打包】================

UART的FPGA实现设计文档,包含完整的源代码和工程ourdev_446226.rar(文件大小:1.96M) (原文件名:UART的FPGA设计文档.rar)

=========文档预览==========

文档开始简要地介绍了UART协议,如下图是ART帧格式示意图:

图 1.1 UART帧格式示意图 (原文件名:图 1.1 UART帧格式示意图.jpg)

UART可以使用奇偶校验,如下图介绍了奇偶校验实现的RTL级示意图

图 1.2 奇偶校验RTL示意图 (原文件名:图 1.2 奇偶校验RTL示意图.jpg)

以下是我设计过程的工作环境,作为参考,目的是为了以最快的速度测试本设计,再进入学习和改进:

表 2.1 工作环境参考表 (原文件名:表 2.1 工作环境参考表.jpg)

下图是设计的ModelSim仿真的截图,至于为什么不使用Quaatus的仿真工具?可以在ModelSim的版面了解它的优点

图 2.5 ModelSim仿真截图 (原文件名:图 2.5 ModelSim仿真截图.jpg)

以下是硬件测试的截图,这时,单片机操作FPGA实现的UART的方法和操作内部UART的方法一样,因为本设计的UART是根据AVR的UART而设计的,只是基址不同,其它者一样。

图 2.6 PC测试过程 (原文件名:图 2.6 PC测试过程.jpg)

下图是设计的框图,分为波特率发生模块,接收器模块和发送器模块。

图 2.7 UART项层框图 (原文件名:图 2.7 UART项层框图.jpg)

考虑到本设计的维护,升级等的方便,本设计使用状态机的编码方式设计硬件,下图是UART接收器的状态图

图 2.12 接收器状态机 (原文件名:图 2.12 接收器状态机.jpg)

标签: testbench

如何编写testbench的总结(转)

如何编写testbench的总结

2008年11月19日 星期三 04:31 A.M.

如何编写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\将.dat文件读入寄存器mem中 initial $readmemh ( \"mem.data\参数为寄存器加载数据的地址始终

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型的位数据 $time ex: ...

time condition_happen_time; ...

condition_happen_time = $time; ...

$monitor($time,\"data output = %d\... //参数

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\

eg:$fmonitor(file_id, \"%m: %t in1=%d o1=%h\//$fwrite需要触发条件才记录

$fwrite(file_id, \"%format_char\//$fdisplay需要触发条件才记录

$fdisplay(file_id, \"%format_char\$fstrobe(); 3.读取文件 integer file_id;

file_id = $fread(\"file_path/file_name\4.关闭文件

$fclose(fjile_id); 5.由文件设定存储器初值

$readmemh(\"file_name\初始化数据为十六进制 $readmemb(\"file_name\初始化数据为二进制 //仿真控制

$finish(parameter); //parameter = 0,1,2 $stop(parameter); //读入SDF文件

$sdf_annotate(\"sdf_file_name\//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\

$display(\" Time Clk Rst Ld SftRg Data Sel\");

$monitor(\"%t %b %b %b %b %b %b\clock, reset, load, shiftreg, data, sel); end endmodule

标签: Testbench

Testbench——读写紊乱状态

Testbench——HDL的并行性

为什么C不能取代verilog和VHDL作为硬件描述语言?因为C缺少了硬件描

述最基本的三个思想:连通性(Connectivity),时间性(Time)和并行性(Concurrency)。

连通性是指使用一个简单并相互连接的模块来描述设计的能力,原理图设计

工具就是连通性完美的支持工具。

时间性是指表现设计状态演进的时间变化的能力,这个能力不同于衡量一个

代码执行运行多久的时间。

并行性是指描述同时发生相互的行为的能力。

Testbench——读写紊乱状态

在同一时刻对同一个寄存器进行读写容易发生紊乱状态。以下的例子,第一个always块对count操作(写),第二个always却要显示它。那么会出现什么状态呢?

module rw_race(clk);

input clk; integer count;

always @ (posedge clk) begin

count = count + 1; end

always @ (posedge clk) begin

$write(\"Count is equal to %0d\\n\end

endmodule

由于testbench基于计算机,那么处理的时候也是分时复用的,从而这两个

always块会先后执行。也就是说会出现两种情况,这里假设count在执行前为10,若先执行第一个块,那么后第二个块执行的结果显示count=11;若先执行第二个块再执行第一个块,显示的结果count=10。

往往这样的紊乱状态不是我们希望看到的,很可能会给我们的测试工作带来许多不必要的麻烦。那么,有什么解决办法呢?

module rw_race(clk);

input clk; integer count;

always @ (posedge clk) begin

count <= count + 1; end

always @ (posedge clk) begin

$write(“Count is equal to %0d\\n\end

endmodule

采用非阻塞赋值语句后,这个紊乱的状态就会得到解决。在第一个always

块count增加的同时第二个always块也在执行,那么最后显示的count值是count增1之前的数值。

module rw_race;

wire [7:0] out;

assign out = count + 1;

integer count; initial begin

count = 0;

$write(\"Out = %b\\n\end

endmodule

会得到什么结果呢?这取决于你所使用的仿真器和命令行。一般的,再看下面的例子。

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; initial begin

count = 0; #4; // \"out\" will be 0 or x’s. $write(\"Out = %b\\n\end

endmodule

这些都是一个好的testbench应该注意的细节。

标签: testbench

有关testbench的一些问题

task test_task;//task的定义,以task关键字开始,紧接后面是task的id,名字

input [1:0] a; //task的输入输出定义,用来和外面的变量进行交换,是通向外面的接口 output [1:0] b; begin b = ~a; end

endtask

reg [1:0] task_a;

reg [1:0] task_b; //因为task必须在过程语句中调用,所以其实参必须为reg类型的 initial begin

task_a = 2'b0; test_task (task_a,task_b);//task的调用,直接调用,没有什么像module一样例化的东西

#10 task_a = 2'b10; end

initial begin

#20 task_a = 2'b11;

test_task (task_a,task_b);//同样可以改变task内部的值,和module不同,这是同一个task #10 task_a = 2'b10; end

reg [1:0] task1_a; reg [1:0] task1_b; task test_task1; output [1:0] b;

assign b = ~task1_a;//可以使用全局变量,不一定是task内部的变量 endtask initial

begin

task1_a = 2'd0;

#1 test_task1(task1_b); #10 task1_a = 2'd1;

#1 test_task1(task1_b); #10 task1_a = 2'd2;

#1 test_task1(task1_b); #10 task1_a = 2'd3;

#1 test_task1(task1_b); end

task和module有点类似,都可以通过输入输出来和外面发生关系,这个时候module可以例化多次,这样就是不同的单元,和task 还是有区别的。

1,task只能定义在module内部,不能单独在一个文件中,不能定义在module外面。

2,在task调用的是必须在过程性语句内部使用,initial,begin ... end 3,task可以没有参数,直接使用全局变量来实现功能。 4,可以使用延时控制,可以调用其他task和函数。

函数:可以调用函数,不能调用任务,不可有时序控制。

function [1:0] invert; //只有一个返回值,invert,寄存器类型的 //input [1:0] a; //可以有一个输入,或者多个,或者没有输入 //begin

invert = 2'd3; //end endfunction reg [1:0] fun_a; reg [1:0] fun_out;

initial begin

fun_a = 2'd2;

fun_out = invert();//(fun_a);//调用函数,函数返回一个值,必须在过程性语句中调用。

#50 fun_a = 2'd1;

fun_out = invert();//(fun_a); #50 fun_a = 2'd0;

fun_out = invert();//(fun_a); #50 fun_a = 2'd3;

fun_out = invert();//(fun_a);

end

task test_task1; //任务调用函数 output [1:0] b; b = ~add(task1_a); endtask

function [1:0] invert;//函数调用函数 input [1:0] a; invert = ~add(a); endfunction

//显示和文件操作

integer i; initial begin a1 = 0;

for(i=0;i<100;i=i+1) begin

if(a1 == 8'd50) begin

$write(\"a1 is %d\显示 $write(\"\\n\\n\\n\\n\\n\\n\");

$fwrite(f_id,\"a1 is %d\写入文件 $fwrite(f_id,\"\\n\\n\\n\\n\\n\\n\"); end

else if(a1 == 8'd70) begin

$display(\"a1 is %d\\n\\n\显示

$fdisplay(f_id,\"a1 is %d\\n\\n\写入文件 end

else if (a1 == 8'd90) begin

$fclose(f_id); end

#2 a1 = a1 +1; end end

initial

begin

$monitor(\"test %d\\n\变量变化就会显示 end

integer f_id;

initial begin

f_id = $fopen(\"./test.txt\");//打开一个文件 end

initial

begin

$strobe(\"tes1t %d\\n\在时间步0结束的时候显示这一句 end

integer cool;

initial begin

cool = 4; //这是时间步0要执行的,

$strobe (\"strobe cool1 is %d at time %t\\n\这是时间步0要执行的,在时间步的最后显示

$display(\"cool1 is %d at time %t\\n\这是时间步0要执行的,

#4 cool = 8; //这是时间步4要执行的,

$strobe (\"strobe2 cool1 is %d at time %t\\n\这是时间步4要执行的,

$display(\"cool2 is %d at time %t\\n\这是时间步4要执行的, 标签: testbench 模型

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

本文来源于:电子工程世界

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.cn 版权所有 湘ICP备2023017654号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务