FFT是一种DFT的高效算法,称为快速傅立叶变换(fast Fourier transform)。傅里叶变换是时域一频域变换分析中最基本的方法之一。在数字处理领域应用的离散傅里叶变换(DFT:Discrete Fourier Transform)是许多数字信号处理方法的基础。
FFT基本上可分为时间抽取法和频率抽取法,而一般的时间抽取法和频率抽取法只能处理长度N=2^M的情况,另外还有组合数基四FFT来处理一般长度的FFT。
所谓抽选,就是把长序列分为短序列的过程,可在时域也可在频域进行。最常用的时域抽选方法是按奇偶将长序列不断地变为短序列,结果使输入序列为倒序,输出序列为顺序排列。
基于频率的抽取法如下所示:
基于时间的抽取方法如下所示:
备注: s_axis_代表核作为丛机,m_axis代表核作为主机,数据宽度一般为8的整数倍
Number of Channels: 通道数
Transform Length: FFT的点数
Target clock Frequency: 工作的时钟
Architecture Choice: FFT的结构选择。
FFT IP核提供了四种可选择的计算结构架构,可以在资源和转换时间之间进行权衡,具体包括:
流水线I/O: 允许连续数据处理;
Radix-4突发I/O: 使用迭代方法分别加载和处理数据。使用资源大小比流水线解决方案小,但是转换时间较长;
Radix-2突发I/O: 使用与基-4相同的迭代方法,但蝶形较小。使用资源比基-4更少,但是转换时间更长;
Radix-2 Lite突发I/O: 基于基2体系结构,该变体使用时间复用方法使用更小的内核执行蝶形运算,代价是转换时间更长
在implementation Details可以看懂具体那些位表示实部,那些表示虚部。
matlab数据的生成:
Fs = 100e6; %采样频率
N = 2^10; %采样点数
M=16; %量化位数
t = 0:1/Fs:(N-1)/Fs;%时间跨度
f0=5e6; %频率
s = floor(sin(2*pi*f0*t)*(2^(M-1)-1));
figure(1);
subplot(2,1,1);
plot(t,s,'r','LineWidth',1.2);
title('时域波形');
%画频谱图
f=(0:N-1)*Fs/N;%其中每点的频率
Y1=fft(s,N);
subplot(2,1,2);
% 对称频谱图的一半
plot(f,abs(Y1),'r','LineWidth',1.2);
title('频谱图');
set(gca,'LineWidth',1.2);
% 保存数据为二进制
fp = fopen('data_before_fft.txt','w');
for i = 1:N
if(s(i)>=0)
temp= dec2bin(s(i),M);
else
temp= dec2bin(s(i)+2^M, M);
end
for j=1:M
fprintf(fp,'%s',temp(j));
end
fprintf(fp,'\r\n');
end
fclose(fp);
主程序:
module fft_test();
reg aclk;
reg rst_n;
wire s_axis_config_tready ;
reg signed [31 : 0] s_axis_data_tdata ;
reg s_axis_data_tvalid ;
wire s_axis_data_tready ;
reg s_axis_data_tlast;
wire signed [63 : 0] m_axis_data_tdata ;
wire m_axis_data_tvalid;
reg m_axis_data_tready ;
wire m_axis_data_tlast ;
wire [15 : 0] m_axis_data_tuser ;
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;
reg signed [59 : 0] fft_abs;
reg [26:0] fft_real;
reg [26:0] fft_imag;
wire [59 : 0] data_max;
wire DataMax_Vaild;
wire [15:0] data_max_index;
reg flag=0;
parameter data_num=1024; //仿真数据长度
parameter sample_rate =100;
reg signed [15:0] stimulus[data_num-1:0];
integer s,fid1,j=0;
integer i;
reg [7:0] fs;
/*********************时钟的产生***********************/
initial aclk =0;
always #5 aclk=~aclk;
/*********************fft核的例化***********************/
xfft_0 fft_inst0 (
.aclk(aclk), // input wire aclk
.aresetn(rst_n), // input wire aresetn
.s_axis_config_tdata(8'd1), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid(1'd1), // 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 [63 : 0] m_axis_data_tdata
.m_axis_data_tuser(m_axis_data_tuser), // output wire [15 : 0] m_axis_data_tuser
.m_axis_data_tvalid(m_axis_data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tready(1'b1), // 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
);
/*********************初始化程序***********************/
initial begin
rst_n =0;
s_axis_data_tvalid<=1'b0;
s_axis_data_tdata <= 16'd0;
s_axis_data_tlast <= 1'b0;
$readmemb("E:/vivado_project/fft_ip/matlab/data_before_fft.txt",stimulus);
#20;
rst_n =1;
end
/*********************将数据送入fft ip核***********************/
always@(posedge aclk) begin
if(!rst_n) begin
s_axis_data_tvalid <= 1'b0;
s_axis_data_tdata <=32'd0;
s_axis_data_tlast <=1'b0;
i<=0;
end
else begin
if(i==data_num-1 && s_axis_data_tready==1'b1) begin
s_axis_data_tdata <= {16'd0,stimulus[i]};
s_axis_data_tvalid <= 1'b1;
s_axis_data_tlast <=1'b1;
i<=0;
end
else if(s_axis_data_tready==1'b1 && i<data_num-1) begin
s_axis_data_tdata <= {16'd0,stimulus[i]};
s_axis_data_tvalid <= 1'b1;
s_axis_data_tlast <=1'b0;
i <= i+1;
end
else begin
s_axis_data_tvalid <= 1'b0;
s_axis_data_tdata <=s_axis_data_tdata;
s_axis_data_tlast <=1'b0;
i <= i;
end
end
end
/*********************得到fft之后的实部和虚部***********************/
always@(posedge aclk) begin
if(m_axis_data_tvalid) begin
fft_real<= m_axis_data_tdata[26:0];
fft_imag <= m_axis_data_tdata[58:32];
end
end
/*********************求fft之后幅度值***********************/
always@(posedge aclk) begin
fft_abs <= $signed(m_axis_data_tdata[26:0])* $signed(m_axis_data_tdata[26:0])+ $signed(m_axis_data_tdata[58:32])* $signed(m_axis_data_tdata[58:32]);
end
/*********************找fft模的最大值**********************/
max_get#
(
.num(1024),
.width(60)
)
max_get_inst0
(
.clk(aclk) ,
.reset(~rst_n) ,
.En(m_axis_data_tvalid) ,//寻找最大值模块开始工作的使能信号
.data_in_index(m_axis_data_tuser),
.data_in(fft_abs) ,// 输入数据
.data_max(data_max) ,//搜索得到的最大值
.data_max_index(data_max_index),
.DataMax_Vaild(DataMax_Vaild)
);
//always@(posedge aclk) begin
// if(DataMax_Vaild==1) begin
// if(data_max_index<=512) begin // 因为做1024点的fft
// fs=sample_rate/data_num * data_max_index;
// end
// else
// fs=sample_rate/data_num * (data_num-data_max_index);
// end
//end
endmodule
max_get模块(参考文献5):
// 寻找寻找一串序列(1024个点)的最大值
module max_get#
(
parameter num=1024,
parameter width = 64
)
(
input clk ,
input reset ,
input En ,//寻找最大值模块开始工作的使能信号
input [width-1:0] data_in ,// 输入数据
input [15:0] data_in_index,
output reg [width-1:0] data_max ,//搜索得到的最大值
output reg [15:0] data_max_index,
output reg DataMax_Vaild
);
reg [width-1:0] data_max_temp; //最大值寄存器
reg [15:0] data_max_index_temp;
reg [3:0] ST_max_search;//状态机
reg [15:0] count;//计数器
always@(posedge clk)
begin
if(reset)
begin
data_max_temp<='d0;
data_max_index_temp <= 'd0;
DataMax_Vaild<=1'b0;
ST_max_search<=4'd0;
count<=16'd0;
end
else
begin
case(ST_max_search)
0:
begin
DataMax_Vaild <= 0;
if(En==1'b1)//使能有效,模块开始工作
begin
ST_max_search<=4'd1;
end
else
begin
ST_max_search<=4'd0;
end
end
1:
begin
if(count<num)//还没有比较到1024个点
begin
count<=count+1'd1;
ST_max_search<=4'd1;//
if(data_in>data_max_temp) begin
data_max_temp<=data_in;
data_max_index_temp <= data_in_index;
end
else begin
data_max_temp<=data_max_temp;
data_max_index_temp <= data_max_index_temp;
end
end
else//if(count==16'd1024)
begin
count<=16'd0;
ST_max_search<=4'd0;
data_max<= data_max_temp;
data_max_index <= data_max_index_temp;
data_max_index_temp <= 'd0;
data_max_temp<=64'd0;//最大值数据寄存器数据归零,避免影响下一次寻求最大值
DataMax_Vaild<=1'b1;
end
end
default: begin
ST_max_search<= 4'd0;//跳到状态0 end
DataMax_Vaild <= 0;
end
endcase
end
end
endmodule
仿真结果:
采样率100MHz,输入信号5MHZ
输入信号:
1024点fft之后的结果:
峰值为第52个或者第974个点(假设1024位第1个数,则为1024-974+1=51),则频率为
52*100/1024 = 5.078125MHZ