简言
看了一些网上的其他的帖子,很多地方都是错的,踩了一些雷,写一个记录贴分享一下。
大致流程如下:
用MATLAB生成原始信号数据并做定点数转换,将转换后的信号数据写入TXT里供IP核使用,通过调用IP核进行FFT运算,最后将运算结果存入TXT文件,用MATLAB解析并绘制频谱图。
定点数介绍
定点数由符号位1位,整数位x位,小数位y位组成,总位宽为1+x+y。简单说一下XILINX的定点数的格式。
fixQ_N : 这里Q代表总的位宽,N代表小数的位宽,比如fix16_15,表明该定点数位宽为16,小数位宽为15,符号位宽为1位,故整数位宽为0。这个定点数最小为1000000000000000,最大为0111111111111111,能表示的范围为[-1,1-(0.5)^15]
VIVADO:
IP核配置
VIVADO版本为2018.3 ,IP核版本为9.1,下面逐一介绍IP核配置信息:
信号输入和输出时,实部和虚部是分开传输的,故设置信号输入位宽是实际的一半。比如我设置的信号输入位宽是16,表示实部和虚部的位宽都是16,拼接在一起就是32位。输出也是同理。
定点数格式是针对实部和虚部自身的部分。比如输出的数据一共是48位,实部和虚部都是总长度24位,小数位15位的定点数,两个定点数拼接凑成一个48位的数据。
创建好IP核后,可以在IP SOURCES里看到有一个fft128.veo文件:
点击查看,往下拉可以看到IP核的例化模版:
fft128 your_instance_name (
.aclk(aclk), // input wire aclk
.aresetn(aresetn), // input wire aresetn
.s_axis_config_tdata(s_axis_config_tdata), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid
.s_axis_config_tready(s_axis_config_tready), // output wire s_axis_config_tready
.s_axis_data_tdata(s_axis_data_tdata), // input wire [31 : 0] s_axis_data_tdata
.s_axis_data_tvalid(s_axis_data_tvalid), // input wire s_axis_data_tvalid
.s_axis_data_tready(s_axis_data_tready), // output wire s_axis_data_tready
.s_axis_data_tlast(s_axis_data_tlast), // input wire s_axis_data_tlast
.m_axis_data_tdata(m_axis_data_tdata), // output wire [47 : 0] m_axis_data_tdata
.m_axis_data_tuser(m_axis_data_tuser), // output wire [7 : 0] m_axis_data_tuser
.m_axis_data_tvalid(m_axis_data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tready(m_axis_data_tready), // input wire m_axis_data_tready
.m_axis_data_tlast(m_axis_data_tlast), // output wire m_axis_data_tlast
.event_frame_started(event_frame_started), // output wire event_frame_started
.event_tlast_unexpected(event_tlast_unexpected), // output wire event_tlast_unexpected
.event_tlast_missing(event_tlast_missing), // output wire event_tlast_missing
.event_status_channel_halt(event_status_channel_halt), // output wire event_status_channel_halt
.event_data_in_channel_halt(event_data_in_channel_halt), // output wire event_data_in_channel_halt
.event_data_out_channel_halt(event_data_out_channel_halt) // output wire event_data_out_channel_halt
);
在例化时只需要自己修改外部端口名即可。
顶层模块编写:
module FFT128(
input i_clk, //100MHz, 10ns
input aresetn,
input[7:0] s_axis_config_tdata,
input s_axis_config_tvalid,
output s_axis_config_tready,
output s_axis_data_tready,
input[31:0] s_axis_data_tdata, //输入的16位数据 16*2
input s_axis_data_tvalid, //拉高N个周期,输入N个数据进行fft
input s_axis_data_tlast, //输入N个数据后拉高,停止数据输入
output[47:0] m_axis_data_tdata,
output[7:0] m_axis_data_tuser,
output m_axis_data_tvalid,
input m_axis_data_tready,
output m_axis_data_tlast
);
wire event_frame_started;
wire event_tlast_unexpected;
wire event_tlast_missing;
wire event_status_channel_halt;
wire event_data_in_channel_halt;
wire event_data_out_channel_halt;
fft128 fft128_mod (
.aclk(i_clk), // input wire aclk
.aresetn(aresetn), // input wire aresetn
.s_axis_config_tdata(s_axis_config_tdata), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid
.s_axis_config_tready(s_axis_config_tready), // output wire s_axis_config_tready
.s_axis_data_tdata(s_axis_data_tdata), // input wire [31 : 0] s_axis_data_tdata
.s_axis_data_tvalid(s_axis_data_tvalid), // input wire s_axis_data_tvalid
.s_axis_data_tready(s_axis_data_tready), // output wire s_axis_data_tready
.s_axis_data_tlast(s_axis_data_tlast), // input wire s_axis_data_tlast
.m_axis_data_tdata(m_axis_data_tdata), // output wire [47 : 0] m_axis_data_tdata
.m_axis_data_tuser(m_axis_data_tuser), // output wire [7 : 0] m_axis_data_tuser
.m_axis_data_tvalid(m_axis_data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tready(m_axis_data_tready), // input wire m_axis_data_tready
.m_axis_data_tlast(m_axis_data_tlast), // output wire m_axis_data_tlast
.event_frame_started(event_frame_started), // output wire event_frame_started
.event_tlast_unexpected(event_tlast_unexpected), // output wire event_tlast_unexpected
.event_tlast_missing(event_tlast_missing), // output wire event_tlast_missing
.event_status_channel_halt(event_status_channel_halt), // output wire event_status_channel_halt
.event_data_in_channel_halt(event_data_in_channel_halt), // output wire event_data_in_channel_halt
.event_data_out_channel_halt(event_data_out_channel_halt) // output wire event_data_out_channel_halt
);
endmodule
实际上就是对IP核模块套了一层壳,输入输出端口都一致。
仿真文件编写:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/02/26 19:15:10
// Design Name:
// Module Name: fft_sim
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments: 用于128位IP核测试
//
//////////////////////////////////////////////////////////////////////////////////
module fft128_sim(
);
//参数定义
`define CLK_PERIORD 10 //时钟周期设置为10ns(100MHz)
//接口申明
reg clk;
reg rst_n;
reg aresetn;
reg[7:0] s_axis_config_tdata;
reg s_axis_config_tvalid;
wire s_axis_config_tready;
wire s_axis_data_tready;
reg[31:0] s_axis_data_tdata; //输入的16位数据 16*2
reg s_axis_data_tvalid; //拉高4096个周期,输入4096个数据进行fft
reg s_axis_data_tlast; //输入4096个数据后拉高,停止数据输入
wire[47:0] m_axis_data_tdata;
wire[7:0] m_axis_data_tuser;
wire m_axis_data_tvalid;
reg m_axis_data_tready;
wire m_axis_data_tlast;
reg [15:0] mem0_re[0:127];
reg [7:0] outCnt;
reg signed[23:0] o_data_real[0:127];
reg signed[23:0] o_data_imag[0:127];
reg transFlag;
//对被测试的设计进行例化
FFT128 tb_fft128(
.i_clk (clk), //100MHz, 10ns // 时钟信号(input)
.aresetn (aresetn), // 复位信号,低有效(input)
.s_axis_config_tdata (s_axis_config_tdata), // ip核设置参数内容,为1时做FFT运算,为0时做IFFT运算(input)
.s_axis_config_tvalid(s_axis_config_tvalid), // ip核配置输入有效,可直接设置为1(input)
.s_axis_config_tready(s_axis_config_tready), // output wire s_axis_config_tready
.s_axis_data_tdata (s_axis_data_tdata), // 把时域信号往FFT IP核传输的数据通道,[31:16]为虚部,[15:0]为实部(input,主->从)
.s_axis_data_tvalid (s_axis_data_tvalid), // 表示主设备正在驱动一个有效的传输(input,主->从)
.s_axis_data_tready (s_axis_data_tready), // 表示从设备已经准备好接收一次数据传输(output,从->主),当tvalid和tready同时为高时,启动数据传
.s_axis_data_tlast (s_axis_data_tlast), // 主设备向从设备发送传输结束信号(input,主->从,拉高为结束)
.m_axis_data_tdata (m_axis_data_tdata), // FFT输出的频谱数据,[47:24]对应的是虚部数据,[23:0]对应的是实部数据(output,主->从)。
.m_axis_data_tuser (m_axis_data_tuser), // 输出频谱的索引(output,主->从),该值*fs/N即为对应频点;
.m_axis_data_tvalid (m_axis_data_tvalid), // 表示主设备正在驱动一个有效的传输(output,主->从)
.m_axis_data_tready (m_axis_data_tready), // 表示从设备已经准备好接收一次数据传输(input,从->主),当tvalid和tready同时为高时,启动数据传输
.m_axis_data_tlast (m_axis_data_tlast) // 主设备向从设备发送传输结束信号(output,主->从,拉高为结束)
);
//时钟产生
always #(`CLK_PERIORD/2) clk = ~clk;
integer i = 0;
initial begin
for (i = 0; i < 128; i=i+1) begin
o_data_real[i] = 0;
o_data_imag[i] = 0;
end
end
//测试激励产生
initial $readmemb("E:/Project/FPGA/fft_hy_2/data_before_fft_fixed_point_128.txt", mem0_re);
integer delay = 0;
integer save_file;
//时钟和复位初始化、复位产生
initial begin
// Initialize Inputs
clk = 0;
aresetn = 0;
s_axis_config_tdata = 0;
s_axis_config_tvalid = 0;
s_axis_data_tvalid = 0;
s_axis_data_tdata = 0;
s_axis_data_tlast = 0;
m_axis_data_tready = 0;
outCnt = 8'b0;
transFlag = 1;
delay = 0;
// Wait 200 ns for global reset to finish
#200;
i = 0;
aresetn = 1;
s_axis_data_tvalid = 1;
m_axis_data_tready = 1;
s_axis_config_tvalid = 1;
s_axis_config_tdata = 8'd1;
#9000
s_axis_config_tvalid = 0;
aresetn = 0;
#300;
$fclose(save_file);
$stop;
end
initial begin
save_file = $fopen("E:/Project/FPGA/fft_hy_2/vivado_testbench_128.txt", "w"); // Open or create a text file for writing
if (save_file == 0) begin
$display("Error: Cannot open file");
end
else begin
$display("OPEN OK!!!!!!");
end
end
always @(posedge clk)
begin
if(s_axis_data_tlast == 1)
begin
i <= 0;
s_axis_data_tlast <= 0;
s_axis_data_tvalid <= 0;//拉低,主机结束传送
end
else if(s_axis_data_tready == 1 && s_axis_data_tvalid == 1 && transFlag == 1)
begin
//s_axis_data_tvalid <= 1;
s_axis_data_tdata[15:0] <= mem0_re[i]; //实部
s_axis_data_tdata[31:16] <= 16'b0; //虚部
$display("mem_a[%d] = %d", i, mem0_re[i]);
i <= i + 1;
if(i == 126)
begin
s_axis_data_tlast <= 1; //最后一个数据
end
end
end
always @(posedge clk)
begin
if(m_axis_data_tvalid == 1)
begin
o_data_real[outCnt] = m_axis_data_tdata[23:0]; // 实部
o_data_imag[outCnt] = m_axis_data_tdata[47:24];//虚部
$fdisplay(save_file, "outCnt: %d, real: %b ,image: %b", outCnt, o_data_real[outCnt],o_data_imag[outCnt]);
outCnt = outCnt + 1;
end
end
always @(posedge clk)
begin
if(m_axis_data_tlast == 1)
begin
outCnt <= 8'b0 ;
end
end
endmodule
仿真的时钟周期设置为10ns,原始信号数据由MATLAB生成,存储在“E:/Project/FPGA/fft_hy_2/data_before_fft_fixed_point_128.txt”中。IP核的运算结果存储在"E:/Project/FPGA/fft_hy_2/vivado_testbench_128.txt"中。文件路径根据自己实际情况修改。
注意问题:
对于IP核的操作,要注意外部时序和输入数据的处理。FFT IP核采用的是AXI总线,有握手机制(体现在valid和ready信号上)。对于数据输入,要在s_axis_data_tvalid和s_axis_data_tready两个端口都是高电平的情况下才能执行数据输入;对于数据输出,要在m_axis_data_tvalid和m_axis_data_tready两个端口都是高电平的情况下才能执行数据输出。IP核控制的是s_axis_data_tready和m_axis_data_tvalid两个端口的电平高低(这两个端口是IP核的输出端口),s_axis_data_tvalid和m_axis_data_tready两个端口由我们手动控制。
在valid和ready信号都为高的时候,可以进行有效的数据传输。无论输入输出,每一个时钟周期都只能传输一个数据。在最后一个数据来临时,s_axis_data_tlast或m_axis_data_tlast端口会被拉高(谁被拉高取决于是送入数据还是送出数据),然后在下一个上升沿到来时被拉低,代表这一串数据传输完毕。
MATLAB:
MATLAB生成的信号数据文本和验证用的数据文本的路径需要根据自己实际情况设置。
原始数据生成:
% 这个版本生成的原始数据信号没有量化,直接转的定点数
% clear
Fs=100; %采样率1ns一个点
%t=0:1/Fs:63/Fs; %数据时长:64个采样周期
N = 128;
n = 0:N-1;
t = n/Fs;
f = n*Fs/N;
% 生成测试信号
f1 = 10; %
f2 = 30; %
s1 = cos(2*pi*f1*t);
s2 = cos(2*pi*f2*t);
data_before_fft = 0.5 *(s1 + s2);
fixed_total_length = 16; %定点数总长度
fixed_deci_length = 15; %定点数小数位长度
q = quantizer('fixed','round','saturate',[fixed_total_length,fixed_deci_length]);%量化器
temp = num2bin(q,data_before_fft);
filepath = ['E:\Project\FPGA\fft_hy_2\data_before_fft_fixed_point_',num2str(N),'.txt'] ;
fp = fopen(filepath,'w');
for i = 1:N
fprintf(fp,'%c',temp(i,:));
fprintf(fp,'\r\n');
end
fclose(fp);
y1 = fft(data_before_fft,N);
y1 = y1.';
y2 = abs(y1);
y3 = angle(y1);
figure;
% plot(f,y2/max(y2));
plot(f,y2);
title('MATLAB结果幅度谱');
% figure;
% % plot(f,y2/max(y2));
% plot(t,data_before_fft);
% axis tight;
% title('MATLAB信号时域图');
% figure;
% stem(f, y3/pi*180, 'filled');
% title('MATLAB结果相位谱');
% ylim([-180 180]);
采样率100Hz,点数128个(匹配IP核的点数),生成两个余弦信号。浮点数到定点数的转化用的MATLAB的量化器函数。按照IP核输出格式(定点数长度16位,小数位15位)转换后的定点数字符串存储在temp中,按行写入txt文件里。
VIVADO数据验证:
验证代码:
% % 浮点数转换补码并验证
% clear
N = 128;%%FFT点数/信号个数
Deci_Digit = 15; %%小数位数
%% 实部
filename = ['E:\Project\FPGA\fft_hy_2\VIVADO_',num2str(N),'\REAL.txt'];% VIVADO输出实部
fid = fopen(filename, 'r');
bin_str = textscan(fid, '%s', 'Delimiter', '\n');
bin_str = bin_str{1}; % 提取细胞数组
b = zeros(N,3);
for cnt = 1:N
str = bin_str(cnt);%逐行提取
str = str{1};
b(cnt,1) = fixed2Point_V2(str,Deci_Digit);
end
fclose(fid);
fprintf('分界线--------------------------------\n');
%% 虚部
filename = ['E:\Project\FPGA\fft_hy_2\VIVADO_',num2str(N),'\IMAGE.txt'];% VIVADO输出虚部
fid = fopen(filename, 'r');
bin_str = textscan(fid, '%s', 'Delimiter', '\n');
bin_str = bin_str{1}; % 提取细胞数组
for cnt = 1:N
str = bin_str(cnt);%逐行提取
str = str{1};
b(cnt,2) = fixed2Point_V2(str,Deci_Digit);
b(cnt,3) = sqrt(b(cnt,1)^2 + b(cnt,2)^2);
end
fclose(fid);
%% 画图
Fs=100; %采样率1ns一个点
data = b(:,1)+1i*b(:,2);
n = 0:N-1;
t = n/Fs;
f = n*Fs/N;
figure;
% plot(f,b(:,3)/max(b(:,3)));
plot(f,abs(data));
title('VERILOG结果幅度谱');
fixed2Point_V2函数:
%str :二进制字符串 digit:小数位数
function out = fixed2Point_V2(str,digit)
% 转换为有符号整数
stored_integer = bin2dec(str);
if stored_integer >= 2^(length(str)-1) % 符号位判断(8位时阈值为128)
stored_integer = stored_integer - 2^length(str); % 补码转换
end
% 应用小数位缩放
out = stored_integer / 2^digit;
% 输出结果
fprintf('二进制: %s → 十进制: %.4f\n', str, out);
end
仿真结果:
开始输入时:
结束输入时 :
开始输出时:
结束输出时:
频谱图对比:
从两种不同的FFT运算得到的频谱图上可以看出它们的波形基本一致,且幅值的峰值点出现的位置也保持一致,只是两者的峰值有些许差异 。
问题
左边是MATLAB的输出结果,右边是VIVADO的输出结果。明显可以看出实部和虚部的差异比较明显,但是频谱图对比又比较正常。网上说是因为蝶形运算导致的,但是换了RADIX-4结构计算还是没有解决这个问题。
实部虚部差异大问题:
在s_axis_data_tvalid拉高的第一帧,IP核会直接读取s_axis_data_tdata的数据,两种解决方法:1.第一帧提前给s_axis_data_tdata赋值(信号的第一个值)
2.在拉高s_axis_data_tvalid时给s_axis_data_tdata赋信号的第一个值。
第一帧的流程控制要手动控制,第二帧及以后的可以放在一个always块里统一控制。
我用的第一个方法,TB代码如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/02/26 19:15:10
// Design Name:
// Module Name: fft_sim
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments: 用于128位IP核测试
//
//////////////////////////////////////////////////////////////////////////////////
module fft128_sim(
);
//参数定义
`define CLK_PERIORD 10 //时钟周期设置为10ns(100MHz)
//接口申明
reg clk;
reg rst_n;
reg aresetn;
reg[7:0] s_axis_config_tdata;
reg s_axis_config_tvalid;
wire s_axis_config_tready;
wire s_axis_data_tready;
reg[31:0] s_axis_data_tdata; //输入的16位数据 16*2
reg s_axis_data_tvalid; //拉高4096个周期,输入4096个数据进行fft
reg s_axis_data_tlast; //输入4096个数据后拉高,停止数据输入
wire[47:0] m_axis_data_tdata;
wire[7:0] m_axis_data_tuser;
wire m_axis_data_tvalid;
reg m_axis_data_tready;
wire m_axis_data_tlast;
reg [15:0] mem0_re[0:127];
reg [7:0] outCnt;
reg signed[23:0] o_data_real[0:127];
reg signed[23:0] o_data_imag[0:127];
//reg signed[45:0] o_data_abs[0:63];
// wire [23:0] realWire;
// wire [23:0] imageWire;
reg transFlag;
// assign realWire = m_axis_data_tdata[23:0];
// assign imageWire = m_axis_data_tdata[47:24];
//对被测试的设计进行例化
FFT128 tb_fft128(
.i_clk (clk), //100MHz, 10ns // 时钟信号(input)
.aresetn (aresetn), // 复位信号,低有效(input)
.s_axis_config_tdata (s_axis_config_tdata), // ip核设置参数内容,为1时做FFT运算,为0时做IFFT运算(input)
.s_axis_config_tvalid(s_axis_config_tvalid), // ip核配置输入有效,可直接设置为1(input)
.s_axis_config_tready(s_axis_config_tready), // output wire s_axis_config_tready
.s_axis_data_tdata (s_axis_data_tdata), // 把时域信号往FFT IP核传输的数据通道,[31:16]为虚部,[15:0]为实部(input,主->从)
.s_axis_data_tvalid (s_axis_data_tvalid), // 表示主设备正在驱动一个有效的传输(input,主->从)
.s_axis_data_tready (s_axis_data_tready), // 表示从设备已经准备好接收一次数据传输(output,从->主),当tvalid和tready同时为高时,启动数据传
.s_axis_data_tlast (s_axis_data_tlast), // 主设备向从设备发送传输结束信号(input,主->从,拉高为结束)
.m_axis_data_tdata (m_axis_data_tdata), // FFT输出的频谱数据,[47:24]对应的是虚部数据,[23:0]对应的是实部数据(output,主->从)。
.m_axis_data_tuser (m_axis_data_tuser), // 输出频谱的索引(output,主->从),该值*fs/N即为对应频点;
.m_axis_data_tvalid (m_axis_data_tvalid), // 表示主设备正在驱动一个有效的传输(output,主->从)
.m_axis_data_tready (m_axis_data_tready), // 表示从设备已经准备好接收一次数据传输(input,从->主),当tvalid和tready同时为高时,启动数据传输
.m_axis_data_tlast (m_axis_data_tlast) // 主设备向从设备发送传输结束信号(output,主->从,拉高为结束)
);
//时钟产生
always #(`CLK_PERIORD/2) clk = ~clk;
integer i = 0;
initial begin
for (i = 0; i < 128; i=i+1) begin
o_data_real[i] = 0;
o_data_imag[i] = 0;
end
end
//测试激励产生
//initial $readmemb("E:/Project/FPGA/fft_hy_2/lfm_fixed_point_128.txt", mem0_re);
initial $readmemb("E:/Project/FPGA/fft_hy_2/data_before_fft_fixed_point_128.txt", mem0_re);
//initial $readmemb("E:/Project/FPGA/fft_hy_2/test.txt", mem0_re);
integer delay = 0;
integer save_file;
//时钟和复位初始化、复位产生
initial begin
// Initialize Inputs
clk = 0;
aresetn = 0;
s_axis_config_tdata = 0;
s_axis_config_tvalid = 0;
s_axis_data_tvalid = 0;
s_axis_data_tdata = 0;
s_axis_data_tlast = 0;
m_axis_data_tready = 0;
outCnt = 8'b0;
transFlag = 1;
delay = 0;
// Wait 200 ns for global reset to finish
#200;
i = 0;
aresetn = 1;
s_axis_data_tvalid = 1;
m_axis_data_tready = 1;
s_axis_config_tvalid = 1;
s_axis_config_tdata = 8'd1;
s_axis_data_tdata = {16'b0,mem0_re[0]}; //虚部,实部
i = i + 1;
#9000
s_axis_config_tvalid = 0;
aresetn = 0;
#300;
$fclose(save_file);
$stop;
end
initial begin
save_file = $fopen("E:/Project/FPGA/fft_hy_2/vivado_testbench_128.txt", "w"); // Open or create a text file for writing
if (save_file == 0) begin
$display("Error: Cannot open file");
end
else begin
$display("OPEN OK!!!!!!");
end
end
always @(posedge clk)
begin
if(s_axis_data_tlast == 1)
begin
i <= 0;
s_axis_data_tlast <= 0;
s_axis_data_tvalid <= 0;//拉低,主机结束传送
end
else if(s_axis_data_tready == 1 && s_axis_data_tvalid == 1 && transFlag == 1)
begin
//s_axis_data_tvalid <= 1;
// s_axis_data_tdata[15:0] <= mem0_re[i]; //实部
// s_axis_data_tdata[31:16] <= 16'b0; //虚部
s_axis_data_tdata <= {16'b0,mem0_re[i]}; //虚部,实部
$display("mem_a[%d] = %d", i, mem0_re[i]);
i <= i + 1;
if(i == 127)
begin
s_axis_data_tlast <= 1; //最后一个数据
end
end
end
always @(posedge clk)
begin
if(m_axis_data_tvalid == 1)
begin
o_data_real[outCnt] = m_axis_data_tdata[23:0]; // 实部
o_data_imag[outCnt] = m_axis_data_tdata[47:24];//虚部
$fdisplay(save_file, "outCnt: %d, real: %b ,image: %b", outCnt, o_data_real[outCnt],o_data_imag[outCnt]);
outCnt = outCnt + 1;
end
end
always @(posedge clk)
begin
if(m_axis_data_tlast == 1)
begin
outCnt <= 8'b0 ;
end
end
endmodule
跟之前的代码相比,主要修改了s_axis_data_tdata的初始值和结束时s_axis_data_tlast拉高的逻辑。仿真时序:
将仿真得到的数据送入MATLAB,对定点数进行转化、实部虚部拼接后进行对比:
左边是VIVADO的结果,右边是MATLAB的结果,可以看出实部虚部的差异几乎没有了。误差来源于定点数的转化和蝶形运算。