我们从2011年坚守至今,只想做存粹的技术论坛。  由于网站在外面,点击附件后要很长世间才弹出下载,请耐心等待,勿重复点击不要用Edge和IE浏览器下载,否则提示不安全下载不了

 找回密码
 立即注册
搜索
查看: 771|回复: 24

[技术文章] 使用VIVADO xfft IP核进行基于流水线架构(Pipelined Streaming I/O )的快速傅里叶变换

  [复制链接]

1

主题

2

回帖

4

积分

一级逆天

积分
4

终身成就奖

发表于 2026-1-10 11:22 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区

您需要 登录 才可以下载或查看,没有账号?立即注册

×
## 简介
快速傅里叶变换(Fast Fourier Transform, FFT)是一种计算效率高的算法,用于计算样本大小为2的正整数幂的离散傅里叶变换(DFT)。序列$x(n),n=0,\dots,N-1$的离散傅里叶变换DFT$X(k), k=1,\dots,N-1$的定义为:

$X(k)=\sum_{n=0}^{N-1}x(n)e^{-jnk2\pi/N}$

其中N是变换大小 $j=\sqrt{-1}$。逆DFT(IDFT)由下式给出:

$x(n)=\frac{1}{N}\sum_{k=0}^{N-1}X(k)e^{jnk2\pi/N}$

FFT核心使用Radix-4和Radix-2分解(基4或者基2分解)来计算DFT。对于Burst I/O架构,使用时间抽取(decimation-in-time, DIT)方法,而流水线I/O(the Pipelined Streaming)架构使用频率抽取(decimation-in-frequency,DIF)方法。当使用Radix-4分解时,N点FFT由log4(N)个阶段组成,每个阶段包含N/4个Radix-4蝶形运算。不是4的幂的点大小需要额外的Radix-2级来组合数据。使用Radix-2分解的N点FFT有log2(N)个阶段,每个阶段包含N/2个Radix-2蝶形。

通过共轭相应正向FFT的相位因子来计算逆FFT(IFFT)。FFT核心不实现逆FFT的1/N缩放。因此,缩放是按照正向FFT进行的,只是使用共轭相位因子(旋转因子)。

