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

相关推荐
aprilaaaaa5 小时前
(TC397 学习笔记)二、ICU和TIM的时钟计算
笔记·学习·fpga开发
X_xcccc8 小时前
2026高端FPGA开发平台权威评测与选型指南
fpga开发
威嵌神州9 小时前
军工加固计算机七大核心特性解析|国产化、宽温、强加固工程实现
嵌入式硬件·计算机网络·fpga开发
FPGA小迷弟9 小时前
高云半导体深度解读:国产FPGA车规赛道的领跑者
fpga开发
努力搬砖的小H9 小时前
FPGA应用技巧之Block Design基本使用方法
功能测试·fpga开发·zynq·block design
ALINX技术博客9 小时前
【黑金云课堂】FPGA技术教程FPGA 基础:HDMI视频输入与环路输出实验
fpga开发·音视频
hahaha601610 小时前
HLS高层次综合设计--axi之burst纠偏
开发语言·算法·fpga开发·c#
珞光电子USRP SDR软件无线电平台1 天前
国产嵌入式SDR平台LW-E320-G详解:射频、FPGA、ARM架构与应用选型
arm开发·fpga开发·架构
FPGA小迷弟1 天前
【硬核通俗】FPGA实现SDR软件无线电完整技术解析(原理+架构+实战)
fpga开发·dsp开发·sdr·无线电
FPGA开源工坊1 天前
ISP图像处理--Bayer Raw格式
图像处理·人工智能·verilog·fpga·isp