【FPGA】:ip核----fft

文章目录

  • 一、概述
  • 二、端口说明
  • 三、 ip核的生成
  • 四、例子
  • 五、 参考资料

一、概述

    FFT是一种DFT的高效算法,称为快速傅立叶变换(fast Fourier transform)。傅里叶变换是时域一频域变换分析中最基本的方法之一。在数字处理领域应用的离散傅里叶变换(DFT:Discrete Fourier Transform)是许多数字信号处理方法的基础。
FFT基本上可分为时间抽取法和频率抽取法,而一般的时间抽取法和频率抽取法只能处理长度N=2^M的情况,另外还有组合数基四FFT来处理一般长度的FFT。
所谓抽选,就是把长序列分为短序列的过程,可在时域也可在频域进行。最常用的时域抽选方法是按奇偶将长序列不断地变为短序列,结果使输入序列为倒序,输出序列为顺序排列。
基于频率的抽取法如下所示:
【FPGA】:ip核----fft_第1张图片
【FPGA】:ip核----fft_第2张图片
【FPGA】:ip核----fft_第3张图片
基于时间的抽取方法如下所示:
【FPGA】:ip核----fft_第4张图片
【FPGA】:ip核----fft_第5张图片

二、端口说明

备注: s_axis_代表核作为丛机,m_axis代表核作为主机,数据宽度一般为8的整数倍
【FPGA】:ip核----fft_第6张图片
【FPGA】:ip核----fft_第7张图片
【FPGA】:ip核----fft_第8张图片
【FPGA】:ip核----fft_第9张图片
在这里插入图片描述

三、 ip核的生成

【FPGA】:ip核----fft_第10张图片
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体系结构,该变体使用时间复用方法使用更小的内核执行蝶形运算,代价是转换时间更长

【FPGA】:ip核----fft_第11张图片
【FPGA】:ip核----fft_第12张图片
【FPGA】:ip核----fft_第13张图片
在implementation Details可以看懂具体那些位表示实部,那些表示虚部。
【FPGA】:ip核----fft_第14张图片

四、例子

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
【FPGA】:ip核----fft_第15张图片
输入信号:
【FPGA】:ip核----fft_第16张图片
1024点fft之后的结果:
【FPGA】:ip核----fft_第17张图片
峰值为第52个或者第974个点(假设1024位第1个数,则为1024-974+1=51),则频率为
52*100/1024 = 5.078125MHZ

五、 参考资料

  1. VivadoXilinxFFTIP核v9.0使用详解(附仿真实例)
  2. Vivado IP核:FFT实验(重点,有进制转换在,已经验证正确)
  3. 基于VIVADO 2015.4 的FFT IP核仿真
  4. Vivado2017.4上实现FFT,使用FFT IP核
  5. Vivado FFT IP的使用说明
  6. 数字信号处理(三):Xilinx FFT IP核详解(二
  7. Xilinx Vivado (FFT IP核)
  8. Xilinx FFT IP核的使用
  9. Vivado中的Xilinx FFT/IFFT IP核详细使用流程介绍
  10. Xilinx FFT IP核的使用

你可能感兴趣的:(fpga开发)