如何编写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 = 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

 

posted @ 2009-10-28 11:24 神一样驴子 阅读(508) 评论(1) 编辑

(转)verilog 中文件输入/输出任务

全文地址:http://hubeixunbaoren.blog.163.com/blog/static/111127004200982725032948/

系统函数$fopen用于打开一个文件,并还回一个整数指针.然后,$fdisplay就可以使用这个文件指针在文件中写入信息,写完后,则可以使用$fclose系统关闭这个文件

例如:

integer write_out_file;//定义一个文件指针
integer write_out_file=$fopen("write_out_file.txt");
$fdisplay(write_out_file,"@%h\n%h",addr,data);

$fclose("write_out_file");

以上语法是将addr,data分别显示在"@%h\n%h"中的2个%h的位置,并写入write_out_file文件指针所指向的write_out_file.txt中.

从文件中读取数据,可以用 $readmemb       $readmemh  从文件中读入数据,该文件格式是一定的.

reg[7:0] data[47:0];

$readmemh("file_name.txt",data );

就是将file_name.txt中的数据读入到data数组中,然后就可以使用这些数据了.

          还有一种方式可以把指定的数据放入指定的存储器地址单元内,就是在存放数据的文本文件内,给相应的数据规定其内存地址,形式如下:

@address_in_hexadecimal   data

@2f             20

 

两个系统任务可以在仿真的任何时刻被执行使用,其使用格式共有以下六种:

 

             1)     $readmemb("<数据文件名>",<存贮器名>);

             2)     $readmemb("<数据文件名>",<存贮器名>,<起始地址>);

             3)     $readmemb("<数据文件名>",<存贮器名>,<起始地址>,<结束地址>);

             4)     $readmemh("<数据文件名>",<存贮器名>);

             5)     $readmemh("<数据文件名>",<存贮器名>,<起始地址>);

             6)     $readmemh("<数据文件名>",<存贮器名>,<起始地址>,<结束地址>);

 

在这两个系统任务中,被读取的数据文件的内容只能包含:空白位置(空格,换行,制表格(tab)和form-feeds),注释行(//形式的和/*...*/形式的都允许),二进制或十六进制的数字。数字中不能包含位宽说明和格式说明,对于$readmemb系统任务,每个数字必须是二进制数字,对于$readmemh系统任务,每个数字必须是十六进制数字。数字中不定值x或X,高阻值z或Z,和下划线(_)的使用方法及代表的意义与一般Verilog HDL程序中的用法及意义是一样的。另外数字必须用空白位置或注释行来分隔开。

 

在下面的讨论中,地址一词指对存贮器(memory)建模的数组的寻址指针。当数据文件被读取时,每一个被读取的数字都被存放到地址连续的存贮器单元中去。存贮器单元的存放地址范围由系统任务声明语句中的起始地址和结束地址来说明,每个数据的存放地址在数据文件中进行说明。当地址出现在数据文件中,其格式为字符“@”后跟上十六进制数。如:

 

@hh...h

 

对于这个十六进制的地址数中,允许大写和小写的数字。在字符“@”和数字之间不允许存在空白位置。可以在数据文件里出现多个地址。当系统任务遇到一个地址说明时,系统任务将该地址后的数据存放到存贮器中相应的地址单元中去。

 

对于上面六种系统任务格式,需补充说明以下五点:

 

1)       如果系统任务声明语句中和数据文件里都没有进行地址说明,则缺省的存放起始地址为该存贮器定义语句中的起始地址。数据文件里的数据被连续存放到该存贮器中,直到该存贮器单元存满为止或数据文件里的数据存完。

2)       如果系统任务中说明了存放的起始地址,没有说明存放的结束地址,则数据从起始地址开始存放,存放到该存贮器定义语句中的结束地址为止。

3)       如果在系统任务声明语句中,起始地址和结束地址都进行了说明,则数据文件里的数据按该起始地址开始存放到存贮器单元中,直到该结束地址,而不考虑该存贮器的定义语句中的起始地址和结束地址。

4)       如果地址信息在系统任务和数据文件里都进行了说明,那么数据文件里的地址必须在系统任务中地址参数声明的范围之内。否则将提示错误信息,并且装载数据到存贮器中的操作被中断。

5)       如果数据文件里的数据个数和系统任务中起始地址及结束地址暗示的数据个数不同的话,也要提示错误信息。

 

下面举例说明:

先定义一个有256个地址的字节存贮器 mem:

 

reg[7:0] mem[1:256];

 

下面给出的系统任务以各自不同的方式装载数据到存贮器mem中。

 

initial  $readmemh("mem.data",mem);

initial  $readmemh("mem.data",mem,16);

initial  $readmemh("mem.data",mem,128,1);

 

第一条语句在仿真时刻为0时,将装载数据到以地址是1的存贮器单元为起始存放单元的存贮器中去。第二条语句将装载数据到以单元地址是16的存贮器单元为起始存放单元的存贮器中去,一直到地址是256的单元为止。第三条语句将从地址是128的单元开始装载数据,一直到地址为1的单元。在第三种情况中,当装载完毕,系统要检查在数据文件里是否有128个数据,如果没有,系统将提示错误信息。

 

 

 

"引用 参考"
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"); //初始化数据为二进制

 

转:

http://zengbo.blogbus.com/logs/19569780.htmlVerilog

提供了丰富的系统函数,这为Testbench的编写提供了方便。尤其是IEEE1364-2005,其系统级建模的能力更强。

    以前我一般常用到的系统函数只有几个:$readmemb,$readmemh,$display,$fmonitor,$fwrite,$fopen,$fclose等。通常需要对文件作预处理,才能用于Testbench读取。今天又尝试了几个其他的文件输入输出函数,不需要对文件进行预处理,直接使用需要的文件,只对需要的部分进行读取。

    $fseek,文件定位,可以从任意点对文件进行操作;

    $fscanf,对文件一行进行读写。

    下面是一些常见的应用:
    1、读写文件
