(Testbench用法总结)1. Testbench中文本数据的存储读取操作对比
写时一时爽,调试火葬场;多么深刻的说明了在整体设计过程中,调试所占据的重要性,而仿真调试也是调试里面重要的一环。
为了巩固自己的Testbench编写技术,为了与大家交流调试经验,写出这篇文章与大家共同交流。
本文主要讲述Testbench中如何读取和存储文本文件的数据,涉及到基础的Verilog语法。
对一些数据处理的模块进行调试仿真,模块需要特定的数据输入,比如单一频率的正弦波;为了解决这个问题,我们可以用matlab,python等工具生成文本数据,然后使用Verilog将数据读取进来;Testbench可以使用2种方法进行文本数据操作
- readmemb, readmemh, writememb, writememh操作
- fscanf, fwrite等操作
readmemb, readmemh, writememb, writememh操作
从字面意思理解,readmem是读取数据到memory,后缀的b, h代表了数据的进制;同理,writemem是将memory的数据写入到文件中;
所以,在使用这一类系统自带函数时,首先要有一个memory类型的变量。定义方法如下:
reg [M-1:0] mem [N:1];
mem”变量“(应该叫寄存器组)有N个”一维“变量,每个”一维“变量的bit宽度为M;你可以将mem理解为C语言中的二维数组,里面包含了N个一维数组,每个一维数组有M个元素,元素为bit。
事实上,我们称M为mem的数据宽度,N为mem的数据深度。
以readmemb为例,进行数据读取操作
initial
begin
$readmemb("data.txt", mem);
readmemb的第一个参数为文件名,第二个参数为memory变量名;至此,data.txt内部的N行数据存入了mem里。readmemh操作类似,不同的是data.txt的数据要求为16进制。那么可能有人会有疑问了,假如有以下问题,mem存入的数据会是啥样:
- data.txt每行的数据位宽小于M或者大于M
经过试验,M大于数据位宽,数据可以正常读取,高位补0;小于数据位宽,数据无法正常读取。
- data.txt的数据不是二进制,或者不是纯数字
与M小于数据位宽的情况一致,无法正常读取数据。
- 如果data.txt的行数小于N或大于N
经过试验,行数大于N,仿真器会出警告,但数据可以正常读取。小于N时,多余的部分memory的值为不定状态。
数据存入mem但还没有进入到模块的输入,接下来的操作可以参考下列代码:
reg [M-1:0] data_in;
integer index = 1;
initial
begin
forever
begin
@(posegde clk);
data_in = mem[index];
index = (index >= N) ? 1 : index + 1;
代码里面,等待clk的上升沿,然后将mem的index元素赋值给data_in,然后index完成加1操作;整个过程不断循环;这里设置了index计数到N返回1的计数保护,防止出现无效数据。再将data_in与被测模块的数据输入端口相连,数据就送入进去了。
writememb的操作与readmemb反过来,将mem的数据存储为文本操作如下:
initial
begin
$writememb("new_data_b.txt", mem);
$writememh("new_data_h.txt", mem);
存储之后的
fscanf, fwrite等操作
Verilog本身的语法与C类似,其自身也有文本操作的函数,也与C类似。使用Verilog对文本操作,首先需要进行如下操作:
integer fid;
initial
begin
fid = $fopen("data.txt", "r");
//fid = $fopen("data.txt", "w"); //write
if (!fid)
$display("file open error");
如同C语言中的fopen一样,第一个参数为文件名,第二个参数为操作模式,包括读(r, rb),写(w, wb)等操作;根据返回值判断文件操作是否有错误。
然后,根据文本文件的数据格式,进行数据读取操作。
reg [M-1:0] data_in;
always @ (posedge clk)
$fscanf(fid, "%d %d %d", data_in, mem[0], mem[1]);
fscanf用法与C语言类似,文件句柄为第一个参数,第二个参数为格式参数,第三个为数据保存变量,但不需要加&了。 读取文件的时候第二个参数与第三个参数需要对应,否则数据读取可能会出错。(亲身经历)
数据存储操作如下,在前面fopen使用w模式下:
always @ (posegde clk)
$fwrite(fid, "%d, %d, %d\n", $signed(data_in), $signed(data_in)+1, $signed(data_in)+2);
数据可以按照第二个参数的格式存储进文本文件。还有一系列如fdisplay, 相对于fwrite, 它的文件写入数据之后会自动到下一行,所以第二个参数不需要加入“\n”;ftell等函数。 注意,想要存储十进制的负数,除了第二个参数用%d,第三个参数的寄存器变量还要使用$signed转换为有符号数形式
注意,当存储的寄存器位宽小于数据位宽时,数据会被自动截去高位保留低位。
之前使用文件操作存储被测模块的输出时,每次文件的数据量(行数)都与理想中的数目对不上,找各种原因,最后才发现自己犯了一个低级错误,没有使用fclose关闭文件句柄。
initial