Vivado调用FFT IP核进行数据频谱分析

本文进行FFT核调用,对输入的正弦波进行频谱分析。

作者想要做一个FPGA音频频谱分析仪,先进行模块测试,测试内容如下:

首先进行FFT核配置:


元件例化

cs 复制代码
`timescale 1ns / 1ps


module fft_t(
    input              aclk,
    input              aresetn,

    input       [7:0]  adc_data,
    input              s_axis_data_tvalid,
    input              s_axis_data_tlast,
    output             s_axis_data_tready,
    // AXIS data out
    output      [15:0] m_axis_data_tdata,   
    output             m_axis_data_tvalid,
    output             m_axis_data_tlast,
    output      [15:0] m_axis_data_tuser    // 宽度依 IP 设置;这里先用 16 做占位,实际12位
);

  // 固定配置
  wire [15:0] s_axis_config_tdata = {5'b0, 10'b1010101010, 1'b1};
  wire        s_axis_config_tvalid = 1'b1;
  wire        s_axis_config_tready;

  // 拼接输入复数数据(Im=0, Re=adc_data)
  wire [15:0] s_axis_data_tdata = {8'd0, adc_data};



//配置FFT变换核
xfft_0 fft_inst (
  .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_config_tready),
  //数据输入input
  .s_axis_data_tdata(s_axis_data_tdata),
  //输入数据有效信号 信号input
  .s_axis_data_tvalid(s_axis_data_tvalid),
  //可以接受外来信号 信号output
  .s_axis_data_tready(s_axis_data_tready),
  //输入数据最后一个信号 信号input
  .s_axis_data_tlast(s_axis_data_tlast),
  //输出数据(0-7Re,8-15Im)
  .m_axis_data_tdata(m_axis_data_tdata),
  //输出数据有效信号 信号output(0-4096)
  .m_axis_data_tuser(m_axis_data_tuser),
  //输出数据有效信号 信号output
  .m_axis_data_tvalid(m_axis_data_tvalid),
  //从机可以接受信号 信号input
  .m_axis_data_tready(1'b1),
  //输出数据最后一个信号 信号output
  .m_axis_data_tlast(m_axis_data_tlast),
  //其他事件信号
  .event_frame_started(),
  .event_tlast_unexpected(),
  .event_tlast_missing(),
  .event_status_channel_halt(),
  .event_data_in_channel_halt(),
  .event_data_out_channel_halt()
);

endmodule

FFT核的信号输入输出非常多,上手很容易乱,因此在此处作者对每一个信号都进行详细的说明,可以提供对信号的快速查找。

简单例化了fft的ip核之后,对主要引脚进行一个链接,对输入配置部分可以直接进行定义,避免不必要的麻烦,在此处我们没有在运行中改变FFT配置的要求,因此此处直接写死。


仿真文件

cs 复制代码
`timescale 1ns / 1ps
module tb_fft_t;

reg aclk = 0;
always #5 aclk = ~aclk; // 100 MHz

reg aresetn;
initial begin
    aresetn = 0;
    #100;
    aresetn = 1;
end

// DUT 端口
reg  [7:0] adc_data;
reg        s_axis_data_tvalid;
reg        s_axis_data_tlast;
wire       s_axis_data_tready;

wire [15:0] m_axis_data_tdata;
wire        m_axis_data_tvalid;
wire        m_axis_data_tlast;
wire [15:0] m_axis_data_tuser;

// 例化 DUT
fft_t dut(
    .aclk(aclk),
    .aresetn(aresetn),
    .adc_data(adc_data),
    .s_axis_data_tvalid(s_axis_data_tvalid),
    .s_axis_data_tlast(s_axis_data_tlast),
    .s_axis_data_tready(s_axis_data_tready),
    .m_axis_data_tdata(m_axis_data_tdata),
    .m_axis_data_tvalid(m_axis_data_tvalid),
    .m_axis_data_tlast(m_axis_data_tlast),
    .m_axis_data_tuser(m_axis_data_tuser)
);

