vivado仿真读取txt文件数据方法整理
在信号处理或者一些功能仿真中,很多场景下都需要将txt文件中的数据批量导入到vivado中进行仿真测试,在vivado里提供了一些常用的文件处理函数,例如fcanf,sscanf,readmemh等,但是应用过程中有时候txt文件数据格式不一样,这时候直接使用这些函数就不太适用了,通常需要对txt数据格式进行处理,一般情况下原始数据转成带符号的16进制定点数后再用vivado去读取。本文经过各种探索和测试,整理了两种在vivado里读取txt文件的方式。
数据格式说明
数据最好是通过python或者MATLAB转成十六进制数据之后再写入txt文件,如图:

图中是每行32个位宽为16bit的十六进制数据,txt文件一共2048行,需要讲这些数据导入到vivado里进行仿真测试,为了方便,可以将每行32个数据拼成一个512bit的数据,像这样:

其实就是把数据中间的空格去掉就可以了,但前提是原本的数据一定是十六进制的,要不然拼起来到vivado里还要进行额外处理。
到这里就可以在vivado里使用函数读取txt文件了,这里提供两种方法。
方法一
在vivado里的仿真文件里添加下面语句:
parameter FILE_PATH_A = "E:/vivado_work/data.txt ";
(* ram_style = "block" *) reg [511:0] data[2047:0]; // 数据存放位置
integer file_r,i;
initial begin
file_r = $fopen(FILE_PATH_A,"r");//r:以读形式打开
for(i=0;i<2048;i=i+1) //一共2048行数据
$fscanf(file_r,"%h",data[i]);
$fclose(file_r);
end
reg clk = 0 ;
always #2.5 clk = ~clk ;
reg rst ;
initial begin
rst = 1 ;
#100
rst = 0 ;
end
//将数据从数据存储块内按时钟读取出来
reg [511:0] data_chan = 0 ;
reg [11:0] j =0;
reg data_chan_vld = 0 ;
always @(posedge clk) begin
if(rst) begin
data_chan <= 0 ;
data_chan_vld <= 1'b0 ;
end
else if(j<2048) begin
data_chan <= data[j] ;
data_chan_vld <= 1'b1 ;
j <= j + 1 ;
end
else begin
data_chan <= data_chan ;
data_chan_vld <= 1'b0 ;
end
end
这种方法需要先将数据放到一块内存里,后续再按照时钟周期一个个读出来,不太灵活。作为对比,下面提供另一中方法。
方法二
parameter FILE_PATH_A = "E:/vivado_work/data.txt ";
(* ram_style = "block" *) reg [511:0] data; // 32个8位数据
reg clk = 0 ;
always #2.5 clk = ~clk ;
reg rst ;
initial begin
rst = 1 ;
#100
rst = 0 ;
end
reg [511:0] data_chan = 0 ;
reg [11:0] j =0;
reg data_chan_vld = 0 ;
integer file_r,i;
initial begin
file_r = $fopen(FILE_PATH_A,"r");//r:以读形式打开
end
always @(posedge clk) begin
if(rst) begin
data_chan <= 0 ;
data_chan_vld <= 1'b0 ;
end
else if(j<2048) begin
$fscanf(file_r,"%h",data);
data_chan <= data ;
data_chan_vld <= 1'b1 ;
j <= j + 1 ;
end
else begin
$fclose(file_r);
data_chan <= data_chan ;
data_chan_vld <= 1'b0 ;
end
end
在方法二里如果不想再txt文件里拼接数据,也可以修改代码为这样:
parameter FILE_PATH_A = "E:/vivado_work/.txt ";
(* ram_style = "block" *) reg [511:0] data_out; // 32个8位数据
reg clk = 0 ;
always #2.5 clk = ~clk ;
reg rst ;
initial begin
rst = 1 ;
#100
rst = 0 ;
end
reg [511:0] data_chan = 0 ;
reg [11:0] j =0;
reg data_chan_vld = 0 ;
integer file_r,i;
initial begin
file_r = $fopen(FILE_PATH_A,"r");//r:以读形式打开
end
always @(posedge clk) begin
if(rst) begin
data_chan <= 0 ;
data_chan_vld <= 1'b0 ;
end
else if(j<2048) begin
$fscanf(file_r, "%h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h %h",
data_out[496+:16], data_out[480+:16], data_out[464+:16], data_out[448+:16],
data_out[432+:16], data_out[416+:16], data_out[400+:16], data_out[384+:16],
data_out[368+:16], data_out[352+:16], data_out[336+:16], data_out[320+:16],
data_out[304+:16], data_out[288+:16], data_out[272+:16], data_out[256+:16],
data_out[240+:16], data_out[224+:16], data_out[208+:16], data_out[192+:16],
data_out[176+:16], data_out[160+:16], data_out[144+:16], data_out[128+:16],
data_out[112+:16], data_out[96+:16], data_out[80+:16], data_out[64+:16],
data_out[48+:16], data_out[32+:16], data_out[16+:16], data_out[0+:16]);
data_chan <= data_out ;
data_chan_vld <= 1'b1 ;
j <= j + 1 ;
end
else begin
$fclose(file_r);
data_chan <= data_chan ;
data_chan_vld <= 1'b0 ;
end
end
效果其实是一样的,只不过一个是在txt文件里拼接数据,一个是在vivado拼接数据。
到这里就可以直接按照我们提供的时钟周期来读取txt文件里的数据,并输出到后续其它模块,相比第一种方法这种方式可以更灵活的读取。

仿真结果可以看到数据可以正确的读到vivado里了。
其它方法
在测试过程中其实也试过其它方法,例如对图1里的数据可以直接用readmemh函数来读取数据,然后将数据存放在一个三维数组里,但是这种方法在后边按时钟周期去读取数据时会比较麻烦,需要自己去控制读取的行数和列数。推荐文中所用的两种方法。
全部0条评论
快来发表一下你的评论吧 !