在进行verilog模块的开发过程中,常常需要对数据处理过程进行行为级仿真,以验证代码的功能逻辑是否正确,因此需要将RTL级仿真的结果与MATLAB或python的处理结果进行对比验证。将输入输出结果数据存入文件进行对比是在数据对比中常用的方法。本文讲述了Matlab,Verilog/SV,python之间进行的数据交互,包括matlab/python 写txt 或者dat 文件,verilog/SV读取与写入txt文件。脚本均进行了功能仿真,有问题请在评论区或者私信交流。
Matlab是一个非常好用的工具,对于浮点仿真数据,我们需要进行定点化生成2进制或者16进制数据写入txt或者dat文档,方便后续Verilog代码读取($readmemb,$reammemh)。下面的代码有两种方式生成16进制或者2进制数据,写入数据的前提都是用round、floor等函数将浮点小数生成整数(round*(2^(N-1)))。下面的Matlab代码是将整形数据写入txt文件,第一种hex file代码简单,且写入迅速,美中不足的是只能写入正数,需要将有符号数转化成无符号数;第二种方法(%注释代码)是以二进制补码写入txt文件,逐bit判断,速度很慢,可以满足写入有符号数的需求。此外示例中给了两个基础函数用法,即有符号数与无符号数的转换,可以使用。
%% methon1:signed to unsigned and output to txt.file in hex
fid=fopen('C:\... \Desktop\DataIn.txt','w');
unsigned_si = s2us(si,24);
fprintf(fid,"%06x\n",unsigned_si); % fprintf only unsigned hex(%x)number
%% methon2:output binary complement data to txt.file
% for k=1:length(si)
% B_s=dec2bin(si(k)+(si(k)<0)*2^N,N);
% for j=1:N
% if B_s(j)=='1'
% tb=1;
% else
% tb=0;
% end
% fprintf(fid,'%s',B_s(j));
% end
% fprintf(fid,'\r\n');
% end
% fprintf(fid,';');
fclose(fid);
%% base function:signed to unsigned
function out = s2us(in,dw)
% narginchk(2,3);
re = real(in) + (real(in)<0)*2^dw;
im = imag(in) + (imag(in)<0)*2^dw;
out = re + im*2^dw;
% if nargin == 3
% if comp == 0 %real
% out = in + (in<0)*2^dw;
% else % complex
% re = real(in) + (real(in)<0)*2^dw;
% im = imag(in) + (imag(in)<0)*2^dw;
% out = re + im*2^dw;
% end
% else
% out = in + (in<0)*2^dw;
% end
%% base function :unsigned to signed
function out = us2s(in,dw)
if isReal(in)
re = mod(in,2^dw) - (mod(in,2^dw) >=2^(dw-1))*2^dw;
im = floor(in/2^dw) - (floor(in/2^dw) >=2^(dw-1))*2^dw;
out = re + 1j*im;
else
re = real(in)-(real(in)>=2^(dw-1))*2^dw;
im = imag(in)-(imag(in)>=2^(dw-1))*2^dw;
out = re + 1j*im;
end
% narginchk(2,3);
% if nargin == 3
% if comp == 0 %real
% out = in - (in>=2^(dw-1))*2^dw;
% else % complex
% real = mod(in,2^dw) - (mod(in,2^dw) >=2^(dw-1))*2^dw;
% imag = fix(in/2^dw) - (fix(in/2^dw) >=2^(dw-1))*2^dw;
% out = real + 1j*imag;
% end
% else
% out = in - (in>=2^(dw-1))*2^dw;
% end
目前随着python的流行,利用python 生成仿真数据,并与verilog进行数据交互的情形也非常之多。下面给出一段示例代码,将python的浮点数据(整数)打印成.dat文件数据格式。
def print2dat_qi(data=0, bw=16, name="file"):
"""
:param data:打印数据
:param bw: 位宽
:param name:打印文件名称
"""
data_qi = []
file = str(name + ".dat")
bwhex = np.ceil(bw / 4)
modbw = 4 - np.mod(bw, 4)
with open(file, "w") as f:
pass
with open(file, "a") as f:
for i in range(len(data)):
if isinstance(data[i, 0], complex):
# data_rnd = complex(round_new(data[i].real), round_new(data[i].imag))
data_rnd = data[i, 0]
data_i = data_rnd.real
data_q = data_rnd.imag
if data_i < 0:
data_i = data_i + 2 ** bw
else:
data_i = data_i
if data_q < 0:
data_q = data_q + 2 ** bw
else:
data_q = data_q
if modbw == 4:
bwhexh = bwhex
bwhexl = bwhex
else:
data_i = np.mod(data_q, 2 ** modbw) * 2 ** bw + data_i # 高Q低I
data_q = np.floor(data_q / 2 ** modbw)
bwhexh = int(np.ceil((bw - modbw) / 4))
bwhexl = int(bwhex)
data_str = "%0" + str(bwhexh) + "x%0" + str(bwhexl) + "x" + "\n"
f.write(data_str % (int(data_q), int(data_i)))
# f.write(data_str % (int(data_i), int(data_q)))
else:
# data_rnd = round_new(data[i])
data_rnd = data[i,0]
if data_rnd < 0:
data_qi = data_rnd + 2 ** bw
else:
data_qi = data_rnd
data_str = "%0" + str(bwhex) + "x\n"
f.write(data_str % int(data_qi))
# how to use the function
# print2dat_qi(np.array([data_in]).T, 16, "print_datain")
除了与verilog数据交互外,python也可以读取matlab的数据格式.mat,方便我们python 与matlab进行数据传递。
coefFile = './coef_light_fix.mat'
dataFile = './sign_fix.mat'
sign_fix = scio.loadmat(dataFile)
print(sign_fix)
signal_data = sign_fix['sigin_fix']
coef_tmp = scio.loadmat(coefFile)
coef = coef_tmp['coef_light_fix']
signal_data_flatten = signal_data.flatten()
fir_in = signal_data_flatten[32767:32767+8192]
coef_in = coef.flatten()
intp = 2
fir_in_intp2=np.zeros(len(fir_in)+(len(fir_in)-1)*intp,dtype=complex)
fir_in_intp2[::intp+1]=fir_in
经过以上几个部分,我们大致了解了如何利用python或者matlab进行数据格式如txt,dat生成,那么在我们的仿真中,如何在testbench上利用这些数据呢?
Verilog中读写文件常用到的系统任务主要有以下几个:
1.文件打开、关闭与定位操作:$fopen(file_name)、$fclose(file_name)、$fseek();
2.文件读取:$fscanf和 $readmemh,其中$readmemh的作用是件文件中的数据一次性的读入某个数组中,然后依次从数组中取出1个数据进行处理;而$fscanf的作用是从文件中读出一行的数据。两个系统任务的常见用法如下:
$readmemh or $readmemb
logic x_in[0:8191];
logic [12:0] cnt_in;
initial begin
$readmemb({"D:./algorithm_sim/pdm_1b.dat"},x_in,0,8191); //建议使用绝对路径
end
always #1 clk = ~clk;
initial
begin
clk <= 1'b0;
d_in <= 1'b0;
repeat(10) @(posedge clk);
rst <= 1'b1;
@(posedge clk);
rst <= 1'b0;
end
always @(posedge clk or posedge rst) begin
if (rst)
cnt_in <= 13'd0;
else
cnt_in <= cnt_in + 1'b1;
end
always @(posedge clk) begin
d_in <= x_in[cnt_in];
end
Verilog在读取仿真数据的时候,有时候也需要进行仿真数据文件的输出,常用的函数有$fwrite(file_name)、$fdisplay(file_name),相应的脚本这里一并给出。
`timescale 1ns / 1ns
module tb_read_write( );
integer fp_r,fp_w;
logic [7:0]count;
logic clk;
logic [15:0] reg1;
initial
begin
clk = 1'b0;
reg1 = 16'h0;
count = 8'd0;
// method 1 : fopen fwrite
fp_r=$fopen("data.txt","r");//以读的方式打开文件
fp_w=$fopen("data_out.txt","w");//以写的方式打开文件
end
always #5 clk = ~clk;
always@(posedge clk)
begin
if(count < 9'd500)
begin
$fscanf(fp_r,"%d" ,reg1) ;//每次读一行
count <= count + 1'b1;
$fwrite(fp_w,"%d\n",reg1) ;//写入文件
end
else
begin
$fclose(fp_r);//关闭已打开的文件
$fclose(fp_w);
end
end
//method 2 :fdisplay
integer file_Sn;
initial
begin
//文件放置在"工程目录\simulation\modelsim"路径下
file_Sn = $fopen("Sn.txt");
end
always @(posedge clk)
$fdisplay(file_Sn,"%d",Sn);//将数据Sn写入文本