问答 店铺
热搜: ZYNQ FPGA discuz

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

微信扫一扫 分享朋友圈

已有 31 人浏览分享

开启左侧

第九章FFT与IFFT回环实验

[复制链接]
31 0
      傅里叶变换是洞察信号频域特征的“眼睛”,而快速傅里叶变换(FFT)算法则赋予了这双“眼睛”实时观察的能力。本章我们将进行一个标志性的综合实验:FFT/IFFT回环分析。我们将首先生成一个由1MHz和5MHz正弦波叠加的时域混合信号;然后利用FFT将其变换到频域,清晰地分离并观察这两个频率成分;最后,再将频域结果通过逆FFT(IFFT) 变换回时域,目标是完美地重建出原始混合信号。
      这个“时域→频域→时域”的完整回环,是验证任何频域算法和处理链路正确性的黄金标准。通过本章,您不仅将理解FFT/IFFT的核心原理及其在信号分析中的不可替代性,还将学习如何使用Xilinx FFT IP核完成正/逆变换、如何通过求模运算获取频谱幅度,以及如何设计寻峰模块来自动识别信号中的主要频率成分。这为您未来实现频谱分析仪、OFDM通信系统等复杂应用奠定了坚实基础。
1 项目目标
      用DDS分别产生采样率为50MHz,频率为1MHz以及采样率为50MHz,频率为5MHz的正弦信号,将它们混合,然后将这个混合信号过FFT处理,观察输出的结果是否与MATLAB仿真的结果一致,接着再将这个结果进行IFFT复原这个混合信号,如果成功复原实验成功。


2 FFT 简介
      想象一下,你听到了一段复杂的音乐,里面有钢琴、吉他、鼓声和小提琴。你的耳朵能自然地分辨出这些不同的乐器声。傅里叶变换(Fourier Transform)就是一种数学工具,它能像你的耳朵一样,将一段复杂的信号(比如音乐声波)分解成它所包含的各种简单的正弦波(比如不同乐器的音高)。
FFT(Fast Fourier Transform) 并不是一种新的变换,而是一种快速计算离散傅里叶变换(DFT)的算法。
      简单来说:
            目标 (DFT):将一个信号从“时域”转换到“频域”。
            方法 (FFT):一种实现这个转换的、极其高效的计算方法。

1. 核心概念:时域 vs. 频域
      理解FFT,首先要理解两个看待信号的角度:
时域 (Time Domain)
      这是我们最熟悉的视角。它描述了信号的振幅(大小)如何随时间变化。
      比喻:一段音乐的乐谱,它告诉你在哪个时间点弹奏哪个音符。
频域 (Frequency Domain)
      它描述了信号由哪些频率的波组成,以及每个频率波的强度(振幅)和相位。
      比喻:一个音乐频谱分析仪,它告诉你这段音乐里包含了哪些音高 (频率),以及每个音高有多响亮 (振幅)。它不关心这些音高出现的时间顺序。
从时域到频域的转换,就是傅里叶变换要做的事。FFT就是完成这个转换的“高速公路”。

2. FFT 的核心价值:效率!
      为什么FFT如此重要,甚至被誉为“20世纪最重要的算法之一”?答案是效率。
      直接计算离散傅里叶变换(DFT)的计算复杂度是 O(N²)。
      而FFT算法的计算复杂度是 O(N log N)。
这二者差距有多大?举个例子:
      假设我们有 N = 1024 个数据点。
      用 DFT 计算:大约需要 1024 × 1024 ≈ 1,000,000 次运算。
      用 FFT 计算:大约需要 1024 × log₂(1024) = 1024 × 10 ≈ 10,000 次运算。
      FFT比直接计算快了将近 100 倍!当数据点N更大时,这种优势会变得更加惊人。正是这种巨大的效率提升,使得许多需要实时处理信号的应用成为了可能。没有FFT,今天的数字通信、音频处理、图像处理等领域都将无法实现。

