Xilinx FFT IP核的仿真验证


一、什么是FFT?

  首先来看傅里叶提出的理论:任何连续周期信号可以由一组适当的正弦曲线组合而成。因此傅里叶变换的目的是可将时域上的信号转变为频域的信号,这样可以使我们在时域上不好观察的信号转变一个角度观察,这提供了一个非常好的处理视角,在《【学习笔记】奥本海姆第二版《信号与系统》第五章:离散时间傅里叶变换》我们知道傅里叶变换的公式为:

  1. 连续时间傅里叶变换:

X ( j ω ) = ∫ − ∞ + ∞ x ( t ) e − j ω t d t X(jω)=∫_{-∞}^{+∞}x(t)e^{-jωt}dt X()=+x(t)etdt
x ( t ) = 1 2 π ∫ − ∞ + ∞ X ( j ω ) e j ω t d ω x(t)=\frac{1}{2π}∫_{-∞}^{+∞}X(jω)e^{jωt}dω x(t)=2π1+X()etdω
  函数 X ( j ω ) X(jω) X()称为 x ( t ) x(t) x(t)连续时间傅里叶变换。一个非周期信号的傅里叶变换 X ( j ω ) X(jω) X()通常称为 x ( t ) x(t) x(t)的频谱。

  1. 离散时间傅里叶变换:

x [ n ] = 1 2 π ∫ 2 π X ( e j ω ) e j ω n d ω x[n]=\frac{1}{2π}∫_{2π}X(e^{jω})e^{jωn}dω x[n]=2π12πX(e)ejωndω
X ( e j ω ) = ∑ n = − ∞ + ∞ x [ n ] e − j ω n X(e^{jω})=\sum_{n=-∞}^{+∞}x[n]e^{-jωn} X(e)=n=+x[n]ejωn

   X ( e j ω ) X(e^{jω}) X(e)称为离散时间傅里叶变换,这一对式子就是离散时间傅里叶变换对。与连续时间情况一样,傅里叶变换 X ( e j ω ) X(e^{jω}) X(e)往往称为 x [ n ] x[n] x[n]的频谱。

  FFT也称快速傅里叶变换,是对傅里叶变换公式的一种优化的算法,在N个采样点的DFT中,傅里叶变换的时间复杂度为 O ( N 2 ) O(N^2) O(N2)因为需要进行 N 2 N^2 N2次乘法和加法运算;为FFT的时间复杂度只需要 O ( N l o g N ) O(NlogN) O(NlogN),显著的提高了计算效率,特别是处理采样点越多的信号。

二、FFT后频谱分析

  对于一个长度为N的离散信号的傅里叶变换,变换后会得到N个频率分量;这些频率分量对应于信号在不同频率上的幅度和相位信息。

  1. 频率分量的数量:进行N点离散傅里叶变换后,输出结果是一个包含N个复数的数组,每个复数表示一个频率分量,从0 — N-1。
  2. 第K个频率分量X[K]对应的频率 f k f_k fk的计算公式如下: f k = k ∗ F S N ,( K = 0 , 1 , 2..... N − 1 ; F S 为采样频率) f_k=\frac{k*F_S}{N},(K=0,1,2.....N-1;F_S为采样频率) fk=NkFS,(K=0,1,2.....N1FS为采样频率)
  3. 频谱对称性:对于实值信号,频率分量具有对称性。即X[k]=X[N-k]*,这意味着只需考虑前一半的频率分量(0到N/2)就能获得完整的信息。

2.1 频谱分辨率

Δ f = F S N Δf=\frac{F_S}{N} Δf=NFS
   F S F_S FS为采样频率,N为FFT的点数,意思是只要 F S F_S FS和N都确定了,频谱就确定了。根据采样定理,采样频率要>信号频率的2倍。

2.2 频谱幅度

  假如原始信号的幅度为A,在FFT计算过后,频谱会出现N个点的峰值对应不同频率。第一个点是直流分量(0HZ)的频谱幅度为A*N,其它频率的幅度为N/2。

三、MATLAB使用FFT观察信号频谱

  我们显示一下实信号的FFT变换,观察频谱,MATLAB代码如下:

clc; close all;    
Adc=2;            %直流分量幅度
A1=3;             %频率F1信号的幅度
A2=1.5;           %频率F2信号的幅度
F1=50;            %信号1频率(Hz)
F2=75;            %信号2频率(Hz)
Fs=1024;          %采样频率(Hz)
P1=-30;           %信号1相位(度)
P2=90;            %信号相位(度)
N=1024;           %采样点数
t=[0:1/Fs:N/Fs];  %采样时刻
 
%信号
S=Adc+A1*cos(2*pi*F1*t+pi*P1/180)+A2*cos(2*pi*F2*t+pi*P2/180);
 
%显示原始信号
plot(S);
title('原始信号');
 
figure;
Y = fft(S,N);                 %做FFT变换
Ayy = (abs(Y));               %取模
plot(Ayy(1:N));               %显示原始的FFT模值结果
title('fft后的频谱');
 
figure;
Ayy=Ayy/(N/2);                %换算成实际的幅度
Ayy(1)=Ayy(1)/2;
F=([1:N]-1)*Fs/N;             %换算成实际的频率值
plot(F(1:N/2),Ayy(1:N/2));    %显示换算后的FFT模值结果
title('实际频谱');
 

在这里插入图片描述
  由代码可看出,原始信号是一个实信号,由一个幅度为2直流分量和两个余弦信号组成,信号的波形如上所示。

在这里插入图片描述
  对信号进行FFT后,第一个峰值为直流分量的幅度,由图可以看出幅度值为2048= 2* 1024 ,符合预期。因为信号是实信号,因此频谱呈现出共轭对称,因此我们只观察N/2之前的频谱图就可以。

在这里插入图片描述
  第二个峰值的坐标为(51,1536),对应的频率就为50HZ,幅度1536=3*1024/2 ,也是符合预期。

