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核配置是否正确,能否对输入数据进行响应,模块化的试验、仿真,可以避免在项目中寻找错误时无法下手。

相关推荐
神电测控13 小时前
新品发布:国产新型三合一多功能PG-ZYNQ7100 sbRIO板卡(PCIe+USB3.0+Ethernet+FMC+4个40pin扩展口)
fpga·zynq·crio·神电测控·王电令·sbrio·linuxrt
ALINX技术博客1 天前
ALINX 亮相 2026 WAIC 世界人工智能大会,展示 AI 视觉 FPGA+GPU 异构计算与电子后视镜解决方案
人工智能·ai·fpga·世界人工智能大会·电子后视镜
仲南音2 天前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner2 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20202 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy3 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia3 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20203 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子3 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
Eloudy4 天前
RFSoC 4x2 参考手册
fpga