文档库 最新最全的文档下载
当前位置:文档库 › verilog_test

verilog_test

verilog_test
verilog_test

Writing Efficient Testbenches

原文作者:Mujtaba Hamid

翻译:phixcoco@https://www.wendangku.net/doc/cb1068473.html,(浙江大学飘渺水云间论坛)

[请阅读文档最后的说明]

摘要:

本应用笔记是专门为没有测试编写经验并对HDL验证流程陌生的逻辑设计者而编写的。

编写测试代码是验证HDL设计的主要手段。本应用笔记为创建或构建有效的测试设计提供了准则。同时给出了一个为任何设计开发自检测测试的算法。

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

PC: ftp://https://www.wendangku.net/doc/cb1068473.html,/pub/applications/xapp/xapp199.zip

UNIX: ftp://https://www.wendangku.net/doc/cb1068473.html,/pub/applications/xapp/xapp199.tar.gz

1简介

由于设计的规模越来越大和越来越复杂,数字设计的验证已经成为一个日益困难和繁琐的任务。验证工程师们运用多种验证工具和方法来应对挑战。对于大的系统,如几百万门的设计,工程师们通常使用一系列完善的验证工具。当然,对于一些小的设计,设计工程师常常发现能编写测试设计的hdl仿真器就可以做得很好。

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

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

·在测试中实例化设计模块Design Under Test(DUT);

·向要进行测试的模块(DUT)输入测试向量进行仿真;

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

·仿真结果输出到终端或波形窗口以观察结果;

·将实际结果和预期结果进行比较(可选步骤)。

一般测试使用工业标准的VHDL或Verilog硬件描述语言来编写。测试中调用功能设计,然后仿真。复杂的测试设计完成一些附加的功能――如它们包含逻辑模块来为设计产生适当的激励或者将实际结果与预期结果做比较。

后续的章节说明了一个优良的测试设计的结构,并提供了一个自检测测试的例子――用以自动化地比较实际结果和测试设计的预期结果。

图1给出了符合上述步骤的一个标准的HDL验证流程。由于测试设计使用VHDL或VerilogHDL编写,因此测试设计的验证流程可以在各平台或各公司提供的软件工具间移植。另外,由于VHDL或VerilogHDL是公开化的标准语言,用VHDL或VerilogHDL编写的验证测试包可以方便地以后的设计中重用。

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

2构建测试设计

测试设计可以用VHDL或VerilogHDL编写。因为测试设计只用来进行仿真,不需要受综合中仅能使用RTL语言子集这样的语法约束。因此它可以使用所有的行为级结构。从而测试可以编写地更为通用,从而方便维护。

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

表1 测试设计的基本程序段

下面的例子给出了常用的测试设计的模块。

2.1产生时钟信号

使用系统时钟控制逻辑工作的时序逻辑设计必然需要一个时钟。重复的时钟信号可以很容易地用VHDL或Verilog代码实现。以下是VHDL和Verilog编写的时钟产生器的示例。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

2.2准备激励信号

要得到测试验证的结果,就要为测试的设计模块提供激励。必要时,需要在测试设计中使用并行的激励程序块。通常采用两种方式给出激励:绝对时间激励和相对时间激励。第一种方式,仿真激励的数值是以对应于仿真时刻零点的绝对时间的形式列出。相比而言,相对时间激励首先给出激励的初始值,然后等待事件触发然后赋予激励新的数值。根据设计者的需要,两种方式可以在测试设计中组合使用。

表2和表3用VHDL和Verilog分别给出了一段绝对时间激励和相对时间激励的代码。

表2绝对时间激励

表3相对时间激励

VHDL进程块和Verilog初始块与设计文件中的其他的进程块或初始块并行执行。然而,在每一个进程块或初始块中,事件是按照代码书写的顺序依次执行的。就是说各个模块是在仿

真的零时刻同时启动的。我们应该将复杂的激励序列分解为多个模块来编写以保障代码的可读性和易于维护性。

2.2显示结果

在Verilog中我们使用关键字$display 和 $monitor 显示结果。虽然VHDL没有等效的显示指令,它提供了std_textio标准文本输入输出程序包。它允许文件的i/o重定向到显示终端窗口(这个技术的一个示例,参看后面的自检测测试设计)

下面是Verilog示例,它将在终端屏幕上显示数值。

// pipes the ASCII results to the terminal or text editor

initial begin

$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