## 接口及端口说明
![IP核端口](

                               
登录/注册后可看大图
)
总的来说,配置接口分为时钟、复位接口、IP核配置接口(s_axis_config)、IP核数据输入接口(s_axis_data)、IP数据输出接口(m_axis_data)、IP核状态接口(m_axis_status)和event接口,作为从机(slave)时,IP核作为数据输入,作为主机(master)时,IP核作为数据输出,下表[^1]是官方数据手册上介绍的各个接口的功能及相关描述。
| 接口名称 | 接口属性  | 选择性 | 描述 |
|--|--|--|--|
|aclk        |I|        N        |上升沿时钟|
|aclken        |I        |Y        |高电平有效时钟使能信号|
|aresetn        |I        |Y        |低电平有效同步复位(清零)优先级高于aclken最少需要两个时钟周期来进行复位|
|s_axis_config_tvalid|        I        |N        |配置通道的TVALID(有效)信号 由外部主控提供信号,表明其可以提供数据|
|s_axis_config_tready|        O        |N        |配置通道的TREADY(准备完成)信号 由IP核内部提供,表明其可以接受该数据|
|s_axis_config_tdata        |I        |N        |配置通道的TDATA信号 |
|s_axis_data_tvalid|        I        |N        |数据输入通道的TVALID信号 由外部主控提供信号,表明其可以提供数据|
|s_axis_data_tready        |O|        N        |数据输入通道的TREADY信号,由内部IP提供,表面其可以接受该数据|
|s_axis_data_tdata|        I        | N  | 数据输入通道的TDATA信号, 携带着未经处理的信号:XN_RE和XN_IM|
|s_axis_data_tlast        |I        | N        | 输入数据通道的TLAST信号,由外部主控在帧的最后一个样本上提供。核心不使用它,除非生成事件event_tlast_unexpected和event_tlast_missing事件|
|m_axis_data_tvalid        |O        | N        |输出数据通道的TVALID信号,由IP核内部提供,表示其可以输出信号|
|m_axis_data_tready|        I        |N        |输出数据通道的TREADY 信号,由外部从机提供,表明其已经准备好开始接收信号,其仅在非实时模式下存在。|
|m_axis_data_tdata        |O        |N        |数据输出通道的TDATA信号,携带了处理之后的信号XK_RE和XK_IM|
|m_axis_data_tuser        |O        |N        |输出数据通道的TUSER信号,携带了可选择的每个数据信息,例如:XK_INDEX, OVFLO 和 BLK_EXP|
|m_axis_data_tlast        |O        |N        |输出数据通道的TLAST信号,由内部核心提供的每一帧的最后一个数据|
|m_axis_status_tvalid        |O        |N        |状态通道的TVALID信号,IP核内部提供,表明其可以提供状态数据|
|m_axis_status_tready        |I        |N        |状态通道的TREADY信号,外部从机提供的信号,表面其准备好接收数据,仅在非实时模式下可用|
|m_axis_status_tdata        |O        |N        |状态通道的TDATA信号,携带状态信号:BLK_EXP和OVFLO|
|event_frame_started        |O        |N        |声明(Asserted)IP开始处理新的一帧信号
单个脉冲信号|
|event_tlast_unexpected        |O        |N        |当核心在不是帧中最后一个的数据样本上看到s_axis_data_tlast High时声明。|
|event_tlast_missing        |O        |N        |当s_axis_data_tlast在每一帧数据的最后一个为低|
|event_fft_overflow        |O        |Y        |当从数据输出通道卸载的数据样本中出现溢出时,断言。仅当溢出是一个有效选项时才存在。|
|event_data_in_channel_halt        |O        |N        |当核心从数据输入通道请求数据并且没有可用数据时,则断言。|
|event_data_out_channel_halt        |O        |N        |当核心试图将数据写入数据输出通道但无法这样做时,会断言。仅在非实时模式下存在。|
|event_status_channel_halt        |O        |N        |当核心试图将数据写入状态通道但无法这样做时,会断言。仅在非实时模式下存在。|
### s_axis_config_tdata配置数据
s_axis_config_tdata 携带的配置信息包含:
配置字段按以下顺序(从LSB开始)打包到s_axis_config_tdata向量中:
1、(optional)NFFT plus padding
2、(optional)CP_LEN plus padding
3、FDW_INV
4、(optional)SCALE_SCH
![TDATA的数据配置格式](

                               
登录/注册后可看大图
)
- NFFT:FFT点数,小于等于设置的最大点数,eg,1024位为2^10^,NFFT 最大为10。
- CP_LEN:循环前缀长度(log2最大点数尺寸)
- FWD_INV:变换形式,1:正变换,0:逆变换(宽度和通道数有关,由低到高分别为通道由低到高,最大宽度为8)
- SCALE_SCH:缩放规模表(2×ceil( $\frac{NFFT}{2}$)),对于突发I/O架构,缩放时间表为每个阶段指定两个比特,第一阶段的缩放由两个LSB给出。缩放可以指定为3、2、1或0,表示要移位的位数。
   例如:N=1024,Radix-4突发I/O的示例缩放时间表为[1 0 2 3 2](从最后一级到第一级排序(倒序),基-4架构由五级运算组成,因此缩放表有五级参数)。对于N=128、Radix-2突发I/O或Radix-2精简突发I/O,一种可能的缩放规模是[1 1 1 1 0 1 2](从最后一级到第一级排序)。
   对于流水线IO架构,缩放规模表从两个LSB开始为每对Radix-2级指定两个比特。例如,N=256的缩放规模可以是[2 2 2 3]。当N不是4的幂时,最后一级的最大比特增长为1比特。例如,[0 2 2 2 2]或[1 2 2 2]是N=512的有效缩放计划,但[2 2 2 2]无效。

  需要注意的是:**如果所有带填充的字段尚未在8位边界上结束,则应将其扩展到下一个8位边界**。核心忽略填充位的值,因此可以将其驱动为任何值。将它们连接到常数值可能有助于减少设备资源的使用。