`timescale 1 ns/1 ns

module FileIO_tb;

integer fp_r, fp_w, cnt;
reg [7:0] reg1, reg2, reg3;

initial begin
  fp_r = $fopen("data_in.txt", "r");
  fp_w = $fopen("data_out.txt", "w");
  
  while(!$feof(fp_r)) begin
    cnt = $fscanf(fp_r, "%d %d %d", reg1, reg2, reg3);
    $display("%d %d %d", reg1, reg2, reg3);
    $fwrite(fp_w, "%d %d %d\n", reg3, reg2, reg1);
  end
  
  $fclose(fp_r);
  $fclose(fp_w);
end

endmodule

    2、

integer file, char;
reg eof;

initial begin
   file = $fopenr("myfile.txt");
   eof = 0;
   while (eof == 0) begin
       char = $fgetc(file);
       eof = $feof (file);
       $display ("%s", char); 
   end
end

    3、文件处理定位
`define SEEK_SET 0
`define SEEK_CUR 1
`define SEEK_END 2
integer file, offset, position, r;
r = $fseek(file, 0, `SEEK_SET); /* Beginning */
r = $fseek(file, 0, `SEEK_CUR); /* No effect */
r = $fseek(file, 0, `SEEK_END); /* End of file */
r = $fseek(file, position, `SEEK_SET); /* Previous loc */

    4、
integer r, file, start, count;
reg [15:0] mem[0:10], r16;
r = $fread(file, mem[0], start, count);
r = $fread(file, r16);

    5、
integer file, position;
position = $ftell(file);

   6、
integer file, r, a, b;
reg [80*8:1] string;
file = $fopenw("output.log");
r = $sformat(string, "Formatted %d %x", a, b);
r = $sprintf(string, "Formatted %d %x", a, b);
r = $fprintf(file, "Formatted %d %x", a, b);

   7、
integer file, r;
file = $fopenw("output.log");
r = $fflush(file);

    8、
// This is a pattern file - read_pattern.pat 
// time bin dec hex
10: 001 1 1
20.0: 010 20 020
50.02: 111 5 FFF
62.345: 100 4 DEADBEEF
75.789: XXX 2 ZzZzZzZz

`timescale 1ns / 10 ps
`define EOF 32'hFFFF_FFFF
`define NULL 0
`define MAX_LINE_LENGTH 1000

module read_pattern;
integer file, c, r;
reg [3:0] bin;
reg [31:0] dec, hex;
real real_time;
reg [8*`MAX_LINE_LENGTH:0] line; /* Line of text read from file */

initial
    begin : file_block
    $timeformat(-9, 3, "ns", 6);
    $display("time bin decimal hex");
    file = $fopenr("read_pattern.pat");
    if (file == `NULL) // If error opening file
        disable file_block; // Just quit

    c = $fgetc(file);
    while (c != `EOF)
        begin
        /* Check the first character for comment */
        if (c == "/")
            r = $fgets(line, `MAX_LINE_LENGTH, file);
        else
            begin
            // Push the character back to the file then read the next time
            r = $ungetc(c, file);
            r = $fscanf(file," %f:\n", real_time);

            // Wait until the absolute time in the file, then read stimulus
            if ($realtime > real_time)
                $display("Error - absolute time in file is out of order - %t",
                        real_time);
                else
                    #(real_time - $realtime)
                        r = $fscanf(file," %b %d %h\n",bin,dec,hex);
                end // if c else
            c = $fgetc(file);
        end // while not EOF

    r = $fcloser(file);
    end // initial

// Display changes to the signals
always @(bin or dec or hex)
    $display("%t %b %d %h", $realtime, bin, dec, hex);

endmodule // read_pattern

    9、自动比较输出结果
`define EOF 32'hFFFF_FFFF
`define NULL 0
`define MAX_LINE_LENGTH 1000
module compare;
integer file, r;
reg a, b, expect, clock;
wire out;
reg [`MAX_LINE_LENGTH*8:1];
parameter cycle = 20;

initial
    begin : file_block
    $display("Time Stim Expect Output");
    clock = 0;

    file = $fopenr("compare.pat");
    if (file == `NULL)
        disable file_block;

    r = $fgets(line, MAX_LINE_LENGTH, file); // Skip comments
    r = $fgets(line, MAX_LINE_LENGTH, file);

    while (!$feof(file))
        begin
        // Wait until rising clock, read stimulus
        @(posedge clock)
        r = $fscanf(file, " %b %b %b\n", a, b, expect);

        // Wait just before the end of cycle to do compare
        #(cycle - 1)
        $display("%d %b %b %b %b", $stime, a, b, expect, out);
        $strobe_compare(expect, out);
        end // while not EOF

    r = $fcloser(file);
    $stop;
    end // initial

always #(cycle / 2) clock = !clock; // Clock generator

and #4 (out, a, b); // Circuit under test
endmodule // compare

    10、从文件中读数据到mem(这个好像一般人用的最多了)

`define EOF 32'HFFFF_FFFF 
`define MEM_SIZE 200_000 
module load_mem; 
integer file, i; 
reg [7:0] mem[0:`MEM_SIZE]; 
reg [80*8:1] file_name; 
initial     
begin     
file_name = "data.bin";     
file = $fopenr(file_name);     
i = $fread(file, mem[0]);     
$display("Loaded %0d entries \n", i);     
i = $fcloser(file);     
$stop;     
end endmodule // load_mem

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值