文档库 最新最全的文档下载
当前位置:文档库 › testbench

testbench

testbench
testbench

Verilog testbench总结(一)

1.激励的产生

对于testbench而言,端口应当和被测试的module一一对应。端口分为input,output和i nout类型产生激励信号的时候,input对应的端口应当申明为reg,output对应的端口申明为wire,inout端口比较特殊,下面专门讲解。

1)直接赋值。

一般用initial块给信号赋初值,initial块执行一次,always或者forever表示由事件激发反复执行。

举例,一个module

module exam();

reg rst_n;

reg clk;

reg data;

initial

begin

clk=1'b0;

rst=1'b1;

#10

rst=1'b0;

#500

rst=1'b1;

end

always

begin

#10

clk=~clk;

end

大家应该注意到有个#符号,该符号的意思是指延迟相应的时间单位。该时间单位由timsca le决定.一般在testbench的开头定义时间单位和仿真精度,比如`timescale1ns/1ps,前面一个是代表时间单位,后面一个代表仿真时间精度。以上面的例子而言,一个时钟周期是20个单位,也就是20ns。而仿真时间精度的概念就是,你能看到1.001ns时对应的信号值,而假如timescale1ns/1ns,1.001ns时候的值就无法看到。对于一个设计而言,时间刻度应该统一,如果设计文件和testbench里面的时间刻度不一致,仿真器默认以test bench为准。一个较好的办法是写一个global.v文件,然后用include的办法,可以防止这个问题。

对于反复执行的操作,可写成task,然后调用,比如

task load_count;

input[3:0]load_value;

begin

@(negedge clk_50);

$display($time,"<>",load_value);

load_l=1’b0;

count_in=load_value;

@(negedge clk_50);

load_l=1’b1;

end

endtask//of load_count

initial

begin

load_count(4’hA);//调用task

end

其他像forever,for,function等等语句用法类似,虽然不一定都能综合,但是用在testbe nch里面很方便,大家可以自行查阅参考文档

2)文件输入

有时候,需要大量的数据输入,直接赋值的话比较繁琐,可以先生成数据,再将数据读入到寄存器中,需要时取出即可。用$readmemb系统任务从文本文件中读取二进制向量(可以包含输入激励和输出期望值)。$readmemh用于读取十六进制文件。例如:

reg[7:0]mem[1:256]//a8-bit,256-word定义存储器mem

initial$readmemh("E:/readhex/mem.dat",mem)//将.dat文件读入寄存器mem 中

initial$readmemh("E:/readhex/mem.dat",mem,128,1)//参数为寄存器加载数据的地址始终

2.查看仿真结果

对于简单的module来说,要在modelsim的仿真窗口里面看波形,就用add wave..命令比如,testbench的顶层module名叫tb,要看时钟信号,就用add wave tb.clk

