第5章 CHAPTER 5 仿真测试文件 测试文件用来在仿真软件环境下测试设计电路的功能是否符合设计要求。编写测试文件最主要的工作就是设计激励信号,也就是需要测试电路模块输入端子的信号,通过给被测试电路输入端子特定的激励信号,观察被测电路的输出是否满足设计要求,以此来辅助和验证电路设计。这里介绍ModelSim仿真软件支持下的测试文件的设计。 5.1测试文件结构 由3.2.3节的介绍可知,Quartus Prime软件可以辅助进行测试程序的编写。在电路设计综合完成后,通过菜单命令Processing→Start→Start testbench template writer,可以生成一个与项目顶层文件同名的testbench测试文件模板。 可以看到,此测试电路模块没有外部端子,模块名为myexam_vlg_tst,内部包含了3个主要部分: 信号定义、实例化、施加激励。施加激励通过initial模块和always模块实现,设计者需要根据测试需求,设计需要的激励信号,其中initial模块用于产生执行一次的激励信号,如复位信号、非周期性输入信号等; always模块用于产生由敏感事件列表触发的信号,如时钟信号、周期性输入信号等。 `timescale 1 ps/ 1 ps module <测试电路模块名>( );//测试电路没有外部端子 constant  //常量说明 reg   //通用寄存器信号说明 wire   //net型变量说明 reg   //被测电路输入信号说明,输入信号数据类型定义为reg <被测电路模块名> <被测电路模块例化名>(   //被测电路模块实例化 //端口映射表 .被测模块端口(测试模块信号), .被测模块端口(测试模块信号), … .被测模块端口(测试模块信号) ); //一次性激励信号产生模块initial initial begin //只需要执行一次的程序代码 $display("Running testbench"); end //周期性激励信号产生模块always always @(敏感信号列表)   //敏感信号列表是否存在可选 begin //需要在敏感信号激励下执行的程序代码 end assign   // 功能描述语句 endmodule 5.2`timescale指令 该指令用于指定测试程序仿真时的时间单位和时间精度,时间单位是模拟时间和延迟值等时间值的度量单位。如果测试程序中没有该指令,则ModelSim仿真器采用默认的时间单位1ps。可以通过系统任务$printtimescale显示测试程序当前使用的时间单位和时间精度。 例5.1时间单位仿真验证 `timescale 10 ns / 1 ns module test; reg set; initial begin #1 set = 0;//经过1个时间单位,即10ns,set值赋为0 #2 set = 1;   //经过2个时间单位,即20ns,set值在30ns处赋为0 $printtimescale; end endmodule 此段代码的执行结果如图51所示。 图51时间单位仿真验证 同时在ModelSim 的Transcript窗口中显示如下信息: # Time scale of (test) is 10ns / 1ns 5.3initial语句 initial语句面向模拟仿真过程,不能被综合。initial语句通常用来对信号进行初始化,或者产生一次性变化的信号。initial语句格式如下: initial begin 语句1; 语句2; … end 例如, initial begin data=0; for(addr=0;addr"); (2) <文件句柄>=$fopen("<文件名>", ); type为打开文件的类型,如表52所示。type类型可以省略,默认以“w”的方式打开文件。注意,以此方式打开文件,若文件原来并不存在,则$fopen会先创建此文件再打开; 若文件存在,则文件会被清空。 若文件名中有路径,则文件路径间隔为“/”而不是“\”。例如, h1=$fopen("D:/example/file1.txt"); 表52文件打开类型 参数描述 "r"或"rb"以读的方式打开文件 "w"或"wb"将文件清空或创建文件,并以写的方式打开文件 "a"或"ab"以追加写的方式打开文件; 若文件不存在,则先创建文件 "r+"、"r+b"或"rb+"打开以更新(读/写)文件 "w+"、"w+b"或"wb+"将文件清空或创建文件,以进行更新(读/写) "a+"、"a+b"或"ab+"打开或创建一个以追加写方式的文件 4. $fdisplay $fdisplay将数据写入指定的文件中,使用格式如下: $fdisplay(<文件句柄>,"写入内容"); 例如, integer h1; h1=$fopen("file1.txt");//取一个文件的句柄 $fdisplay(h1,"This is a test.");   //将数据写入文件 如果写入内容为双引号括起来的信息,则作为字符串写入,否则作为数据写入。 除$fdisplay任务外,还有如下3个任务: $fdisplayb、$fdisplayh、$fdisplayo,分别表示将整型数以二进制、十六进制和八进制的方式写入文件,且数据长度为32bit,对整型数以外的数据无效。例如: $fdisplayb(h1,10); $fdisplayb(h1,10.2); 写入文件中的内容显示如下: 00000000000000000000000000001010 10.2 5. $fwrite $fwrite将数据写入指定的文件中,使用格式如下: $fwrite(<文件句柄>,"写入数据"); $fwrite和$fdisplay的区别在于$fdisplay写完就会自动换行,$fwrite不会换行。 6. $fclose $fclose的功能是关闭文件,使用格式如下: $fclose(<文件句柄>); 7. $readmemb $readmemb用于读取二进制数据文件内容到存储器,此系统任务常用来初始化一个内存空间。使用格式有如下3种: (1) $readmemb("<数据文件名>",<存储器名>); (2) $readmemb("<数据文件名>",<存储器名>,<起始地址>); (3) $readmemb("<数据文件名>",<存储器名>,<起始地址>,<终止地址>); 例5.4假设文件file1.dat的内容如下: 01010000 1000_1010 1010 zzxx11xx 若要将此文件的内容读至存储器memory中,程序代码如下: `timescale 10ns/1ns module test; reg[7:0] memory[0:3];//申请4个8bit空间的存储单元 integer n; initial begin $readmemb("file1.dat",memory);   //读取file1.dat中的内容到memory for(n=0;n<=3;n=n+1)   //显示读取到的4个存储单元的内容 $display("%b",memory[n]); end endmodule 程序执行后,Transcript窗口的显示内容如下: # 01010000 # 10001010 # 00001010 # zzxx11xx 可见,$readmemb是以分行符或空格为间隔符读取文件数据,且忽略下画线,并以地址从低到高的顺序存储至存储器中的。若读取的数据位数少于存储单元的长度,则高位自动补零。 8. $readmemh $readmemh用于读取十六进制数据文件内容到存储器,使用格式有如下3种: (1) $readmemh("<数据文件名>",<存储器名>); (2) $readmemh("<数据文件名>",<存储器名>,<起始地址>); (3) $readmemh("<数据文件名>",<存储器名>,<起始地址>,<终止地址>); 9. $time、$realtime $time用于返回整数型的仿真时间,$realtime用于返回实数型的仿真时间。具体返回值取决于`timescale的设置,如下例所示。 例5.5仿真时间获取 `timescale 10ns/1ns module test; initial begin #10.2 ; $display("time_end=%t",$realtime); $display("time_end=%t",$time); end endmodule 程序执行后,Transcript窗口的显示内容如下: # time_end=102 # time_end=100 本例中,时间单位为10ns,时间精度为1ns,当延迟数为10.2时,$realtime返回的值为: 10.2×10ns,$time返回值的获得则要将10.2转换为整数10,再乘以时间单位,即: 10×10ns。 10. $random $random函数用于产生随机整数,使用格式有如下两种: (1) $random%b//产生 (-b+1)~(b-1)范围内的随机数 (2) {$random}%b//产生0~(b-1)范围内的随机数 其中,b为十进制整数,例如, reg [7:0] rand; rand = {$random} % 60; 可以获得8bit宽的随机数,且数值在0~59范围内。