可选字段显示为虚线。请注意,s_axis_config_tdata的总线宽度可能超过容纳所有字段(包括SCALE_SCH)所需的宽度,即使SCALE_SCH字段被填充以确保宽度是8位的倍数。额外的比特未被使用,因此它们将在合成过程中被优化掉。

### s_axis_data_tdata输入数据
数据输入通道(s_axis_data)是一个AXI通道,在其TDATA矢量中携带下表中的字段,其中XN_RE和XN_IM就表示文章开头介绍的待进行FFT变换的时域数据,而下面的XK_RE和XK_IM表示FFT变换后的频域数据。
|名称|位宽|是否需要位填充|描述|
|-|-|-|-|
|XN_RE|$b_{xn}$|Y|输入数据的实部($b_{xn}$ = 8-34),采用**二进制补码(有符号整数,该点需要注意)**或者单精度浮点数格式|
|XN_IM|$b_{xn}$|Y|输入数据的虚部($b_{xn}$ = 8-34),同样采用**二进制补码(有符号整数,该点需要注意)**或者单精度浮点数格式|

需要注意的是,**如果所有带填充的字段尚未在8位边界上结束,则应将其扩展到下一个8位边界**。IP核会忽略填充位的值,因此可以将其驱动为任何值。将它们连接到常量值可以帮助减少设备资源的使用。然后对设计中的每个通道重复这些字段。

![s_axis_data_tdata数据格式](

                               
登录/注册后可看大图
)
### m_axis_data_tdata输出数据

数据输出通道包含FFT变换后的实部XK_RE和虚部XK_IM。TUSER携带了与TDATA上样本数据相关的每个数据的状态信息。该状态信息旨在供直接处理数据样本的下游从属设备使用。由于数据在同一通道中传输,它不能与数据不同步。以下信息按样品TDATA的数据分类:

#### TDATA
数据输出通道(m_axis_data)是一个AXI通道,在其TDATA矢量中携带表3-9中的字段。
|名称|位宽|是否需要位填充|描述|
|-|-|-|-|
|XK_RE|$b_{xk}$|Y|输出数据的实部,采用**二进制补码(有符号整数,该点需要注意)**或者单精度浮点数格式,在配置时选用了缩放或者块浮点算法下,$b_{xk}=b_{xn}$;对于非缩放算法,$b_{xk}=b_{xn}+NFFT+1$,这里的NFFT为配置时,输入数据位宽;对于单精度浮点运算,$b_{xk}=32$|
|XK_IM|$b_{xk}$|Y|输出数据的虚部($b_{xk}$ = 8-34),同样采用**二进制补码(有符号整数,该点需要注意)**或者单精度浮点数格式,其位宽和XK_RE相同|

与s_axis_data_tdata数据格式类似,**如果所有带填充的字段尚未在8位边界上结束,则应将其扩展到下一个8位边界**,其数据格式如下表,对应的参数示例可查看数据手册:
![m_axis_data_tdata数据格式](

                               
登录/注册后可看大图
)
#### TUSER
TUSER携带了输出数据的状态信息
|名称|位宽|是否需要位填充|描述|
|-|-|-|-|
|XK_INDEX|NFFT|Y|输出数据索引,可选字段,格式为无符号二进制补码|
|BLK_EXP|8|Y|块指数,所应用的缩放系数,无符号二进制补码,可选字段,仅在块浮点运算时可用(block floating-point)|
|OVFLO|1|N|算术溢出指示器,高电平有效,如果数据帧中的任何值溢出,则OVFLO在结果卸载期间为高。OVFLO信号在新数据帧开始时重置。核心具有的每个FFT信道都包含一个单独的OVFLO字段。此端口是可选的,仅适用于缩放算术或单精度浮点I/O。|