在这里插入图片描述
  第三个峰值的坐标为(76,768),对应的频率为75HZ,幅度768=1.5/1024/2,也是符合预期。为了方便观察,我们可以进行移频操作,将对称的频谱移动到直流分量两边,具体代码如下:

clc; close all;    
Adc=2;            % 直流分量幅度
A1=3;             % 频率F1信号的幅度
A2=1.5;           % 频率F2信号的幅度
F1=50;            % 信号1频率(Hz)
F2=75;            % 信号2频率(Hz)
Fs=1024;          % 采样频率(Hz)
P1=-30;           % 信号1相位(度)
P2=90;            % 信号相位(度)
N=1024;           % 采样点数
t = (0:N-1)/Fs;  % 生成时间向量
 
% 信号
S = Adc + A1*cos(2*pi*F1*t + pi*P1/180) + A2*cos(2*pi*F2*t + pi*P2/180);
 
% 显示原始信号
figure;
plot(t, S);
title('原始信号');

% FFT 变换
Y = fft(S, N);                 % 做 FFT 变换

% 计算频率轴
freq = (0:N-1) * (Fs/N); % 频率向量
X_shifted = fftshift(Y); % 使用 fftshift 调整频率轴和频谱
freq_shifted = freq - Fs/2; % 中心化频率轴

% 取模
Ayy = abs(X_shifted); 
Ayy1 = abs(Y); 
% 绘制频谱
figure;
plot(freq_shifted, Ayy); % 显示 FFT 模值结果
title('fft后的频谱');

% 绘制实际频谱
figure;
% 取模并归一化
Ayy = Ayy1/(N/2); 
Ayy(1) = Ayy(1)/2;
F = ([1:N]-1) * Fs/N;             % 换算成实际的频率值
plot(F(1:N/2), Ayy(1:N/2));    % 显示换算后的 FFT 模值结果
title('实际频谱');

在这里插入图片描述
在这里插入图片描述
  在归一化模值后,频谱就显示了原始信号正确的频率和幅度。

  我们在给一个复信号来看看和实信号的频谱有啥不一样,matlab代码如下:

clc;
close all;
% 参数设置
Fs = 1000;         % 采样频率 (Hz)
T = 1/Fs;         % 采样周期 (s)
L = 1000;         % 信号长度
t = (0:L-1)*T;    % 时间向量

% 生成复信号
f1 = 50;          % 第一个频率 (Hz)
f2 = 120;         % 第二个频率 (Hz)
A1 = 3;           % 第一个频率的幅度
A2 = 5.5;         % 第二个频率的幅度

% 复信号
S_real = A1 * sin(2 * pi * f1 * t);  % 实部
S_imag = A2 * sin(2 * pi * f2 * t + pi/4);  % 虚部(相位偏移)
S = 4 +S_real + 1i * S_imag;  % 复信号

% 计算 FFT
Y = fft(S);

% 计算频率轴
f = (0:L-1)*(Fs/L);  % 频率向量
P2 = abs(Y/L);       % 双边幅度谱
P1 = P2(1:L/2+1);    % 单边幅度谱
P1(2:end-1) = 2*P1(2:end-1);  % 单边幅度谱修正
f1 = f(1:L/2+1);     % 单边频率向量

% 绘制频谱
figure;
plot(f1, P1);
title('复信号的频谱');
xlabel('频率 (Hz)');
ylabel('|P1(f)|');
grid on;

在这里插入图片描述
  可以看到复信号的频谱没有共轭对称。

四、 Xilinx FFT IP的端口说明

4.1 端口说明

在这里插入图片描述

端口名称 输入方向 端口说明
aclk 输入 上升沿有效
aclken 输入 时钟使能信号,高电平有效
aresetn 输入 低电平有效同步复位信号。 需要至少两个周期的 aresetn 有效脉冲
s_axis_config_tvalid 输入 配置数据有效信号
s_axis_config_tready 输出 配置数据准备信号
s_axis_config_tdata 输入 配置数据
s_axis_data_tvalid 输入 输入数据有效信号
s_axis_data_tready 输出 输入数据准备信号
s_axis_data_tdata 输入 输入数据,带未处理的样本数据:XN_RE 和 XN_IM
s_axis_data_tlast 输入 输入最后一个数据指示信号
端口名称 输入方向 端口说明
m_axis_data_tvalid 输出 输出数据有效信号
m_axis_data_tready输入 输出数据准备信号
m_axis_data_tdata 输出 输出数据
m_axis_data_tuser 输出 数据输出通道的 TUSER。 携带额外的每个样本信息,例如 XK_INDEX、OVFLO 和 BLK_EXP
m_axis_data_tlast 输出 输出最后一个数据指示信号
m_axis_status_tvalid 输出 输出状态有效信号
m_axis_status_tready输入 输出状态准备信号
m_axis_status_tdata输出 输出状态数据
端口名称 输入方向 端口说明
event_frame_started 输出 当核心开始处理新帧时拉高
event_tlast_unexpected 输出 当输入的 s_axis_data_tlast不在最后一个数据对应位置时,拉高
event_tlast_missing 输出 当 s_axis_data_tlast 在帧的最后一个数据样本上为低时,拉高
event_fft_overflow 输出 当从数据输出通道卸载的数据样本中发现有溢出时,会拉高。仅当溢出是有效选项时才会出现
event_data_in_channel_halt输出 当核心从数据输入通道请求数据但没有可用数据时,置位
event_data_out_channel_halt输出 当内核尝试将数据写入数据输出通道但无法执行时,会触发此信号。仅在非实时模式下出现
event_status_channel_halt输出 当内核尝试将数据写入状态通道但无法执行时,会触发此信号。仅在非实时模式下出现

