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

匿名 (未验证) 提交于 2019-12-03 00:27:02

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



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


与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结构或选择可以提高计算速度的优化选项,消耗更多的资源来缩短计算周期。


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!