![m_axis_data_tuser数据格式](

                               
登录/注册后可看大图
)

#### 有限字长考虑的因素

每次通过运算时,该算法都会执行Radix-4或Radix-2蝶形,其中每个蝶形分别拾取四个或两个复数,并将四个或二个复数返回到同一内存中。核心返回到内存的数字可能大于从内存中提取的数字。
使用**全精度未缩放算术(full-precision unscaled arithmetic)**时,保留所有有效整数位。数据路径的宽度增加,以适应通过蝶形结构的比特增长。乘法产生的分数位的增长在乘法后被截断(或四舍五入)。**输出的宽度为(输入宽度+log2(变换长度)+1)**。这适应了比特增长的最坏情况。
使用**缩放(scaling)**时,在每个阶段使用缩放计划(scaling schedule)除以1、2、4或8。如果缩放不足,蝶形输出可能会超出动态范围并导致溢出。由于在FFT实现中应用了缩放,因此计算出的变换是缩放变换。比例因子s定义为:

$s=2^{\sum_{i=0}^{log(N-1)}b_i}$

其中$b_i$是每个阶段的缩放因子
缩放结果导致最终的输出序列以$1/s$倍调整,对于FFT,经过核心计算后的输出序列$X^{\prime}(k), k=0, \dots, N-1$为:

$X^{\prime}(k)=\frac{1}{s}X(k)=\frac{1}{s}\sum_{n=0}^{N-1}x(n)e^{-jnk2\pi/N}$

对于傅里叶逆变换IFFT而言,输出的序列为:

$x(n)=\frac{1}{s}\sum_{k=0}^{N-1}X(k)e^{jnk2\pi/N}$

#### 架构选择
FFT IP核提供了四个可用的架构,以便在转换时间和资源消耗两个方面进行权衡。其中流水型架构可用于连续数据处理,下图为四种架构的吞吐量和资源消耗的对比。

![架构选择:资源消耗与吞吐量的对比](

                               
登录/注册后可看大图
)
每种架构都提供输出数据的自然或反向排序选项,数据以自然顺序输入。FFT算法在处理过程中对样本进行重新排序,使得以自然顺序输入的数据以相反的顺序输出。核心可以选择按自然顺序输出数据。然而,这给每种架构都带来了成本。对于突发I/O架构,这会带来时间损失,因为卸载数据不能与加载下一帧的输入数据同时进行,因此需要单独的卸载和加载阶段。在流水线架构中,它需要额外的RAM存储来执行重新排序。


流水线流式I/O解决方案将多个Radix-2蝶形处理引擎流水线化,以提供连续的数据处理。每个处理引擎都有自己的存储体来存储输入和中间数据(图3-36)。核心能够同时对当前数据帧执行变换计算,加载下一帧数据的输入数据,并卸载前一帧数据结果。您可以连续输入数据,并在计算延迟后连续卸载结果。如果愿意,这种设计也可以单独计算一个帧或中间有间隙的帧。
![流水线架构](

                               
登录/注册后可看大图
)
*连续流数据并不意味着可以忽略来自FFT核心的AXI4流等待状态。在某些情况下,FFT核心可能必须插入等待状态来暂停传入的采样数据。
在缩放定点模式(scaled fixed-point mode)下,数据在每对Radix-2级后缩放。块浮点模式(block floating-point mode)可能比缩放模式(the scaled mode)使用更多的资源,因为它必须保持额外的精度,以允许动态缩放而不影响性能。因此,如果输入数据被很好地理解并且不太可能表现出较大的幅度波动,则使用缩放算法(具有适当的缩放时间表以避免已知最坏情况下的溢出)就足够了,并且可以节省资源。