3. FFT 的核心思想:分治法 (Divide and Conquer)
      FFT是如何做到这么快的呢?它的核心思想是分治法。
      最著名的FFT算法是库利-图基算法 (Cooley-Tukey Algorithm),其基本思路是:
      分解 (Divide):将一个大规模的DFT计算问题,巧妙地分解成两个规模减半的子问题。例如,将一个1024点的DFT分解成两个512点的DFT。
      解决 (Conquer):递归地对子问题进行分解,直到分解为最简单的1点DFT(1点DFT的计算结果就是它本身,非常简单)。
      合并 (Combine):将子问题的计算结果巧妙地合并起来,得到最终大规模问题的解。
      通过利用数据中的周期性和对称性,FFT避免了大量的重复计算,从而实现了计算速度的飞跃。
注意:FFT算法通常要求数据点的数量N是2的整数次幂(如64, 256, 1024, 4096等),这样分解过程最为高效。


4. FFT 的主要应用领域
      FFT的应用无处不在,是数字信号处理的基石。
通信工程:
      4G/5G/Wi-Fi (OFDM技术):现代无线通信的核心技术OFDM,就是利用FFT和其逆变换(IFFT)来高效地在多个频率上同时传输数据,极大地提高了频谱利用率和抗干扰能力。
音频处理:
      频谱分析:音乐播放器里的频谱可视化效果。
      均衡器 (EQ):增强或减弱特定频率(如低音、高音)的声音。
      降噪:识别并消除特定频率的噪声(如电流声)。
图像处理:
      图像滤波:在频域中进行高通(锐化)或低通(模糊)滤波。
      图像压缩:JPEG压缩算法就利用了与FFT相关的离散余弦变换(DCT)。
科学与工程:
      振动分析:分析桥梁、建筑或机器的振动频率,以检测结构问题。
      医疗成像:核磁共振(MRI)的图像重建过程严重依赖FFT。
      天文学:分析来自天体的光或射电信号的频谱。
数据分析:
      在股票市场、天气数据等时间序列数据中寻找周期性规律。


3 MATLAB仿真
我们先在MATLAB上生成这个混合信号,代码如下:

clc;
clear;
close all;

fs=50e6;%采样率50M
N=1024;%采样点数1024
t=0:N-1;
t=t/fs; %时间序列
f1=1e6;%频点1 1MHZ
f2=5e6;%频点2 5MHZ

s1=sin(2*pi*f1*t);%信号1
s2=sin(2*pi*f2*t);%信号2

%mixsign=s1.*s2;%混频 sin值相乘
mixsign=s1 + s2;%混频 sin值叠加

fftsign=fft(mixsign);%求fft
fftabs=abs(fftsign);%取模运算

%绘制信号的时域波形
subplot(2,1,1);
plot(mixsign);
xlabel('时间/幅度');
ylabel('幅度/V');

%绘制信号FFT后的频域波形
subplot(2,1,2);
plot(fftabs);
xlabel('FFT的位置');
ylabel('FFT的模');

运行后可得一下现象:
image.jpg


4 FPFA 实现
       由于FFT纯verilog实现过于复杂,这里使用官方的IP核来进行演示,其中的cordic开方,也可以不用省略用其实数和复数的平方和除以2近似,而寻峰模块也可以省了,因为官方的IP核有位置信号(XK_INDEX),这个代码也已经给出。
       对于cordic的配置看参考具体工程,这里不过多介绍,同样,对于寻峰模块也不过多介绍,代码有比较完整的注释,也不过多介绍。
