Vivado中FFT IP核的使用

原文作者:FPGA设计论坛

        FFT(快速傅里叶变换)作为数字信号处理的核心算法具有重要的研究价值,可应用于傅里叶变换所能涉及的任何领域,如图像处理、音频编码、频谱分析、雷达信号脉冲压缩等数字信号处理领域。FFT的鲜明特征之一是计算离散傅里叶变换(DFT)的高效算法,把计算N点DFT的乘法运算量从N2次降低到N/2*log2N次。而采用FPGA实现FFT的缘由在于:FPGA具有并行处理、流水线处理、易编程、片上资源丰富等方面特点,用于实现高速、大点数的FFT优势明显。

        本设计使用的软件编程环境是Xilinx公司的Vivado 2018.3,笔者将从FFT IP核的创建,模块文件的编写,波形仿真等方面来具体讲解FFT在Xilinx FPGA上的实现。

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结构或选择可以提高运算速度的优化选项

模块文件的编写

        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

功能仿真

        模块编写完成后,需要通过功能仿真来验证我们设计逻辑的正确性。进行仿真之前,我们需要编写仿真测试文件(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,'\n');

end

fclose(fid1);

        利用modelsim进行功能仿真时我们将仿真时长设置为20us。为了直观验证fft是否正确,可将输入的时域数据的实部和做完fft后信号功率值的数据格式均设置为anolog(模拟),如下图,可以看到fft后的功率谱为两根独立的谱线,分别代表50Hz和200Hz两个频率点,和matlab仿真结果一致。

        对于该IP核更复杂的应用,大家可以阅读Xilinx官方提供的文档,根据自己的实际需要进行设计。

  • 3
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值