关键字 $display在终端屏幕上输出用(“…”)括起的附加的文字。关键字$monitor操作不同,它的输出是由事件驱动的。例子中的变量$realtime(用户用于赋值以当前的仿真时间)用于触发信号列表中各个值的显示。信号表由变量 $realtime开始,随后是需要显示的信号(clock, reset, load等)。起头的一连串“%”关键字是一些格式标识符,用来控制信号列表中的数值以何种格式显示。格式列表是有位置关系的――每个格式标识符对应于信号列表中的相应位置的信号。比如%t标识符规定了$realtime的值以时间格式给出,而第一个%b标识符规定clock 信号的值以二进制形式给出。Verilog还提供其它的一些格式标识符,比如%h说明十六进制,%d说明十进制,%c说明显示为八进制。(参见Verilog参考手册以了解完整的关键字及格式标识符)

格式化的输出结果如图2所示。

图2仿真结果返回结果

3简单的测试设计

简单的测试设计就是实例化用户设计,然后向它提供激励。测试的输出可以图形化地显示在仿真器的波形窗口中或者以文本的方式显示在用户终端或者是管道重定向输出到文本中。以下是一个简单的用Verilog实现的设计,它实现了一个移位寄存器的功能。

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

以下是实例化了移位寄存器的一个简单的测试设计

Verilog示例

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