1.XFFT IP 介绍
1.1.在vivado工程界面左栏点击 IP Catalog 搜索 FFT,点击进入IP配置。
image.jpg
1.2第一个配置界面为Configuration Tab,如下图所示:
image.jpg
Configuration Tab
       Channels:从1到12选择通道数。多通道操作可用于三种突发I/O架构。当使用多通道时,控制逻辑在数据路径上共享,从而节省资源,尽管额外的扇出和路由可能会降低可实现的时钟速率。对于浮点格式,通道必须为1。
       Transform Length:选择所需的点大小。从8到65536的所有2次方都可用。
       Target Clock Frequency:目标时钟频率,这个设置越大计算速度越快。
       Target Data Throughput:目标数据吞吐量。
       Target Data Throughput 为输入数据的采样率,
       Target Clock Frequency * SSR = Target Data Throughput,SSR默认1。
       Architecture Choice:选择一个实现选项,如架构选项中所述。
       流水线流式I/O、Radix-2 Burst I/O和Radix-2 Lite Burst I/O架构支持8到65536的点大小。
       Radix-4 Burst I/O架构支持64到65536的点大小。
       SSR(Super Sample Rate) 是 AMD(原 Xilinx)在其 FFT IP 核中支持的一种并行处理机制。它表示 FFT 运算中同时并行处理的输入样本数,即 FFT 并行度。当你使用 SSR = 2 或 4 时(不论是原生浮点 native floating-point 还是定点 fixed-point 实现):支持的 FFT 点数范围是 从 8 点到 65536 点;如果你选择的是 除了 SSR = 2, 4 以外的 SSR 值(比如 SSR = 8, 16, 32, 64 等):那么你的 FFT 点数必须 ≥ SSR,即最小点数限制被提升了。
       选中自动选择以选择满足指定目标数据吞吐量的最小实现,前提是在FPGA上实现FFT内核时实现了指定的目标时钟频率。
       目标时钟频率和目标数据吞吐量仅用于自动选择实现和计算延迟。内核不能保证以指定的目标时钟频率或目标数据吞吐量运行。
       只有流水线流I/O支持原生浮点格式。自动选择适用,最终将核心配置为流水线流I/O。
       Transform Length Options:选择转换长度是否可在运行时配置。当转换长度不可在运行时配置时,内核使用更少的逻辑资源,并具有更快的最大时钟速度。当选择原生浮点格式或定点SSR>1时,此选项将被禁用。

1.3第二个配置界面为Implementation Tab,如下图所示:
image.jpg
Implementation Tab
       Data Format:选择输入和输出数据采样是定点格式,还是IEEE-754单精度(32位)浮点格式。当内核处于多通道配置中时,浮点格式不可用。第三种称为**原生浮点格式(native Floating-point format)**的选项,在 Versal 器件中使用的是 DSPFP32 原语(primitives)。传统浮点选项(legacy Floating-point option)已被迁移为伪浮点格式(pseudo Floating-point)。定点格式(Fixed-point)和原生浮点格式(native Floating-point)均支持超采样率(SSR, Super Sample Rate)。“传统浮点”现在被归类为“伪浮点”(这可能意味着它内部仍使用定点实现+缩放逻辑)。
       Scaling Options:对于所有架构,有三种选择:
       Scaled 缩放,用户定义的缩放计划决定了如何在FFT阶段之间缩放数据。
       unscaled 不做缩放,所有整数位增长都被传送到输出。这可以使用更多的FPGA资源。
       Block Floating-Point 块浮点,IP核确定需要多少缩放才能最好地利用可用的动态范围,并将缩放因子报告为块指数。
       Rounding Modes:舍入模式,truncated :直接截断;convergent rounding:收敛性舍入,当数字的小数部分正好等于二分之一时,如果数字是奇数,则收敛舍入向上舍入;如果数字是偶数,则向下舍入。
       Precision Options:输入数据(Input data)和相位因子(phase factors)可以独立设置为 8 到 34 位的位宽(bit width);**当数据格式为浮点(Floating-Point)**时:输入数据的位宽固定为 32 位(即 IEEE-754 单精度格式);相位因子的位宽可以设置为 24 或 25 位,具体取决于所需的噪声性能和可用的硬件资源;对于“原生浮点”(native Floating-point)格式,相位因子的位宽固定为 32 位;对于定点格式(Fixed-point)并且 SSR > 1 的情况:相位因子的位宽固定为 19 位;输入数据的位宽可以设置为:8 到 18 位,当 FFT 变换长度(transform length)小于 65536;8 到 16 位,当变换长度为 65536。
       Super Sample Rate:原生浮点格式和定点格式支持每个时钟周期的多个样本。原生浮点的SSR活动值为2、4、8、16、32和64。对于定点,SSR值为1、2和4。对于伪浮点格式,SSR固定为1。
       Control Signals:控制信号配置,时钟使能 ( aclken ) 和同步清除 ( aresetn ) 是可选引脚。