4.2 事件指示信号说明

  1. event_frame_started:当内核开始处理新帧时,此事件信号将持续一个时钟周期。
  2. event_tlast_missing: 当 s_axis_data_tlast 在帧的最后一个传入数据样本上为低时,此事件信号在一个时钟周期内有效。这表明核心和上游数据源在帧大小方面的配置不匹配,并表明上游数据源的点大小配置大于核心。
  3. event_tlast_unexpected:当核心在任何传入数据样本(不是帧中的最后一个)上看到 s_axis_data_tlast 高电平时,此事件信号会持续一个时钟周期。这表明核心和上游数据源在帧大小方面的配置不匹配,并表明上游数据源配置为比核心更小的点大小。
  4. event_fft_overflow:当在 m_axis_data_tdata 上传输的数据样本中看到溢出时,此事件信号在每个时钟周期都会被置位。 只有在使用缩放算法或单精度浮点 I/O 时才有可能获得 FFT 溢出。在所有其他配置中,引脚都会从核心中移除。
  5. event_data_in_channel_halt:当核心需要来自数据输入通道的数据但没有可用数据时,每个周期都会断言此事件。
  6. event_data_out_channel_halt:此事件在每个周期中被置位,当核心需要将数据写入数据输出通道但因为通道中的缓冲区已满而无法写入时。发生这种情况时,核心处理将停止,所有活动都将停止,直到通道缓冲区中有可用空间。帧不会损坏。
  7. event_status_channel_halt:此事件在每个周期中被置位,当核心需要将数据写入状态通道但因为通道上的缓冲区已满而无法写入时。发生这种情况时,核心处理将停止,所有活动都将停止,直到通道缓冲区中有可用空间。帧不会损坏。事件引脚仅在非实时模式下可用。

4.3 s_axis_config_tdata 格式

在这里插入图片描述

  1. (可选) NFFT:宽度5位,变换的点大小;NFFT 可以是最大变换的大小或任何较小的点大小。例如,1024 点 FFT 可以计算点大小 1024、512、256 等。NFFT 的值是 l o g 2 log_2 log2(点大小)
  2. (可选)CP_LEN :宽度 l o g 2 log_2 log2(最大点数),循环前缀长度:在输出整个变换之前,最初作为循环前缀输出的变换末尾的样本数。
  3. FWD_INV:宽度1bit,指示执行的是正向 FFT 变换还是反向 FFT 变换。当 FWD_INV = 1 时,计算正向变换。如果 FWD_INV = 0,则计算反向变换。
  4. (可选)SCALE_SCH

4.4 数据输入通道s_axis_data_tdata

  数据通道包括虚部和实部数据。

  1. XN_RE:实部 (bxn = 8 - 34) 采用二进制补码或单精度浮点格式
  2. XN_IM:虚部 (bxn = 8 - 34) 采用二进制补码或单精度浮点格式

  多通道数据格式:

在这里插入图片描述

  单通道数据格式:
在这里插入图片描述
  例如:核心已配置为具有两个 FFT 数据通道,数据为 12 位。通道 0 具有以下样本值:

  1. RE: 0010 1101 1001
  2. IM :0011 1110 0110

  通道 1 具有以下样本值:

  1. RE: 0111 0000 0000
  2. IM: 0000 0000 0000

  由于数据必须是8的整数被,因此高四位必须补0:

在这里插入图片描述
在这里插入图片描述

4.5 数据输出通道

  数据输出通道包含变换的实部和虚部结果,这些结果在 TDATA 上传输。此外,TUSER 还传输与 TDATA 上的样本数据相关的每个样本状态信息。数据通道包括虚部和实部数据。

  1. XK_RE:实部 (bxn = 8 - 34) 采用二进制补码或单精度浮点格式
  2. XK_IM:虚部 (bxn = 8 - 34) 采用二进制补码或单精度浮点格式

  多通道数据格式:

在这里插入图片描述
  单通道数据格式:

在这里插入图片描述
  例如:核心已配置为具有两个 FFT 数据通道,数据为 12 位。输出通道 0 具有以下样本值:

  1. RE: 0010 1101 1001
  2. IM:1011 1110 0110

  输出通道 1 具有以下样本值:
3. RE: 0111 0000 0000
4. IM: 1000 0000 0000

在这里插入图片描述

在这里插入图片描述

五、FFT IP 的例化

  我们先用DDS IP核生成一个10M的正弦波,让信号经过FFT后观察频谱,FFT IP例化步骤如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  DDS IP 的例化参考《FPGA实现频率、幅度、相位可调的DDS以及DDS Compiler IP核的使用验证》,本文就不再赘述。

六、仿真验证

6.1 验证单音信号的FFT

  我们先用DDS生成一个5M和一个10M的正弦波,分别通过FFT后观察频谱,仿真代码如下:

