如何编写testbench的总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何编写testbench的总结相关的知识,希望对你有一定的参考价值。

参考技术A 一、 基本概念和基础知识
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,输出
);

Testbench 的编写与应用

1. Testbench的概念

Testbench 是一种用任意语言编写的程序或模块,用于在模拟过程中执行和验证硬件模型的功能正确性。 Verilog 主要用于硬件建模(模拟),该语言包含各种资源,用于格式化,读取,存储,动态分配,比较和写入模拟数据,包括输入激励和输出结果。

2. Testbench的组成组件

Testbench 的主要组件如下:

  1. 时间表声明:指定所有延迟的时间单位
  2. Module:它定义了测试文件的top模块,测试文件的top模块通常没有输入输出端口,测试是直接监控寄存器和线网这些内部信号的活动
  3. 内部信号:它将驱动激励信号进入 UUT 并监控 UUT 的响应,信号驱动和监控
  4. UUT 实例化
  5. 激励生成:编写语句以创建激励和程序块
  6. 响应监控和比较:自我测试语句,能报告数值,错误和警告

2-1. Testbench的延迟建模

Verilog 支持两种类型的延迟建模:(i)惯性和(ii)传输。 惯性延迟是门(gate)或电路由于其物理特性而可能经历的延迟。 根据所使用的技术,它可以是 ps 或 ns。 惯性延迟还用于确定输入是否对门或电路有影响。如果输入至少在初始延迟时没有保持变化,则忽略输入变化。 例如,5 ns 的惯性延迟意味着无论何时输入发生变化,它都应保持至少 5 ns 的变化, 以使其被视为已更改,否则将忽略该变化(被视为噪声尖峰)。传输延迟是传输电路导线的信号的飞行时间。 以下是运输和惯性延迟的示例:

wire #2 a_long_wire; // 运输延迟两单元时间
xor #1 M1(sum, a, b); // 惯性延迟1单元时间

2-2. Testbench中的初始语句

初始语句在 testbenchs 中用于生成激励和控制仿真执行。参照下面的一个例子:

initial begin
#100 $finish; // run simulation for 100 units
end

initial begin
#10 a=0; b=0; // a, b zero after 10 units delay. Between 0 and 10,
// it is x
#10 b=1; // At 20, make b=1
#10 a=1; // at 30, make a=1
#10 b=0; // at 40, make b=0
end

下面是生成称为时钟的周期信号的初始语句用法的另一个示例。它将产生 50%占空比的时钟信号,周期为 20 个单位。

reg clock;
parameter half_cycle = 10;
initial begin
clock = 0;
forever begin
#half_cycle clock = 1;
#half_cycle clock = 0;
end
end

3. 使用Testbench的一个例子

Testbench

第 1 行定义了`timescale 指令。

第 2 行和第 3 行定义了测试平台模块名称。请注意,通常, testbench 模块的端口列表中不列出端口。

第 5 行将拨动开关定义为 reg 数据类型,因为它将用于提供激励。它连接到被测试(tutorial)的实例化设备。

第 11 行使用实例名称 tut1 和输入/输出端口实例化测试(tutorial)中的设计。

第 13 行到第 22 行定义了计算预期输出的function。

第 24 至 35 行使用 initial 过程描述来定义激励。可以在仿真器控制台窗口中使用 system task $ display 来查看第 31 行和第 33 行生成的消息。

第 29 行通过传递开关参数调用函数 expected_led,并将返回的(计算过的)输出分配给 e_led。 e_led 在第 7 行定义为 reg 类型,因为它在过程语句(初始)中接收函数调用的输出。

第 28 和 29 行还分别定义惯性延迟 50 和 10,以模拟延迟。

4. 开发一个生成特定波形的Testbench

开发一个 testbench,生成如下所示的波形。

Testbench

实验步骤:

  1. 打开Vivado 并创建一个名为 Testbench的空白项目。
    创建并添加输出上面显示的波形的 Verilog 模块。
    将设计模拟 150 ns 并验证是否生成了正确的输出。

参考代码:

module Testbench();
reg A,G1,G2;
initial begin
A = 0;
G1 = 0;
G2 = 1;
#40 A = 1;
#20 G1 = 1;
#20 G2 = 0;
#20 A = 0;
#20 G1 = 0;
#20 G2 = 1;
end
endmodule

以上是关于如何编写testbench的总结的主要内容,如果未能解决你的问题,请参考以下文章

Linux 脚本编写 如何编写一个脚本,修改已有文件中的内容

如何编写Linux下Nand Flash驱动

如何编写驱动程序?

Linux2.6 如何编写Makefile,使驱动程序能够编译链接静态库

如何编写Linux下Nand Flash驱动

如何在 linux 中编写和加载 GUI? [关闭]