复位信号aresetn要勾选,至少保持两个时钟的低电平,可复位整个IP。如果未选择某个选项,可以节省一些逻辑资源,并且可以获得更高的时钟频率。
       Output Ordering:输出数据位序排序,Bit/Digit Reversed Order,位/数字逆序;Natural Order,自然顺序。选择以在选择原生浮点或定点SSR>1时执行逆FFT。在此架构中,所有运行时可配置选项都被禁用,包括INV/FWD选项。
       输出数据选择是位/数字反转顺序或自然顺序。基于Radix-2的架构(流水线流I/O、Radix-2 Burst I/O和Radix-2 Lite Burst I/O)提供位反转排序,基于Radix-4的架构(Radix-4 Burst I/O)提供数字反转排序。对于流水线流I/O架构,选择自然顺序输出排序会增加核心使用的内存。对于Burst I/O架构,选择自然顺序输出会增加整体转换时间,因为需要单独的卸载阶段。
       如果输出顺序为自然顺序,则可以选择循环前缀插入。循环前缀插入适用于原生浮点SSR流水线I/O架构以外的所有架构,通常用于OFDM无线通信系统。
       对于原生浮点SSR和固定点SSR>1流水线流I/O架构,输出排序固定在自然顺序,但这些架构中没有循环前缀插入。
       Optional Output Fields :可选输出字段,XK_INDEX是可选字段“数据输出通道”, 是否输出FFT 变换的结果索引,可在m_axis_data_user中有相应的字段。OVFLO是数据输出通道和状态通道中的可选字段,是变换中溢出的指示信号,对应event_fft_overflow.当选择原生浮点或定点SSR>1时,既没有XK_INDEX也没有OVFLO。
       Throttle Schemes :节流方案选择性能和数据时序要求之间的权衡。Real Time 实时模式通常提供更小、更快的设计,但对必须提供和使用数据的时间有严格的限制。Not Real Time 非实时模式没有这样的限制,但设计可能更大、更慢。


1.4第三个配置界面为Detailed Implementation,如下图所示:
image.jpg
Detailed Implementation:
       Memory Options:内存选项,受数据格式和相位因子影响,可配置选择块 RAM 或分布式 RAM。
       Optimize Options:优化选项Complex Multipliers 复数乘法器
       Use CLB logic,使用CLB逻辑,适用于性能要求较低的目标应用程序或具有很少 DSP 片的目标设备。
       Use 3-multiplier structure (resource optimization),使用3乘法结构(资源优化),所有复数乘法器均使用三个实数乘法、五个加/减结构,其中乘法器使用 DSP Slice。这减少了 DSP 切片数量,但使用了一些切片逻辑。这种结构可以利用DSP Slice预加器来减少或消除对额外Slice逻辑的需要,并提高性能。
       Use 4-multiplier structure (performance optimization) 使用4乘法器结构(性能优化),所有复数乘法器均使用四个实数乘法、两个加/减结构,并使用 DSP 片。这种结构可产生最高的时钟性能,但需要更多的专用乘法器。在具有 DSP Slice 的设备中,加/减运算在 DSP Slice 内实现。对于原生浮点数据格式,使用 4 乘法器结构。
       Butterfly Arithmetic 蝶形运算
       Use CLB logic 使用CLB逻辑
       Use XtremeDSP Slices 使用 XtremeDSP 切片,此选项强制使用 DSP 片中的加法器/减法器来实现所有蝶形级。对于原生浮点数据格式和定点SSR>1,蝶形级使用DSP切片实现