输入数据以自然顺序呈现。未加载的输出数据可以是位反转顺序或自然顺序。当选择自然顺序输出数据时,会利用额外的内存资源。
## 配置流程



- step1
![IP核配置step1](

                               
登录/注册后可看大图
)
-  step2
![参数配置](

                               
登录/注册后可看大图
)
- step3
![参数配置2](

                               
登录/注册后可看大图
)
  - step4
![参数配置4](

                               
登录/注册后可看大图
)
- step5
-![延迟信息](

                               
登录/注册后可看大图
)
- step5
![例化步骤](

                               
登录/注册后可看大图
)

例化模板
```verilog
xfft_N1024_pipelined_streaming_IO your_instance_name (
  .aclk(aclk),                                                // input wire aclk
  .aclken(aclken),                                            // input wire aclken
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(s_axis_config_tdata),                  // input wire [23 : 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_fft_overflow(event_fft_overflow),                    // output wire event_fft_overflow
  .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
);
```



## 代码设置
### 控制(握手)规则
下图显示了AXI4流通道中的数据传输。TVALID由信道的源(master)侧驱动,TREADY由接收器(slave)驱动。TVALID表示有效载荷字段(TDATA、TUSER和TLAST)中的值有效。TREADY表示从属设备已准备好接收数据。当TVALID和TREADY在一个循环中都为真时,就会发生传输。主设备和从设备分别为下一次传输设置了TVALID和TREADY。

所有AXI通道都遵循相同的规则:

所有TDATA和TUSER字段都以低位编址(little endian)格式打包。也就是说,子字段的位0与TDATA或TUSER的位0对齐到同一侧。

字段不包含在TDATA或TUSER中,除非核心配置为需要字段存在。例如,如果核心被配置为具有定点大小,则没有比特被分配给指定点大小的NFFT字段。

所有TDATA和TUSER矢量都是8位的倍数。当TDATA或TUSER向量中的所有字段被连接时,整个向量会被填充,使其达到8位边界。
![握手信号](

                               
登录/注册后可看大图
)

按照上述规则在主程序中进行例化,并配置相关控制信号 (这里没有对齐这一步,具体使用时应该使用FIFO进行数据缓冲和对齐)