要查看所有信号的时候,就用add wave/*

当然,也可以在workspace下的sim窗口里面右键单击instance来添加波形

对于复杂的仿真,免不了要记录波形和数据到文件里面去。

1)波形文件记录

常见的波形文件一般有两种,vcd和fsdb,debussy是个很好的工具,支持fsdb,所以最好是modelsim+debussy的组合

默认情况下,modelsim不认识fsdb,所以需要先装debussy,再生成fsdb文件。

$dumpfile和$dumpvar是verilog语言中的两个系统任务,可以调用这两个系统任务来创建和将指定信息导入VCD文件.

对于fsdb文件来说,对应的命令是fsdbDumpfile,dumpfsdbvars

(什么是VCD文件?答:VCD文件是在对设计进行的仿真过程中,记录各种信号取值变化情况的信息记录文件。EDA工具通过读取VCD格式的文件,显示图形化的仿真波形,所以,可以把VCD文件简单地视为波形记录文件.)下面分别描述它们的用法并举例说明之。

$dumpfile系统任务:为所要创建的VCD文件指定文件名。

举例("//"符号后的内容为注释文字):

initial

$dumpfile("myfile.dump");//指定VCD文件的名字为myfile.dump,仿真信息将记录到此文件

$dumpvar系统任务:指定需要记录到VCD文件中的信号,可以指定某一模块层次上的所有信号,也可以单独指定某一个信号。

典型语法为$dumpvar(level,module_name);参数level为一个整数,用于指定层次数,参数module则指定要记录的模块。整句的意思就是,对于指定的模块,包括其下各个层次(层次数由level指定)的信号,都需要记录到VCD文件中去。

举例:

initial

$dumpvar(0,top);//指定层次数为0,则top模块及其下面各层次的所有信号将被记录

initial

$dumpvar(1,top);//记录模块实例top以下一层的信号

//层次数为1,即记录top模块这一层次的信号

//对于top模块中调用的更深层次的模块实例,则不记录其信号变化

initial

$dumpvar(2,top);//记录模块实例top以下两层的信号

//即top模块及其下一层的信号将被记录

假设模块top中包含有子模块module1,而我们希望记录top.module1模块以下两层的信号,

则语法举例如下:

initial

$dumpvar(2,top.module1);//模块实例top.module1及其下一层的信号将被记录

假设模块top包含信号signal1和signal2(注意是变量而不是子模块),如我们希望只记录这两个信号,则语法举例如下:

initial

$dumpvar(0,top.signal1,top.signal2);//虽然指定了层次数,但层次数是不影响单独指定的信号的

//即指定层次数和单独指定的信号无关

我们甚至可以在同一个$dumpvar的调用中,同时指定某些层次上的所有信号和某个单独的信号,假设模块top包含信号signal1,同时包含有子模块module1,如果我们不但希望记录signal1这个独立的信号,而且还希望记录子模块module1以下三层的所有信号,则语法举例如下:

initial

$dumpvar(3,top.signal1,top.module1);//指定层次数和单独指定的信号无关

//所以层次数3只作用于模块top.module1,而与信号

top.signal1无关

上面这个例子和下面的语句是等效的:

initial

begin

$dumpvar(0,top.signal1);

$dumpvar(3,top.module1);

end

$dumpvar的特别用法(不带任何参数):

initial

$dumpvar;//无参数,表示设计中的所有信号都将被记录

最后,我们将$dumpfile和$dumpvar这两个系统任务的使用方法在下面的例子中综合说明,假设我们有一个设计实例,名为i_design,此设计中包含模块module1,模块module1下面还有很多层次,我们希望对这个设计进行仿真,并将仿真过程中模块module1及其以下所有层次中所有信号的变化情况,记录存储到名为mydesign.dump的VCD文件中去,则例示如下:

initial

begin

$dumpfile("mydesign.dump");//指定VCD文件名为mydesign.dump

$dumpvar(0,i_design.module1);//记录i_design.module1模块及其下面层次中所有模块的所有信号

end

对于生成fsdb文件而言,也是类似的

initial

begin

$fsdbDumpfile("tb_xxx.fsdb");

$fsdbDumpvars(0,tb_xxx);

end

2)文件输出结果

integer out_file;//out_file是一个文件描述,需要定义为integer类型

out_file=$fopen("cpu.data");//cpu.data是需要打开的文件,也就是最终的输出文本

设计中的信号值可以通过$fmonitor,$fdisplay,$fwrite

其中$fmonitor只要有变化就一直记录,$fdisplay和$fwrite需要触发条件才记录

例子:

initial begin

$fmonitor(file_id,"%m:%t in1=%d o1=%h",$time,in1,o1);

end

always@(a or b)

begin

$fwrite(file_id,"At time%t a=%b b=%b",$realtime,a,b);

end

3testbench的技巧

1).如果激励中有一些重复的项目,可以考虑将这些语句编写成一个task,这样会给书写

和仿真带来很大方便。例如,一个存储器的testbench的激励可以包含write,read等tas k。

2).如果DUT中包含双向信号(inout),在编写testbench时要注意。需要一个reg变量来表示其输入,还需要一个wire变量表示其输出。

3).如果initial块语句过于复杂,可以考虑将其分为互补相干的几个部分,用数个initi al块来描述。在仿真时,这些initial块会并发运行。这样方便阅读和修改。

4).每个testbench都最好包含$stop语句,用以指明仿真何时结束。

5).加载测试向量时,避免在时钟的上下沿变化,比如数据最好在时钟上升沿之前变化,这也符合建立时间的要求。

4.一个简单的例子

module counter(clk,reset,enable,count);

input clk,reset,enable;

output[3:0]count;

reg[3:0]count;

always@(posedge clk)

if(reset==1'b1)begin

count<=0;

end else if(enable==1'b1)begin

count<=count+1;

end

endmodule

testbench

module counter_tb;

reg clk,reset,enable;

wire[3:0]count;

counter U0(

.clk(clk),

.reset(reset),

.enable(enable),

.count(count)

);

initial begin

clk=0;

reset=0;

enable=0;

end

always

#5clk=!clk;

initial begin

$dumpfile("counter.vcd");

$dumpvars;

end

initial begin

$display("\t\ttime,\tclk,\treset,\tenable,\tcount");

$monitor("‰d,\t‰b,\t‰b,\t‰b,\t‰d",$time,clk,reset,enable,count); end

initial

#100$finish;

//Rest of testbench code after this line

endmodule

5双向端口

这个我没用过,完全是从网上google的,如果有问题,大家再讨论吧

芯片外部引脚很多都使用inout类型的,为的是节省管腿。一般信号线用做总线等双向数据传输的时候就要用到INOUT类型了。就是一个端口同时做输入和输出。inout在具体实现上一般用三态门来实现。三态门的第三个状态就是高阻'Z'。当inout端口不输出时,将三态门置高阻。这样信号就不会因为两端同时输出而出错了,更详细的内容可以搜索一下三态门tri-state的资料.

1使用inout类型数据,可以用如下写法:

inout data_inout;

input data_in;

reg data_reg;//data_inout的映象寄存器

reg link_data;

assign data_inout=link_data?data_reg:1’bz;//link_data控制三态门

//对于data_reg,可以通过组合逻辑或者时序逻辑根据data_in对其赋值.通过控制link_d ata的高低电平,从而设置data_inout是输出数据还是处于高阻态,如果处于高阻态,则此时当作输入端口使用.link_data可以通过相关电路来控制.

2编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而其它输入端口都定义成reg类型,这两者是有区别的.

当上面例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可以断开.此时可以用assign语句实现:assign data_inout=link?data_in_t:1’bz;其中的link,data_i n_t是reg类型变量,在测试模块中赋值.

另外,可以设置一个输出端口观察data_inout用作输出的情况:

Wire data_out;

Assign data_out_t=(!link)?data_inout:1’bz;

else,in RTL

inout use in top module(PAD)

dont use inout(tri)in sub module

也就是说,在内部模块最好不要出现inout,如果确实需要,那么用两个port实现,到顶层的时候再用三态实现。理由是:在非顶层模块用双向口的话,该双向口必然有它的上层跟它相连。既然是双向口,则上层至少有一个输入口和一个输出口联到该双向口上,则发生

两个内部输出单元连接到一起的情况出现,这样在综合时往往会出错。

对双向口,我们可以将其理解为2个分量:一个输入分量,一个输出分量。另外还需要一个控制信号控制输出分量何时输出。此时,我们就可以很容易地对双向端口建模。

例子:

CODE:

module dual_port(

....

inout_pin,

....

);

inout inout_pin;

wire inout_pin;

wire input_of_inout;

wire output_of_inout;

wire out_en;

assign input_of_inout=inout_pin;

assign inout_pin=out_en?output_of_inout:高阻;

endmodule

可见,此时input_of_inout和output_of_inout就可以当作普通信号使用了。

在仿真的时候,需要注意双向口的处理。如果是直接与另外一个模块的双向口连接,那么只要保证一个模块在输出的时候,另外一个模块没有输出(处于高阻态)就可以了。

如果是在ModelSim中作为单独的模块仿真,那么在模块输出的时候,不能使用force命令将其设为高阻态,而是使用release命令将总线释放掉

很多初学者在写testbench进行仿真和验证的时候,被inout双向口难住了。仿真器老是提

示错误不能进行。下面是我个人对inout端口写testbench仿真的一些总结,并举例进行说明。在这里先要说明一下inout口在testbench中要定义为wire型变量。

先假设有一源代码为:

module xx(data_inout,........);

inout data_inout;

........................

assign data_inout=(!link)?datareg:1'bz;

endmodule

方法一:使用相反控制信号inout口,等于两个模块之间用inout双向口互连。这种方法要注意assign语句只能放在initial和always块内。

module test();

wire data_inout;

reg data_reg;

reg link;

initial begin

..........

end

assign data_inout=link?data_reg:1'bz;

endmodule

方法二:使用force和release语句,但这种方法不能准确反映双向端口的信号变化,但这种方法可以反在块内。

module test();

wire data_inout;

reg data_reg;

reg link;

#xx;//延时

force data_inout=1'bx;//强制作为输入端口

...............

#xx;

release data_inout;//释放输入端口

endmodule

很多读者反映仿真双向端口的时候遇到困难,这里介绍一下双向端口的仿真方法。一个典型的双向端口如图1所示。

其中inner_port与芯片内部其他逻辑相连,outer_port为芯片外部管脚,out_en用于控制双向端口的方向,out_en为1时,端口为输出方向,out_en为0时,端口为输入方向。

用Verilog语言描述如下:

module bidirection_io(inner_port,out_en,outer_port);

input out_en;

inout[7:0]inner_port;

inout[7:0]outer_port;

assign outer_port=(out_en==1)?inner_port:8'hzz;

assign inner_port=(out_en==0)?outer_port:8'hzz;

endmodule

用VHDL语言描述双向端口如下:

library ieee;

use IEEE.STD_LOGIC_1164.ALL;

entity bidirection_io is

port(inner_port:inout std_logic_vector(7downto0);

out_en:in std_logic;

outer_port:inout std_logic_vector(7downto0));

end bidirection_io;

architecture behavioral of bidirection_io is

begin

outer_port<=inner_port when out_en='1'else(OTHERS=>'Z');

inner_port<=outer_port when out_en='0'else(OTHERS=>'Z');

end behavioral;

仿真时需要验证双向端口能正确输出数据,以及正确读入数据,因此需要驱动out_en端口,当out_en端口为1时,testbench驱动inner_port端口,然后检查outer_port端口输出的数据是否正确;当out_en端口为0时,testbench驱动outer_port端口,然后检查inn er_port端口读入的数据是否正确。由于inner_port和outer_port端口都是双向端口(在VHDL和Verilog语言中都用inout定义),因此驱动方法与单向端口有所不同。

验证该双向端口的testbench结构如图2所示。

这是一个self-checking testbench,可以自动检查仿真结果是否正确,并在Modelsim控制台上打印出提示信息。图中Monitor完成信号采样、结果自动比较的功能。

testbench的工作过程为

1)out_en=1时,双向端口处于输出状态,testbench给inner_port_tb_reg信号赋值,然后读取outer_port_tb_wire的值,如果两者一致,双向端口工作正常。

2)out_en=0时,双向端口处于输如状态,testbench给outer_port_tb_reg信号赋值,然后读取inner_port_tb_wire的值,如果两者一致,双向端口工作正常。

用Verilog代码编写的testbench如下,其中使用了自动结果比较,随机化激励产生等技术。

`timescale1ns/10ps

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;

reg out_en_tb;

integer i;

initial

begin

out_en_tb=0;

inner_port_tb_reg=0;

outer_port_tb_reg=0;

i=0;

repeat(20)

begin

#50

i=$random;

out_en_tb=i[0];//randomize out_en_tb

inner_port_tb_reg=$random;//randomize data

outer_port_tb_reg=$random;

end

end

//****drive the ports connecting to bidirction_io

assign inner_port_tb_wire=(out_en_tb==1)?inner_port_tb_reg:8'hzz; assign outer_port_tb_wire=(out_en_tb==0)?outer_port_tb_reg:8'hzz;

//instatiate the bidirction_io module

bidirection_io bidirection_io_inst(.inner_port(inner_port_tb_wire), .out_en(out_en_tb),

.outer_port(outer_port_tb_wire));

//*****monitor******

always@(out_en_tb,inner_port_tb_wire,outer_port_tb_wire)

begin

#1;

if(outer_port_tb_wire===inner_port_tb_wire)

begin

$display("\n****time=%t****",$time);

$display("OK!out_en=%d",out_en_tb);

$display("OK!outer_port_tb_wire=%d,inner_port_tb_wire=%d",

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

6.高级用法

比如pli之类的东西,我也没用过。。。有需要的,大家再讨论

总体感觉,testbench是个很难的事情,这里讨论的只是一些最基本的东西。真正有技术含量的是testcase的设计,设计阶段合理层次设计以及模块划分等等,我没有做过很大的项目,所以这方面也没有办法提供更多的帮助。经验丰富的大牛不妨出来讲讲经验,^ _^

怎样写testbench(内有一个实例分析)

怎样写testbench 本文的实际编程环境:ISE 6.2i.03 ModelSim 5.8 SE Synplify 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所示: 图1 l Simulator Behavioral Model 也就是所说的功能仿真、行为仿真、前仿真。验证功能是否正确,这是设计的第一步。功能仿真正确的程序不一定能被正确综合,也 就是硬件实现。有的在综合时报错误,有的虽然能综合但结果并不正确。当然,功 能仿真如果都不能通过,以后的步骤也就无法进行。这是必做的仿真。 l Simulator Post-translate VHDL Model 也就是翻译后仿真。对源程序进行编译后首先排除了语法错误,对一些像类属命令(Generic)、生成语句(Generate)等进 行了展开。不是必做的仿真。 l Simulator Post-Map VHDL Model也就是映射后仿真。不同的器件内部结构也不尽相同,映射的作用就是将综合后产生的网表文件对应到实际的器件上去。由于映射 不包含布线,也就是要用什么类型的逻辑单元虽然已经确定但要用哪个位置的还没 有确定,因此,映射后仿真不包含布线延时。不是必做的仿真。 l Simulator Post-Place & Route VHDL Model也就是所说的布局布线后仿真、时序仿真、后仿真。这是最完整的仿真,既包含逻辑延时又包含布线延时。在做布局布 线后仿真时要用到一个叫SDF的文件。SDF文件包含设计中每个单元(Cell)的延

编写高效率的testbench

Writing Efficient Testbenches 编写高效的测试设计(testbenches) 原文作者:Mujtaba Hamid 注: 一个设计的测试验证是非常重要的。有效的测试可以助我们快速的完成或改善设计。Testbenches建议编写有效的测试代码来通过软件实现可靠的验证。无意中发现,顺手译为中文,以备将来方便。也贴给没有找到更好中文版本的同道人。 Testbenches本意应该是测试平台更合理,但是在中文中阅读起来很不舒服。所以本文中有时译为“测试设计”,“测试代码”,有时干脆是“测试”。 摘要: 应用笔记为HDL验证设计的新手,或者是没有丰富的测试设计经验的逻辑设计者而编写。 测试设计是验证HDL设计的主要手段。本应用笔记为创建或准备和构建有效的测试设计提供准则。它也提供一个为任何设计开发自较验测的测试设计的一个代数方法。 涉及的所有设计文件可以从以下的站点获得: PC: ftp://https://www.wendangku.net/doc/9718824708.html,/pub/applications/xapp/xapp199.zip UNIX: ftp://https://www.wendangku.net/doc/9718824708.html,/pub/applications/xapp/xapp199.tar.gz 简介: 由于设计的规模越来越大也越来越复杂,数字设计的验证已经成为一个日益困难和繁琐的任务。验证工程师们依靠一些验证工具和方法来应付这个挑战。对于几百万门的大型设计,工程师们一般使用一套形式验证(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)。

verilog,testbench

Testbench专题所谓testbench,即测试平台,详细的说就是给待验证的设计添加激励,同时观察输出响应是否符合设计要求。 也许我们会把把程序开发出来算作一个工程项目的重大的比例,这在今天的FPGA设计中,并不是如此,往往在仿真验证上的工作量占到一半以上。试想这么一个测试,一个16位的

输入总线,它可以有多少种组合?如果每次随机产生一种输入,用波形的去画一画,眼花! 波形是最直观的测试手段,但不是唯一手段。 一个完整的测试平台如下图所示,

它是分结构组成的,其中对设计测试结果的判断不仅可以通过观察对比波形得到,而且可以灵活使用脚本命令将有用的输出信息打印到终端或者产生文本进行观察,也可以写一段代码让它们自动比较输出结果。 TB的设计是多种多样,可以使用灵活的VERILOG的验证脚本,但是它也是基于硬件语言但是又服务于软件测试的语

言,有时并行有时顺序,只有掌握这些关键点,才能很好服务测试。 技巧1 Tb中的例化应该把INPUT转换成REG,因为待测设计的输入值是由TB 决定的。相应的OUTPUT 就应该转换成WIRE,因为待测设计的输出值不是由tb决定的。这里需要注意Inout端口,在例化中也是一个wire型。

技巧2 时钟产生 第一种:parameter PERIOD=XX; Initial begin Clk=0; Forever #( PERIOD /2) clk =~clk; End 第二种 parameter PERIOD=XX; always begin #( PERIOD /2) clk=0;

# (PERIOD /2) clk=1; End 技巧3 复位信号 Initial begin Reset_task(XX);//注意时间尺度 ……. End Task reset_task; Input [15:0]reset_time; Begin Reset=0; # reset_time;

testbench时钟信号的编写(verilog)

testbench时钟信号的编写 2011-01-13 11:07:38| 分类:FPGA的分享| 标签:clock parameter reg 占空比 time_period |举报|字号大中小订阅 /******************************************************* 时钟信号的编写 *******************************************************/ 'timescale 1ns/1ps //定义时间单位/时间精度 /******************占空比50%(采用initial)**************/ parameter TIME_PERIOD = 10; reg clock; initial begin clock = 0;//初始化clock为0 forever # (TIME_PERIOD/2) clock = ~clock; end /******************占空比50%(采用always)***************/ parameter TIME_PERIOD = 10; reg clock; initial clock = 0;//初始化clock为0 always # (TIME_PERIOD/2) clock = ~clock; /******************非50%占空比(采用always)*************/ parameter HI_TIME = 5, LO_TIME = 10; reg clock; always begin # HI_TIME clock = 0; # LO_TIME clock = 1; end /***********固定数目时钟占空比50%(采用initial)*********/ parameter PULSE_COUNT = 4, TIME_PERIOD = 10; reg clock; initial begin clock = 0;//初始化clock为0 repeat (2*PULSE_COUNT)