2.代码如下
顶层代码如下,具体子模块可参考配套demo:
`timescale 1ns / 1ps

module top (
    input                               I_sysclk_p                 ,//100Mhz差分时钟
    input                               I_sysclk_n                 ,//100Mhz差分时钟
    input                               I_rst_n                    ,
    output                              O_led            
);
assign O_led = 1'b0;            
/******************************************************************\
                          变量声明
\******************************************************************/
//clock
    wire                                clk_50m                     ;
    wire                                locked                      ;

//DDS
    wire                                dds_5m_data_tvalid          ;
    wire    signed     [   7: 0]        dds_5m_data_tdata           ;
    wire                                dds_5m_phase_tvalid         ;
    wire               [  31: 0]        dds_5m_phase_tdata          ;

    wire                                dds_1m_data_tvalid          ;
    wire    signed     [   7: 0]        dds_1m_data_tdata           ;
    wire                                dds_1m_phase_tvalid         ;
    wire               [  31: 0]        dds_1m_phase_tdata          ;

//叠加
    reg     signed     [  15: 0]        dds_mix                     ;
    reg                                 dds_mix_valid               ;

//XFFT
    //FFT
    //config interface
    wire               [   7: 0]        s_axis_config_tdata         ;
    wire                                s_axis_config_tvalid        ;
    wire                                s_axis_config_tready        ;
    //s_axistream data interface
    wire               [  31: 0]        s_axis_data_tdata           ;
    wire                                s_axis_data_tvalid          ;
    wire                                s_axis_data_tready          ;
    wire                                s_axis_data_tlast           ;
  //m_axistream data interface
    wire               [  31: 0]        m_axis_data_tdata           ;
    wire               [  23: 0]        m_axis_data_tuser           ;
    wire                                m_axis_data_tvalid          ;
    wire                                m_axis_data_tready          ;
    wire                                m_axis_data_tlast           ;
    //m_axistream status interface
    wire               [   7: 0]        m_axis_status_tdata         ;
    wire                                m_axis_status_tvalid        ;
    wire                                m_axis_status_tready        ;
    //m_axistream event interface
    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  ;
    //其他
    wire    signed     [  15: 0]        x_real                      ;
    wire    signed     [  15: 0]        x_imag                      ;
    wire               [   9: 0]        XK_INDEX                    ;
    wire    signed     [  15: 0]        ifft_data                   ;

    //IFFT
    //config interface
    wire               [   7: 0]        ifft_config_tdata           ;
    wire                                ifft_config_tvalid          ;
    wire                                ifft_config_tready          ;

    //s_axistream data interface
    wire                                s_ifft_axis_data_tready     ;

    //m_axistream data interface
    wire               [  31: 0]        m_ifft_axis_data_tdata      ;
    wire               [  23: 0]        m_ifft_axis_data_tuser      ;
    wire                                m_ifft_axis_data_tvalid     ;
    wire                                m_ifft_axis_data_tready     ;
    wire                                m_ifft_axis_data_tlast      ;

    //m_axistream status interface
    wire                                m_ifft_axis_status_tready   ;

//求模
    wire                                cordic_data_valid           ;
    wire               [  79: 0]        cordic_data                 ;
    wire               [  32: 0]        cordic_data_real            ;

//寻峰
    wire               [  32: 0]        data_max1                   ;
    wire               [  32: 0]        data_max2                   ;
    wire               [  32: 0]        data_max3                   ;
    wire               [  32: 0]        data_max4                   ;
    wire               [  10: 0]        data_max_addr1              ;
    wire               [  10: 0]        data_max_addr2              ;
    wire               [  10: 0]        data_max_addr3              ;
    wire               [  10: 0]        data_max_addr4              ;
    wire                                data_max_process_work1      ;
    wire                                data_max_process_work2      ;
    wire                                data_max_process_work3      ;
    wire                                data_max_process_work4      ;

/******************************************************************\
                          时钟管理模块
\******************************************************************/

    clk_wiz_0 clock_and_reset
    (
      .clk_out1(clk_50m),
      .locked(locked),       // output locked

      .clk_in1_p(I_sysclk_p),
      .clk_in1_n(I_sysclk_n)
    );  

/******************************************************************\
                          DDS数据模块
\******************************************************************/

    dds_compiler_0 dds_50_5m (
      .aclk(clk_50m),                                // input wire aclk
      .m_axis_data_tvalid (dds_5m_data_tvalid  ),    // output wire m_axis_data_tvalid
      .m_axis_data_tdata  (dds_5m_data_tdata   ),      // output wire [7 : 0] m_axis_data_tdata
      .m_axis_phase_tvalid(dds_5m_phase_tvalid ),  // output wire m_axis_phase_tvalid
      .m_axis_phase_tdata (dds_5m_phase_tdata  )    // output wire [31 : 0] m_axis_phase_tdata
    );

    dds_compiler_1 dds_50_1m (
      .aclk(clk_50m),                                // input wire aclk
      .m_axis_data_tvalid (dds_1m_data_tvalid  ),    // output wire m_axis_data_tvalid
      .m_axis_data_tdata  (dds_1m_data_tdata   ),      // output wire [7 : 0] m_axis_data_tdata
      .m_axis_phase_tvalid(dds_1m_phase_tvalid ),  // output wire m_axis_phase_tvalid
      .m_axis_phase_tdata (dds_1m_phase_tdata  )    // output wire [31 : 0] m_axis_phase_tdata
    );

/******************************************************************\
                          信号叠加模块
\******************************************************************/
    always @(posedge clk_50m ) begin
        if(~I_rst_n)begin
            dds_mix       <= 0;
            dds_mix_valid <= 0;
        end
        else if (dds_1m_data_tvalid & dds_5m_data_tvalid) begin
            dds_mix       <= dds_1m_data_tdata +  dds_5m_data_tdata ;
            dds_mix_valid <= dds_1m_data_tvalid & dds_5m_data_tvalid;
        end
        else  begin
            dds_mix       <= 0 ;
            dds_mix_valid <= 0 ;
        end
    end

/******************************************************************\
                          FFT模块
\******************************************************************/

//config配置信号
    assign s_axis_config_tvalid = 1'b1;//slave用于握手,告诉master已经准备ok,master可以接收数据
    assign s_axis_config_tdata  = (s_axis_config_tready & s_axis_config_tvalid) ? 8'd1 : s_axis_config_tdata;

//输入数据待处理
    assign s_axis_data_tvalid  = dds_mix_valid;//slave用于握手,告诉master已经准备ok,master可以接收数据
    assign s_axis_data_tdata   = {16'd0,dds_mix};
    assign s_axis_data_tlast   = 1'b0;  //tlast 最后一个有效数据,此处连续传输,故数据一直存在

//输出处理完的数据
    assign m_axis_data_tready = 1'b1;//slave用于握手,告诉master已经准备ok,slave可以接收数据
    assign x_real   = m_axis_data_tdata[15:0]  ;//输出信号实部
    assign x_imag   = m_axis_data_tdata[31:16] ;//输出信号虚部
    assign XK_INDEX = m_axis_data_tuser[9:0]   ;//输出信号坐标

//输入状态信号
    assign m_axis_status_tready = 1'b1;//slave用于握手,告诉master已经准备ok,slave可以接收数据

    xfft_0 FFT_0 (
      .aclk(clk_50m),                                             // input wire aclk
      .aresetn(I_rst_n),                                          // input wire aresetn

      .s_axis_config_tdata(s_axis_config_tdata),                  // input wire [7 : 0] s_axis_config_tdata
      .s_axis_config_tvalid(s_axis_config_tvalid),                // input wire s_axis_config_tvalid
      .s_axis_config_tready(s_axis_config_tready),                // output wire s_axis_config_tready

      .s_axis_data_tdata(s_axis_data_tdata),                      // input wire [31 : 0] s_axis_data_tdata
      .s_axis_data_tvalid(s_axis_data_tvalid),                    // input wire s_axis_data_tvalid
      .s_axis_data_tready(s_axis_data_tready),                    // output wire s_axis_data_tready
      .s_axis_data_tlast(s_axis_data_tlast),                      // input wire s_axis_data_tlast

      .m_axis_data_tdata(m_axis_data_tdata),                      // output wire [31 : 0] m_axis_data_tdata
      .m_axis_data_tuser(m_axis_data_tuser),                      // output wire [23 : 0] m_axis_data_tuser
      .m_axis_data_tvalid(m_axis_data_tvalid),                    // output wire m_axis_data_tvalid
      .m_axis_data_tready(m_axis_data_tready),                    // input wire m_axis_data_tready
      .m_axis_data_tlast(m_axis_data_tlast),                      // output wire m_axis_data_tlast

      .m_axis_status_tdata(m_axis_status_tdata),                  // output wire [7 : 0] m_axis_status_tdata
      .m_axis_status_tvalid(m_axis_status_tvalid),                // output wire m_axis_status_tvalid
      .m_axis_status_tready(m_axis_status_tready),                // input wire m_axis_status_tready

      .event_frame_started(event_frame_started),                  // output wire event_frame_started
      .event_tlast_unexpected(event_tlast_unexpected),            // output wire event_tlast_unexpected
      .event_tlast_missing(event_tlast_missing),                  // output wire event_tlast_missing
      .event_status_channel_halt(event_status_channel_halt),      // output wire event_status_channel_halt
      .event_data_in_channel_halt(event_data_in_channel_halt),    // output wire event_data_in_channel_halt
      .event_data_out_channel_halt(event_data_out_channel_halt)  // output wire event_data_out_channel_halt
    );

/******************************************************************\
                          IFFT模块
\******************************************************************/
//config配置信号
    assign ifft_config_tvalid = 1'b1;//slave用于握手,告诉master已经准备ok,master可以接收数据
    assign ifft_config_tdata  = (ifft_config_tready & ifft_config_tvalid) ? 8'd0 : ifft_config_tdata;

//输出处理完的数据
    assign m_ifft_axis_data_tready = 1'b1;//slave用于握手,告诉master已经准备ok,slave可以接收数据
    assign ifft_data               = m_ifft_axis_data_tdata[15:0];

//输入状态信号
    assign m_ifft_axis_status_tready = 1'b1;//slave用于握手,告诉master已经准备ok,slave可以接收数据

    xfft_1 IFFT_0 (
      .aclk                               (clk_50m                   ),// input wire aclk
      .aresetn                            (I_rst_n                   ),// input wire aresetn

      .s_axis_config_tdata                (ifft_config_tdata         ),// input wire [7 : 0] s_axis_config_tdata
      .s_axis_config_tvalid               (ifft_config_tvalid        ),// input wire s_axis_config_tvalid
      .s_axis_config_tready               (ifft_config_tready        ),// output wire s_axis_config_tready

      .s_axis_data_tdata                  (m_axis_data_tdata         ),// input wire [31 : 0] s_axis_data_tdata
      .s_axis_data_tvalid                 (m_axis_data_tvalid        ),// input wire s_axis_data_tvalid
      .s_axis_data_tready                 (s_ifft_axis_data_tready   ),// output wire s_axis_data_tready
      .s_axis_data_tlast                  (m_axis_data_tlast         ),// input wire s_axis_data_tlast

      .m_axis_data_tdata                  (m_ifft_axis_data_tdata    ),// output wire [31 : 0] m_axis_data_tdata
      .m_axis_data_tuser                  (m_ifft_axis_data_tuser    ),// output wire [23 : 0] m_axis_data_tuser
      .m_axis_data_tvalid                 (m_ifft_axis_data_tvalid   ),// output wire m_axis_data_tvalid
      .m_axis_data_tready                 (m_ifft_axis_data_tready   ),// input wire m_axis_data_tready
      .m_axis_data_tlast                  (m_ifft_axis_data_tlast    ),// output wire m_axis_data_tlast

      .m_axis_status_tdata                (                          ),// output wire [7 : 0] m_axis_status_tdata
      .m_axis_status_tvalid               (                          ),// output wire m_axis_status_tvalid
      .m_axis_status_tready               (m_ifft_axis_status_tready ),// input wire m_axis_status_tready

      .event_frame_started                (                          ),// output wire event_frame_started
      .event_tlast_unexpected             (                          ),// output wire event_tlast_unexpected
      .event_tlast_missing                (                          ),// output wire event_tlast_missing
      .event_status_channel_halt          (                          ),// output wire event_status_channel_halt
      .event_data_in_channel_halt         (                          ),// output wire event_data_in_channel_halt
      .event_data_out_channel_halt        (                          ) // output wire event_data_out_channel_halt
    );

/******************************************************************\
                          FFT求模模块
\******************************************************************/

assign cordic_data_real = cordic_data[32:0];

cordic_0 cordic_translate (
  .aclk(clk_50m),                                        // input wire aclk
  .s_axis_cartesian_tvalid(m_axis_data_tvalid),  // input wire s_axis_cartesian_tvalid
  .s_axis_cartesian_tdata(m_axis_data_tdata),    // input wire [31 : 0] s_axis_cartesian_tdata
  .m_axis_dout_tvalid(cordic_data_valid),            // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(cordic_data)              // output wire [79 : 0] m_axis_dout_tdata
);

/******************************************************************\
                                  寻峰模块
\******************************************************************/

  data_max_top # (
    .DATA_WIDTH                         ('d33                      ),
    .ADDR_WIDTH                         ('d11                      ),
    .DATA_ADDR_START_NUM           ('d0                       ),
    .DATA_ADDR_STOP_NUM            ('d50                      ),
    .DATA_ADDR_STEP                    ('d1                       )
  )
  data_max_top_u1 (
    .i_clk                              (clk_50m                   ),
    .i_rst                              (~locked                   ),
    .i_fft_sum                          (cordic_data_real          ),
    .i_fft_data_tvalid                  (cordic_data_valid         ),
    .o_data_max_process_work            (data_max_process_work1    ),
    .o_data_max                         (data_max1                 ),
    .o_data_max_addr                    (data_max_addr1            )
  );

  data_max_top # (
    .DATA_WIDTH                         ('d33                      ),
    .ADDR_WIDTH                         ('d11                      ),
    .DATA_ADDR_START_NUM                ('d75                      ),
    .DATA_ADDR_STOP_NUM                 ('d125                     ),
    .DATA_ADDR_STEP                     ('d1                       )
  )
  data_max_top_u2 (
    .i_clk                              (clk_50m                   ),
    .i_rst                              (~locked                   ),
    .i_fft_sum                          (cordic_data_real          ),
    .i_fft_data_tvalid                  (cordic_data_valid         ),
    .o_data_max_process_work            (data_max_process_work2    ),
    .o_data_max                         (data_max2                 ),
    .o_data_max_addr                    (data_max_addr2            )
  );

  data_max_top # (
    .DATA_WIDTH                         ('d33                      ),
    .ADDR_WIDTH                         ('d11                      ),
    .DATA_ADDR_START_NUM                ('d850                     ),
    .DATA_ADDR_STOP_NUM                 ('d950                     ),
    .DATA_ADDR_STEP                     ('d1                       )
  )
  data_max_top_u3 (
    .i_clk                              (clk_50m                   ),
    .i_rst                              (~locked                   ),
    .i_fft_sum                          (cordic_data_real          ),
    .i_fft_data_tvalid                  (cordic_data_valid         ),
    .o_data_max_process_work            (data_max_process_work3    ),
    .o_data_max                         (data_max3                 ),
    .o_data_max_addr                    (data_max_addr3            )
  );

  data_max_top # (
    .DATA_WIDTH                         ('d33                      ),
    .ADDR_WIDTH                         ('d11                      ),
    .DATA_ADDR_START_NUM                ('d975                     ),
    .DATA_ADDR_STOP_NUM                 ('d1023                    ),
    .DATA_ADDR_STEP                     ('d1                       )
  )
  data_max_top_u4 (
    .i_clk                              (clk_50m                   ),
    .i_rst                              (~locked                   ),
    .i_fft_sum                          (cordic_data_real          ),
    .i_fft_data_tvalid                  (cordic_data_valid         ),
    .o_data_max_process_work            (data_max_process_work4    ),
    .o_data_max                         (data_max4                 ),
    .o_data_max_addr                    (data_max_addr4            )
  );


   

endmodule


5 仿真验证
仿真代码如下:

`timescale 1ns / 1ps

module tb_top();

//变量定义
  reg I_sysclk_p;
  reg I_rst_n;
  wire O_led;

//信号初始化
  initial begin
    I_sysclk_p = 1;
    I_rst_n = 0;
    #3000 I_rst_n = 1;

  end

//100Mhz时钟
  always #5  I_sysclk_p = ! I_sysclk_p ;

  top  top_inst (
    .I_sysclk_p(I_sysclk_p),
    .I_sysclk_n(~I_sysclk_p),
    .I_rst_n(I_rst_n),
    .O_led(O_led)
  );

endmodule

通过仿真来看,我们的实验是成功的,dds_mix为混合信号,通过FFT处理后,得到四个峰值,这四个峰值可以通过寻峰算法来定位,当然也可以直接观察m_axis_data_tdata(紫色)和XK_INDEX,通过对比MATLAB仿真的结果,四个峰值基本是正确的,说明FFT部分成功;对于IFFT部分,观察ifft_data(橙色)这个信号基本与dds_mix(黄色)一致,故IFFT也是成功的。
image.jpg
image.jpg
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

10

粉丝

150

主题
精彩推荐
热门资讯
    网友晒图
      图文推荐
        
        • 微信公众平台

        • 扫描访问手机版