全面讲解FFT在Xilinx FPGA上的实现

fft(快速傅里叶变换)作为数字信号处理的核心算法具有重要的研究价值,可应用于傅里叶变换所能涉及的任何领域,如图像处理、音频编码、频谱分析、雷达信号脉冲压缩等数字信号处理领域。fft的鲜明特征之一是计算离散傅里叶变换(dft)的高效算法,把计算n点dft的乘法运算量从n2次降低到n/2*log2n次。而采用fpga实现fft的缘由在于:fpga具有并行处理、流水线处理、易编程、片上资源丰富等方面特点,用于实现高速、大点数的fft优势明显。 本设计使用的软件编程环境是xilinx公司的vivado 2018.3,笔者将从fft ip核的创建,模块文件的编写,波形仿真等方面来具体讲解fft在xilinx fpga上的实现。
1.fft ip核的创建
(1)在vivado软件主界面,打开ip catalog,在搜索框内输入fft,然后找到digital signal processing->transforms->ffts目录下的fast fourier transform,双击进入配置界面。
(2)进入到配置界面,左边是ip核的接口图、实现的一些细节信息和fft的延迟,右边是configuration、implementation和detailed implementation三个标签卡。 vivado的fft ip核支持多通道输入(number of channels)和实时更改fft的点数(run time configurable transform length)。configuration标签下可设置fft的点数(transform length)和工作时钟(target clock frequency),以及选择一种fft结构。fft的结构包括流水线streaming、基4 burst、基2 burst和轻量级基2 burst,它们的计算速度和消耗的资源依次减少,可根据工程实际进行选择。
implementation标签卡下可设置fft的数据格式为定点fixed point或浮点float point;输出截位方式选择:不截位(unscaled),截位(scaled),块浮点(block floating point);设置输入数据的位宽和相位因子位宽。还有一些可选的附加信号,如时钟使能(aclken),复位信号(aresetn,低有效)等。“output ordering”用以选择fft计算结果以自然顺序(nature order)或位倒序(bit/digit reversed order)输出。
detailed implementation里可设置优化方式、存储的类型。存储类型分为两种:block ram(块ram)和distributed ram(分布式ram);优化方式可选择资源最优或者速度最优。
(3)配置完成后,可在latency下看到计算fft所需的时间,可以以此衡量设计是否满足实时处理的要求。如不满足,可选择性能更好的fft结构或选择可以提高运算速度的优化选项
2.模块文件的编写 ip核工作必须要满足一定的时序要求,所以需要将数据按照一定时序送入ip核。ip核交互是用axi-stream接口,关于axi-stream接口的时序可自行查一些相关资料,这里不做详细介绍。简言之,axi-stream接口分为主机(master)和从机(slave),主机为发起端,从机为响应端,只有ready信号和valid信号同时为高时数据才能被有效写入或读出。举个例子,主机检测从机发出的ready信号,当为高时将valid信号拉高即可从从机读出或向从机写入数据。   module fft_test( input clk, input rst_n, input  tvalid_i, input  [31:0] tdata_i, //input  fft_s_data_tlast, output fft_s_config_tready,   output     
    fft_s_data_tready, output [47:0] fft_m_data_tdata, output   
      fft_m_data_tvalid, output       
  fft_m_data_tlast, output [7:0]   
fft_m_data_tuser, output       
  fft_event_frame_started, output     
    fft_event_tlast_unexpected, output       
  fft_event_tlast_missing, output       
  fft_event_status_channel_halt, output
fft_event_data_in_channel_halt, output     
    fft_event_data_out_channel_halt );   reg  fft_s_data_tvalid=1'b0; reg  [31:0] fft_s_data_tdata=32'd0; reg  fft_s_data_tlast=1'b0; reg[7:0]  count=8'd0;  
always @(posedge  clk)  begin   if(!rst_n) begin  
  fft_s_data_tvalid<=1'b0;    
fft_s_data_tdata<=32'd0;     fft_s_data_tlast<=1'b0;    
count<=8'd0;   end   else if (tvalid_i && fft_s_data_tready) begin  
  if(count==127)begin
       fft_s_data_tvalid<=1'b1;  
 fft_s_data_tlast<=1'b1;    
