一,FFT的物理意义
FFT是离散傅立叶变换的快速算法,可以将一个信号变换到频域。有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了。这就是很多信号分析采用FFT变换的原因。另外在频谱分析方面,FFT可以将一个信号的频谱提取出来。一个模拟信号,经过ADC采样(采样频率要大于信号频率的两倍)之后,就变成了数字信号。采样得到的数字信号,就可以做FFT变换了。N个采样点,经过FFT之后,就可以得到N个点的FFT结果。为了方便进行FFT运算,通常N取2的整数次方。
IFFT (Inverse Fast Fourier Transform) 就是快速傅里叶逆变换。其为OFDM技术中的一个实现方式:各个子信道的正交调制和解调可以分别通过采用IDFT(Inverse Discrete Fourier Transform)和DFT实现,在子载波数很大的系统中,可以通过采用IFFT(Inverse Fast Fourier Transform)和FFT实现。
二,FFT的Vivado 实现步骤:
运行 Matlab程序 产生测试数据, 绘制 cos 时域和频域波形,运行产生 1 组 cos波形的 1000 个采样点数据,存在time_domain_cos.txt文件,每个数据位宽16bit。
matlab绘制出时域和频域的波形
clc;clear `all;close all;format long gFs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1000; % Length of signal
t = (0:L-1)*T; % Time vectorx1 = cos(2*pi*50*t)*(2^13); % First row wave
%output time domain data
x1_fix = round(x1,0); %convert to fixed signed(3.13)
x1_fix(find(x1_fix<0)) = x1_fix(find(x1_fix<0))+(2^16);
fid0 = fopen('time_domain_cos.txt', 'wt');
fprintf(fid0, '%16x\n', x1_fix);
fid0 = fclose(fid0);
fid1 = fopen('frequency_domain_cos.txt', 'wt');
fprintf(fid1, '%g\n', Y);
fid1 = fclose(fid1);%fft
x1 = x1/2^15; %signed(.15)
figure(1);
plot(t(1:100),x1(1:100))
title(['Cos in the Time Domain'])n = 2^nextpow2(L);
Y = fft(x1,n);%draw wave
y = abs(Y/n);
t1 = (0:n); % Time vectorfigure(2);
plot(t1(1:n),y(1:n))
title(['Cos in the Frequency Domain'])%IFFT and draw wave
IY = ifft(Y,n);
figure(3);
plot(t(1:100),IY(1:100))
title(['Cos after FFT and IFFT'])
1,打开 IP Catalog ,搜索 FFT 或者找到分类 Core -> Digital Signal Processing->Transform ->ffts ,Fast Fourier Transform 。
2,Configuration 页面如图,配置 IP 通道数(Number of Channels )、 FFT 转换长度(Transform Length)、目标时钟频率(Target Clock Frequency )和 FFT 实现架构(Architecture
Choice)
3,Implementation 页面,可以配置数据格式(Data Format)、缩放模式(Scaling Options)、数据末尾处理方式(Rounding Modes)、输入数据和相位的位宽(Input Data
Width)和数据输出顺序(Output Ordering)等。
4,Detailed Implement 页面中,可以对 FPGA 存储器或乘法器相关的资源配置
5,在配置页面左侧,可以查看 IP 接口( IP Symbol )、实现信号位宽细节( Implementation Details)和输出延时(Latency)
6,顶层代码
`timescale 1ns / 1ps`timescale 1ns/1ps
module fft_sim();reg clk;
reg rst_n; reg [7 : 0] s_axis_config_tdata;
reg s_axis_config_tvalid;
wire s_axis_config_tready;reg [15 : 0] s_axis_data_tdata_img,s_axis_data_tdata_real; //signed(1.15)
reg s_axis_data_tvalid;
wire s_axis_data_tready;
reg s_axis_data_tlast;wire [26 : 0] m_axis_data_tdata_img,m_axis_data_tdata_real;
wire [31 : 27] m_axis_data_null;
wire m_axis_data_tvalid;
wire m_axis_data_tlast;//xilinx pg109-xfft.pdf page24
parameter FFTCONFIG_FWD_INV_FFT = 1'b1; //FFT
parameter FFTCONFIG_FWD_INV_IFFT = 1'b0; //IFFT//FFT IP Core wire event_frame_started;
wire event_tlast_unexpected;
wire event_tlast_missing;
wire event_data_in_channel_halt;xfft_0 uut_xfft_0 (.aclk (clk), // input wire aclk.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_img,s_axis_data_tdata_real}), // 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_img,m_axis_data_null,m_axis_data_tdata_real}), // output wire [63 : 0] m_axis_data_tdata.m_axis_data_tvalid (m_axis_data_tvalid), // output wire m_axis_data_tvalid.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_data_in_channel_halt (event_data_in_channel_halt) // output wire event_data_in_channel_halt
);integer i,j;reg[15:0] data_mem [999:0];initial #500 $readmemh("../../time_domain_cos.txt", data_mem); initial beginclk = 0;rst_n = 0;s_axis_config_tdata <= 8'd0;s_axis_config_tvalid <= 1'b0;s_axis_data_tdata_img <= 16'd0;s_axis_data_tdata_real <= 16'd0;s_axis_data_tvalid <= 1'b0;s_axis_data_tlast <= 1'b0;#1000;@(posedge clk);s_axis_config_tdata <= {7'd0,FFTCONFIG_FWD_INV_FFT};s_axis_config_tvalid <= 1'b1; repeat(2) begin @(posedge clk); ends_axis_config_tvalid <= 1'b0; #1000;@(posedge clk);i <= 1;#10000;@(posedge clk); #100_000;$fclose(w1_file);$fclose(w2_file);#1000;$stop;
end always @(*) beginif((i>0) && (i<1001)) s_axis_data_tdata_real <= data_mem[i-1];else s_axis_data_tdata_real <= 16'd0;
end always @(posedge clk) beginif(i == 0) i <= 0;else if(i <= 1024) begin if(s_axis_data_tready && s_axis_data_tvalid) i <= i+1;else ;endelse if(i < 1050) i <= i+1;else ;
endalways @(posedge clk)
beginif((i>=1) && (i<=1023)) s_axis_data_tvalid <= 1'b1; else s_axis_data_tvalid <= 1'b0;
endalways @(posedge clk)
beginif(i==1023) s_axis_data_tlast <= 1'b1;else s_axis_data_tlast <= 1'b0;
endalways #10 clk = ~clk;
integer w1_file,w2_file;initial w1_file = $fopen("./fft_result_real.txt","w");
initial w2_file = $fopen("./fft_result_image.txt","w"); always @(posedge clk)
beginif(m_axis_data_tvalid) begin$fwrite(w1_file, "%x\n", m_axis_data_tdata_real);$fwrite(w2_file, "%x\n", m_axis_data_tdata_img); $display("%x , %x",m_axis_data_tdata_real,m_axis_data_tdata_img);end
end endmodule
7,Flow Navigator 面板中,展开 Simulation ,点击 Run Simulation ,弹出菜单中点击 Run Behavioral Simulation仿真
打开**.sim\sim_1\behav 文 件 夹 下 fft_result_real.txt 和fft_result_image.txt 文本,分别存储 FFT 结果的实部和虚部。这里需要注意定点的小数问题
8,FFT的MATLAB验证
clc;clear `all;close all;
format long g
FFT_NUM = 1024;
%input file
fid1 = fopen('fft_result_real.txt', 'r');
fft_real = fscanf(fid1,'%x');
fclose(fid1);
fid2 = fopen('fft_result_image.txt', 'r');
fft_image = fscanf(fid2,'%x');
fclose(fid2);for i=1:FFT_NUMif fft_real(i) >= 2^26fft_real(i) = fft_real(i)-2^27;endif fft_image(i) >= 2^26fft_image(i) = fft_image(i)-2^27;end
endy = sqrt(fft_real.*fft_real + fft_image.*fft_image)/2^15/FFT_NUM;
t = (0:FFT_NUM); % Time vector
figure(1);
plot(t(1:FFT_NUM),y(1:FFT_NUM))
三,IFFT的vivado实现
`timescale 1ns/1ps
module ifft_sim();
//接口定义reg clk;
reg rst_n;
reg [7 : 0] s_axis_config_tdata;
reg s_axis_config_tvalid;
wire s_axis_config_tready;reg [7 : 0] si_axis_config_tdata;
reg si_axis_config_tvalid;
wire si_axis_config_tready;reg [15 : 0] s_axis_data_tdata_img,s_axis_data_tdata_real;
reg s_axis_data_tvalid;
wire s_axis_data_tready;
reg s_axis_data_tlast;reg [26 : 0] si_axis_data_tdata_img,si_axis_data_tdata_real;
reg si_axis_data_tvalid;
wire si_axis_data_tready;
reg si_axis_data_tlast;wire [26 : 0] m_axis_data_tdata_img,m_axis_data_tdata_real;
wire [31 : 27] m_axis_data_null;
wire m_axis_data_tvalid;
wire m_axis_data_tlast;wire [37 : 0] mi_axis_data_tdata_img,mi_axis_data_tdata_real;
wire [39 : 38] mi_axis_data_null;
wire mi_axis_data_tvalid;
wire mi_axis_data_tlast;//xilinx pg109-xfft.pdf page24
parameter FFTCONFIG_FWD_INV_FFT = 1'b1; //FFT
parameter FFTCONFIG_FWD_INV_IFFT = 1'b0; //IFFT//FFT IP Core wire event_frame_started;
wire event_tlast_unexpected;
wire event_tlast_missing;
wire event_data_in_channel_halt;xfft_0 uut_xfft_0 (.aclk (clk), // input wire aclk.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_img,s_axis_data_tdata_real}), // 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_img,m_axis_data_null,m_axis_data_tdata_real}), // output wire [63 : 0] m_axis_data_tdata.m_axis_data_tvalid (m_axis_data_tvalid), // output wire m_axis_data_tvalid.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_data_in_channel_halt (event_data_in_channel_halt) // output wire event_data_in_channel_halt
);xfft_1 uut_xfft_1 (.aclk (clk), // input wire aclk.s_axis_config_tdata (si_axis_config_tdata), // input wire [7 : 0] s_axis_config_tdata.s_axis_config_tvalid (si_axis_config_tvalid), // input wire s_axis_config_tvalid.s_axis_config_tready (si_axis_config_tready), // output wire s_axis_config_tready.s_axis_data_tdata ({5'd0,si_axis_data_tdata_img,5'd0,si_axis_data_tdata_real}), // input wire [63 : 0] s_axis_data_tdata.s_axis_data_tvalid (si_axis_data_tvalid), // input wire s_axis_data_tvalid.s_axis_data_tready (si_axis_data_tready), // output wire s_axis_data_tready.s_axis_data_tlast (si_axis_data_tlast), // input wire s_axis_data_tlast.m_axis_data_tdata ({mi_axis_data_tdata_img,mi_axis_data_null,mi_axis_data_tdata_real}), // output wire [79 : 0] m_axis_data_tdata.m_axis_data_tvalid (mi_axis_data_tvalid), // output wire m_axis_data_tvalid.m_axis_data_tlast (mi_axis_data_tlast), // output wire m_axis_data_tlast.event_frame_started (), // output wire event_frame_started.event_tlast_unexpected (), // output wire event_tlast_unexpected.event_tlast_missing (), // output wire event_tlast_missing.event_data_in_channel_halt () // output wire event_data_in_channel_halt
);//初始化
integer i,j;reg[15:0] data_mem [999:0];
reg[26:0] fft_data_mem_real [1023:0];
reg[26:0] fft_data_mem_image [1023:0];initial #500 $readmemh("../../time_domain_cos.txt", data_mem); reg flag_fft; //1--fft, 0--ifftinitial beginclk = 0;rst_n = 0;s_axis_config_tdata <= 8'd0;s_axis_config_tvalid <= 1'b0;si_axis_config_tdata <= 8'd0;si_axis_config_tvalid <= 1'b0;s_axis_data_tdata_img <= 16'd0;s_axis_data_tdata_real <= 16'd0;s_axis_data_tvalid <= 1'b0;s_axis_data_tlast <= 1'b0;//FFT#1000;@(posedge clk);s_axis_config_tdata <= {7'd0,FFTCONFIG_FWD_INV_FFT};s_axis_config_tvalid <= 1'b1; repeat(2) begin @(posedge clk); ends_axis_config_tvalid <= 1'b0; flag_fft = 0;#1000;@(posedge clk);i <= 1;#100_000;$fclose(w1_file);$fclose(w2_file);//IFFTflag_fft = 1; #1000;@(posedge clk);si_axis_config_tdata <= {7'd0,FFTCONFIG_FWD_INV_IFFT};si_axis_config_tvalid <= 1'b1; repeat(2) begin @(posedge clk); endsi_axis_config_tvalid <= 1'b0; #1000;@(posedge clk);i <= 1;#100_000;$fclose(w3_file); #1000;$stop;
end always #10 clk = ~clk; //产生FFT IP核的输入数据
always @(*) beginif(!flag_fft) begins_axis_data_tdata_img <= 0;if((i>0) && (i<1001)) s_axis_data_tdata_real <= data_mem[i-1];else s_axis_data_tdata_real <= 16'd0;endelse beginsi_axis_data_tdata_real <= fft_data_mem_real[i-1];si_axis_data_tdata_img <= fft_data_mem_image[i-1];end
end always @(posedge clk) beginif(i == 0) i <= 0;else if(i <= 1024) beginif(!flag_fft)beginif(s_axis_data_tready && s_axis_data_tvalid) i <= i+1;else ;endelse beginif(si_axis_data_tready && si_axis_data_tvalid) i <= i+1;else ;endendelse if(i < 1050) i <= i+1;else ;
endalways @(posedge clk)
beginif(!flag_fft) beginif((i>=1) && (i<=1023)) s_axis_data_tvalid <= 1'b1; else s_axis_data_tvalid <= 1'b0;endelse beginif((i>=1) && (i<=1023)) si_axis_data_tvalid <= 1'b1; else si_axis_data_tvalid <= 1'b0;end
endalways @(posedge clk)
beginif(!flag_fft) beginif(i==1023) s_axis_data_tlast <= 1'b1;else s_axis_data_tlast <= 1'b0; endelse beginif(i==1023) si_axis_data_tlast <= 1'b1;else si_axis_data_tlast <= 1'b0; end
end//文件存储IP核输出数据integer w1_file,w2_file,w3_file;initial w1_file = $fopen("./fft_result_real.txt","w");
initial w2_file = $fopen("./fft_result_image.txt","w");
initial w3_file = $fopen("./ifft_result.txt","w"); integer k = 0; always @(posedge clk)
beginif(m_axis_data_tvalid && !flag_fft) begin$fwrite(w1_file, "%x\n", m_axis_data_tdata_real);$fwrite(w2_file, "%x\n", m_axis_data_tdata_img); fft_data_mem_real[k] <= m_axis_data_tdata_real;fft_data_mem_image[k] <= m_axis_data_tdata_img;k <= k+1;endelse if(mi_axis_data_tvalid && flag_fft) begin$fwrite(w3_file, "%x\n", mi_axis_data_tdata_real[25:10]);end
end endmodule