怎样写testbench

怎样写testbench,第1张

如何编写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", 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 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型的64位数据

$time

ex:

...

time condition_happen_time

...

condition_happen_time = $time

...

$monitor($time,"data utput = %d", dout)

...

//参数

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", parameter)

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

//$fwrite需要触发条件才记录

$fwrite(file_id, "%format_char", parameter)

//$fdisplay需要触发条件才记录

$fdisplay(file_id, "%format_char", parameter)

$fstrobe()

3.读取文件

integer file_id

file_id = $fread("file_path/file_name", "r")

4.关闭文件

$fclose(fjile_id)

5.由文件设定存储器初值

$readmemh("file_name", memory_name")//初始化数据为十六进制

$readmemb("file_name", memory_name")//初始化数据为二进制

//仿真控制

$finish(parameter)//parameter = 0,1,2

$stop(parameter)

//读入SDF文件

$sdf_annotate("sdf_file_name", module_instance, "scale_factors")

//module_instance: sdf文件所对应的instance名.

//scale_factors:针对timming delay中的最小延时min,典型延迟typ,最大延时max调整延迟参数

//generate语句,在Verilog-2001中定义.用于表达重复性动作

//必须事先声明genvar类型变量作为generate循环的指标

eg:

genvar i

generate for(i = 0i <4i = 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",12)

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

$monitor("%t %b %b %b %b %b %b", $realtime,

clock,

reset, load, shiftreg, data, sel)

end

endmodule

如何编写 testbench 的总结(非常实用的总结) 1.激励的设置 相应于被测试模块的输入激励设置为 reg 型,输出相应设置为 wire 类型,双向端口 inout 在 测试中需要进行处理。 方法 1:为双向端口设置中间变量 inout_reg 作为该 inout 的输出寄存,inout 口在 testbench 中要定义为 wire 型变量,然后用输出使能控制传输方向。 eg: inout[0:0]bi_dir_portwire[0:0]bi_dir_portreg[0:0]bi_dir_port_regregbi_dir_port_oeassignbi_dir_port=bi_dir_port_oe?bi_dir_port_reg:1'bz用 bi_dir_port_oe 控制端口数据方向,并利用中间变量寄存器改变其值。等于两个模块之间 用 inout 双向口互连。往端口写(就是往模块里面输入) 方法 2:使用 force 和 release 语句,这种方法不能准确反映双向端口的信号变化,但这种方 法可以反映块内信号的变化。具体如示: moduletest()wiredata_inoutregdata_regreglink#xx//延时 forcedata_inout=1'bx//强制作为输入端口 ............... #xxreleasedata_inout//释放输入端口 endmodule 从文本文件中读取和写入向量 1)读取文本文件:用$readmemb 系统任务从文本文件中读取二进制向量(可以包含输入激 励和输出期望值) 。$readmemh 用于读取十六进制文件。例如: reg[7:0]mem[1:256]//a8-bit,256-word 定义存储器 mem initial$readmemh("mem.data",mem)// 将.dat 文件读入寄存器 mem 中 initial$readmemh("mem.data",mem,128,1)// 参数为寄存器加载数据的地址始终 2)输出文本文件:打开输出文件用?$fopen 例如: integerout_file//out_file 是一个文件描述,需要定义为 integer 类型 out_file=$fopen("cpu.data")//cpu.data 是需要打开的文件,也就是最终的输出文本 设计中的信号值可以通过$fmonitor,$fdisplay, 2.Verilog 和 Ncverilog 命令使用库文件或库目录 ex).ncverilog-frun.f-vlib/lib.v-ylib2+libext+.v//一般编译文件在 run.f 中, 库文件在 lib.v 中,lib2 目录中的.v 文件系统自动搜索 使用库文件或库目录,只编译需要的模块而不必全部编译 3.VerilogTestbench 信号记录的系统任务: 1).SHM 数据库可以记录在设计仿真过程中信号的变化. 它只在 probes 有效的时间内记录你 setprobeon 的信号的变化. ex).$shm_open("waves.shm")//打开波形数据库 $shm_probe(top,"AS")//setprobeon"top", 第二个参数:A--signalsofthespecificscrope S--Portsofthespecifiedscopeandbelow,excludinglibrarycells C--Portsofthespecifiedscopeandbelow,includinglibrarycells AS--Signalsofthespecifiedscopeandbelow,excludinglibrarycells AC--Signalsofthespecifiedscopeandbelow,includinglibrarycells 还有一个 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=allscope=all $dumpvars(0)//depth=allscope=current $dumpvars(1,top.u1)//depth=1scope=top.u1 $dumpoff//暂停记录数据改变,信号变化不写入库文件中 $dumpon//重新恢复记录 3).Debussyfsdb 数据库也可以记录信号的变化,它的优势是可以跟 debussy 结合,方便调试. 如果要在 ncverilog 仿真时,记录信号, 首先要设置 debussy: a.setenvLD_LIBRARY_PATHLD_LIBRARY_PATH (pathfordebpli.sofile(/share/PLI/nc_xl//nc_loadpli1)) b.whileinvokingncverilogusethe+ncloadpli1option. ncverilog-frun.f+debug+ncloadpli1=debpli:deb_PLIPtr fsdb 数据库文件的记录方法,是使用$fsdbDumpfile 和$fsdbDumpvars 系统函数,使用方法参见 VCD 注意: 在用 ncverilog 的时候,为了正确地记录波形,要使用参数:"+access+rw", 否则没有读写 权限 在记录信号或者波形时需要指出被记录信号的路径,如:tb.module.u1.clk. ??????????????????????????????????????? 关于信号记录的系统任务的说明: 在 testbench 中使用信号记录的系统任务,就可以将自己需要的部分的结果以及波形文件记 录下来(可采用 sigalscan 工具查看) ,适用于对较大的系统进行仿真,速度快,优于全局仿 真。使用简单,在 testbench 中添加: initialbegin $shm_open("waves.shm")$shm_probe("要记录信号的路径“, ”AS“) ; #10000 $shm_close即可。 4.ncverilog 编译的顺序:ncverilogfile1file2.... 有时候这些文件存在依存关系,如在 file2 中要用到在 file1 中定义的变量,这时候就要注意其 编译的顺序是 从后到前,就先编译 file2 然后才是 file2. 5.信号的强制赋值 force 首先, force 语句只能在过程语句中出现,即要在 initial 或者 always 中间. 去除 force 用 release 语句. initialbeginforcesig1=1'b1...releasesig1end force 可以对 wire 赋值,这时整个 net 都被赋值也可以对 reg 赋值. 6.加载测试向量时,避免在时钟的上下沿变化 为了模拟真实器件的行为,加载测试向量时,避免在时钟的上下沿变化,而是在时钟的上升 沿延时一个时间单位后,加载的测试向量发生变化。如: assign#5c=a^b ?? @(posedgeclk)#(0.1*`cycle)A=1****************************************************************************** //testbench 的波形输出 moduletop... 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 型的 64 位数据 $time ex: ... timecondition_happen_time... condition_happen_time=$time... $monitor($time,"dataoutput=%d",dout)... //参数 parameterpara1=10, para2=20, para3=30//显示任务 $display()//监视任务 $monitor()//延迟模型 specify ... //describpin-to-pindelay endspecify ex: modulenand_or(Y,A,B,C)inputA,B,CoutputYAND2#0.2(N,A,B)OR2#0.1(Y,C,N)specify (A*->Y)=0.2(B*->Y)=0.3(C*->Y)=0.1endspecify endmodule //时间刻度 `timescale 单位时间/时间精确度 //文件 I/O 1.打开文件 integerfile_idfile_id=fopen("file_path/file_name")2.写入文件 //$fmonitor 只要有变化就一直记录 $fmonitor(file_id,"%format_char",parameter)egfmonitor(file_id,"%m:%tin1=%do1=%h",$time,in1,o1)//$fwrite 需要触发条件才记录 $fwrite(file_id,"%format_char",parameter)//$fdisplay 需要触发条件才记录 $fdisplay(file_id,"%format_char",parameter)$fstrobe()3.读取文件 integerfile_idfile_id=$fread("file_path/file_name","r")4.关闭文件 $fclose(fjile_id)5.由文件设定存储器初值 $readmemh("file_name",memory_name")//初始化数据为十六进制 $readmemb("file_name",memory_name")//初始化数据为二进制 //仿真控制 $finish(parameter)//parameter=0,1,2 $stop(parameter)//读入 SDF 文件 $sdf_annotate("sdf_file_name",module_instance,"scale_factors")//module_instance:sdf 文件所对应的 instance 名. //scale_factors:针对 timmingdelay 中的最小延时 min,典型延迟 typ,最大延时 max 调整延迟参 数 //generate 语句,在 Verilog-2001 中定义.用于表达重复性动作 //必须事先声明 genvar 类型变量作为 generate 循环的指标 eg: genvarigeneratefor(i=0i<4i=i+1) begin assign=din=i%2end endgenerate //资源共享 always@(AorBorCorD) sum=sel?(A+B) C+D)//上面例子使用两个加法器和一个 MUX,面积大 //下面例子使用一个加法器和两个 MUX,面积小 always@(AorBorCorD) begin tmp1=sel?A:Ctmp2=sel?B end always@(tmp1ortmp2) sum=tmp1+tmp2****************************************************************************** 模板: moduletestbench//定义一个没有输入输出的 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 语句,用以指明仿真何时结束。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11613563.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-17
下一篇 2023-05-17

发表评论

登录后才能评论

评论列表(0条)

保存