`timescale 1ns / 1ps

module tb_test_fft();
//IP核接口信号
/********时钟复位*********/
    reg                                                 aclk    ;
    reg                                                 aresetn ;
//dds
    wire                                                m_axis_dds_5m_data_tvalid   ;
    wire    signed  [15:0]                              m_axis_dds_5m_data_tdata    ;
    wire                                                m_axis_dds_10m_data_tvalid  ;
    wire    signed  [15:0]                              m_axis_dds_10m_data_tdata   ;
//fft_10M
    wire            [31:0]                              s_axis_fft_10M_data_tdata   ;
    wire                                                s_axis_fft_10M_data_tvalid  ;
    
    wire            [31 : 0]                            m_axis_fft_10M_data_tdata   ;
    wire            [15 : 0]                            m_axis_fft_10M_data_tuser   ;
    wire                                                m_axis_fft_10M_data_tvalid  ;
    reg                                                 m_axis_fft_10M_data_tready  ;
    wire                                                m_axis_fft_10M_data_tlast   ;   
    
    wire                                                event_fft_10M_frame_started ;
    wire                                                event_fft_10M_tlast_unexpected  ;
    wire                                                event_fft_10M_tlast_missing ;
    wire                                                event_fft_10M_status_channel_halt   ;
    wire                                                event_fft_10M_data_in_channel_halt  ;
    wire                                                event_fft_10M_data_out_channel_halt ;

    reg     signed  [15:0]                              fft_10M_data_real   ;
    reg     signed  [15:0]                              fft_10M_data_imag   ;
    wire    signed  [32:0]                              fft_10M_power   ;
//fft_5M
    wire            [31:0]                              s_axis_fft_5M_data_tdata   ;
    wire                                                s_axis_fft_5M_data_tvalid  ;
    
    wire            [31 : 0]                            m_axis_fft_5M_data_tdata   ;
    wire            [15 : 0]                            m_axis_fft_5M_data_tuser   ;
    wire                                                m_axis_fft_5M_data_tvalid  ;
    reg                                                 m_axis_fft_5M_data_tready  ;
    wire                                                m_axis_fft_5M_data_tlast   ;   
    
    wire                                                event_fft_5M_frame_started ;
    wire                                                event_fft_5M_tlast_unexpected  ;
    wire                                                event_fft_5M_tlast_missing ;
    wire                                                event_fft_5M_status_channel_halt   ;
    wire                                                event_fft_5M_data_in_channel_halt  ;
    wire                                                event_fft_5M_data_out_channel_halt ;

    reg     signed  [15:0]                              fft_5M_data_real   ;
    reg     signed  [15:0]                              fft_5M_data_imag   ;
    wire    signed  [32:0]                              fft_5M_power   ;


initial begin
    aclk=1'b0;
    aresetn=1'b0;
    m_axis_fft_5M_data_tready=1'b1;
    m_axis_fft_10M_data_tready=1'b1;
    #15;
    aresetn=1'b1;
end    
always #5 aclk=~aclk;       //产生100M时钟

assign  s_axis_fft_10M_data_tdata = {16'd0,m_axis_dds_10m_data_tdata};   //虚部补0
assign  s_axis_fft_10M_data_tvalid = m_axis_dds_10m_data_tvalid;
assign  s_axis_fft_5M_data_tdata = {16'd0,m_axis_dds_5m_data_tdata};   //虚部补0
assign  s_axis_fft_5M_data_tvalid = m_axis_dds_5m_data_tvalid;
//求模
assign fft_10M_power=fft_10M_data_real*fft_10M_data_real+fft_10M_data_imag*fft_10M_data_imag;
assign fft_5M_power=fft_5M_data_real*fft_5M_data_real+fft_5M_data_imag*fft_5M_data_imag;

always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)begin
       fft_10M_data_real<='b0;
       fft_10M_data_imag<='b0; 
    end
    else if(m_axis_fft_10M_data_tvalid)begin
       //取输出频谱的实部和虚部
       fft_10M_data_real<=m_axis_fft_10M_data_tdata[15:0];
       fft_10M_data_imag<=m_axis_fft_10M_data_tdata[31:16]; 
    end
    else begin
        fft_10M_data_real<=fft_10M_data_real;
        fft_10M_data_imag<=fft_10M_data_imag;
    end
end

always@(posedge aclk or negedge aresetn)begin
    if(!aresetn)begin
       fft_5M_data_real<='b0;
       fft_5M_data_imag<='b0; 
    end
    else if(m_axis_fft_5M_data_tvalid)begin
       //取输出频谱的实部和虚部
       fft_5M_data_real<=m_axis_fft_5M_data_tdata[15:0];
       fft_5M_data_imag<=m_axis_fft_5M_data_tdata[31:16]; 
    end
    else begin
        fft_5M_data_real<=fft_5M_data_real;
        fft_5M_data_imag<=fft_5M_data_imag;
    end
end

//例化IP核
dds_5m u0_dds_5m (
  .aclk(aclk),                                // input wire aclk
  .aresetn(aresetn),                          // input wire aresetn
  .m_axis_data_tvalid(m_axis_dds_5m_data_tvalid),    // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_dds_5m_data_tdata)      // output wire [15 : 0] m_axis_data_tdata
);
dds_10m u0_dds_10m (
  .aclk(aclk),                              // input wire aclk
  .aresetn(aresetn),                        // input wire aresetn
  .m_axis_data_tvalid(m_axis_dds_10m_data_tvalid),  // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_dds_10m_data_tdata)    // output wire [15 : 0] m_axis_data_tdata
);

test_fft u0_test_fft (
  .aclk(aclk),                                                // input wire aclk
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(8'b1),                                 // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                    // output wire s_axis_config_tready
  .s_axis_data_tdata(s_axis_fft_10M_data_tdata),      // input wire [31 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(m_axis_dds_10m_data_tvalid),            // input wire s_axis_data_tvalid
  .s_axis_data_tready(),                                      // output wire s_axis_data_tready
  .s_axis_data_tlast(),                                       // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_fft_10M_data_tdata),                      // output wire [47 : 0] m_axis_data_tdata
  .m_axis_data_tuser(m_axis_fft_10M_data_tuser),                      // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_fft_10M_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(m_axis_fft_10M_data_tready),                    // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_fft_10M_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(1'b1),                                        // input wire m_axis_status_tready
  .event_frame_started(event_fft_10M_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_fft_10M_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_fft_10M_tlast_missing),                  // output wire event_tlast_missing
  .event_status_channel_halt(event_fft_10M_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_fft_10M_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_fft_10M_data_out_channel_halt)  // output  wire event_data_out_channel_halt
);

test_fft u1_test_fft (
  .aclk(aclk),                                                // input wire aclk
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(8'b1),                                 // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                    // output wire s_axis_config_tready
  .s_axis_data_tdata(s_axis_fft_5M_data_tdata),      // input wire [31 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(m_axis_dds_5m_data_tvalid),            // input wire s_axis_data_tvalid
  .s_axis_data_tready(),                                      // output wire s_axis_data_tready
  .s_axis_data_tlast(),                                       // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_fft_5M_data_tdata),                      // output wire [47 : 0] m_axis_data_tdata
  .m_axis_data_tuser(m_axis_fft_5M_data_tuser),                      // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_fft_5M_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(m_axis_fft_5M_data_tready),                    // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_fft_5M_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(1'b1),                                        // input wire m_axis_status_tready
  .event_frame_started(event_fft_5M_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_fft_5M_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_fft_5M_tlast_missing),                  // output wire event_tlast_missing
  .event_status_channel_halt(event_fft_5M_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_fft_5M_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_fft_5M_data_out_channel_halt)  // output  wire event_data_out_channel_halt
);
endmodule

  运行仿真:

在这里插入图片描述
  我们可以看到两个单音信号经过FFT后都产生了一个波峰,我们先放大看10M信号的频谱波峰的坐标:

在这里插入图片描述
  10M信号的频谱波峰在102的位置,我们来计算一下原始信号的频率 =坐标K * 采样频率 / 采样点数。 f c l k = 102 ∗ 100000000 / 1024 = 9960937 ≈ 10 M f_clk = 102 *100000000 / 1024 = 9960937 ≈ 10M fclk=102100000000/1024=996093710M

  我们再来先放大看5M信号的频谱波峰的坐标:

在这里插入图片描述  10M信号的频谱波峰在51的位置,我们来计算一下原始信号的频率 =坐标K * 采样频率 / 采样点数。 f c l k = 51 ∗ 100000000 / 1024 = 4980468 ≈ 5 M f_clk = 51 *100000000 / 1024 = 4980468 ≈ 5M fclk=51100000000/1024=49804685M
  再用matlab生成一个5M正弦波和10M正弦波,采样频率为100M,采样点数为1024,进行FFT来验真频谱是否正确,matlab代码如下:

clc; clear; close all;

% 参数设置
Fs = 100e6;          % 采样频率 100 MHz
N = 1024;            % 采样点数
F1 = 5e6;           % 信号1频率 5 MHz
F2 = 10e6;          % 信号2频率 10 MHz

% 生成时间向量
t = (0:N-1)/Fs;     % 时间向量

% 生成信号
S1 = cos(2*pi*F1*t); % 5 MHz 正弦波
S2 = cos(2*pi*F2*t); % 10 MHz 正弦波
S = S1 + S2;         % 合成信号

% 显示原始信号
figure;
plot(t, S);
title('合成信号');
xlabel('时间 (s)');
ylabel('幅度');

% FFT 变换
Y = fft(S, N);                 % 做 FFT 变换

% 计算频率轴
Ayy = abs(Y) / N;              % 取模并归一化

% 绘制频谱
figure;
plot(1:N/2, Ayy(1:N/2)); % 只显示正频率部分的幅度
title('频谱');
xlabel('点数');
ylabel('幅度');
xlim([1 N/2]);              % 限制x轴范围到正频率部分
grid on;

在这里插入图片描述
  可以看到使用matlab仿真出来的频谱坐标和我们用vivado仿真一致。

6.2 验证IFFT

  我们让前面两个正弦波信号经过FFT后再经过IFFT观测能否恢复出原始波形,IFFT也是使用FFT的IP核,只需要给配置数据设置为0即可,仿真代码如下:

`timescale 1ns / 1ps

