集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 7669|回复: 7

提问 testbench

[复制链接]
hzp080401234 发表于 2010-10-21 15:36:30 | 显示全部楼层 |阅读模式
本帖最后由 fpgaw 于 2011-8-17 13:37 编辑

这个必不可少testbench,很让人心痛呀 资料也少 ,例程也没有怎么看见  还望有更多的学习资源呀
 楼主| hzp080401234 发表于 2010-10-21 15:37:58 | 显示全部楼层
74ls138 的Verilog HDL 和 Testbench  有的师兄提供一下 谢谢了
vvt 发表于 2010-10-21 15:50:19 | 显示全部楼层

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=============================   | | | | | | ================== | | | 激励生成 |====》 | | | 输出校验 | | |预想输入 | 设计 |==》 | | | | | | 设计 | | =============== ================== 输出 ===================   (字符画好累人。。不知道效果好么)
vvt 发表于 2010-10-21 15:50:51 | 显示全部楼层

testbench

testbench里面包含了三个东西:   1、激励生成。也就是我们刚才初级时候说的所谓的“testbench”。英文么就是simulator,这个只用来生成输出,他自己没有输入,只是按照一定的规律去给你的设计激励,激励通过设计的输入端口送到你的设计中。其余的事情不管。这里的激励,都是预先设想好的,比如根据某个协议,或者某种通信方式传递。 2、你的设计。英文可以叫做DUT:design under testbench或者DUV:design under verification。当然咯。这个是你主要目标。   3、输出校验。校验你的输出。英文叫markerboard,他所管的事情就是,接收你设计的输入,然后通过校验,找出对应的问题。然后报错,或者统计错误。等等。通俗的讲,你设计它就是把你自己解脱出来,让他来帮你找错误。他输出给你的可能就是通过打印啊,通知啊,等等方法了解你设计的正确性。   那么你有可能问了,这个东西用verilog或者VHDL能写么,modelsim里能用么?的确是可以的,有写甚至可以用c的代码通过程序接口来转换到modelsim里面来帮助验证。
vvt 发表于 2010-10-21 15:52:13 | 显示全部楼层
如何编写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);

egfmonitor(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;

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
vvt 发表于 2010-10-21 15:56:29 | 显示全部楼层
写testbench,最浪费时间的是写端口的映射,现在许多软件都可以帮我们完成这些工做:比如
以Quartus II,VHDL为例,可以在processing菜单->Start->Start Test Bench Template Writer,系统会生产一个“xxx.vht”这就是你要的testbench文件,不过这个文件不会有任何操作,具体的操作需要你自己写,比如给你的实例一个时钟,某个端口赋值等待,其实就是写VHDL。这里之写了一个产生时钟的例子,当然你可以一写的复杂一点比如检测一下其他端口输出的数据是否正确,或把这些数据写到文件中等等看你想测啥了。
--------------------------------------------------
- 这是程序文件
--------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;

entity BT656 is
    generic(d_setup: time := 14 ns);
    port(CLK:     in     std_logic;
         HSYNC: out std_logic := '0';  
         VSYNC: out std_logic := '0';   
         PCLK:     out std_logic;        
         YUV:    out std_logic_vector( 7 downto 0) := (others =>'0')    -- 8Bits
         );
end BT656;     

architecture ARCH_BT656 of BT656 is  
---- 以下省略
---
----
      
--------------------------------------------------
- 系统产生的 testbench
--------------------------------------------------
LIBRARY ieee;                                               
USE ieee.std_logic_1164.all;                                

ENTITY BT656_vhd_tst IS
END BT656_vhd_tst;
ARCHITECTURE BT656_arch OF BT656_vhd_tst IS
-- constants                                                
-- signals                                                   
SIGNAL CLK : STD_LOGIC;
SIGNAL HSYNC : STD_LOGIC;
SIGNAL PCLK : STD_LOGIC;
SIGNAL VSYNC : STD_LOGIC;
SIGNAL YUV : STD_LOGIC_VECTOR(7 DOWNTO 0);
COMPONENT BT656
    PORT (
    CLK : IN STD_LOGIC;
    HSYNC : OUT STD_LOGIC;
    PCLK : OUT STD_LOGIC;
    VSYNC : OUT STD_LOGIC;
    YUV : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
    );

    constant clock_period: time := 37 ns;        -- 这是俺添加的
    signal stop_the_clock: boolean;               -- 这是俺添加的

END COMPONENT;
BEGIN
    i1 : BT656
    PORT MAP (
-- list connections between master ports and signals
    CLK => CLK,
    HSYNC => HSYNC,
    PCLK => PCLK,
    VSYNC => VSYNC,
    YUV => YUV
    );
init : PROCESS                                               
-- variable declarations                                    
BEGIN                                                        
        -- code that executes only once                     
WAIT;                                                      
END PROCESS init;                                          
always : PROCESS                                             
-- optional sensitivity list                                 
-- (        )                                                
-- variable declarations                                      
BEGIN                                                         
        -- code executes for every event on sensitivity list  
      wait for 700 ms;                        -- 自己添加,我就测试700ms就结束测试
        stop_the_clock <= true;  

        wait;                                         

WAIT;                                                        
END PROCESS always;   

    -- 这也是俺添加的产生时钟
    clocking: process
    begin

        while not stop_the_clock loop
            CLK <= '0', '1' after clock_period / 2;
            wait for clock_period;
        end loop;

        wait;
    end process;
                                      
END BT656_arch;
TCL 发表于 2010-10-21 20:37:15 | 显示全部楼层
verilog 编写testbench教程下载

http://www.fpgaw.com/redirect.ph ... o=lastpost#lastpost
fpga_feixiang 发表于 2021-10-29 15:17:20 | 显示全部楼层
66666666666666666
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2025-6-18 12:54 , Processed in 0.072902 second(s), 24 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表