以上的测试中实例化了设计,设置时钟,然后提供激励信号。所有的进程块在仿真时间零点并行启动。井号(#)表明下一个激励作用前的延迟。$stop命令使仿真器停止测试的仿真(所有测试设计中都应该包含一个停止命令)。最后,$monitor语句返回ASCII格式的结果到屏幕或者管道输出到一个文本编辑器。接下来的是一个VHDL描述的的测试设计,它实例化设计并提供激励到如前用Verilog描述的移位寄存器的设计中。

VHDL示例

library IEEE;

use IEEE.std_logic_1164.all;

entity testbench is

end entity testbench;

architecture test_reg of testbench is

component shift_reg is

port ( clock : in std_logic;

reset : in std_logic;

load : in std_logic;

sel : in std_logic_vector(1 downto 0);

data : in std_logic_vector(4 downto 0);

shiftreg : out std_logic_vector(4 downto 0));

end component;

signal clock, reset, load: std_logic;

signal shiftreg, data: std_logic_vector(4 downto 0);

signal sel: std_logic_vector(1 downto 0);

constant ClockPeriod : TIME := 50 ns;

begin

UUT : shift_reg port map (clock => clock, reset => reset,

load => load, data => data,

shiftreg => shiftreg);

process begin

clock <= not clock after (ClockPeriod / 2);

end process;

process begin

reset <= ’1’;

data <= "00000";

load <= ’0’;

set <= "00";

wait for 200 ns;

reset <= ’0’;

load <= ’1’;

wait for 200 ns;

data <= "00001";

wait for 100 ns;

sel <= "01";

load <= ’0’;

wait for 200 ns;

sel <= "10";

wait for 1000 ns;

end process;

end architecture test_reg;

上述VHDL测试设计与之前提到的Verilog测试设计在功能上是相似的,除了将结果输出到终端上的命令以外。在VHDL中,std_textio程序包被用于在终端上显示信息,它将在下一节说明。

4自动验证

我们推荐实现自动化的测试结果的验证,特别对于较大的设计。自动化减少了检查设计正确性所需的时间,并将人为错误减到最少。

一般有以下几种常用的自动测试验证的方法:

1、数据库比较。首先,要创建一个包含预期输出(一个“黄金向量”文件)的数据库文件。然后,捕获仿真输出并与黄金向量文件中参考的向量比较(在UNIX中的diff 工具可以用来比较ASCII格式的数据库文件)。然而,因为缺少从输出到输入文件的指针,所以这种方法的一个缺点在于难以跟踪得到导致一个错误输出的错误源。

2、波形比较。波形比较可以自动或是手动的运行。自动的方法使用一个测试比较器来比较黄金波形与测试输出的波形。Xilinx的HDL Bencher工具可以用于执行一个自动波形比较(关于HDL Bencher的相关信息,请参看:

https://www.wendangku.net/doc/cb1068473.html,/products/software/statecad/index.htm

3、自检测测试。一个自检测测试实时检查预期的结果与实际的结果,而不是在仿真结束时。因为可以在一个测试设计中内建有用的错误跟踪信息用来说明哪些地方设计有误,从而大大缩短了调试时间。更多的关于自检测测试的信息会在下一节说明。

5自检测测试

自检测测试设计将一系列的预期向量放置在测试文件中。这些向量间隔地与实际仿真结果比较。如果实际结果与预期结果匹配,仿真成功。如果结果不匹配,测试报告两者的差异。为同步设计实现自检测测试显得更简单一些,因为预期结果与真实结果相比较可以在一个时钟边沿或任何一个整数倍的时钟周期后进行。比较的方法还与设计本身的特性相关。比如一个用于内存I/O的测试应该检查应该在每一次往内存中写入或从内存中读出新的数据时进行。类似的,如果一个设计用了相当数量的组合逻辑,在预期结果描述时就必须考虑组合逻辑的时延。

在自检测测试中,预期输出与实际输出以一个特定的运行时间间隔进行比较以便提供自动的错误检查。这个技术在中小型的设计中效果很好。但是,因为当设计复杂后,可能的输出组合数目成指数倍的增长,为一个大型设计编写一个自检测测试是十分困难和费时的。

以下是一个用Verilog和VHDL描述的自检测测试的简单的例子:

Verilog例子

下面的设计实例中,预期的结果被详细罗列。随后的代码,进行预期与实际的结果比较,比较结果被返回终端。如果没有不同,显示“end of good simulation”的消息。如果不匹配,错误报告中会给出不匹配的期望值和实际值。

‘timescale 1 ns / 1 ps

module test_sc;

reg tbreset, tbstrtstop;

reg tbclk;

wire [6:0] onesout, tensout;

wire [9:0] tbtenthsout;

parameter cycles = 25;

reg [9:0] Data_in_t [0:cycles];

// /////////////////////////////

// Instantiation of the Design

// /////////////////////////////

stopwatch UUT (.CLK (tbclk), .RESET (tbreset), .STRTSTOP (tbstrtstop), .ONESOUT (onesout), .TENSOUT (tensout), .TENTHSOUT (tbtenthsout)); wire [4:0] tbonesout, tbtensout;

assign tbtensout = led2hex(tensout);

assign tbonesout = led2hex(onesout);

///////////////////////////////////////////////////////////////

//EXPECTED RESULTS

///////////////////////////////////////////////////////////////

initial begin

Data_in_t[1] =10’b1111111110;

Data_in_t[2] =10’b1111111101;

Data_in_t[3] =10’b1111111011;

Data_in_t[4] =10’b1111110111;

Data_in_t[5] =10’b1111101111;

Data_in_t[6] =10’b1111011111;

Data_in_t[7] =10’b1110111111;

Data_in_t[8] =10’b1101111111;

Data_in_t[9] =10’b1011111111;

Data_in_t[10]=10’b0111111111;

Data_in_t[11]=10’b1111111110;

Data_in_t[12]=10’b1111111110;

Data_in_t[13]=10’b1111111101;

Data_in_t[14]=10’b1111111011;

Data_in_t[15]=10’b1111110111;

Data_in_t[16]=10’b1111101111;

Data_in_t[17]=10’b1111011111;

Data_in_t[18]=10’b1110111111;

Data_in_t[19]=10’b1101111111;

Data_in_t[20]=10’b1011111111;

Data_in_t[21]=10’b0111111111;

Data_in_t[22]=10’b1111111110;

Data_in_t[23]=10’b1111111110;

Data_in_t[24]=10’b1111111101;

Data_in_t[25]=10’b1111111011;

end

reg GSR;

assign glbl.GSR = GSR;

initial begin

GSR = 1;

// ///////////////////////////////

// Wait till Global Reset Finished

// ///////////////////////////////

#100 GSR = 0;

end

// ////////////////

// Create the clock

// ////////////////

initial begin

tbclk = 0;

// Wait till Global Reset Finished, then cycle clock #100 forever #60 tbclk = ~tbclk;

end

initial begin

// //////////////////////////

// Initialize All Input Ports

// //////////////////////////

tbreset = 1;

tbstrtstop = 1;

// /////////////////////

// Apply Design Stimulus

// /////////////////////

#240 tbreset = 0;

tbstrtstop = 0;

#5000 tbstrtstop = 1;

#8125 tbstrtstop = 0;

#500 tbstrtstop = 1;

#875 tbreset = 1;

#375 tbreset = 0;

#700 tbstrtstop = 0;

#550 tbstrtstop = 1;

// /////////////////////////////////////////////////////

// simulation must be halted inside an initial statement

// /////////////////////////////////////////////////////

// #100000 $stop;

end

integer i,errors;

///////////////////////////////////////////////////////////////////

///////////////

// Block below compares the expected vs. actual results

// at every negative clock edge.

///////////////////////////////////////////////////////////////////

///////////////

always @ (posedge tbclk)

begin

if (tbstrtstop)

begin

i = 0;

errors = 0;

end

else

begin

for (i = 1; i <= cycles; i = i + 1)

begin

@(negedge tbclk)

// check result at negedge

$display("Time%d ns; TBSTRTSTOP=%b; Reset=%h; Expected

TenthsOut=%b; Actual TenthsOut=%b", $stime, tbstrtstop, tbreset,

Data_in_t[i], tbtenthsout);

if ( tbtenthsout !== Data_in_t[i] )

begin

$display(" ------ERROR. A mismatch has occurred-----");

errors = errors + 1;

end

end

if (errors == 0)

$display("Simulation finished Successfully.");

else if (errors > 1)

$display("%0d ERROR! See log above for details.",errors);

else

$display("ERROR! See log above for details.");

#100 $stop;

end

end

endmodule

这种简单的自检测测试设计移植到任何测试中――当然,预期的输出值以及信号名在重用时需要更改。如果不需要每个时钟沿都做检查,可以根据需要修改for-loop块。

如果仿真成功,下图的信息就会在终端上显示:

图3 Verilog示例验证

VHDL 示例

在VHDL中,预期的结果放在一个向量文件中。VHDL 中的textio程序包用于从向量文件中读取数据,以及显示错误信息。这个测试用VHDL描述了秒表设计。

LIBRARY IEEE;

USE IEEE.std_logic_1164.all;

LIBRARY ieee;

USE IEEE.STD_LOGIC_TEXTIO.ALL;

USE STD.TEXTIO.ALL;

ENTITY testbench IS

END testbench;

ARCHITECTURE testbench_arch OF testbench IS COMPONENT stopwatch

PORT (

CLK : in STD_LOGIC;

RESET : in STD_LOGIC;

STRTSTOP : in STD_LOGIC;

TENTHSOUT : out STD_LOGIC_VECTOR (9 DOWNTO 0);

Figure 3: Verilog Example Verification

ONESOUT : out STD_LOGIC_VECTOR (6 DOWNTO 0);

TENSOUT : out STD_LOGIC_VECTOR (6 DOWNTO 0)

);

END COMPONENT;

SIGNAL CLK : STD_LOGIC;

SIGNAL RESET : STD_LOGIC;

SIGNAL STRTSTOP : STD_LOGIC;

SIGNAL TENTHSOUT : STD_LOGIC_VECTOR (9 DOWNTO 0); SIGNAL ONESOUT : STD_LOGIC_VECTOR (6 DOWNTO 0); SIGNAL TENSOUT : STD_LOGIC_VECTOR (6 DOWNTO 0); constant ClockPeriod : Time := 60 ns;

FILE RESULTS: TEXT IS OUT "results.txt";

signal i: std_logic;

BEGIN

UUT : stopwatch

PORT MAP (

CLK => CLK,

RESET => RESET,

STRTSTOP => STRTSTOP,

TENTHSOUT => TENTHSOUT,

ONESOUT => ONESOUT,

TENSOUT => TENSOUT

);

stimulus: PROCESS

begin

reset <= ’1’;

strtstop <= ’1’;

wait for 240 ns;

reset <= ’0’;

strtstop <= ’0’;

wait for 5000 ns;

strtstop <= ’1’;

wait for 8125 ns;

strtstop <= ’0’;

wait for 500 ns;

strtstop <= ’1’;

wait for 875 ns;

reset <= ’1’;

wait for 375 ns;

reset <= ’0’;

wait for 700 ns;

strtstop <= ’0’;

wait for 550 ns;

strtstop <= ’1’;

end process stimulus;

clock: process

begin

clk <= ’1’;

wait for 100 ns;

loop

wait for (ClockPeriod / 2);

CLK <= not CLK;

end loop;

end process clock;

check_results : process

variable tmptenthsout: std_logic_vector(9 downto 0); variable l: line;

variable good_val, good_number, errordet: boolean; variable r : real;

variable vector_time: time;

variable space: character;

file vector_file: text is in "values.txt";

begin

while not endfile(vector_file) loop

readline(vector_file, l);

read(l, r, good => good_number);

next when not good_number;

vector_time := r * 1 ns;

if (now < vector_time) then

wait for vector_time - now;

end if;

read(l, space);

read(l, tmptenthsout, good_val);

assert good_val REPORT "bad tenthsoutvalue";

wait for 10 ns;

if (tmptenthsout /= tenthsout) then

assert errordet REPORT "vector mismatch";

end if;

end loop;

wait;

end process check_results;

end testbench_arch;

library XilinxCoreLib;

CONFIGURATION stopwatch_cfg OF testbench IS

FOR testbench_arch

FOR ALL : stopwatch use configuration work.cfg_tenths;

END FOR;

END FOR;

END stopwatch_cfg;

以下向量文件用于上述的测试。它包含了预期的仿真值。

-- Vector file containing expected results

0 1111111110

340 1111111110

400 1111111101

460 1111111011

520 1111110111

580 1111101111

640 1111011111

700 1110111111

760 1101111111

820 1011111111

880 0111111111

940 1111111110

1000 1111111110

1060 1111111101

1120 1111111011

1180 1111110111

1240 1111101111

1300 1111011111

1360 1110111111

1420 1101111111

1480 1011111111

1540 0111111111

1600 1111111110

1660 1111111110

1720 1111111101

1780 1111111011

如果检测到错误,它会显示在一个仿真提示器中。图4展示了MTI脚本窗口中的错误信息:

图4 仿真命令错误报告

6编写测试的准则

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

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

虽然通用仿真工具遵从HDL工业标准,但标准并没有说明一些关于仿真的重要问题。

不同的仿真器有不同的功能,兼容性,和性能特性,从而会得到不同的仿真结果。

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

仿真器采用基于事件或者基于周期的方法运行仿真。基于事件的仿真器,当输入,信号,或逻辑门改变数值时触发仿真事件。在一个基于事件的仿真器中,一个延时值可以关联到逻辑门或是连线上来得到最佳的时序仿真。基于周期的仿真器面向同步设计。他们基于时钟周期来优化组合逻辑和分析结果。这使得基于周期的仿真器比基于事件的仿真器更快更有效。然而,由于基于周期的仿真器不允许详细的时序说明,所以并不如基于事件的仿真器准确。关于两者差异的更多的信息请看“Digital Logic Simulation: Event-Driven, Cycle-Based, and Home-Brewed”,参见:

https://www.wendangku.net/doc/cb1068473.html,/ednmag/reg/1996/070496/14df4.htm

- 事件调度

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

·避免使用无限循环

当基于事件的仿真器中添加了一个事件,cpu和内存的使用就增加,仿真过程就会变慢。

除非测试中特别的需要,否则无限循环不应该用来设计激励。通常,时钟用无限循环定义(如Verilog中的'forever'循环),但是其他信号事件不能。

·分解激励到逻辑模块

在测试中,所有初始块(Verilog)或进程块(VHDL)并行地运行。如果不相关的激励被分离到各自独立的块中,测试激励序列将变得更简洁实现并易于检查。因为每个并行的块均相对于仿真零点运行,为分离的块提供激励变得更容易。分离的激励块使得测试的建立,维护和升级更加简单(参看后面的高级测试技术,以及该技术的示例)

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

大型设计的测试可能包含10万以上的事件或繁多的信号。显示大量的仿真数据极大地降低了仿真的速度。最好只是每整数时钟周期采样相关的信号以确保足够的仿真速度。

7 Xilinx仿真流程要决

7.1配置语句(VHDL)

一个VHDL配置语句允许一个设计链接到一个面向综合或者仿真的特点结构。在Xilinx CORE Generator VHDL功能仿真流程中,配置语句用于调用一个设计的核心仿真模块。如果核心仿真模块没有被调用到设计中,仿真将不能正确运行。关于配置语句使用的例子,参看前面的用VHDL描述的自检测测试程序代码。关于Xilinx CORE Generator中使用配置语句的详细信息可以在Modelsim VHDL仿真入门指南中找到,参见:

https://www.wendangku.net/doc/cb1068473.html,/support/techsup/tutorials/tutorials31i.htm#Modelsim

7.2为仿真初始化块RAM

Xilinx Virtex TM块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;

8高级测试技术

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

在创建一个大的测试设计时,应该分割激励以使代码清晰和易于修改。任务(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

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

……

将激励分解为一系列独立的任务,使得添加激励更加容易,也使得代码的可读性更好。

8.2在仿真时控制双向信号

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

VHDL示例

以下是一个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;

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

Verilog示例

以下是如上的双向总线设计的Verilog描述示例。

module bidir_infer (DATA, READ_WRITE);

相关文档