来源:http://blog.ednchina.com/jlx_cuc/1993759/Message.aspx
大多数硬件设计人员对verilog的testbench比较熟悉,那是因为verilog被设计出来的目的就是为了用于测试使用,也正是因为这样verilog的语法规则才被设计得更像C语言,而verilog发展到后来却因为它更接近C语言的语法规则,设计起来更加方便,不像VHDL那也死板严密,所以verilog又渐渐受到硬件设计者们的青睐。但其实VHDL在最开始也是具有测试能力的,而且它的语法严密,但我们同样可以用它来编写我们的测试文件。
下面以一个8bit计数器为例子给出个简单的testbench模板及注释:
library
ieee;
use
ieee.std_logic_1164.
all
;
use
ieee.std_logic_arith.
all
;
use
ieee.std_logic_unsigned.
all
;
entity
tb_CNT
is
end
tb_CNT ;
architecture
testbench
of
tb_CNT
is
--
Component Declaration
for
the Unit Under Test(UUT)
component
MY_CNT
port
(
CLK,RESET:
in
std_logic
;
D_IN:
in
std_logic_vector
(
7
downto
0
);
LOAD:
in
std_logic
;
CE :
in
std_logic
;
UPDW:
in
std_logic
;
Q_OUT:
out
std_logic_vector
(
7
downto
0
)
);
end
component
;
--
Inputs
signal
CLK:
std_logic
:
=
'
0
'
;
signal
RESET:
std_logic
:
=
'
0
'
;
signal
D_IN:
std_logic_vector
(
7
downto
0
):
=
(
others
=>
'
0
'
);
signal
LOAD:
std_logic
:
=
'
0
'
;
signal
CE:
std_logic
:
=
'
0
'
;
signal
UPDW:
std_logic
:
=
'
0
'
;
--
Outputs
signal
Q_OUT:
std_logic_vector
(
7
downto
0
);
constant
CLK_period:
TIME
:
=
1ns;
begin
--
例化计数器模块
UUT: MY_CNT
port
map
(
CLK
=>
CLK,
RESET
=>
RESET,
D_IN
=>
D_IN,
LOAD
=>
LOAD,
CE
=>
CE,
UPDW
=>
UPDW,
Q_OUT
=>
Q_OUT
);
--
时钟实现模块
--
上面定义了CLK period时间常量来表示时钟周期
--
以下通过一个process来实现时钟
CLK_process:
process
begin
CLK
<=
'
0
'
;
wait
for
CLK_period
/
2
;
CLK
<=
'
1
'
;
wait
for
CLK_period
/
2
;
end
process
;
--
其它信号量的控制
--
时序逻辑的仿真的时间单位都为CLK周期来实现的
--
所以将时钟周期定义为常量是个很方便的做法
stim_proc:
process
begin
--
hold reset state
for
100ms.
wait
for
100ms;
wait
for
CLK_period
*
10
;
--
insert stimulus here.
end
process
;
end
testbench;
通过编写testbench来仿真和通过拖波形来仿真,最大的好处就是,当测试数据无比庞大时,可以简易得通过testbench中的算法来实现,而另一个更为重要的方面就是,可以通过testbench对数据文件进行读写操作,从而简化我们的仿真工作。
首先介绍下时间控制语句——wait:(其实wait语句是通过控制仿真的两种状态——执行和挂起,来控制时间的)
1.wait——无线等待;语法【wait;】,类似于Verilog中的¥Stop
2.wait on——敏感信号量变化;语法【wait on 信号;】,表示当信号发生变化的时候,仿真开始继续执行,从而结束挂起状态
3.wait until——条件满足;语法【wait until 表达式】,表达式为一个布尔表达式,表示当表达式为“真”时,仿真继续执行,结束挂起状态
4.wait for——时间控制;语法【wait for 时间表达式】,例:【wait for 30ns;】
VHDL也提供了文件I/O的操作,以下简单介绍在我们大部分情况下如何通过VHDL来进行文件操作。
--
File
process
file_process:
process
file
file_out:
text
is
out
"
data_out.txt
"
;
file
file_in :
text
;
variable
fstatus: FILE_OPEN_STATUS;
--
定义文件状态指示变量,一般有OPEN OK, STATUS
ERROR
, NAME
ERROR
, MODE
ERROR
四种状态;
variable
count:
integer
:
=
5
;
--
定义integer 型写入数据;
variable
stringdata:
string
(
5
downto
1
):
=
"
whwhn
"
;
--
定义string 型写入数据;
variable
vectordata:
bit_vector
(
5
downto
0
):
=
"
001000
"
;
--
定义bit_vector 型写入数据;
variable
value:
std_logic_vector
(
3
downto
0
):
=
"
1111
"
;
--
定义std_logic_vector型的写入数据;
variable
stddata:
std_logic
;
variable
BUFO:
LINE
;
begin
--
file_open
(fstatus,file_out,
"
data_out.txt
"
,
write_mode
);
file_open
(fstatus,file_in,
"
data_in.txt
"
,
read_mode
);
file类型:文件句柄,用于定义文件。语法1【file 文件变量名:text is 读取或者写入类型 “文件名”;】text——文件类型为文本类型,读取类型为in,写入类型为out;语法2【file 文件变量名:text;】只是定义了文件变量名,并没有给赋予初值。
用第二种方式定义文件变量则需要用到函数file_open();语法【file_open(文件状态指示,文件变量,“文件名”,读写状态);】如上图实例。
LINE类型:如下图,为std库中TEXTIO文件中的定义
--
LINE类型:为std库中TEXTIO文件中的定义
for
i
in
0
to
29
loop
readline
(file_in,BUF0);
read
(BUF0,stddata);
LOAD
<=
stddata;
wait
for
CLK_period;
end
loop
;
LINE 为存取类型的变量,它表示该变量是指向字符串的指针,它是TEXTIO 中所有操作的基本单元。读文件时,先按行(LINE)读出一行数据,再对LINE 操作来读取各种数据类型的数据;写文件时, 先将各种的数据类型组合成LINE,再将LINE 写入文件。在用户使用时, 必须注意只有变量才可以是存取类型, 而信号则不能是存取类型。
TEXTIO还定义了一些基本的文件操作过程:
【READLINE(文件变量,行变量);】从指定的文件中读取一行。
【READ(行变量,数据变量);】从一行中读取一个数据。
【WRITELINE(文件变量,行变量);】将行变量中数据写入到指定文件。
【WRITE(行变量,数据变量);】将数据写入到一行中。
【WRITE(行变量,数据变量,起始位置,字符数);】比上个过程多了起始位置和字符数的指定。
---
为循环从数据文件中读取出数据,赋值给信号量
write
(file_out,
string
'
("the first parameter is="));
write
(BUF0,count);
writeline
(file_out,BUF0);
wait
for
20
ns;
write
(file_out,
string
'
("the second parameter is="));
write
(BUF0,value);
writeline
(file_out,BUF0);
wait
for
20
ns;
write
(file_out,
string
'
("the third parameter is="));
write
(BUF0,vectordata);
writeline
(file_out,BUF0);
wait
for
20
ns;
write
(file_out,
string
'
("the forth parameter is="));
write
(BUF0,stringdata);
writeline
(file_out,BUF0);
write
(BUF0,
string
'
("end of file"));
writeline
(file_out,BUF0);
wait
for
10
ns;
wait
for
2000
ns;
file_close
(file_out);
file_close
(file_in);
end
process
;
上图事例为循环从数据文件中读取出数据,赋值给信号量。
--
为了说明行变量与各个变量之间的转换,行变量可与任何变量类型进行转换,
--
并且原样输出到指定文件中。
package
TEXTIO
is
--
Type definitions
for
text
I
/
O;
type
LINE
is
access
STRING
;
--
A
LINE
is
a pointer
to
a
STRING
value.
type
TEXT
is
file
of
STRING
;
--
A
file
of
variable
-
length ASCII records.
type
SIDE
is
(
RIGHT
,
LEFT
);
--
For justifying
output
data within fields.
subtype
WIDTH
is
NATURAL;
--
For specifying widths
of
output
fields.
上图事例为了说明行变量与各个变量之间的转换,行变量可与任何变量类型进行转换,并且原样输出到指定文件中。
另外再推荐个语句:ASSERT——断言语句。它和VC中的_TRACE语句一样,在调试程序中非常有用非常方便。
assert是一个调试仿真时的专用语法,他可以判断一个boolean变量,如果该变量为假就输出一个用户指定的信息到终端(控制台),用户可以附带输出信息的严格等级,从低到高依次是:note,warning,error,failure,可以让用户区分信息的类型。同样assert语句也是不能被综合的。
assert可以是同步语句(在process外),此时assert后面的任何变量变化都会引起assert语句判断一次。
assert还可以是顺序语句,此时assert存在于process中。