如何写TESTBENCH, 给个教程吧
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何写TESTBENCH, 给个教程吧相关的知识,希望对你有一定的参考价值。
参考技术A http://read.pudn.com/downloads167/ebook/771015/testbench.pdf 参考技术B 一、 基本概念和基础知识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在进行功能仿真时能正确进行,而在进行布局布线后仿真时就不
能运行。有两点要注意的地方:(1)、在做映射后仿真或布局布线后仿真时,都已经经过了
综合工具的综合,源程序中的类属命令(Generic)、生成语句(Generate)等都已经进行展
开。例如,如果用Generic 定义了一个参数width,综合工具进行综合时已经按照一个确定
的width 值进行了综合。它生成的电路已经具有一个确定的结构,不能再随意调整。所以在
映射后仿真和布局布线后仿真的testbench中,往往不能出现Generic 语句。(2)映射后仿
真和布局布线后仿真都要用到SDF 文件,并且要将SDF文件关联到设计中的实例。所以在映
射后仿真和布局布线后仿真的testbench中,第一,要将你的设计声明成一个元件。第二,
实例化你设计的元件并且实例名要取为UUT(默认的,当然也可以改)。
关于断言语句
在仿真中为了能得到更多信息,经常要用到断言语句(assert)。其语法如下:
Assert<条件>
Report<消息>
Severity<出错级别>;
出错级别共有5 种:
l Note
l Warning
l Error
l Failure
l Fatal
在VHDL 模型的模拟过程中,一旦断言语句的条件为假,则发送消息并将出错级别发送
给模拟器。通常可以设置一个中止模拟器运行的出错级别,一般默认的中止运行的出错级别
为Failure。
我们来看一个例子:
assert false
report "********* " & IMAGE(DWIDTH) & "BIT DIVIDER SEQUENCE FINISHED
AT " & IMAGE(now) & " !" & " *********"
severity note;
断言的条件不是一个条件表达式,而直接是false。这说明只要程序执行到这里断言就
一定会成立,送出消息。出错级别为note,在模拟器的输出窗口将会显示:
图2
再看一个例子:
assert (s_cyi((DWIDTH-1)/4) = '0')
and (s_ovi = '0')
and (s_qutnt = conv_std_logic_vector(v_quot,DWIDTH))
and (s_rmndr = conv_std_logic_vector(v_remd,DWIDTH))
report "ERROR in division!"
severity failure;
断言的条件有4 个并且是与的关系,只要其中一个条件不成立则整个表达式为假,断言
成立。如果断言成立将输出“ERROR in division!“这个消息。并且通知模拟器出错级别为
failure,这一般会停止模拟。这个断言实际是在对结果进行验证。
二、实际testbench分析
下面将详细分析一个实际的testbench,它是用来测试8051 的ALU单元的除法功能的。
8 位的除法器,被除数和除数的组合共有256×256=65536 种。我们采用的方法是穷举所有
的输入组合,这样的代码覆盖率可以达到100%。它的验证必须通过程序自动完成,否则通
过人工方法工作量太大。
把要测试的程序当作一个元件,例如想象成一个74 系列数字电路。Testbench 的作用
是在被测试电路的输入端加上激励,然后比较被测试电路的输出和计算出来的期望值是否一
致。对我们这个例子来说,在要仿真的ALU 输入端产生65536 种输入组合,然后将ALU产生
的对应输出值和testbench 算出的期望值相比较,如果有错误产生则停止模拟并输出信息。
ALU 的除法单元的输入有4 个,分别是被除数、除数、进位、溢出位;输出也有4 个,分别
是商、余数、新的进位、新的溢出位。
1、 testbench 的输出s_dvdnd(被除数)、s_dvsor(除数)、s_cyo(进位)、s_ovo(溢
出位)连接到ALU 的输入acc_i(被除数)、ram_data_i(除数)、cy_i(进位)、ov_i
(溢出位);
2、 testbench 的输入s_qutnt(商)、s_rmndr(余数)、s_cyi(进位)、s_ovi(溢出位)
连接到ALU的输出result_a_o(商)、 result_b_o(余数)、new_cy_o(进位)、new_ov_o
(溢出位)。
3、 总之,testbench 驱动被测试单元,同时对被测试单元的输出进行验证。
4、assert (s_cyi((DWIDTH-1)/4) = '0')
and (s_ovi = '0')
and (s_qutnt = conv_std_logic_vector(v_quot,DWIDTH))
and (s_rmndr = conv_std_logic_vector(v_remd,DWIDTH))
report "ERROR in division!"
severity failure;
根据51 指令系统规定,除法运算的cy 位固定为0,如果除数为0则ov 置1,否则置0。
程序中
s_qutnt = conv_std_logic_vector(v_quot,DWIDTH)
s_rmndr = conv_std_logic_vector(v_remd,DWIDTH)
用来对运算结果进行比较。conv_std_logic_vector()是类型转换函数。
――首先是对库的引用
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
library work;
use work.mc8051_p.all;
library STD;
use STD.textio.all;
――定义结构体,testbench程序的结构体是空的。因为testbench是用来仿真的,不存在
--对外的接口,所以entity是空的。但是必须要有,这是语法的要求。
entity TBX_mc8051_alu is
end TBX_mc8051_alu;
-------------------------------------------------------------------------------
architecture TBX_ARCH_DIV of TBX_mc8051_alu is
――定义元件,映射后仿真和布局布线后仿真要使用SDF 文件,必须指定实例名。要实例
--化元件首先必须定义元件。
component mc8051_alu
port (
new_ov_o : out STD_LOGIC; ――新的ov位,输出
ov_i : in STD_LOGIC := 'X'; ――ov位,输入
new_cy_o : out STD_LOGIC_VECTOR ( 1 downto 0 ); ――新的cy位,输出
acc_i : in STD_LOGIC_VECTOR ( 7 downto 0 ); ――acc,输入
rom_data_i : in STD_LOGIC_VECTOR ( 7 downto 0 ); ――rom_data,输入
cmd_i : in STD_LOGIC_VECTOR ( 5 downto 0 ); ――命令,输入
ram_data_i : in STD_LOGIC_VECTOR ( 7 downto 0 ); ――ram_data,输入
cy_i : in STD_LOGIC_VECTOR ( 1 downto 0 ); ――cy,输入
result_b_o : out STD_LOGIC_VECTOR ( 7 downto 0 ); ――结果b,输出
result_a_o : out STD_LOGIC_VECTOR ( 7 downto 0 ) -―结果a,输出
);
end component;
――定义函数
-----------------------------------------------------------------------------
--
-- IMAGE - Convert a special data type to string --
--
This function uses the STD.TEXTIO.WRITE procedure to convert different --
-- VHDL data types to a string to be able to output the information via --
-- a report statement to the simulator. --
-- (VHDL'93 provides a dedicated predefinded attribute 'IMAGE) --
-----------------------------------------------------------------------------
――定义了一个函数IMAGE,之所以有两个定义,是因为对IMAGE进行了重载,一个是把
――time变量转换成string,另一个是把integer变量转换成string
function IMAGE (constant tme : time) return string is
variable v_line : line;
variable v_tme : string(1 to 20) := (others => ' ');
begin
write(v_line, tme);
v_tme(v_line.all'range) := v_line.all;
deallocate(v_line);
return v_tme;
end IMAGE;
function IMAGE (constant nmbr : integer) return string is
variable v_line : line;
variable v_nmbr : string(1 to 11) := (others => ' ');
begin
write(v_line, nmbr);
v_nmbr(v_line.all'range) := v_line.all;
deallocate(v_line);
return v_nmbr;
end IMAGE;
――定义过程,它产生所有的测试输入数据并将产生结果和期望值进行比较,如果有误
――产生,模拟将停止并输出一个错误信息。注意到testbench要产生被测试单元的输入
――信号,因此testbench的输出接到被测试单元的输入,被测试单元的输出接到
――testbench的输入。
procedure PROC_DIV_ACC_RAM (
constant DWIDTH : in positive;
constant PROP_DELAY : in time;
signal s_cyi : in std_logic_vector;
signal s_ovi : in std_logic;
signal s_qutnt : in std_logic_vector;
signal s_rmndr : in std_logic_vector;
signal s_cyo : out std_logic_vector;
signal s_ovo : out std_logic;
signal s_dvdnd : out std_logic_vector;
signal s_dvsor : out std_logic_vector;
signal s_dvdr_end : out boolean) is
variable v_quot : integer;
variable v_remd : integer;
variable v_flags : std_logic_vector((DWIDTH-1)/4+1 downto 0);
begin
s_dvdr_end <= false;
for j in 0 to 2**DWIDTH-1 loop
s_dvdnd <= conv_std_logic_vector(j,DWIDTH); ――产生被除数
for i in 0 to 2**DWIDTH-1 loop
s_dvsor <= conv_std_logic_vector(i,DWIDTH); ――产生除数
for f in 0 to 2**(((DWIDTH-1)/4)+2)-1 loop ――产生cy和ov
v_flags := conv_std_logic_vector(f,((DWIDTH-1)/4)+2);
s_cyo <= v_flags(((DWIDTH-1)/4) downto 0); ――s_cyo和s_ovo输出到
s_ovo <= v_flags(v_flags'HIGH); ――mc8051_alu的cy_i和ov_i
wait for PROP_DELAY; ――等待100ns
if i /= 0 then
v_quot := j/i; ――产生期待的商
v_remd := j rem i; ――产生期待的余数
assert (s_cyi((DWIDTH-1)/4) = '0') ――对结果进行比较
and (s_ovi = '0')
and (s_qutnt = conv_std_logic_vector(v_quot,DWIDTH))
and (s_rmndr = conv_std_logic_vector(v_remd,DWIDTH))
report "ERROR in division!"
severity failure;
else ―― 否则除数为0
assert (s_cyi((DWIDTH-1)/4) = '0')
and (s_ovi = '1')
report "ERROR in division by zero - flags not correct!"
severity failure;
end if;
end loop; -- f
end loop; -- i
end loop; -- j
assert false ―― 程序执行到这里表示没有问题产生
report "********* " & IMAGE(DWIDTH) & "BIT DIVIDER SEQUENCE FINISHED AT "
& IMAGE(now) & " !" & " *********"
severity note;
s_dvdr_end <= true;
wait;
end PROC_DIV_ACC_RAM;
-----------------------------------------------------------------------------
――定义常数和信号
constant PROP_DELAY : time := 100 ns;
signal rom_data_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal ram_data_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal acc_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal hlp_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal cmd_DIV_ACC_RAM : std_logic_vector(5 downto 0);
signal cy_DIV_ACC_RAM : std_logic_vector(1 downto 0);
signal ov_DIV_ACC_RAM : std_logic;
signal new_cy_DIV_ACC_RAM : std_logic_vector(1 downto 0);
signal new_ov_DIV_ACC_RAM : std_logic;
signal result_a_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal result_b_DIV_ACC_RAM : std_logic_vector(7 downto 0);
signal end_DIV_ACC_RAM : boolean;
-----------------------------------------------------------------------------
begin
-----------------------------------------------------------------------------
-- Test the DIV_ACC_RAM command --
-----------------------------------------------------------------------------
rom_data_DIV_ACC_RAM <= conv_std_logic_vector(0,8);
cmd_DIV_ACC_RAM <= conv_std_logic_vector(43,6); ――43表示除法命令
UUT : mc8051_alu ――例化元件
port map (
rom_data_i => rom_data_DIV_ACC_RAM(7 downto 0),
ram_data_i => ram_data_DIV_ACC_RAM(7 downto 0),
acc_i => acc_DIV_ACC_RAM(7 downto 0),
cmd_i => cmd_DIV_ACC_RAM,
cy_i => cy_DIV_ACC_RAM(1 downto 0),
ov_i => ov_DIV_ACC_RAM,
new_cy_o => new_cy_DIV_ACC_RAM(1 downto 0),
new_ov_o => new_ov_DIV_ACC_RAM,
result_a_o => result_a_DIV_ACC_RAM(7 downto 0),
result_b_o => result_b_DIV_ACC_RAM(7 downto 0));
――mc8051_alu的被除数、除数、cyi、ovi由PROC_DIV_ACC_RAM产生
――注意到PROC_DIV_ACC_RAM的商(s_qutnt)、余数(s_rmndr)的Port方向是in,
――用来连接mc8051_alu的输出result_a_DIV_ACC_RAM和result_b_DIV_ACC_RAM
PROC_DIV_ACC_RAM (DWIDTH => 8, ――调用过程
PROP_DELAY => PROP_DELAY,
s_cyi => new_cy_DIV_ACC_RAM(1 downto 0),
s_ovi => new_ov_DIV_ACC_RAM,
s_qutnt => result_a_DIV_ACC_RAM(7 downto 0), ――商
s_rmndr => result_b_DIV_ACC_RAM(7 downto 0), ――余数
s_cyo => cy_DIV_ACC_RAM(1 downto 0),
s_ovo => ov_DIV_ACC_RAM,
s_dvdnd => acc_DIV_ACC_RAM(7 downto 0), ――被除数
s_dvsor => ram_data_DIV_ACC_RAM(7 downto 0), ――除数
s_dvdr_end => end_DIV_ACC_RAM);
-----------------------------------------------------------------------------
end TBX_ARCH_DIV;
――配置,表示TBX_mc8051_alu这个entity使用TBX_ARCH_DIV这个architecture
configuration TBX_CFG_mc8051_alu_TBX_ARCH_DIV of TBX_mc8051_alu is
for TBX_ARCH_DIV
end for;
end TBX_CFG_mc8051_alu_TBX_ARCH_DIV;本回答被提问者采纳
调试成功的简单异步FIFO--verilog实现+testbench
最近在写一个异步FIFO的时候,从网上找了许多资料,文章都写的相当不错,只是附在后面的代码都多多少少有些小错误。
于是自己写了一个调试成功的代码,放上来供大家参考。
非原创 原理参考下面:
原文 https://www.cnblogs.com/SYoong/p/6110328.html
上代码:
1 module Asyn_FIFO 2 #( 3 parameter WIDTH = 8, 4 parameter DEPTH = 4 5 ) 6 ( 7 input clk_wr, 8 input clk_rd, 9 input rst_n_rd, 10 input rst_n_wr, 11 input wr_en, 12 input rd_en, 13 input [WIDTH-1:0] data_wr, 14 output [WIDTH-1:0] data_rd, 15 output reg rd_empty, 16 output reg wr_full 17 ); 18 19 //defination 20 reg [WIDTH-1 : 0] mem [0 : (1<<DEPTH)-1]; //2^DEPTH numbers 21 reg [DEPTH : 0] wp, rp; 22 reg [DEPTH : 0] wr1_rp, wr2_rp, rd1_wp, rd2_wp; 23 reg [DEPTH : 0] wbin, rbin; 24 25 26 wire [DEPTH-1 : 0] waddr, raddr; 27 wire [DEPTH : 0] wbin_next, rbin_next; //bincode 28 wire [DEPTH : 0] wgray_next, rgray_next; //graycode 29 30 wire rd_empty_val, wr_full_val; 31 32 //output 33 assign data_rd = mem[raddr]; 34 35 //input 36 always@(posedge clk_wr) 37 if(wr_en && !wr_full) 38 mem[waddr] <= data_wr; 39 40 /*----------generate waddr and raddr-------------------------*/ 41 //gen raddr and read gray code 42 always@(posedge clk_rd or negedge rst_n_rd) 43 if(!rst_n_rd) 44 {rbin, rp} <= 0; 45 else 46 {rbin, rp} <= {rbin_next, rgray_next}; 47 48 assign raddr = rbin[DEPTH-1 : 0]; 49 assign rbin_next = rbin + (rd_en & ~rd_empty); 50 assign rgray_next = rbin_next ^ (rbin_next >> 1); 51 52 //gen waddr and write gray code 53 always@(posedge clk_wr or negedge rst_n_wr) 54 if(!rst_n_wr) 55 {wbin, wp} <= 0; 56 else 57 {wbin, wp} <= {wbin_next, wgray_next}; 58 59 assign waddr = wbin[DEPTH-1 : 0]; 60 assign wbin_next = wbin + (wr_en & ~wr_full); 61 assign wgray_next = wbin_next ^ (wbin_next >> 1); 62 63 /*---------------synchro rp and wp--------------------------*/ 64 //synchro rp 65 always@(posedge clk_wr or negedge rst_n_wr) 66 if(!rst_n_wr) 67 {wr2_rp, wr1_rp} <= 0; 68 else 69 {wr2_rp, wr1_rp} <= {wr1_rp, rp}; //delay two clock 70 71 //synchro wp 72 always@(posedge clk_rd or negedge rst_n_rd) 73 if(!rst_n_rd) 74 {rd2_wp, rd1_wp} <= 0; 75 else 76 {rd2_wp, rd1_wp} <= {rd1_wp, wp}; 77 78 /*---------------empty and full flags--------------------------*/ 79 //gen rd_empty 80 assign rd_empty_val = (rd2_wp == rgray_next); 81 always@(posedge clk_rd or negedge rst_n_rd) 82 if(!rst_n_rd) 83 rd_empty <= 1‘b1; 84 else 85 rd_empty <= rd_empty_val; 86 87 //gen wr_full, two high bit do not equal 88 assign wr_full_val = ({~wr2_rp[DEPTH : DEPTH-1], wr2_rp[DEPTH-2 : 0]} == wgray_next); 89 always@(posedge clk_wr or negedge rst_n_wr) 90 if(!rst_n_wr) 91 wr_full <= 1‘b0; 92 else 93 wr_full <= wr_full_val; 94 95 endmodule
1 module Asyn_FIFO_tb; 2 3 parameter WIDTH = 8; 4 5 reg clk_wr; 6 reg clk_rd; 7 reg rst_n_rd; 8 reg rst_n_wr; 9 10 reg [WIDTH-1:0] data_wr; 11 reg wr_en; 12 wire wr_full; 13 14 wire [WIDTH-1:0] data_rd; 15 reg rd_en; 16 wire rd_empty; 17 18 19 Asyn_FIFO fifo_inst( 20 .clk_wr(clk_wr), 21 .rst_n_rd(rst_n_rd), 22 .rst_n_wr(rst_n_wr), 23 .wr_en(wr_en), 24 .data_wr(data_wr), 25 .clk_rd(clk_rd), 26 .rd_en(rd_en), 27 .data_rd(data_rd), 28 .rd_empty(rd_empty), 29 .wr_full(wr_full) 30 ); 31 32 initial begin 33 rst_n_rd = 0; 34 rst_n_wr = 0; 35 clk_wr = 0; 36 clk_rd = 0; 37 wr_en = 0; 38 rd_en = 0; 39 40 #20 41 rst_n_rd = 1; 42 rst_n_wr = 1; 43 44 #80 45 wr_en = 1; 46 rd_en = 0; 47 48 #10000 49 wr_en = 0; 50 rd_en = 1; 51 end 52 53 always #10 clk_wr = ~clk_wr; 54 always #20 clk_rd = ~clk_rd; 55 56 /* always @(posedge clk_rd) 57 rd_en <= ($random) % 2; 58 59 always @(posedge clk_wr) 60 wr_en <= ($random) % 2; */ 61 62 always @(posedge clk_wr) 63 data_wr <= ($random) % 256; 64 65 endmodule
注意wire、reg类型的赋值。
以上是关于如何写TESTBENCH, 给个教程吧的主要内容,如果未能解决你的问题,请参考以下文章
python unittest 比如我写了两个测试函数testA,testB,如何在unittest.main()执行时让testB先执行?
FPGA教程案例60深度学习案例7——基于FPGA的CNN卷积神经网络之testbench编写以及CNN仿真测试