fft_s_data_tdata<=tdata_i;      
 count<=0;  end
     else begin    
   fft_s_data_tvalid=1'b1;    
   count<=count+1;  
 fft_s_data_tlast<=1'b0;  
 fft_s_data_tdata<=tdata_i;  
   end   end   else begin
    fft_s_data_tvalid<=1'b0; fft_s_data_tlast<=1'b0; fft_s_data_tdata<=fft_s_data_tdata;   end end  
  xfft_0 u_fft(  
  .aclk(clk),
// 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'b1), 
                             // input wire s_axis_config_tvalid  
  .s_axis_config_tready(fft_s_config_tready),   
            // output wire s_axis_config_tready  
  .s_axis_data_tdata(fft_s_data_tdata),           
          // input wire [31 : 0] s_axis_data_tdata  
  .s_axis_data_tvalid(fft_s_data_tvalid),             
      // input wire s_axis_data_tvalid  
  .s_axis_data_tready(fft_s_data_tready),           
        // output wire s_axis_data_tready    
.s_axis_data_tlast(fft_s_data_tlast),                   
  // input wire s_axis_data_tlast  
  .m_axis_data_tdata(fft_m_data_tdata),                   
  // output wire [47 : 0] m_axis_data_tdata    
.m_axis_data_tuser(fft_m_data_tuser),         
            // output wire [7 : 0] m_axis_data_tuser
    .m_axis_data_tvalid(fft_m_data_tvalid),           
        // output wire m_axis_data_tvalid    
.m_axis_data_tready(1'b1),                       
         // input wire m_axis_data_tready
    .m_axis_data_tlast(fft_m_data_tlast),         
            // output wire m_axis_data_tlast  
  .event_frame_started(fft_event_frame_started),       
          // output wire event_frame_started    
.event_tlast_unexpected(fft_event_tlast_unexpected),     
      // output wire event_tlast_unexpected  
  .event_tlast_missing(fft_event_tlast_missing),               
  // output wire event_tlast_missing  
  .event_status_channel_halt(fft_event_status_channel_halt), 
    // output wire event_status_channel_halt  
  .event_data_in_channel_halt(fft_event_data_in_channel_halt), 
  // output wire event_data_in_channel_halt  
  .event_data_out_channel_halt(fft_event_data_out_channel_halt) 
 // output wire event_data_out_channel_halt   ); endmodule
3.功能仿真 模块编写完成后,需要通过功能仿真来验证我们设计逻辑的正确性。进行仿真之前,我们需要编写仿真测试文件(testbench)。
module testbench; reg  clk; reg  rst_n; reg [15:0] dati_in; reg [15:0] datq_in; reg [23:0]
  dati_out; reg [23:0]  datq_out; reg [15:0] 
 datai [127:0]; reg [47:0]  fft_abs;  
reg  fft_s_data_tvalid; wire  [31:0] fft_s_data_tdata; //reg  fft_s_data_tlast; wire fft_s_config_tready; wire   
      fft_s_data_tready; wire [47:0] fft_m_data_tdata; wire       
  fft_m_data_tvalid; wire     
    fft_m_data_tlast; wire [7:0]   
fft_m_data_tuser; wire     
    fft_event_frame_started; wire     
    fft_event_tlast_unexpected; wire     
    fft_event_tlast_missing; wire       
  fft_event_status_channel_halt; wire     
    fft_event_data_in_channel_halt; wire     
    fft_event_data_out_channel_halt;  
initial  begin clk=1; rst_n=0; //fft_s_data_tlast=1'b0; fft_s_data_tvalid=1'b0;
dati_in=0; datq_in=0; dati_out=0; datq_out=0; fft_abs=0;  
$readmemb(c:/users/radar/desktop/science/fpga/fft/y1.txt,datai); #100 rst_n=1; end
  always #5  clk=~clk; reg[7:0]  count=0;  
always @(posedge  clk)  begin
   if (fft_s_data_tready) begin    
 if(count==128) begin  
     fft_s_data_tvalid=1'b0;  
    //fft_s_data_tlast=1'b0;  
 #10000  
     count=0;  
   end else if(count==127)begin    
   dati_in<= datai[count];
       datq_in<=16'd0;    
   fft_s_data_tvalid<=1'b1;  
 //fft_s_data_tlast<=1'b1;      
 count<=count+1; 
end    
 else begin      
 dati_in<= datai[count];  
     datq_in<=16'd0;  
     fft_s_data_tvalid=1'b1;  
     count<=count+1;  
 //fft_s_data_tlast<=1'b0;      
end    
end  
end
  assign fft_s_data_tdata = {datq_in,dati_in};  
fft_test u_fft_test( .clk(clk), .rst_n(rst_n), .tvalid_i(fft_s_data_tvalid), .tdata_i(fft_s_data_tdata), //.fft_s_data_tlast(fft_s_data_tlast),
.fft_s_config_tready(fft_s_config_tready), .fft_s_data_tready(fft_s_data_tready), .fft_m_data_tdata(fft_m_data_tdata),
.fft_m_data_tvalid(fft_m_data_tvalid), .fft_m_data_tlast(fft_m_data_tlast), .fft_m_data_tuser(fft_m_data_tuser),
.fft_event_frame_started(fft_event_frame_started),
.fft_event_tlast_unexpected(fft_event_tlast_unexpected), .fft_event_tlast_missing(fft_event_tlast_missing),
.fft_event_status_channel_halt(fft_event_status_channel_halt), .fft_event_data_in_channel_halt(fft_event_data_in_channel_halt),
.fft_event_data_out_channel_halt(fft_event_data_out_channel_halt) );  
always @(posedge clk) begin
if(fft_m_data_tvalid) begin  
  dati_out<=fft_m_data_tdata[23:0];    
datq_out<=fft_m_data_tdata[47:24];    
  end end   always @(posedge clk) begin  
fft_abs<=$signed(dati_out)* $signed(dati_out)+ $signed(datq_out)* $signed(datq_out);
end endmodule testbench中输入的时域波形数据是我们通过matlab生成的,在matlab中我们仿真的是采样率为2khz情况下,频率分别为50hz和200hz的两正弦波叠加后的信号。
n=128; n=1:n; f0=50; f1=200; fs=2e3;
y=sin(2*pi*f0.*n/fs)+2*sin(2*pi*f1.*n/fs);
figure; plot(y); y=fft(y);
figure; plot(abs(y)); y1=y';
q=quantizer([16 12]);
y2=num2bin(q,y1);
fid1=fopen('c:/users/radar/desktop/y1.txt','wt'); for i=1:n
    fwrite(fid1,y2(i,:));  
  fprintf(fid1,' ');
end fclose(fid1); 利用modelsim进行功能仿真时我们将仿真时长设置为20us。为了直观验证fft是否正确,可将输入的时域数据的实部和做完fft后信号功率值的数据格式均设置为anolog(模拟),如下图,可以看到fft后的功率谱为两根独立的谱线,分别代表50hz和200hz两个频率点,和matlab仿真结果一致。
对于该ip核更复杂的应用,大家可以阅读xilinx官方提供的文档,根据自己的实际需要进行设计。


7纳米制程需求量高,台积电计划花费100亿美元扩大其新竹总部的生产设施
功率放大电路的分类和要求
任泽平称苹果灵动岛是伪创新
这才是高端人士该有的手机,金立M2017震撼发布
瑞萨电子推14个IPD 针对汽车应用
全面讲解FFT在Xilinx FPGA上的实现
京东方去年OLED出货量是同期7倍 势头强劲
一个简单的迷你FM发射机电路
区块链对供应商管理有哪些影响
糖果屋联手Augmently开发一款应用程序 为游客们提供身临其境的AR体验
新款荣耀Play曝光搭载麒麟970处理器并首发GPU Turbo技术
一张图看懂小米CC9与CC9e的区别
高通骁龙875 SoC预计使用5nm工艺制造,或将于2020年底发布
京东方造出世界上最大液晶电视屏
微软云史诗般的胜利,值得阿里巴巴和腾讯好好学习
AR学习平台在地质专业考察实训中发挥了什么作用?
协作机器人鼻祖_协作机器人与普通机器人有哪些不同
eyeSight计算机视觉IP产品组合不断升级,注册人机直接互动新专利
TI凭借业内最高集成的分解器传感器接口实现更小、更安全、更准确的旋转位置感测
2020年2月全国汽车市场的销量情况分析