FPGA数字信号处理(九)Vivado FFT IP核实现

该篇是FPGA数字信号处理的第9篇,选题为DSP系统中极其常用的FFT运算。上篇介绍了Quartus环境下FFT IP核的使用“FPGA数字信号处理(八)Quartus FFT IP核实现https://blog.csdn.net/fpgadesigner/article/details/80690345 ”。本文将介绍在Vivado开发环境下使用Xilinx提供的FFT IP核进行FFT运算的设计。


IP核概述

这里写图片描述
Xilinx的FFT IP核属于收费IP,但是不需要像 Quartus那样通过修改license文件来破解。如果是个人学习,现在网络上流传的license破解文件在破解Vivado的同时也破解了绝大多数可以破解的IP核。只要在IP Catalog界面中Fast Fourier Transform的License状态为“Included”即可正常使用。


IP核参数设置

与Quartus中FFT IP核相比,Vivado的FFT IP核配置起来更复杂,功能也更强大。 打开主界面,左边是IP核的接口图(IP Symbol)、实现消耗的资源等信息(Implementation Details)和计算FFT所需的时间(Latency),右边是Configuration、Implementation和Detailed Implementation三个标签卡。

这里写图片描述
Vivado的FFT IP核支持多通道输入(Number of Channels)和实时更改FFT的点数(Run Time Configurable Transform Length)。Configuration标签下设置FFT的点数(Transform Length)和工作时钟(Target Clock Frequency),选择一种FFT结构,包括流水线Streaming、基4 Burst、基2 Burst和轻量级基2 Burst,计算速度和消耗的资源依次减少。
这里写图片描述
Implementation标签卡下设置FFT的数据格式为定点Fixed Point或浮点Float Point;设置输入数据的位宽和相位因子位宽(相当于旋转因子)。还有一些可选的附加信号。“Output Ordering”设置FFT计算结果以自然顺序(Nature Order)或位/数值反序(Bit/Digit Reversed Order)输出。
这里写图片描述
Detailed Implementation这个Tab中设置优化方式、存储的类型、是否使用DSP单元等与综合、实现有关的信息,比如可以选择复数乘法器和蝶形运算单元的实现结构。

设置完成后,系统会在Latency中显示出计算FFT所需的时间,如下图所示:
这里写图片描述
可以据此衡量计算速度是否满足设计需求,如不满足可以使用更好性能的FFT结构或选择可以提高计算速度的优化选项,消耗更多的资源来缩短计算周期。


FPGA设计

IP核的接口在Verilog HDL中进行设计时,一定要参考官方文档中给出的时序图。在IP核的配置界面点击“documentation”,可以找到IP核的user guide。 也可以在Xilinx官网或DocNav工具中搜索pg109,查阅FFT IP核的说明。Burst模式、自然顺序输出的时序图如下:
这里写图片描述
驱动接口时序的Verilog HDL示例代码如下所示:

`timescale 1ns / 1ps
//--------------------------------------------------------
//  使用Xilinx FFT IP核完成FFT运算
//--------------------------------------------------------
module Xilinx_FFT_Guide_liuqi(      

        input aclk,
        input aresetn,

        input [11:0]    input_data_ch1,
        output [23:0]   fft_real,
        output [23:0]   fft_imag,
        output reg [46:0]   amp,
        output fft_out_valid
);
    reg [11:0] input_data_ch1_reg;    
    wire [7:0] s_axis_config_tdata;
    reg s_axis_config_tvalid;

    wire s_axis_data_tready;
    reg [31:0] s_axis_data_tdata;
    reg s_axis_data_tvalid;
    reg s_axis_data_tlast;

    wire [47:0] m_axis_data_tdata;
    wire [15:0] m_axis_data_tuser;
    wire m_axis_data_tvalid;
    wire m_axis_data_tlast;
    reg [7:0]   cfg_cnt;
    reg [13:0]  sink_ctl_cnt;   
    reg [23:0]  fft_real,fft_imag; 
    reg fft_out_valid;
    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;

    xfft_0 usr_FFT(

            .aclk(aclk),
            .aresetn(aresetn),

            .s_axis_config_tdata(s_axis_config_tdata),
            .s_axis_config_tvalid(s_axis_config_tvalid),
            .s_axis_config_tready(),

            .s_axis_data_tdata(s_axis_data_tdata),
            .s_axis_data_tvalid(s_axis_data_tvalid),
            .s_axis_data_tready(),
            .s_axis_data_tlast(s_axis_data_tlast),

            .m_axis_data_tdata(m_axis_data_tdata),
            .m_axis_data_tuser(m_axis_data_tuser),
            .m_axis_data_tvalid(m_axis_data_tvalid),
            .m_axis_data_tready(1'b1),
            .m_axis_data_tlast(m_axis_data_tlast),

            .event_frame_started(event_frame_started),
            .event_tlast_unexpected(event_tlast_unexpected),
            .event_tlast_missing(event_tlast_missing),
            .event_status_channel_halt(event_status_channel_halt),
            .event_data_in_channel_halt(event_data_in_channel_halt),
            .event_data_out_channel_halt(event_data_out_channel_halt)
    );   

//////////////////////////////fft core config////////////////////////
    always@(posedge aclk or negedge aresetn)
    begin
        if(!aresetn)
            cfg_cnt <= 8'd0;
        else
        begin
            if(cfg_cnt < 8'd200)
                cfg_cnt <= cfg_cnt + 1'b1;
            else
                cfg_cnt <= cfg_cnt;
        end
    end

    always@(posedge aclk or negedge aresetn)
    begin
        if(!aresetn)
            s_axis_config_tvalid <= 1'b0;
        else
        begin
            if(cfg_cnt < 8'd200)
                s_axis_config_tvalid <= 1'b1;
            else
                s_axis_config_tvalid <= 1'b0;
        end
    end
assign s_axis_config_tdata = 8'd1;

/////////////////////////////fft sink_in ctl/////////////////////////
    always@(posedge aclk or negedge aresetn)
    begin
        if(!aresetn)
            s_axis_data_tdata <= 32'b0;
        else
            s_axis_data_tdata <= {20'd0,input_data_ch1};
    end

    always@(posedge aclk or negedge aresetn)
    begin
        if(!aresetn)
            sink_ctl_cnt <= 14'd8194;
        else if(s_axis_config_tvalid)
            sink_ctl_cnt <= 14'd0;
        else if(sink_ctl_cnt == 14'd8192)
            sink_ctl_cnt <= 14'd1;
        else
            sink_ctl_cnt <= sink_ctl_cnt + 1'b1;
    end 

    //s_axis_data_tvalid
    always@(posedge aclk or negedge aresetn)
    begin
        if(!aresetn)
            s_axis_data_tvalid <= 1'b0;
        else if(sink_ctl_cnt < 14'd1)
            s_axis_data_tvalid <= 1'b0;
        else if(sink_ctl_cnt < 14'd2049)
            s_axis_data_tvalid <= 1'b1;
        else
            s_axis_data_tvalid <= 1'b0;
    end

    //s_axis_data_tlast
    always@(posedge aclk or negedge aresetn)
        begin
        if(!aresetn)
            s_axis_data_tlast <= 1'b0;
        else
        begin
            if(sink_ctl_cnt == 14'd2048)
                s_axis_data_tlast <= 1'b1;
            else
                s_axis_data_tlast <= 1'b0;
        end
    end
/////////////////////////////fft sink_in ctl/////////////////////////    
    always@(posedge aclk)
    begin
        fft_real <= m_axis_data_tdata[23:0];
    end

    always@(posedge aclk)
    begin
        fft_imag <= m_axis_data_tdata[47:24];
    end

    always@(posedge aclk)
    begin
        fft_out_valid <= m_axis_data_tvalid;
    end

    /********** 计算频谱的幅值信号 **********/

    wire signed [45:0] xkre_square, xkim_square;
    assign xkre_square = fft_real * fft_real;
    assign xkim_square = fft_imag * fft_imag;

    always @(posedge aclk)
      amp <= xkre_square + xkim_square;

endmodule

注意FFT计算结果输出的实部和虚部供用m_axis_data_tdata数据总线,因此在代码中需要截位分别得到实部和虚部。FFT计算结果的分析可以参考“FPGA数字信号处理(八)Quartus FFT IP核实现https://blog.csdn.net/fpgadesigner/article/details/80690345 ”中的内容。


仿真与工程下载

系统时钟(即FFT计算时钟)为50MHz,因此频谱范围为0~25MHz。使用MATLAB生成2MHz与15MHz的正弦波信号,分别写入txt文件。编写Testbench分别读取两个txt文件对两个单频信号做FFT分析,文件操作方法参考“Testbench编写指南(一)文件的读写操作”https://blog.csdn.net/fpgadesigner/article/details/80470972

首先仿真对2MHz信号的FFT分析。根据上文Latency中的估计,计算时间需要145μs,因此仿真需要运行到大约150μs以上。结果如下:
这里写图片描述
整个频谱的持续时间恰好是out_valid信号保持高电平的时间,很明显可以看到2MHz信号在频谱中对应的一个频率点。将txt文件替换为15MHz的信号,结果如下:
这里写图片描述
观察到15Mhz信号的频谱峰值位置明显比2MHz频率靠中,符合事实。

完整的Vivado工程(含testbench仿真)可以在这里下载:https://download.csdn.net/download/fpgadesigner/10478838

猜你喜欢

转载自blog.csdn.net/fpgadesigner/article/details/80694673