在编写testbench时,我们会用到一些VHDL语言,这些语言是我们不常用的,甚至以前没有见过的,但是在testbench中他们却是被经常用到,而且对我们的仿真编写非常有用。今天我们就来简单讲一下常用的几个VHDL句法。
一、文件I/O
上节讲了,在testbench常将数据写入文件,或者从文件读入数据等,这都需要用到VHDL语言中与文件打交道的语言,即是常说的file I/O. 这里强调两点:文件I/O语言是不能被综合的,文件I/O与FPGA的I/Opin是两回事。
根据存在文件中数据的不同,文件有多种类型,存入的数据可以是integer ,string,std_logic_vector 等等,但是定义时有区别,后面会说到。
对文件的操作有,定义文件,打开文件,从文件中读取数据,写入数据到文件等。
一、定义文件。
文件有两个大的类型,integer:文件中的数据是以二进制存取的,不能被人识别,只有integer型的数据能够存入这类文件。string:文件是以可以读取的ASCII码,可以被人识别,integer ,bit_vector(x downto x),string(x downto 1),std_logic_vector(x downto 0) ,bit等都可以被存入此类文件。
定义语法 file filein:text; type integerfile is file of integer; file filein:integerfile;
二、打开文件。
86版的VHDL在定义文件时即将文件隐式的打开了,这里我们讲93版的文件打开语句。
在上一步定义了文件句柄后就可以在程序中打开指定文件,同时指定打开模式。
file_open(fstatus,file file_handle:file_type, filename:string,openmode)
fstatus指示当前文件状态,但是在使用前首先得定义一名variable fstatus:FILE_OPEN_STATUS;,一般有OPEN_OK,STATUS_ERROR,NAME_ERROR,MODE_ERROR四种状态。
file_handle即是上一步定义的文件类型的句柄filein.
filename是以双隐号括起的文件名,如"datain.txt";
openmode是指打开该文件的模式,文件打开有read_mode,write_mode,append_mode三种。
三、读写文件。
在上一步以什么模式打开,该步就可以对文件进行操作了,当上一步打开文件后,就可以用read(file_handle,value:type) write(file_handle,value:type)向文件读写数据了,但是这里特别注意,如果直接使用这两个语句,你只能向文件中写入指定类型的数据,如integer类型的只能写入integer型数据,text型只能写入string的数据,而不是像第二步说的那么多类型,要想写入其他类型必须遵守以下操作步骤!
(1)定义line型变量 variable buf:line,(2)将需要写入的数据写入line变量 write(buf,value)(这里的value就可以多种值),(3)从line变量把数据写入文件 writeline(file_handle,buf)。一定要按前后顺序来操作。
四、文件关闭。
在文件读写完毕后记得关闭文件file_close(file_handle),当然在文件操作中还有一个函数在反映是否读取到文件末尾ENDFILE(file_handle) 如果到达文件末尾将返回真(ture)否则返回假(false);
笔记:这里还要补充两种特殊的文件,input,output,也可以是“STD_INPUT","STD_OUTPUT"其实他们都是代表modelsim中的控制台,input和”STD_INPUT"就是从控制台输入数据,output "STD_OUTPUT"就是输出数据到控制台。后面两种格式要做为文件打开,前面一种可以直接作为文件句柄操作。如 file_open(fstatus,file_out,"STD_OUTPUT",write_mode); readline(output,buf);
同时有两个比较有用的写入语句 write(file_out,string'("hello")) write(file_out,bit_vector'(110"));
我们做了一个最完全的例子,将以上所有的操作包括进去,写了一个testbench,附录到下一节,同时将输出信息与被操作文件信息附录到图片,欢迎在下一节中获取源代码。
声明:以上操作均是基于VHDL1993版本!
TEXTIO 在VHDL 仿真与磁盘文件之间架起了桥梁,使用文本文件扩展VHDL 的仿真功能。本文介绍TEXTIO 程序包,以一个加法器实例说明TEXTIO 的使用方法,最后使用ModelSim 对设计进行仿真,并分析仿真结果。
在对VHDL 源程序进行仿真时,由于有的输入输出关系仅仅靠输入波形或编写testbench 中的信号输入是难以验证结果正确性的,例如,设计8 位加法器,如果将所有的输入都验证一遍,是非常麻烦的,因为要全面判断输出是否正确需要一个个的验证。此外,若用VHDL 设计一个处理器,需要读入指令,这时候采用文本文件非常必要。
TEXTIO 提供了VHDL 仿真时与磁盘文件的交互。在验证加法器时候,可以将所有输入保存在一个文本文件中,将其它软件计算出的结果保存在另外的文件中。在VHDL 仿真时,可以直接读取输入文件作为设计的输入参数,并自动将结果与事先保存的文件相比较,给出一定的信息来确定结果的正确与否。在对VHDL 编写的处理器调试时,可以将包括指令类型、源地址、目标地址在内的指令保存成文本文件,利用TEXTIO 来读取这些指令。同时,将结果及中间变量保存成文本文件,以事后判断是否正确及便于查找原因。
由于TEXTIO 的文本输入输出功能非常有限,一些公司提供了扩展其功能的程序包,例如std_developerskit库中的std_iopak 程序包。在本文中,仅仅对TEXTIO 程序包做简单的介绍及其简单的使用。
1 TEXTIO介绍
TEXTIO 是VHDL 标准库STD 中的一个程序包(Package)。在该包中定义了三个类型:LINE 类型、TEXT类型以及SIDE 类型。另外,还有一个子类型(subtype)WIDTH。此外,在该程序包中还定义了一些访问文件所必须的过程(Procedure)。
1.1 类型定义
(1)type LINE is access string
定义了LINE 为存取类型的变量,它表示该变量是指向字符串的指针,它是TEXTIO 中所有操作的基本单元。读文件时,先按行(LINE)读出一行数据,再对LINE 操作来读取各种数据类型的数据;写文件时,先将各种的数据类型组合成LINE,再将LINE 写入文件。在用户使用时,必须注意只有变量才可以是存取类型,而信号则不能是存取类型。例如,我们可以定义
variable DLine : LINE;
但不能定义成
signal DLine : LINE;
(2)type TEXT is file of string
定义了TEXT 为ASCII 文件类型。定义成为TEXT 类型的文件是长度可变的ASCII 文件。例如在TEXTIO 中定义了两个标准的文本文件。
file input : TEXT open read_mode is"STD_INPUT";
file output : TEXT open write_mode is"STD_OUTPUT";
定义好以后,就可以通过文件类型变量input 和output 来访问其对应的文件STD _ INPUT 和STD_OUTPUT。
需要注意的是VHDL'87 和VHDL'93 在使用文件方面有较大的差异,在编译时注意选中对应的标准。
(3)type SIDE is (right,left)
定义了SIDE 类型。表示定义了一个名为SIDE 的数据类型,其中只能有两种状态,即right 和left,分别表示将数据从左边还是右边写入行变量。该类型主要是在TEXTIO 程序包包含的过程中使用。
(4)subtype WIDTH is natural
定义WIDTH 为自然数的子类型。所谓子类型表示其取值范围是父类型范围的子集。
1.2 过程定义
TEXTIO 提供了基本的用于访问文本文件的过程。类似于C++,VHDL 提供了重载功能,即完成相近功能的不同过程可以有相同的过程名,但其参数列表不同,或参数类型不同或参数个数不同。
TEXTIO 提供的基本过程有:
procedure READLINE(文件变量;行变量);用于从指定文件读取一行数据到行变量中。
procedure WRITELINE(文件变量;行变量);用于向指定文件写入行变量所包含的数据。
procedure READ(行变量;数据类型);用于从行变量中读取相应数据类型的数据。
根据参数数据类型及参数个数的不同,有多种重载方式,TEXTIO 提供了bit、bit_vector 、BOOLEAN 、character、 integer、real、string、time数据类型的重载。同时,提供了返回过程是否正确执行的BOOLEAN 数据类型的重载。例如,读取整数的过程为
procedure READ(L:inout LINE; VALUE: out integer; GOOD: out BOOLEAN);
其中,GOOD 用于返回过程是否正确执行,若正确执行,则返回TRUE 。
procedure WRITE(行变量; 数据变量; 写入方式; 位宽);
该过程将数据写入行变量。其中写入方式表示写在行变量的左边还是右边,且其值只能为left 或right,位宽表示写入数据时占的位宽。例如:
write(OutLine,OutData,left,2);
表示将变量OutData 写入LINE 变量OutLine 的左边占2 个字节。
2 TEXTIO应用实例
下面以一个简单的8 位加法器来说明TEXTIO 的使用。输入数据为两个8 位的有符号数,输出为9 位的有符号数,以防止溢出。在编写加法器的描述文件时,首先要对两个数进行位的扩展,再进行加法运算。在编写测试文件时,要注意读入数据与得到结果之间相差一个时钟周期。因此,需要在读出的结果与计算的结果之间插入一个时钟周期的等待。
①生成输入及预定结果文件的C++程序。
我们可以使用VC++、Matlab 等高级软件工具编写生成输入和预定结果文件的程序。由于设定输入为8 位有符号数, 因此,其范围为[-127,127]。C++程序如下:
#include " iostream.h"
#include " stream.h"
void main(void){
int i,j;
ofstreamfsIn("d:\\yuproj\\modelsim\\NineBitAdder2\\TestData.dat");
ofstreamfsOut("d:\\yuproj\\modelsim\\NineBitAdder2\\Result.dat");
for(i=-127;i<128;i++) {
for(j=-127;j<128;j++) {
fsIn<<i<<""<j<<endl;
fsOut<<i+j<<endl;
}
}
fsIn.close();
fsOut.close();
}
在程序中,使用了C++类库iostream.h和fstream.h,主要使用了"<< " 的输出功能,读者可以参考相应的C++书 籍。运行该程序可以在规定的目录下生成TestData.dat 和Result.dat 两个文本格式的文件。注意,一行输入多个数据时,之间以空格隔开即可。
②编写的加法器描述如下。
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_signed.all;
entity Add2In is
port( D1 : in std_logic_vector(7 downto 0);
D2 : in std_logic_vector(7 downto 0);
Q : out std_logic_vector(8 downto 0);
Clk: instd_logic);
end Add2In;
architecture A_Add2In of Add2In is
begin
process(Clk)
begin
if Clk = ‘1’and Clk’event then
Q <= (D1(D1’left) & D1) + (D2(D2’eft) & D2);
endif;
endprocess;
end A_Add2In;
在进行加法前,首先进行位的扩展,再进行加法运算,在时钟的上升沿完成加法运算。
③编写测试文件。
在测试程序中,首先读出输入文件的一行内容,再从该行中提取出两个值输入加法器。从预定结果文件中提取出一个值,将加法器计算结果与该值比较,若两者不同则输出警告信息。也可以将输出写入一个文本文件,再比较两个文本文件的异同以获知出错的地方。这里要注意的是要使用TEXTIO 程序包,另外,测试文件实体内的端口为空,相当于一块独立的电路板。使用Component 在其中包含了上面定义的加法器,该独立的电路板所完成的功能是对设计的加法器进行测试。在程序中使用了assert 断言语句,要注意该语句后的表达式或变量为真时不执行后续的输出,为假时执行后续的输出。在程序中,还使用了类型转换函数CONV _ STD _LOGIC_VECTOR (),将整数转换为8 位的标准类型。另外,在程序中定义了变量Dlatch ,该变量的作用是将计算结果与预定结果的比较延迟一个时钟周期,周期地与预定结果比较。
3 仿真结果
在ModelSim 中可以将它们编译仿真,注意在编译时选择语法标准为VHDL'93 标准。编译后,可以输入如下的仿真Macro 来执行仿真。
vsim work.tb
该命令将执行仿真work 库中的tb,其中tb 为测试文件的实体名。
add wave -dec *
该命令将所有的信号以十进制添加到波形文件中。
run-all
该命令将使仿真一直执行下去。
输入以上各个命令,仿真结束后会在ModelSim 的提示符下出现如下信息:
# ** Fatal: (vsim-3551) TEXTIO : Read past end of file" TestData.dat".
# Time: 2601020ns Iteration: 0 Process:/tb/line__34File: D:/YuProj/ModelSim/NineBitAdder2/NineBitAdder2.vhd
# FatalerroratD:/YuProj/ModelSim/NineBitAdder2/NineBitAdder2.vhd line 41
表示在读完TestData.dat 后,因读空出现错误。其中没有出现程序中所设定的warning ,表示加法器的仿真结果与高级软件得到的预定结果相符合。我们可以改进程序,在其中加入ENDFILE()函数来判断是否读取到文件的结尾。仿真得到的波形如图1 所示。
若人为地更改Result.dat文件中第9行的-246为100,重新仿真,则出现如下信息:
# ** Warning: Two values are different
# Time: 420 ns Iteration: 0 Instance: /tb
# ** Fatal: (vsim-3551) TEXTIO : Read past end of file" TestData.dat".
# Time: 2601020ns Iteration: 0 Process:/tb/line__34File: D:/YuProj/ModelSim/NineBitAdder2/NineBitAdder2.vhd
# FatalerroratD:/YuProj/ModelSim/NineBitAdder2/NineBitAdder2.vhd line 41
#
双击对应的警告信息,可以看到仿真结果与Result.dat 中预定结果不一致的地方。仿真图形如图2所示。
结语
TEXTIO 使得VHDL 的仿真功能有了大的飞跃,在使用VHDL 描述处理器等模型及判断系统对不规则输入的响应时,起到了非常大的作用。本文通过简单的实例,说明了TEXTIO 库及其简单的应用流程。在当前嵌入式系统广泛应用的背景下,将更为需要TEXTIO 的应用。