module tb_test_fft();
//IP核接口信号
/********时钟复位*********/
    reg                                                 aclk    ;
    reg                                                 aresetn ;
//dds
    wire                                                m_axis_dds_5m_data_tvalid   ;
    wire    signed  [15:0]                              m_axis_dds_5m_data_tdata    ;
    wire                                                m_axis_dds_10m_data_tvalid  ;
    wire    signed  [15:0]                              m_axis_dds_10m_data_tdata   ;
//fft_10M
    wire            [31:0]                              s_axis_fft_10M_data_tdata   ;
    wire                                                s_axis_fft_10M_data_tvalid  ;
    
    wire            [31 : 0]                            m_axis_fft_10M_data_tdata   ;
    wire            [23 : 0]                            m_axis_fft_10M_data_tuser   ;
    wire                                                m_axis_fft_10M_data_tvalid  ;
    wire                                                m_axis_fft_10M_data_tready  ;
    wire                                                m_axis_fft_10M_data_tlast   ;   
    
    wire                                                event_fft_10M_frame_started ;
    wire                                                event_fft_10M_tlast_unexpected  ;
    wire                                                event_fft_10M_tlast_missing ;
    wire                                                event_fft_10M_status_channel_halt   ;
    wire                                                event_fft_10M_data_in_channel_halt  ;
    wire                                                event_fft_10M_data_out_channel_halt ;

    reg     signed  [15:0]                              fft_10M_data_real   ;
    reg     signed  [15:0]                              fft_10M_data_imag   ;
    wire    signed  [32:0]                              fft_10M_power   ;
//fft_5M
    wire            [31:0]                              s_axis_fft_5M_data_tdata   ;
    wire                                                s_axis_fft_5M_data_tvalid  ;
    
    wire            [31 : 0]                            m_axis_fft_5M_data_tdata   ;
    wire            [23 : 0]                            m_axis_fft_5M_data_tuser   ;
    wire                                                m_axis_fft_5M_data_tvalid  ;
    wire                                                m_axis_fft_5M_data_tready  ;
    wire                                                m_axis_fft_5M_data_tlast   ;   
    
    wire                                                event_fft_5M_frame_started ;
    wire                                                event_fft_5M_tlast_unexpected  ;
    wire                                                event_fft_5M_tlast_missing ;
    wire                                                event_fft_5M_status_channel_halt   ;
    wire                                                event_fft_5M_data_in_channel_halt  ;
    wire                                                event_fft_5M_data_out_channel_halt ;

    reg     signed  [15:0]                              fft_5M_data_real   ;
    reg     signed  [15:0]                              fft_5M_data_imag   ;
    wire    signed  [32:0]                              fft_5M_power   ;
