这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » FPGA » 编写高效的测试设计(testbenches)

共6条 1/1 1 跳转至

编写高效的测试设计(testbenches)

工程师
2007-04-26 22:51:51     打赏

Writing Efficient Testbenches

编写高效的测试设计(testbenches

原文作者:Mujtaba Hamid

注:

一个设计的测试验证是非常重要的。有效的测试可以助我们快速的完成或改善设计。Testbenches建议编写有效的测试代码来通过软件实现可靠的验证。无意中发现,顺手译为中文,以备将来方便。也贴给没有找到更好中文版本的同道人。

Testbenches本意应该是测试平台更合理,但是在中文中阅读起来很不舒服。所以本文中有时译为“测试设计”,“测试代码”,有时干脆是“测试”。

摘要:

应用笔记为HDL验证设计的新手,或者是没有丰富的测试设计经验的逻辑设计者而编写。

测试设计是验证HDL设计的主要手段。本应用笔记为创建或准备和构建有效的测试设计提供准则。它也提供一个为任何设计开发自较验测的测试设计的一个代数方法。

涉及的所有设计文件可以从以下的站点获得:

PC: ftp://ftp.xilinx.com/pub/applications/xapp/xapp199.zip

UNIX: ftp://ftp.xilinx.com/pub/applications/xapp/xapp199.tar.gz

简介:

由于设计的规模越来越大和越来越复杂,数字设计的验证已经成为一个日益困难和繁琐的事。面对挑战,验证工程师们依靠许多的验证工具和方法。对于大的系统,如几百万门的设计,工程师们一般使用一套可靠的验证工具。当然,对于一些小的设计,设计工程师常常发现带有测试的hdl仿真器就可以做得很好。

测试设计已经成为一个验证高级语言设计HLL (High-Level Language) 的标准方法。

典型的,测试设计完成以下任务:

实现测试设计;

仿真通过使用模块的测试向量来仿真测试设计;

输出结果到终端或波形窗口以检视;

可选择的将实际结果和预期结果进行比较。

一般测试设计使用工业标准的VHDLverilog硬件描述语言来编写。测试设计调用功能设计,然后仿真。复杂的测试设计完成一些附加的功能----如它们包含逻辑来为设计决定适当的设计激励或比较实际结果和预期结果。

后续的章节说明了一个非常稳定的测试设计的结构,并且提供了一个自较验测例子----它将自动比较实际结果和测试设计的预期结果。

1说明一个基于以上基本要求的标准的hdl验证流程。由于测试设计使用VHDLverilogHDL来描述,测试设计的验证过程可以在不同的平台或不同公司的软件工具环境完成。另外,由于VHDLverilogHDL是公开的通用标准语言,使用VHDLverilogHDL来描述验证设计可以毫无困难的在将来重用。

1使用测试设计的HDL测试验证流程

构建测试设计:

测试设计可以用VHDLverilogHDL来描述.因为测试设计只用来进行仿真,它们没有那些适应综合中仅应用的rtl语言子集的语法约束的限制.而是所有的行为结构都可以使用。从而测试设计可以编写的更为通用,使得它们可以更容易维护。

所有的测试设计包含了如表1的基本程序段块。正如上面所提到的,测试设计一般包含更多的附加功能,如在终端上可视的结果和内建的错误检测。
1 测试设计的基本程序段

下面的例子说明经常使用的测试设计的结构。

产生时钟信号

使用系统时钟来的时序逻辑设计必须产生时钟。重复的时钟信号可以很容易的在vhdlverilog源码中实现。以下是vhdlverilog的时钟发生示例。

VHDL:

-- Declare a clock period constant.

Constant ClockPeriod : TIME := 10 ns;

-- Clock Generation method 1:

Clock <= not Clock after ClockPeriod / 2;

-- Clock Generation method 2:

GENERATE CLOCK: process

begin

wait for (ClockPeriod / 2)

Clock <= ’1’;

wait for (ClockPeriod / 2)

Clock <= ’0’;

end process;

Verilog:

// Declare a clock period constant.

Parameter ClockPeriod = 10;

// Clock Generation method 1:

initial begin

forever Clock = #(ClockPeriod / 2) ~ Clock;

end

// Clock Generation method 2:

initial begin

always #(ClockPeriod / 2) Clock = ~Clock;

end




关键词: 编写     高效     测试     设计     testbenches         

工程师
2007-04-26 22:52:00     打赏
2楼

编辑测试文件的准则

本节提供测试设计的编辑准则。正如计划一个电路设计可以帮助构建更好的电路性能,计划好测试方案可以提高仿真验证的结果。

在编写测试设计前要了解仿真器

虽然通用仿真工具遵从HDL工业标准,但标准并没有说明多少重要的仿真描述条项。不同的仿真器有不同的功能,兼容能力,和执行性能,形成不同的仿真结果。

--基于事件vs基于周期的仿真

仿真器使用基于事件或基于周期的仿真方法。基于事件的仿真器,当输入,信号,或是门改变了值,来确定仿真器事件的时间。在一个基于事件的仿真器中,一个延时值可以附加在门电路或是电路网络上来构建最适的时间仿真。基于周期的仿真器面向同步设计。他们最优化组合逻辑,在时钟周期内分析结果。这个功能使得基于周期的仿真器比基于事件的仿真器更快更有效。然而,由于基于周期的仿真器不允许详细的时间说明,他们并不如基于事件的仿真器准确。对于更多的关于两者的差异的信息,参看

http://www.ednmag.com/ednmag/reg/1996/070496/14df4.htm

上的"数字逻辑仿真:事件驱动,周期驱动,和Home-Brewed"(Digital Logic Simulation: Event-Driven, Cycle-Based, and Home-Brewed),

--确定事件时间

基于事件的仿真器提供商使用不同的运算法则来确定仿真事件。所以,根据仿真器用来确定的运算法则不同,同一个仿真时间的事件被确定为不同的次序(根据在每个事件之间插入的delta延时)。为避免对运算法则的依赖和确保正确的结果,一个事件驱动测试应该详细描述明确的激励次序。

--避免使用无限循环

当一个事件添加到基于事件的仿真器,cpu和内存的使用就增加了,仿真过程就会变慢。除非是评价测试设计,无限循环不应该使用来作为设计的激励。一般地,时钟被说明为一个内部的无限循环(如verilog中的'forever'循环),但是没有其他信号事件。

--细分激励到逻辑模块

在测试中,所有初始块(verilog)或进程块(VHDL)并列地运行。如果无关的激励被分离到独立的块,测试激励的次序会变得更容易实现和检视。因为每个并行的块相关于零点运行,对于分离的块传递激励更容易。分离激励块的使用使得测试设计建立,维护和升级(参看后面的高级测试技术,及该技术的示例)

--避免显示并不重要的数据

大型设计的测试可能包含10万以上的事件或匿名信号。显示大量的仿真数据会相当地降低仿真的速度。最好只是尝试每整数时钟周期时相应的信号来确保适当的仿真速度。


工程师
2007-04-26 22:53:00     打赏
3楼

Xilinx 仿真流程要决

配置语句(VHDL

一个VHDL配置语句允许一个实体链接到一个面向综合或者仿真的详细结构。在xilinx core generator vhdl功能仿真流程中,配置语句被用于一个设计的核心仿真模块的调用(切分)。如果核心仿真模块没有绑定在一个设计,仿真将不能正确的工作。关于配置语句使用的例子,参看前述的VHDL的自较验查测试代码。在xilinx CORE Generator设计中详细配置语句的使用信息可以在http://support.xilinx.com/support/techsup/tutorials/tutorials31i.htm#Modelsim页面中的Modelsim VHDL仿真教程中找到。

http://support.xilinx.com/support/techsup/tutorials/tutorials31i.htm#Modelsim.

为仿真初始化内存RAM

根据默认值,Xilinx Virtex?块RAMs在所有数据位置都是初始为零,从零点开始的。对于一个post-NGDBuild, post-MAP, or Post-PAR的时序仿真,块RAMs初始为用户在约束文件(ucf)中指定的值或在输入设计文件到NGDBuild时通过init属性来指定值.对于一个pre-synthesis 或 post-synthesis (pre-NGDBuild)的功能仿真,一个配置语句必须用来给RAM块提供初值。下面是一个用来初始化RAM块的配置语句的例子。

library IEEE;

use IEEE.std_logic_1164.all;

use IEEE.std_logic_unsigned.all;

library UNISIM;

use UNISIM.vcomponents.all;

configuration cfg_ex_blkram_tb of ex_blkram_tb is

for tb

for uut : ex_blkram use entity work.ex_blkram(struct);

for struct

for INST_RAMB4_S4 : RAMB4_S4 use entity

unisim.RAMB4_S4(RAMB4_S4_V)

generic map (INIT_00 =>

X"1F1E1D1C1B1A191817161514131211100F0E0D0C0B0A09080706050403020100

",

INIT_01 =>

X"3F3E3D3C3B3A393837363534333231302F2E2D2C2B2A29282726252423222120

",

.

.

.

INIT_0F=>

X"FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0

");

end for;

end for;

end for;

end for;

end cfg_ex_blkram_tb;

高级测试技术

根据任务和过程细分激励模块

在创建一个大的测试设计时,激励将会被分割使得代码清晰而易于修改。任务(verilog)或过程(VHDL)可以被用来分割信号。在下面的例子中,测试激励用于一个SDRAM控制器的设计。设计包括重复的激励模块,以便测试设计中通过声明独立的任务分割激励,这些任务稍后被调用来进行独立块的功能的仿真执行。

Verilog示例

task addr_wr;

input [31 : 0] address;

begin

data_addr_n = 0;

we_rn = 1;

ad = address;

end

endtask

task data_wr;

input [31 : 0] data_in;

begin

data_addr_n = 1;

we_rn = 1;

ad = data_in;

end

endtask

task addr_rd;

input [31 : 0] address;

begin

data_addr_n = 0;

we_rn = 0;

ad = address;

end

endtask

task data_rd;

input [31 : 0] data_in;

begin

data_addr_n = 1;

we_rn = 0;

ad = data_in;

end

endtask

task nop;

begin

data_addr_n = 1;

we_rn = 0;

ad = hi_z;

end

endtask

这些任务指定独立的设计功能元素----地址可读可写,数据可读可写,或者空操作。一量指定,这些任务可以在激励进程中被调用。如下所示:

Initial begin

nop ; // Nop

#( 86* ‘CYCLE +1); addr_wr (32’h20340400); // Precharge, load

Controller MR

#(‘CYCLE); data_wr (32’h0704a076); // value for Controller MR

#(‘CYCLE); nop ; // Nop

#(5 * ‘CYCLE); addr_wr (32’h38000000); // Auto Refresh

#(‘CYCLE); data_wr (32’h00000000); //

#(‘CYCLE); nop ; // Nop

end


工程师
2007-04-26 22:53:00     打赏
4楼

VHDL例程

以下是相当设计的VHDL测试文件,分别细分到独立的过程。

Stimulus : process

procedure addr_wr (address: in std_logic_vector(31 downto 0)) is

begin

data_addr_n <= ‘0’;

we_rn <= ‘1’;

ad <= address;

end addr_wr;

procedure data_wr (data_in: in std_logic_vector(31 downto 0 )) is

begin

data_addr_n <= ‘1’;

we_rn <= ‘1’;

ad <= data_in;

end data_wr;

procedure addr_rd (address: in std_logic_vector(31 downto 0)) is

begin

data_addr_n <= ‘0’;

we_rn <= ‘0’;

ad <= address;

end addr_rd;

procedure data_rd (data_in: in std_logic_vector(31 downto 0)) is

begin

data_addr_n <= ‘1’;

we_rn <= ‘0’;

ad <= data_in;

end data_rd;

procedure nop is

begin

data_addr_n <= ‘1’;

we_rn = ‘0’;

ad = ‘Z’;

end nop;

begin

nop ; -- Nop

wait for 200 ns;

addr_wr (16#20340400#); -- Precharge, load Controller MR

wait for 8 ns;

data_wr (16#0704a076#); -- value for Controller MR

wait for 8 ns;

nop ; -- Nop

wait for 40 ns;

addr_wr (16#38000000#); -- Auto Refresh

wait for 8 ns;

data_wr (16#00000000#);

wait for 8 ns;

nop ; -- Nop

..

..

细分激励到独立的任务使得激励很容易实现,也使得代码的可读性更好。

在仿真时控制双向信号

多数设计使用双向信号,在测试设计中必须区别对待双向信号和单向信号。

VHDL示例

The following is a VHDL bi-directional signal example:

以下是一个vhdl描述的双向信号示例

Library IEEE;

use IEEE.STD_LOGIC_1164.all;

use IEEE.STD_LOGIC_UNSIGNED.all;

entity bidir_infer is

port (DATA : inout STD_LOGIC_VECTOR(1 downto 0);

READ_WRITE : in STD_LOGIC);

end bidir_infer;

architecture XILINX of bidir_infer is

signal LATCH_OUT : STD_LOGIC_VECTOR(1 downto 0);

begin

process(READ_WRITE, DATA)

begin

if (READ_WRITE = ’1’) then

LATCH_OUT <= DATA;

end if;

end process;

process(READ_WRITE, LATCH_OUT)

begin

if (READ_WRITE = ’0’) then

DATA(0) <= LATCH_OUT(0) and LATCH_OUT(1);

DATA(1) <= LATCH_OUT(0) or LATCH_OUT(1);

else

DATA(0) <= ’Z’;

DATA(1) <= ’Z’;

end if;

end process;

end XILINX;

为访问上例中的双向的DATA信号,一个测试可以设置如下:

library ieee;

use ieee.std_logic_1164.all;

Entity testbench is

End testbench;

Architecture test_bidir of testbench is

Component bidir_infer

port (DATA : inout STD_LOGIC_VECTOR(1 downto 0);

READ_WRITE : in STD_LOGIC);

end component;

signal read_writet: std_logic;

signal datat, data_top : std_logic_vector(1 downto 0);

begin

datat <= data_top when (Read_writet = ’1’) else (others => ’Z’);

data_top <= datat when (Read_writet = ’0’) else (others => ’Z’);

uut : bidir_infer port map (datat, read_writet);

process begin

read_writet <= ’1’;

data_top <= "10";

wait for 50 ns;

read_writet <= ’0’;

wait;

end process;

end test_bidir;

双向总线由测试台控制,双向总线的值可以通过数据顶层信号来访问。


工程师
2007-04-26 22:54:00     打赏
5楼

Verilog示例

以下是verilog设计的可决断的双向总线示例。

module bidir_infer (DATA, READ_WRITE);

input READ_WRITE ;

inout [1:0] DATA ;

reg [1:0] LATCH_OUT ;

always @ (READ_WRITE or DATA)

begin

if (READ_WRITE == 1)

LATCH_OUT <= DATA;

end

assign DATA = (READ_WRITE == 1) ? 2’bZ : LATCH_OUT;

endmodule

Verilog测试设计可以如下设置:

module test_bidir_ver;

reg read_writet;

reg [1:0] data_in;

wire [1:0] datat, data_out;

bidir_infer uut (datat, read_writet);

assign datat = (read_writet == 1) ? data_in : 2’bZ;

assign data_out = (read_writet == 0) ? datat : 2’bZ;

initial begin

read_writet = 1;

data_in = 11;

#50 read_writet = 0;

end

endmodule

在这些测试设计中,data_in信号提供激励到设计中的双向DATA数据信号,data_out信号读取该DATA数据信号.

为仿真初始化内存

请参考前段的xilinx仿真流程要决(Xilinx Simulation Flow Tips)


工程师
2007-04-26 22:54:00     打赏
6楼

有用的语言结构

Verilog

有用的Verilog语言结构,如 $monitor, $display, $time,在前面的verilog测试示例中论述过,这一节说明另外的可以在测试设计中使用的verilog语句结构。

force/release

force/release语句可以用来跨越进程对一个寄存器或一个电路网络的赋值。这结结构一般用于强制特定的设计的行为。一旦一个强制值释放,这个信号保持它的状态直到新的值被进程赋值。以下是force/release语句的用法。

module testbench;

..

initial begin

reset = 1;

force DataOut = 101;

#25 reset = 0;

#25 release DataOut;

..

..

end

endmodule

assign/deassign

assign/deassign语句与force/release相类似,但是assign/deassign只用于设计中的寄存器。他们一般用于设置输入值。就象一个force语句,assign语句超越进程语句的赋值。以下是一个assign/deassign语句的用法。

module testbench;

..

..

initial begin

reset = 1;

DataOut = 101;

#25 reset = 0;

release DataOut;

..

..

end

initial begin

#20 assign reset = 1;// this assign statement overrides the earlier

statement #25 reset = 0;

#50 release reset;

endmodule

timescales

timescale指示被用于为测试指定单位时间步。它也影响仿真器的精确度。表示符号为:‘timescale reference_time/precision

Reference_time是一个用于测量的单位时间。Precision决定延时应该达到的精度,为仿真设置单位步距。以下是‘ timescale的使用方法。

timescale 1 ns / 1 ps

// this sets the reference time to 1 ns and precision to 1 ps.

module testbench;

..

..

initial begin

#5 reset = 1; // 5 unit time steps correspond to 5 * 1ns = 5ns in

simulation time

#10 reset = 0;

..

end

initial begin

$display (“%d , Reset = %b”, $time, reset); // this display

// statement will get executed

// on every simulator step, ie, 1 ps.

end

endmodule

如果仿真使用时延值,仿真就必须运行在一个比最小时延还好的精确度以内(为了归一化时延)。例如,如果9ps延时在仿真库中使用,相应仿真的精确度就必须是在1ps9ps之间可调的范围。

只读储器初始化文件

verilog提供$readmemb $readmemh命令来读取ascii文件来初始化存储器的内容。这个命令可以在仿真中用来初始化Xilinx BlockRAM 或者SelectRAM器件。符号表达如下:

$readmemb (“<design.mif>”, design_instance);

MIF是由crorgenerator 生成的存储器初始化文件(Memory Initialization File)。使用者指定MIF的内容。

VHDL

除了前文曾经叙述过的vhdl命令以外(assert,wait,report),以下的结构也对vhdl测试台文件的创建有所帮助。

meminitfile

vhdl提供一个meminitfile记录用来输入存储模块的内容。以下是它的符号说明:

FILE meminitfile: TEXT IS IN “<design.mif>”;

MIF是由crorgenerator 生成的存储器初始化文件(Memory Initialization File)。使用者指定MIF的内容。


共6条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]