Vivado中的Xilinx FFT/IFFT IP核详细使用流程介绍

引言

       快速傅里叶变换或反变换(FFT/IFFT)是一种重要的信号分析方法,在各种如图像处理,通信及信号处理等工程领域具有非常重要的应用,因此研究其工程实现具有非常重要的意义。Xilinx公司在其Vivado开发工具中提供了FFT/IFFT的 IP核,供开发人员很方便的调用和使用,因此,本文主要对Vivado中的Xilinx FFT/IFFT IP核使用流程展开详细介绍。

1、FFT/IFFT IP核的创建

        在使用FFT/IFFT IP核之前,需要在Vivado软件中进行创建,配置相关的参数,流程如下:

  • 查找IP核

  • 进行Configuration窗口下的配置

  • 进行Implementation窗口下的配置

  • 进行Detailed Implementation窗口下的配置

  • 配置完成后可以在配置窗口的右边看到实现的一些具体细节

在下面这个图中尤为需要注意的是,当在对S_AXIS_CONFIG_TDATA送控制数据时,1代表该IP核执行FFT功能,0代表该IP核执行IFFT功能。 

2、FFT/IFFT IP核在Modelsim中的实际仿真

     由于IP核实现FFT或IFFT功能只是一个控制信息的不同,因此下面我们以实现FFT功能为例介绍简单的仿真流程。

     由于此次是跨平台仿真,所以需要进行特定仿真环境的搭建,在这我不在详细介绍,具体细节请见如下链接:如何在QuestaSim或ModelSim中使用Vivado的IP进行仿真

     我们仿真所需要的文件是xxx_netlist.v文件,将该文件与我们的测试文件一起添加到Modelsim工程中后,即可进行仿真,该文件位置如下图:

    由于Vivado中的IP核的接口大部分基于AXI4-Stream协议,因此,在编写测试文件之前,要仔细阅读搞清楚AXI4-Stream协议的相关时序,然后才能进行正确的数据读写,具体内容请见如下链接:AXI4-Stream协议总结

    在本次测试仿真中,我们采用的仿真数据是三种频率数据的混合,进行谱分析,频率分别为2KHz,6KHz和9KHz,测试数据由Matlab产生,产生文件如下:

Fs = 20000; %采样频率
N = 2^12; %采样点数
t = 0:1/Fs:N/Fs-1/Fs;%时间跨度
s = sin(2000*2*pi*t) + sin(6000*2*pi*t) + sin(9000*2*pi*t);
figure(1);
subplot(2,1,1);
plot(t,s,'r','LineWidth',1.2);
title('时域波形');
axis([0,100/Fs,-3,3]);
set(gca,'LineWidth',1.2);
%画频谱图
df1=Fs/(N-1);%分辨率
f1=(0:N-1)*df1;%其中每点的频率
%默认做N=4096点FFT
%不同点数的FFT的频率分辨率时不一样的,点数越多,分辨率越高
%如128点FFT即从输入信号中任选128个点进行傅里叶变换,输出结果表示频谱信息
Y1=fft(s);
subplot(2,1,2);
%对称频谱图的一半
plot(f1,abs(Y1),'r','LineWidth',1.2);
title('频谱图');
set(gca,'LineWidth',1.2);

Matlab仿真下的时域波形及频谱图如下图所示: 

Verilog Testbench文件如下: 