Modelsim 仿真步骤总结

Modelsim 仿真步骤总结 Modelsim 仿真主要有以下三个方面:各版本的方法大体一致。(1)建立库并映射库到物理目录; (2)编译源代码(包括testbench); (3)执行仿真; 下面具体演示每一步的操作方法及流程,为力求简洁,其中有多种操方法的只介绍一种。 一)建立库的演示: 步骤为——启动modelsim>点击file菜单>(Change Directory)>new>(project)>library.注括号内的步骤非必须。 1.1)启动modelsim; (1.2)点击file菜单,此时若需更改路径,可以点击其中的Change Directory.(注modelsim会自动默认路径。我们也可以在1.1步骤之前建立一个文件夹用于modelsim的工程,再将路径更改到我们新建的文件夹路径。)

(1.3)点击file子菜单中的new,选择project用于新建立一个modelsim工程。(虽然此步非必须,但是建立一个新工程有益 于接下来整个仿真文件的管理。)

1.4)点击file子菜单new,再点击new下的library,用于建立 一个库。(仔细观察1.3步,新建工程的同时亦可建立库)。 至此,modelsim仿真第一大步建立库的工作完毕。

二)编译源代码(包括testbench): 大家都已知道在quartus中编译源代码的方法,这里说明一下在modelsim中编译源代码及testbench的方法。 步骤为——建立一个新工程(同时建立库)后,会自动提示你四个选项。如图: 选create new file后提示对话框,(也可以在有源文件的情况下选择add existing file到工程中。例如我们用quartus写好文件后,可以用此方法将quartus中的文件添加到modelsim工程中来)让你命名你即将创建的新文件,完毕后,会自动出现一个空白的编辑窗口,在那里输入源代码和testbench。(提示:这样编写完后点保存,便可以直接保存到上面设臵好的work库中。比起另一种方法,不建工程直接建库,再通过菜单栏source添加源

VHDL——如何写简单的testbench

use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; --use ieee.std_logic_unsigned.all; entity cnt6 is port (clr,en,clk :in std_logic; q :out std_logic_vector(2 downto 0) ); end entity; architecture rtl of cnt6 is signal tmp :std_logic_vector(2 downto 0); begin process(clk) -- variable q6:integer; begin if(clk'event and clk='1') then if(clr='0')then tmp<="000"; elsif(en='1') then if(tmp="101")then tmp<="000"; else

tmp<=unsigned(tmp)+'1'; end if; end if; end if; q<=tmp; -- qa<=q(0); -- qb<=q(1); -- qc<=q(2); end process; end rtl; 二、六进制计数器testbench的代码

signal en :std_logic:='0'; signal clk :std_logic:='0'; signal q :std_logic_vector(2 downto 0); constant clk_period :time :=20 ns; begin instant:cnt6 port map ( clk=>clk,en=>en,clr=>clr,q=>q ); clk_gen:process begin wait for clk_period/2; clk<='1'; wait for clk_period/2; clk<='0'; end process; clr_gen:process begin clr<='0'; wait for 30 ns; clr<='1'; wait; end process; en_gen:process begin en<='0'; wait for 50ns; en<='1'; wait; end process; end rtl;

如何写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。创建完成后,如下图:

这两个代码分别如下: module ParallelSerial_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) begin Multiplicand<=0; Count<=0; Product<=0; Busy<=0; end else begin Product<=NextProduct; if(Load) begin Multiplicand<=MultiplicandIn; Count<=0; Busy<=1'b1; end else begin if(Busy) Count<=Count+1'b1; if(Count[CYCLES]) begin

1 ModelSim的使用与Testbench的编写

ModelSim的使用与Testbench的编写 重温了基本的Quartus操作和语法后,需要对手头的工作进行仿真验证,Quartus 9.x自带的Vector Waveform已经淘汰掉了,必须用 ModelSim进行仿真。现在就开始一步步入手ModelSim,并通过与Quartus无缝衔接实现仿真。本文使用了ModelSim10.0c + QuartusII 10.0,其他版本基本雷同,请自行研究。 源程序如下: module add( mclk, rst_n, a_in, b_in, c_out ); input mclk, rst_n; input[7:0] a_in, b_in; output[8:0] c_out; reg[8:0] c_out; always@(posedge mclk, negedge rst_n) begin if(!rst_n) c_out <= 9'h0; else c_out <= a_in + b_in; end endmodule 请建立工程,将源程序编译通过. 1.设置第三方EDA工具 在Tools -> Options中设置ModelSim的安装路径,注意要设置到win32文件夹(64位软件对应的就是win64)。

仿真工具为ModelSim。这样Quartus就能无缝调用ModelSim了。

当然也可以在建立工程的时候就设置仿真工具。

2.编写Testbench 说到Testbench,你可以叫它Testbench,或者Testbenches,但不是Test Bench。说起来,就连Quartus也没注意这个问题,至于原因嘛参见Common Mistakes In Technical Texts一文。文章中还列举了些别的错误用语,包括Flip-flop不能写成Flipflop,等等。文章链接: https://www.wendangku.net/doc/9718824708.html,/papers/Technical_Text_Mistakes.pdf 我们可以通过Quartus自动生成一个Testbench的模板,选择Processing -> Start -> Start Test Bench Template Writer,等待完成后,在导航栏中打开刚才生成的Testbench,默认是保存在simulation\modelsim文件夹下的.vt格 式文件。

Verilog仿真文件testbench编写样例

Verilog 仿真文件testbench编写样例 `timescale 1ns/100ps module testbench; localparam DATA_WIDTH = 32; localparam CLK_100_PERIOD = 5; localparam CLK_200_PERIOD = 2.5; localparam SIM_TIME = 150000; localparam ; localparam ; reg clk_100, clk_200; wire clk; assign clk = clk_100; always begin clk_100 = 0; forever #CLK_100_PERIOD clk_100 = ~clk_100; end always begin clk_200 = 0; forever #CLK_200_PERIOD clk_200 = ~clk_200;

end reg rstn; integer fp_testin; integer fp_matlab_out; integer fp_sim_out; integer fp_outdiff; reg signed [DATA_WIDTH/2-1:0] matlab_in_re, matlab_in_im; reg signed [DATA_WIDTH/2-1:0] matlab_out_re, matlab_out_im; reg signed [DATA_WIDTH/2-1:0] matlab_diff_re, matlab_diff_im; reg signed [DATA_WIDTH/2-1:0] matlab_diff_re2, matlab_diff_im2; reg signed [DATA_WIDTH/2-1:0] max_diff_re, max_diff_im; initial begin max_diff_re = 0; max_diff_im = 0; rstn = 0; #500 rstn = 1;

Verilog-testbench的写法

数字集成电路设计入门--从HDL到版图 于敦山 北大微电子学系

第十五章Verilog Test Bench使用简介 学习内容: ?用一个复杂的test bench复习设计的组织与仿真 ?建立test bench通常使用的编码风格及方法

设计组织 虚线表示编译时检测输入文件是否存在及可读并允许生成输出文件。

test bench 组织 stimulus 要验证的设计 简单的test bench ?简单的test bench 向要验证的设计提供向量,人工验证输出。?复杂的test bench 是自检测的,其结果自动验证。 复杂的test bench 激励 验证结果 要验证的设计

并行块 ?fork…join块在测试文件中很常用。他们的并行特性使用户可以说明绝对时间,并且可以并行的执行复杂的过程结构,如循环或任务。 module inline_ tb; reg [7: 0] data_ bus; // instance of DUT initial fork data_bus = 8'b00;Time | data_ bus 0 | 8’b0000_0000 10 | 8’b0100_0101 30 | 8’b0100_0110 40 | 8’b0100_0111 45 | 8’b1000_1110 #10 data_bus = 8'h45; #20 repeat (10) #10 data_bus = data_bus + 1; #25 repeat (5) #20 data_bus = data_bus << 1; #140 data_bus = 8'h0f; join endmodule 上面的两个repeat循环从不同时间开始,并行执行。象这样的特殊的激励集在单个的begin…end块中 将很难实现。50 | 8’b1000_1111 60 | 8’b1001_0000 65 | 8’b0010_0000 70 | 8’b0010_0001 80 | 8’b0010_0010 85 | 8’b0100_0100 90 | 8’b0100_0101 100 | 8’b0100_0110 105 | 8’b1000_1100 110 | 8’b1000_1101 120 | 8’b1000_1110 125 | 8’b0001_1100 140 | 8’b0000_1111

1_VHDL修改Quartus自己产生的testbench

VHDL编写testbench(.vht文件) 对modelsim自己产生的.vht文件修改进行仿真时,需要更改两处,分别是如下: 1.对于Signal信号需要赋初值。 例如: 原文件中: SIGNAL CLK_IN : STD_LOGIC; SIGNAL Q : STD_LOGIC_VECTOR(3 DOWNTO 0); 更改后为: SIGNAL CLK_IN : STD_LOGIC := '1'; SIGNAL Q : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000"; 最简单的记法,格式都是如下: SIG<=’1’;WAIT FOR 10 NS; SIG<=’0’;WAIT FOR 10 NS; 时钟信号: process_clk : PROCESS BEGIN CLK_IN <= '0'; WAIT FOR 10NS; CLK_IN <= '1'; WAIT FOR 10NS; END PROCESS process_clk; 复位信号: process_rst : PROCESS BEGIN RST_N <= '1'; WAIT FOR 20NS; RST_N <=’0’; WAIT FOR 20NS; WAIT; END PROCESS process_rst; 一般激励信号: process_sig : PROCESS BEGIN SIG<=’1’;WAIT FOR 10 NS; SIG<=’0’;WAIT FOR 20 NS;

SIG<=’1’;WAIT FOR 20 NS; SIG<=’0’;WAIT FOR 10 NS; END PROCESS process_ sig;

如何编写Testbench

2 如何编写Testbench 1) 何时使用initial和always initial和always 是2个基本的过程结构语句,在仿真的一开始即开始相互并行执行。通常被动的检测响应使用always语句,而主动的产生激励使用initial语句。 initial和always的区别是always 语句不断地重复执行,initial语句则只执行一次。但是,如果希望在initial里的多次运行一个语句块,怎么办?这时可以在initial里嵌入循环语句(while,repeat,for,forever 等),如: initial begin forever /* 无条件连续执行*/ begin …… end end 其它循环语句请参考一些教材,这里不作赘述。 另外,如果希望在仿真的某一时刻同时启动多个任务,可以使用fork....join语句。例如,在仿真开始的 100 ns 后,希望同时启动发送和接收任务,而不是发送完毕后再进行接收,如下所示: initial begin #100 ; fork /*并行执行 */ Send_task ; Receive_task ; join End 2) 如何作多种工作模式的遍历测试 如果设计的工作模式很多,免不了做各种模式的遍历测试,而遍历测试是需要非常大的工作量的。我们经常遇到这样的情况:很多时候,各种模式之间仅仅是部分寄存器配置值的不同,而各模式间的测试都是雷同的。有什么方法可以减轻这种遍历测试的工作量?不妨试

试for循环语句,采用循环变量来传递各种模式的配置值,会帮助减少很多测试代码,而且不会漏掉每一种模式. initial begin for ( i = 0 ; i < m ; i = i + 1 ) /*遍历模式1至模式m*/ for ( j = 0 ; j < n ; j = j +1 ) /*遍历子模式1至子模式n */ begin case ( j ) /* 设置每种模式所需的配置值 */ 0 : 配置值= a ; 1 : 配置值= b ; 2 : 配置值= c ; …… endcase /*共同的测试向量*/ end end 3) 如何加速问题定位过程 在这部分里,通过一些实际例子,介绍在出现问题时如何借助 testbench 加快问题的定位过程。 1、监测内存分配 内存分配和回收示意图 在这个例子里,假设总共有2K的内存块,希望在测试程序里监测内存分配和回收的块号是否正确,监测是否存在同一块号重复分配、重复回收的情况。设置一个 2K位的变量对内存的使用情况进行记录,每一位对应一个内存块,空闲的块号记为1,被占用的块号记为0。

Verilog HDL培训(3)_如何编写TESTBENCH

08:291 如何编写TESTBENCH 计算机学院微电子所627室 陈海燕

08:292 主要内容 Verilog 对验证的支持 系统函数和系统任务 如何编写模块的TESTBENCH

08:293 系统任务和系统函数 是Verilog中预先定义好的,用于调试和编译预处理的任务或函数。 以$开头,用于控制和检测仿真模拟过程 主要有: (1)用于获取仿真时间的系统函数 (2)支持文本输出(检测信号、显示信号)的系统任务 (3)用于文件输入、输出操作的系统任务 (4)用于暂停和退出仿真的系统任务 (5)用于产生随机数的系统任务

08:29 4获取当前仿真时间的系统函数 $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,0111

VHDL——如何写简单的testbench

弄了好长时间vhdl,一直对testbench很迷惑。前几天静下心来好好看了下资料,终于会写简单的testbench了。 六进制计数器的代码 [c-sharp]view plaincopy 1.library ieee; https://www.wendangku.net/doc/9718824708.html,e ieee.std_logic_1164.all; https://www.wendangku.net/doc/9718824708.html,e ieee.std_logic_arith.all; 4.--use ieee.std_logic_unsigned.all; 5. 6.entity cnt6 is 7. port 8. (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 is 14.signal tmp :std_logic_vector(2 downto 0); 15.begin 16. process(clk) 17.-- variable q6:integer; 18. begin 19.if(clk'event and clk='1') then 20.if(clr='0')then 21. tmp<="000"; 22. elsif(en='1') then 23.if(tmp="101")then 24. tmp<="000"; 25.else 26. 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的一些技巧

1 Testbench的结构 1) 单顶层结构 一种结构是testbench 只有一个顶层,顶层再把所有的模块实例化进去。打个比方,类似树结构,只有一个模块有子节点而没有父节点,其它模块都有父节点。如下图结构所示: 测试模块是一些接口模型,接口模型还可能包含了一些激励在内。测试模块和DUV之间通过端口映射进行互连。 2) 多顶层结构 另外一种结构是多顶层结构,如下图所示: 在这种结构中,有一个顶层是作为测试向量模块,一个或多个顶层是一些公用子程序,这些子程序由于完成一些通用的功能被封装成任务、函数等被公用。 还有一个叫harness的顶层,该顶层由DUV和一些接口模型构成一个狭义上的测试平台,其它模块可以调用BFM里面的task 或event 等,向DUV施加激励。注意这些顶层之间是没有端口映射的,它们之间的互相调用和访问是通过层次路径名的方式来访问,上图的虚线表示层次路径名的访问。下面举例说明层次路径是如何访问的。 由于大部分人对C都有所认识,在这里作个比较,便于了解。Verilog HDL的顶层类似于C的结构体,而实例化的模块、任务、函数、变量等就是结构体里的成员,可以通过句点( . )隔开的方式访问结构体里面的每一个成员。如:顶层harness 实例化进来的模块BFM1 里面有一个任务SEND_DATA , 该任务可以产生激励输入到DUV,在testcase 里

调用该任务就可写为: initial begin …… harness . BFM1 . SEND_DATA (……); end 多顶层结构的可扩展和重用性比单顶层结构强得多。层次路径的访问方式非常有用,在下一节会讲述更多的应用。 2 如何编写Testbench 1) 何时使用initial和always initial和always 是2个基本的过程结构语句,在仿真的一开始即开始相互并行执行。通常被动的检测响应使用always语句,而主动的产生激励使用initial语句。 initial和always的区别是always 语句不断地重复执行,initial语句则只执行一次。但是,如果希望在initial里的多次运行一个语句块,怎么办?这时可以在initial里嵌入循环语句(while,repeat,for,forever 等),如: initial begin forever /* 无条件连续执行*/ begin …… end end 其它循环语句请参考一些教材,这里不作赘述。 另外,如果希望在仿真的某一时刻同时启动多个任务,可以使用fork....join语句。例如,在仿真开始的100 ns 后,希望同时启动发送和接收任务,而不是发送完毕后再进行接收,如下所示: initial begin #100 ; fork /*并行执行*/ Send_task ;

编写高效率的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验证流程 构建Testbench Testbench用VHDL或Verilog来编写。由于testbench只用来进行仿真,它们没有那些适用于综合的RTL语言子集的语法约束限制,而是所有的行为结构都可以使用。因而testbench可以编写的更为通用,使得它们可以更容易维护。 所有testbench包含了如表1的基本程序段。正如上面所提到的,testbench通常包含附加功能,如在终端上可视的结果和内建的错误检测。

VivadoHLS工程testbench的三个要素

编写高效Vivado HLS工程testbench的三个要素在C程序的设计中,任何一个C程序的顶层都是main()函数。而在vivado HLS的设计中,只要函数的层次在main()函数以下,都可以被综合。但是每个vivado HLS工程只能指定一个top层函数作为输出RTL模块的顶层,其它和这个函数层次平行,不需要被综合的函数都可以作为testbench来使用。这样就带来一个问题,如何编写vivado HLS工程的testbench更高效,或者说能更好的让HLS工具自动重用C testbench验证产生的RTL代码就变得非常重要。 通常,在Vivado HLS中,好的C testbench设计原则是testbench设计和需要实现的算法函数分别保存在不同的文件中,并且充分利用头文件。Testbench 常常包含了一些HLS综合不支持的操作,比如通过文件的读写取得仿真数据并保存结果,或者打印一些测试结果进行分析。在头文件中,完成对testbench中所有的数据类型和函数的定义,以及包含共享的设计文件和函数库。 Vivado HLS中,只能指定一个top层函数用于综合,top层函数可以包含多个子函数。当需要综合多个并行层次的函数时,可以编写一个wrapper函数作为top层函数,将需要综合的多个并行函数封装起来。 C testbench的目不仅是要验证需要综合的top函数功能正确(C编译器验证环境),同时重用C testbench作为综合产生RTL代码的仿真激励,HLS工具自动调用C testbench来验证RTL功能的一致性(C编译器和RTL仿真器的协同仿真环境)。这样,编写一个好的风格testbench可以很好的提高设计的验证效率,如果在HLS综合前和综合过程中,需要修改综合函数的代码,可以用testbench验证,确保需要综合的C算法功能正确。 Vivado HLS中推荐高效的testbench具有如下三个特征:

testbench万能模版

`timescale 1ns/1ps // 定义时间单位和精度 //定义仿真时间 initial begin #10000 finish; end //设计顶层模块testbench module testbench; //定义内部的信号变量以及类型 wire [7:0]count; //线型定义输出端,连接各模块的输出端口,用线连接没有记忆reg clk; //寄存器定义激励信号,连接各模块的输入端口,具有记忆功能reg reset; //两个最基本的激励信号 //固定参数的赋值,状态的编码 /***parameter tpd_reset_to_count = 3 ; parameter tpd_clk_to_count = 2 ; **/ //变量初始化 /**initial begin a=b; b=c; c=d; end **/ //时钟设置 always #500 clk=~clk; initial begin clk =1; reset=0; //一般是低电平复位 #100 clk =0; reset=1; end //调用函数用modelsim生成fsdb文件 initial begin $fsdbDumpfile("wave_test.fsdb"); $fsdbDumpvars(0); end //调用模块与顶层模块的各个端口的连接(一一对应时自动连接) filename u1( .cp(clk), .clr(reset), .RSTSM(), .DQ_IN(),

.MASTER_CNT(), .SLA VE_CNT() ); //调用输出图像的函数 integer fid; initial begin fid=$fopen("report.txt","w+"); #1000000 $fclose(fid); end always @ (posedge clk negedge reset) $display(fid,"timerh=%d",timerh); endmodule //结束顶层模块

相关文档