```verilog
`timescale 1ns / 1ps
module demo_fft(
    input                               sys_clk                    ,
    input                               reset_n                    ,
    input                [  15: 0]      in_data                    ,
    output               [  15: 0]      XK_RE                      ,
    output               [  15: 0]      XK_IM                       
);
    wire               [  23: 0]        s_axis_config_tdata         ;
    reg                                 s_axis_config_tvalid        ;
    wire                                s_axis_config_tready        ;
    wire               [  31: 0]        s_axis_data_tdata           ;
    reg                                 s_axis_data_tvalid          ;
    wire                                s_axis_data_tready          ;
    reg                                 s_axis_data_tlast           ;
    wire               [  31: 0]        m_axis_data_tdata           ;
    wire               [  23: 0]        m_axis_data_tuser           ;
    wire                                m_axis_data_tvalid          ;
    reg                                 m_axis_data_tready          ;
    wire                                m_axis_data_tlast           ;
    wire               [   7: 0]        m_axis_status_tdata         ;
    wire                                m_axis_status_tvalid        ;
    reg                                 m_axis_status_tready        ;
    wire                                event_frame_started         ;
    wire                                event_tlast_unexpected      ;
    wire                                event_tlast_missing         ;
    wire                                event_fft_overflow          ;
    wire                                event_status_channel_halt   ;
    wire                                event_data_in_channel_halt  ;
    wire                                event_data_out_channel_halt  ;
    reg                [   4: 0]        NFFT                      =10;//变换点数
    reg                                 FWD_INV                   =1;//变换形式
    reg                [   9: 0]        SCALE_SCH                 ={2'd2,2'd2,2'd2,2'd2,2'd2}; //缩放因子

    assign                              s_axis_config_tdata         = {5'b0,SCALE_SCH,FWD_INV,3'b0,NFFT}; //s_axis_config_tdata 参数配置
    assign                              s_axis_data_tdata           = {16'd0,in_data};//s_axis_data_tdata 配置
    //输出数据
    assign                              XK_RE                       = m_axis_data_tdata[15:0];
    assign                              XK_IM                       = m_axis_data_tdata[31:16];

    wire               [   9: 0]        XK_INDEX                    ;//输出数据索引
    wire                                OVFLO                       ;
    reg                [  10: 0]        data_cnt                    ;
    assign                              XK_INDEX                    = m_axis_data_tuser[9:0];
    assign                              OVFLO                       = m_axis_data_tuser[16];
//IP核参数配置信号
always @(posedge sys_clk or negedge reset_n)begin
    if(~reset_n)begin
        s_axis_config_tvalid<= 'b0;
    end else if(event_frame_started == 1)begin
        s_axis_config_tvalid<=0;
    end else begin
        s_axis_config_tvalid<= 1;
    end
end
//IP核数据传输信号
always @(posedge sys_clk or negedge reset_n)begin
    if(~reset_n)begin
        s_axis_data_tvalid  <= 'b0;
        data_cnt            <= 12'd0;
    end else if(s_axis_data_tready == 1)begin
        if(data_cnt == 1022)begin  //在即将传输最后一位时,拉高s_axis_data_tlast
            data_cnt            <= data_cnt +1    ;
            s_axis_data_tlast   <= 1    ;
        end else if(data_cnt == 1023 )begin //最后一位,然后输入数据索引置零
            data_cnt            <= 0;
            s_axis_data_tlast   <= 0    ;
        end else begin
            s_axis_data_tlast   <= 0    ;
            data_cnt            <= data_cnt + 1;
        end
    end else begin
        s_axis_data_tvalid  <= 1    ;
        s_axis_data_tlast   <= 0    ;
        data_cnt            <= data_cnt;
    end
end
//配置接收数据信号
always @(posedge sys_clk or negedge reset_n)begin
    if(~reset_n)begin
        m_axis_data_tready <= 1'b0;
    end else if(m_axis_data_tvalid ==1)begin
        m_axis_data_tready <= 1'b1;
    end else begin
        m_axis_data_tready <= 1'b0;
    end
end
//配置IP核状态信号
always @(posedge sys_clk or negedge reset_n)begin
    if(~reset_n)begin
        m_axis_status_tready <= 1'b0;
    end else if(m_axis_status_tvalid ==1)begin
        m_axis_status_tready <= 1'b1;
    end else begin
        m_axis_status_tready <= 1'b0;
    end
end

xfft_N1024_pipelined_streaming_IO xfft_N1024_pipelined_streaming_IO_inst1 (
    .aclk                               (sys_clk                   ),   // input wire aclk
    .aclken                             (1'b1                      ),   // input wire aclken
    .aresetn                            (reset_n                   ),   // input wire aresetn
    .s_axis_config_tdata                (s_axis_config_tdata       ),   // input wire [23 : 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_fft_overflow                 (event_fft_overflow        ),   // output wire event_fft_overflow
    .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
);
endmodule
```
## 仿真设置
- step1
使用matlab生成测试信号(chatGPT生成)
```verilog
%% MATLAB 脚本:生成 Vivado FFT 仿真输入信号(10帧 × 1024点)
clear; clc; close all;

%----------------------------------
% 参数设置
%----------------------------------
Fs = 200e6;            % 采样率 200 MHz
N_fft = 1024;         % 每帧点数
num_frames = 10;      % 帧数
N_total = N_fft * num_frames; % 总样本数

t = (0:N_total-1)/Fs; % 时间轴
f1 = 1e6;             % 1 MHz
f2 = 5e6;             % 10 MHz
f3 = 10e6;            % 15 MHz
f4 = 20e6;            % 15 MHz
%----------------------------------
% 生成信号
%----------------------------------
x = 0.5*sin(2*pi*f1*t) + 0.5*sin(2*pi*f2*t) + 0.5*sin(2*pi*f3*t) + 0.5*sin(2*pi*f4*t);

%----------------------------------
% 量化为16位有符号整数
%----------------------------------
x_int16 = int16(x * (2^15 - 1)/max(x));

%----------------------------------
% 生成 Vivado 可读取的 HEX 文件
%----------------------------------
filename_hex = 'E:\FPGA_Config\demo\demo_fft_dir\demo_piplined_datastreaming_io_fft\fft_input_10frames_hex.txt';
fid = fopen(filename_hex, 'w');
for i = 1:length(x_int16)
    val = typecast(x_int16(i), 'uint16'); % 转换补码表示
    fprintf(fid, '%04X\n', val);
end
fclose(fid);

disp([' 已生成 Vivado 输入文件: ', filename_hex]);

%----------------------------------
% 绘图验证
%----------------------------------
% 显示第一帧波形
figure;
subplot(2,1,1);
n_show = N_fft; % 显示一帧
plot((0:n_show-1)/Fs*1e6, double(x_int16(1:n_show))/2^15);
xlabel('Time (µs)');
ylabel('Amplitude');
title('Time Domain (Frame 1)');
grid on;

% 显示频谱
subplot(2,1,2);
X = fft(x(1:N_fft));
f_axis = (0:N_fft-1)*(Fs/N_fft)/1e6;
plot(f_axis, (X)/max(abs(X)));
xlabel('Frequency (MHz)');
ylabel('Normalized Magnitude');
title('FFT Spectrum of Frame 1');
xlim([0 Fs/(2*1e6)]);
grid on;
```
运行后得到仿真数据
![fft仿真数据](

                               
登录/注册后可看大图
)
- step2
编写测试代码


```verilog
`timescale 1ns / 1ps
module demo_fft_sim(    );
reg                 sys_clk=0;
reg                 reset_n=0;
reg  [  15: 0]      in_data;
wire [  15: 0]      XK_RE  ;
wire [  15: 0]      XK_IM  ;
//----------------------------------
// 数据存储
//----------------------------------
reg [15:0] mem [0:10240-1]; // 10帧×1024点 = 10240样本
integer i;
initial begin
    #1.5
    reset_n = 0;
    #20
    reset_n = 1;
    // 从文件读取输入数据
    $readmemh("E:/FPGA_Config/demo/demo_fft_dir/demo_piplined_datastreaming_io_fft/fft_input_10frames_hex.txt", mem);
    // 按时钟节拍依次送入FFT输入端口
    for (i = 0; i < 10240; i = i + 1) begin
        @(posedge sys_clk);
        in_data <= mem;
    end
    // 结束仿真

end
always #2.5 sys_clk = ~sys_clk;
demo_fft demo_fft_inst1(
    .sys_clk                            (sys_clk                   ),
    .reset_n                            (reset_n                   ),
    .in_data                            (in_data                   ),
    .XK_RE                              (XK_RE                     ),
    .XK_IM                              (XK_IM                     )
);
endmodule
```
- step3 保存并运行仿真
## 结果
可以看到,从仿真开始,到第一帧的数据输出结束,一共花费了约16us
![运行结果](

                               
登录/注册后可看大图
)
四个峰值点的索引分别为4、25、50和101,对应的频率计算方法为:
$F_K = (b_{xk}+1)*200/1024$
即对应频率为(频率分辨率为$200/1024 \approx 0.195$MHz):0.977、5.078、9.96和19.92MHz,与matlab计算结果一致。
![结果计算](

                               
登录/注册后可看大图
)
ref:
[^1]:官方技术手册:[Fast Fourier Transform LogiCORE IP Product Guide (PG109)](https://docs.amd.com/r/en-US/pg109-xfft)

45e43149c60044fb8cc3ed92af73654a.png
回复

使用道具 举报

  • 打卡等级:以坛为家II
  • 打卡总天数:297
  • 打卡月天数:0
  • 打卡总奖励:1093
  • 最近打卡:2026-04-23 09:39:57

156

主题

615

回帖

2657

积分

PADS20200601高级班

积分
2657

终身成就奖

发表于 2026-1-12 08:12 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家III
  • 打卡总天数:612
  • 打卡月天数:20
  • 打卡总奖励:2190
  • 最近打卡:2026-09-24 09:02:09

122

主题

3201

回帖

1万

积分

三级逆天

积分
16522

终身成就奖特殊贡献奖社区居民忠实会员

QQ
发表于 2026-1-12 08:24 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家III
  • 打卡总天数:534
  • 打卡月天数:21
  • 打卡总奖励:1615
  • 最近打卡:2026-09-24 09:31:22

43

主题

1万

回帖

7万

积分

三级逆天

积分
75168

终身成就奖特殊贡献奖原创先锋奖社区居民忠实会员社区劳模最爱沙发社区明星原创达人优秀斑竹奖宣传大使奖

QQ
发表于 2026-1-12 08:57 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家II
  • 打卡总天数:362
  • 打卡月天数:0
  • 打卡总奖励:1151
  • 最近打卡:2026-08-27 09:05:23

27

主题

653

回帖

2574

积分

PADS20200601高级班

积分
2574

终身成就奖特殊贡献奖

发表于 2026-1-12 09:06 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家III
  • 打卡总天数:692
  • 打卡月天数:20
  • 打卡总奖励:2695
  • 最近打卡:2026-09-24 14:59:32

28

主题

1578

回帖

5595

积分

二级逆天

积分
5595
QQ
发表于 2026-1-12 09:07 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家III
  • 打卡总天数:606
  • 打卡月天数:21
  • 打卡总奖励:1794
  • 最近打卡:2026-09-24 09:08:52

17

主题

1312

回帖

5138

积分

1元学习PADS(2期)

积分
5138

终身成就奖特殊贡献奖原创先锋奖

QQ
发表于 2026-1-12 09:07 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家III
  • 打卡总天数:588
  • 打卡月天数:24
  • 打卡总奖励:2120
  • 最近打卡:2026-09-24 09:00:31

14

主题

4399

回帖

8096

积分

1元学习Pads(221219)旁听

积分
8096

终身成就奖特殊贡献奖原创先锋奖

发表于 2026-1-12 09:13 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家III
  • 打卡总天数:460
  • 打卡月天数:18
  • 打卡总奖励:1406
  • 最近打卡:2026-09-23 12:33:32

39

主题

1万

回帖

2万

积分

三级逆天

你必须非常努力才能显

积分
23654

终身成就奖社区居民忠实会员社区劳模最爱沙发原创达人特殊贡献奖原创先锋奖优秀斑竹奖宣传大使奖

QQ
发表于 2026-1-12 09:20 | 显示全部楼层
回复

使用道具 举报

  • 打卡等级:以坛为家III
  • 打卡总天数:383
  • 打卡月天数:0
  • 打卡总奖励:1282
  • 最近打卡:2026-02-25 09:54:42

1

主题

398

回帖

2018

积分

二级逆天

积分
2018

终身成就奖特殊贡献奖原创先锋奖

发表于 2026-1-12 09:24 | 显示全部楼层
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright ©2011-2024 NTpcb.com All Right Reserved.  Powered by Discuz! (NTpcb)

本站信息均由会员发表,不代表NTpcb立场,如侵犯了您的权利请举报投诉

本站所有资料仅限参考学习交流,PCB原理图源码等所有资料不保证准确没问题,请不要用作生产。否则后果自负。

闽公网安备35062302000276号 | 闽ICP备2024076463号-1 | 论坛技术支持QQ群171867948 | 论坛问题,充值问题请联系QQ1308068381

平平安安
TOP
快速回复 返回顶部 返回列表