// ---------------------------
// 简单激励:15 kHz 正弦波,4096 点
// ---------------------------
integer i;
real    theta;
integer samp;

initial begin
    // 初始化
    s_axis_data_tvalid = 0;
    s_axis_data_tlast  = 0;
    adc_data = 0;

    // 等待复位完成
    @(posedge aresetn);
    @(posedge aclk);

    // 生成 4096 点正弦
    for (i = 0; i < 4096; i = i + 1) begin
        // θ = 2π·f·n/Fs = 2π·15000·i/48000
        theta = 2.0 * 3.1415926 * 15000.0 * i / 48000.0;
        samp  = $rtoi($sin(theta) * 127.0); // -127~+127

        adc_data = samp[7:0];
        s_axis_data_tvalid = 1;
        s_axis_data_tlast  = (i == 4095);

        @(posedge aclk);
        while (!s_axis_data_tready) @(posedge aclk);
    end

    s_axis_data_tvalid = 0;
    s_axis_data_tlast  = 0;

    // 等输出结束
    wait (m_axis_data_tlast);
    #1000;
    $stop;
end

endmodule

仿真文件简单产生了一个激励信号,模拟了AXI输入的几个引脚的时序,向FFT核进行数据写入,运行一下仿真文件,查看现象。


波形图

波形如图所示:

以s_axis_data_tlast信号为1处为分界线,在这个信号出现之前,s_axis_data_tvalid&&s_axis_data_tready一直有效,即一直在输入数据,FFT核也在一直接收数据。

当记录满4096个点之后,即满足FFT变换点数要求时,IP核进行快速傅里叶变化,中间一段时间出现了空窗,就是IP核在进行数据处理计算。当m_axis_data_tvalid有效时,FFT开始对外输出结果。每一个索引m_axis_data_tuser对应一个m_axis_data_tdata,索引即为频率分块点,用于计算所在处频率数值,tdata即为变换结果,此处分为虚部和实部,可以通过计算(根号下虚部实部平方和)得出频谱图来观察,也可以通过功率谱查看结果(虚部实部平方和)。


这里我们图方便,也可以通过直接观察的方式来查看结果(对于模拟输入无其他干扰,观察结果比较明显):

频谱分布:

如图,放大波形之后,可以看到变换结果在索引为1280处,存在峰值8200。

通过计算,48000Hz的采样频率,4096的变换点数,得到分辨率为48000/4096=11.71875

11.71875*1280=15000

15000正是我们模拟输入的正弦波频率!

通过这个实验,简单验证了一下我们FFT的IP核配置是否正确,能否对输入数据进行响应,模块化的试验、仿真,可以避免在项目中寻找错误时无法下手。

相关推荐
奋斗的牛马8 小时前
FPGA—ZYNQ学习Helloward(二)
单片机·嵌入式硬件·学习·fpga开发
FPGA_小田老师18 小时前
FPGA调试利器:JTAG to AXI Master IP核详解与实战演练
fpga开发·jtag测试·jtag2axi ip·ddr3自动化
FPGA_小田老师21 小时前
FPGA开发入门:深入理解计数器——数字逻辑的时序基石
fpga开发·verilog·状态机·计数器·计数器设计
碎碎思21 小时前
用 FPGA 实现 PCIe 传输,开源核 LitePCIe 深度解读
fpga开发
9527华安21 小时前
FPGA纯verilog实现JESD204B协议,基于AD9625数据接收,提供2套工程源码和技术支持
fpga开发·jesd204b·ad9625
Shang180989357261 天前
MS2107高性能USB 2.0视频信号和音频采集,支持NTSC/PAL制式,适用于低成本视频采集设备
嵌入式硬件·fpga开发·音视频·硬件工程·信息与通信·dsp开发
学工科的皮皮志^_^1 天前
网口学习理解
经验分享·笔记·嵌入式硬件·学习·fpga开发·以太网
博览鸿蒙1 天前
FPGA高频面试问题整理—附答案
fpga开发
cmc10282 天前
134.FPGA常见管脚与时钟的约束方法
fpga开发