`timescale 1 ns / 1 ps
module fft_tb ();

glbl glbl();

reg clk;
reg rst_n;
reg signed [15:0]  Time_data_I [127:0];

wire          fft_s_config_tready;

reg  signed [31:0] fft_s_data_tdata;
reg  			  fft_s_data_tvalid;
wire              fft_s_data_tready;
reg               fft_s_data_tlast;

wire signed [47:0] fft_m_data_tdata;
wire signed [7:0]  fft_m_data_tuser;
wire               fft_m_data_tvalid;
reg                fft_m_data_tready;
wire               fft_m_data_tlast;

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;

reg [7:0] count;

reg signed [23:0]  fft_i_out;
reg signed [23:0]  fft_q_out;
reg signed [47:0]  fft_abs;

initial begin
	clk = 1'b1;
	rst_n = 1'b1;
	#5 rst_n = 1'b0;
	#5 rst_n = 1'b1;
	fft_m_data_tready = 1'b1;
	$readmemh("F:/FPGA_DSP/FFT_IP_Test/BeforeFilterHex.txt",Time_data_I);
end

always #5 clk = ~clk;

//发送时域数据到FFT IP核,主->从
always @(posedge clk or negedge rst_n) 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 (fft_s_data_tready) begin//FFT IP核(从设备)已经准备好接收数据,主设备开始发送有效数据
		if(count == 8'd127)begin
			fft_s_data_tvalid <= 1'b1;
			fft_s_data_tlast<=1'b1;//tlast置1
			fft_s_data_tdata <= {16'd0,Time_data_I[count]};
			count <= 8'd0;
		end
		else begin
			fft_s_data_tvalid <= 1'b1;
			fft_s_data_tlast<=1'b0;//tlast置0
			fft_s_data_tdata <= {16'd0,Time_data_I[count]};
			count <= count + 1'b1;
		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

//取频谱数据出来
always @(posedge clk) begin
	if (fft_m_data_tvalid) begin
		fft_i_out<=fft_m_data_tdata[23:0];
    	fft_q_out<=fft_m_data_tdata[47:24];  
	end
end

always @(posedge clk) begin
  fft_abs<=$signed(fft_i_out)* $signed(fft_i_out)+ $signed(fft_q_out)* $signed(fft_q_out);
end

//fft ip核例化
xfft_0 u_fft(
    .aclk(clk),                                                // 时钟信号(input)
    .aresetn(rst_n),                                           // 复位信号,低有效(input)
    .s_axis_config_tdata(8'd1),                                // ip核设置参数内容,为1时做FFT运算,为0时做IFFT运算(input)
    .s_axis_config_tvalid(1'b1),                               // ip核配置输入有效,可直接设置为1(input)
    .s_axis_config_tready(fft_s_config_tready),                // output wire s_axis_config_tready
    //作为接收时域数据时是从设备
    .s_axis_data_tdata(fft_s_data_tdata),                      // 把时域信号往FFT IP核传输的数据通道,[31:16]为虚部,[15:0]为实部(input,主->从)
    .s_axis_data_tvalid(fft_s_data_tvalid),                    // 表示主设备正在驱动一个有效的传输(input,主->从)
    .s_axis_data_tready(fft_s_data_tready),                    // 表示从设备已经准备好接收一次数据传输(output,从->主),当tvalid和tready同时为高时,启动数据传输
    .s_axis_data_tlast(fft_s_data_tlast),                      // 主设备向从设备发送传输结束信号(input,主->从,拉高为结束)
    //作为发送频谱数据时是主设备
    .m_axis_data_tdata(fft_m_data_tdata),                      // FFT输出的频谱数据,[47:24]对应的是虚部数据,[23:0]对应的是实部数据(output,主->从)。
    .m_axis_data_tuser(fft_m_data_tuser),                      // 输出频谱的索引(output,主->从),该值*fs/N即为对应频点;
    .m_axis_data_tvalid(fft_m_data_tvalid),                    // 表示主设备正在驱动一个有效的传输(output,主->从)
    .m_axis_data_tready(fft_m_data_tready),                    // 表示从设备已经准备好接收一次数据传输(input,从->主),当tvalid和tready同时为高时,启动数据传输
    .m_axis_data_tlast(fft_m_data_tlast),                      // 主设备向从设备发送传输结束信号(output,主->从,拉高为结束)
    //其他输出数据
    .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

仿真结果如下图所示:


版权声明:本文为qq_40230112原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。