// ifft_10M
    wire            [31 : 0]                            m_axis_ifft_10M_data_tdata   ;
    wire                                                m_axis_ifft_10M_data_tvalid  ;
    wire                                                m_axis_ifft_10M_data_tlast  ;
    wire            [15:0]                              ifft_10m_wave   ;
// ifft_5M
    wire            [31 : 0]                            m_axis_ifft_5M_data_tdata   ;
    wire                                                m_axis_ifft_5M_data_tvalid  ;
    wire                                                m_axis_ifft_5M_data_tlast  ;
    wire            [15:0]                              ifft_5m_wave   ;
initial begin
    aclk=1'b0;
    aresetn=1'b0;
    fft_10M_data_real = 'd0;
    fft_10M_data_imag ='d0;
    #201;
    aresetn=1'b1;
end    


assign  s_axis_fft_10M_data_tdata   = {16'd0,m_axis_dds_10m_data_tdata};   //虚部补0
assign  s_axis_fft_10M_data_tvalid  = m_axis_dds_10m_data_tvalid;
assign  s_axis_fft_5M_data_tdata    = {16'd0,m_axis_dds_5m_data_tdata};   //虚部补0
assign  s_axis_fft_5M_data_tvalid   = m_axis_dds_5m_data_tvalid;
assign  ifft_10m_wave = m_axis_ifft_10M_data_tdata[15:0];
assign  ifft_5m_wave = m_axis_ifft_5M_data_tdata[15:0];
//求模
assign fft_10M_power=fft_10M_data_real*fft_10M_data_real+fft_10M_data_imag*fft_10M_data_imag;
assign fft_5M_power=fft_5M_data_real*fft_5M_data_real+fft_5M_data_imag*fft_5M_data_imag;

always #5 aclk=~aclk;       //产生100M时钟

always@(posedge aclk)begin
    if(aresetn == 1'b0)begin
       fft_10M_data_real<='b0;
       fft_10M_data_imag<='b0; 
    end
    else if(m_axis_fft_10M_data_tvalid)begin
       //取输出频谱的实部和虚部
       fft_10M_data_real<=m_axis_fft_10M_data_tdata[15:0];
       fft_10M_data_imag<=m_axis_fft_10M_data_tdata[31:16]; 
    end
    else begin
        fft_10M_data_real<=fft_10M_data_real;
        fft_10M_data_imag<=fft_10M_data_imag;
    end
end

always@(posedge aclk)begin
    if(aresetn == 1'b0)begin
       fft_5M_data_real<='b0;
       fft_5M_data_imag<='b0; 
    end
    else if(m_axis_fft_5M_data_tvalid)begin
       //取输出频谱的实部和虚部
       fft_5M_data_real<=m_axis_fft_5M_data_tdata[15:0];
       fft_5M_data_imag<=m_axis_fft_5M_data_tdata[31:16]; 
    end
    else begin
        fft_5M_data_real<=fft_5M_data_real;
        fft_5M_data_imag<=fft_5M_data_imag;
    end
end

//例化IP核
dds_5m u0_dds_5m (
  .aclk(aclk),                                // input wire aclk
  .aresetn(aresetn),                          // input wire aresetn
  .m_axis_data_tvalid(m_axis_dds_5m_data_tvalid),    // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_dds_5m_data_tdata)      // output wire [15 : 0] m_axis_data_tdata
);
dds_10m u0_dds_10m (
  .aclk(aclk),                              // input wire aclk
  .aresetn(aresetn),                        // input wire aresetn
  .m_axis_data_tvalid(m_axis_dds_10m_data_tvalid),  // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_dds_10m_data_tdata)    // output wire [15 : 0] m_axis_data_tdata
);

test_fft u0_test_fft (
  .aclk(aclk),                                                        // input wire aclk
  .aresetn(aresetn),                                                  // input wire aresetn
  .s_axis_config_tdata(8'd1),                                         // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                        // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                            // output wire s_axis_config_tready
  .s_axis_data_tdata(s_axis_fft_10M_data_tdata),                      // input wire [31 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(m_axis_dds_10m_data_tvalid),                    // input wire s_axis_data_tvalid
  .s_axis_data_tready(m_axis_fft_10M_data_tready),                                              // output wire s_axis_data_tready
  .s_axis_data_tlast(m_axis_fft_10M_data_tlast),                                               // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_fft_10M_data_tdata),                      // output wire [47 : 0] m_axis_data_tdata
  .m_axis_data_tuser(m_axis_fft_10M_data_tuser),                      // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_fft_10M_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(m_axis_fft_10M_data_tready),                    // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_fft_10M_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(1'b1),                                        // input wire m_axis_status_tready
  .event_frame_started(event_fft_10M_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_fft_10M_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_fft_10M_tlast_missing),                  // output wire event_tlast_missing
  .event_status_channel_halt(event_fft_10M_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_fft_10M_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_fft_10M_data_out_channel_halt)   // output  wire event_data_out_channel_halt
);

test_fft u1_test_fft (
  .aclk(aclk),                                                // input wire aclk
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(8'd1),                                 // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                    // output wire s_axis_config_tready
  .s_axis_data_tdata(s_axis_fft_5M_data_tdata),      // input wire [31 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(m_axis_dds_5m_data_tvalid),            // input wire s_axis_data_tvalid
  .s_axis_data_tready(m_axis_fft_5M_data_tready),                                      // output wire s_axis_data_tready
  .s_axis_data_tlast(m_axis_fft_5M_data_tlast),                                       // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_fft_5M_data_tdata),                      // output wire [47 : 0] m_axis_data_tdata
  .m_axis_data_tuser(m_axis_fft_5M_data_tuser),                      // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_fft_5M_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(m_axis_fft_5M_data_tready),                    // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_fft_5M_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(1'b1),                                        // input wire m_axis_status_tready
  .event_frame_started(event_fft_5M_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_fft_5M_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_fft_5M_tlast_missing),                  // output wire event_tlast_missing
  .event_status_channel_halt(event_fft_5M_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_fft_5M_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_fft_5M_data_out_channel_halt)  // output  wire event_data_out_channel_halt
);

// IFFT
test_fft u0_test_ifft (
  .aclk(aclk),                                                // input wire aclk
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(8'd0),                                 // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                    // output wire s_axis_config_tready
  .s_axis_data_tdata(m_axis_fft_10M_data_tdata),              // input wire [31 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(m_axis_fft_10M_data_tvalid),            // input wire s_axis_data_tvalid
  .s_axis_data_tready(m_axis_fft_10M_data_tready),                                      // output wire s_axis_data_tready
  .s_axis_data_tlast(m_axis_fft_10M_data_tlast),              // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_ifft_10M_data_tdata),              // output wire [47 : 0] m_axis_data_tdata
  .m_axis_data_tuser(),                                       // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_ifft_10M_data_tvalid),            // output wire m_axis_data_tvalid
  .m_axis_data_tready(1'b1),                                  // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_ifft_10M_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(1'b1),                                        // 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
);

test_fft u1_test_ifft (
  .aclk(aclk),                                                // input wire aclk
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(8'd0),                                 // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                    // output wire s_axis_config_tready
  .s_axis_data_tdata(m_axis_fft_5M_data_tdata),               // input wire [31 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(m_axis_fft_5M_data_tvalid),             // input wire s_axis_data_tvalid
  .s_axis_data_tready(m_axis_fft_5M_data_tready),             // output wire s_axis_data_tready
  .s_axis_data_tlast(m_axis_fft_5M_data_tlast),               // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_ifft_5M_data_tdata),              // output wire [47 : 0] m_axis_data_tdata
  .m_axis_data_tuser(),                                       // output wire [15 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_ifft_5M_data_tvalid),            // output wire m_axis_data_tvalid
  .m_axis_data_tready(1'b1),                                  // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_ifft_5M_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(1'b1),                                        // 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
);

endmodule

在这里插入图片描述
  我们可以看到两个正弦信号经过FFT后再经过IFFT成功恢复出了波形,我们放大看一下频率是否正确:

在这里插入图片描述
  10M波形的周期为100ns,5M波形的周期为200ns,波形频率正确,但是不知道为啥IFFT第一次出来的波形幅度比原来的DDS波形小一半,但是后面的波形幅度正常,这个问题后续再研究一下。

6.3 验证多音信号的FFT

  我们使用《Xilinx 使用DDS实现本振混频上下变频》里面的混频信号进行FFT以及IFFT频谱观察,matlab代码如下:

clc;
clear;
close all;
% 参数设置
Fs = 100e6; % 采样频率为 100 MHz
N = 1024;   % 采样点数
t = (0:N-1) / Fs; % 时间向量

% 生成信号
f1 = 5e6;  % 5 MHz 信号
f2 = 10e6; % 10 MHz 信号
signal = sin(2 * pi * f1 * t) .* sin(2 * pi * f2 * t); % 生成5M和10M正弦波的叠加信号

% 进行FFT
Y = fft(signal);

% 计算频率轴
f = (0:N-1) * (Fs / N); % 频率向量

% 计算幅度谱
P2 = abs(Y / N); % 双边幅度谱
P1 = P2(1:N/2+1); % 单边幅度谱
P1(2:end-1) = 2 * P1(2:end-1); % 单边幅度谱调整

% 进行IFFT
signal_reconstructed = ifft(Y);

% 绘制结果
figure;

% 绘制原始信号
subplot(3, 1, 1);
plot(t, signal);
title('Original Signal (5 MHz * 10 MHz)');
xlabel('Time (s)');
ylabel('Amplitude');
grid on;

% 绘制FFT结果
subplot(3, 1, 2);
plot(f(1:N/2+1), P1);
title('FFT Result');
xlabel('Frequency (Hz)');
ylabel('Magnitude');
grid on;

% 绘制IFFT结果
subplot(3, 1, 3);
plot(t, real(signal_reconstructed));
title('Reconstructed Signal from IFFT');
xlabel('Time (s)');
ylabel('Amplitude');
grid on;

% 调整图形
sgtitle('FFT and IFFT Demonstration');

在这里插入图片描述
  vivado仿真代码如下:

`timescale 1ns / 1ps

module tb_test_fft();
//IP核接口信号
/********时钟复位*********/
    reg                                                 aclk    ;
    reg                                                 aresetn ;
//dds
    wire                                                m_axis_dds_5m_data_tvalid   ;
    wire    signed  [15:0]                              m_axis_dds_5m_data_tdata    ;
    wire                                                m_axis_dds_10m_data_tvalid  ;
    wire    signed  [15:0]                              m_axis_dds_10m_data_tdata   ;
//fft_10M
    wire            [63:0]                              s_axis_fft_10M_data_tdata   ;
    wire                                                s_axis_fft_10M_data_tvalid  ;
    
    wire            [63 : 0]                            m_axis_fft_10M_data_tdata   ;
    wire            [23 : 0]                            m_axis_fft_10M_data_tuser   ;
    wire                                                m_axis_fft_10M_data_tvalid  ;
    wire                                                m_axis_fft_10M_data_tready  ;
    wire                                                m_axis_fft_10M_data_tlast   ;   
    
    wire                                                event_fft_10M_frame_started ;
    wire                                                event_fft_10M_tlast_unexpected  ;
    wire                                                event_fft_10M_tlast_missing ;
    wire                                                event_fft_10M_status_channel_halt   ;
    wire                                                event_fft_10M_data_in_channel_halt  ;
    wire                                                event_fft_10M_data_out_channel_halt ;

    reg     signed  [31:0]                              fft_10M_data_real   ;
    reg     signed  [31:0]                              fft_10M_data_imag   ;
    wire    signed  [63:0]                              fft_10M_power   ;
// ifft_10M
    wire            [31 : 0]                            m_axis_ifft_10M_data_tdata   ;
    wire                                                m_axis_ifft_10M_data_tvalid  ;
    wire                                                m_axis_ifft_10M_data_tlast  ;
    wire            [31:0]                              ifft_10m_wave   ;
// mult
    wire            [31:0]                              mix_dds ;


initial begin
    aclk=1'b0;
    aresetn=1'b0;
    fft_10M_data_real = 'd0;
    fft_10M_data_imag ='d0;
    #201;
    aresetn=1'b1;
end    


assign  s_axis_fft_10M_data_tdata   = {32'd0,mix_dds};   //虚部补0
assign  ifft_10m_wave = m_axis_ifft_10M_data_tdata[31:0];
//求模
assign fft_10M_power=fft_10M_data_real*fft_10M_data_real+fft_10M_data_imag*fft_10M_data_imag;

always #5 aclk=~aclk;       //产生100M时钟

always@(posedge aclk)begin
    if(aresetn == 1'b0)begin
       fft_10M_data_real<='b0;
       fft_10M_data_imag<='b0; 
    end
    else if(m_axis_fft_10M_data_tvalid)begin
       //取输出频谱的实部和虚部
       fft_10M_data_real<=m_axis_fft_10M_data_tdata[31:0];
       fft_10M_data_imag<=m_axis_fft_10M_data_tdata[63:32]; 
    end
    else begin
        fft_10M_data_real<=fft_10M_data_real;
        fft_10M_data_imag<=fft_10M_data_imag;
    end
end


//例化IP核
dds_5m u0_dds_5m (
  .aclk(aclk),                                // input wire aclk
  .aresetn(aresetn),                          // input wire aresetn
  .m_axis_data_tvalid(m_axis_dds_5m_data_tvalid),    // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_dds_5m_data_tdata)      // output wire [15 : 0] m_axis_data_tdata
);
dds_10m u0_dds_10m (
  .aclk(aclk),                              // input wire aclk
  .aresetn(aresetn),                        // input wire aresetn
  .m_axis_data_tvalid(m_axis_dds_10m_data_tvalid),  // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_dds_10m_data_tdata)    // output wire [15 : 0] m_axis_data_tdata
);


mult_dds u0_mult_dds (
  .CLK(aclk),  // input wire CLK
  .A(m_axis_dds_5m_data_tdata),      // input wire [15 : 0] A
  .B(m_axis_dds_10m_data_tdata),      // input wire [15 : 0] B
  .P(mix_dds)      // output wire [31 : 0] P
);

test_fft u0_test_fft (
  .aclk(aclk),                                                        // input wire aclk
  .aresetn(aresetn),                                                  // input wire aresetn
  .s_axis_config_tdata(8'd1),                                         // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                        // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                            // output wire s_axis_config_tready
  .s_axis_data_tdata(s_axis_fft_10M_data_tdata),                      // input wire [63: 0] s_axis_data_tdata
  .s_axis_data_tvalid(1'b1),                                          // input wire s_axis_data_tvalid
  .s_axis_data_tready(m_axis_fft_10M_data_tready),                    // output wire s_axis_data_tready
  .s_axis_data_tlast(),                                               // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_fft_10M_data_tdata),                      // output wire [63 : 0] m_axis_data_tdata
  .m_axis_data_tuser(m_axis_fft_10M_data_tuser),                      // output wire [23 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_fft_10M_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(1'b1),                                          // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_fft_10M_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(1'b1),                                        // input wire m_axis_status_tready
  .event_frame_started(event_fft_10M_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_fft_10M_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_fft_10M_tlast_missing),                  // output wire event_tlast_missing
  .event_status_channel_halt(event_fft_10M_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_fft_10M_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_fft_10M_data_out_channel_halt)   // output  wire event_data_out_channel_halt
);


// IFFT
test_fft u0_test_ifft (
  .aclk(aclk),                                                // input wire aclk
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(8'd0),                                 // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                                // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                                    // output wire s_axis_config_tready
  .s_axis_data_tdata(m_axis_fft_10M_data_tdata),              // input wire [63 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(m_axis_fft_10M_data_tvalid),            // input wire s_axis_data_tvalid
  .s_axis_data_tready(m_axis_fft_10M_data_tready),                                      // output wire s_axis_data_tready
  .s_axis_data_tlast(m_axis_fft_10M_data_tlast),              // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_ifft_10M_data_tdata),              // output wire [63 : 0] m_axis_data_tdata
  .m_axis_data_tuser(),                                       // output wire [23 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_ifft_10M_data_tvalid),            // output wire m_axis_data_tvalid
  .m_axis_data_tready(1'b1),                                  // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_ifft_10M_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(1'b1),                                        // 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
);


endmodule


在这里插入图片描述
  我们可以看到IFFT后恢复出来的波形和混频后的波形一致,我们来放大看FFT后的频谱图坐标:因为是实信号,所以频谱是共轭对称的,所以我们只看正频率部分即可。

在这里插入图片描述
  第一个波峰坐标为51,我们计算一下原始信号的频率 =坐标K * 采样频率 / 采样点数。 f c l k = 51 ∗ 100000000 / 1024 = 4980468 ≈ 5 M f_clk = 51 *100000000 / 1024 = 4980468 ≈ 5M fclk=51100000000/1024=49804685M对应的为混频后的下变频部分,我们再来看第二个波峰坐标。
在这里插入图片描述
  第一个波峰坐标为154,我们计算一下原始信号的频率 =坐标K * 采样频率 / 采样点数。 f c l k = 154 ∗ 100000000 / 1024 = 15039062 ≈ 15 M f_clk = 154 *100000000 / 1024 = 15039062 ≈ 15M fclk=154100000000/1024=1503906215M对应的为混频后的上变频部分。至此Xilinx的FFT IP核仿真完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱奔跑的虎子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值