如何编写testbench的总结(非常实用的总结)

 

 

<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋体; mso-font-kerning:1.0pt;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} -->

 

如何编写 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 output = %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 = 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",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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值