🏠

Channelizer IFFT 与 TDM FIR Graphs 模块深度解析

概述

本模块是 AMD Versal AIE-ML 平台上实现高性能多相信道化器(Polyphase Channelizer)的核心组成部分。它利用 Vitis DSP 库中的两个关键 IP 元素——**TDM FIR(时分复用有限脉冲响应滤波器)**和 2D IFFT(二维逆快速傅里叶变换)——构建了一个能够处理 2 GSPS 采样率、4096 通道的完整信道化解决方案。

想象你正在处理一条高速公路上的交通流量:原始数据就像所有车道的车辆混杂在一起的高速公路,而信道化器就像是一个智能分流系统,将不同目的地的车辆(频域信号)分离开来,送入各自的通道进行处理。TDM FIR 负责预处理(滤波),而 2D IFFT 则完成最终的频域到时域转换。


问题空间与设计动机

信道化器的核心挑战

在现代无线通信系统中,信道化器需要同时下变频多个频分复用(FDM)通道。随着 RF-DAC 和 RF-ADC 技术的进步,采样率持续攀升,这给可重配置设备(如 FPGA)带来了巨大挑战:

参数 数值 说明
采样率 (Fs) 2 GSPS 每秒 20 亿样本
通道数 (M) 4096 同时处理的独立通道
每通道带宽 488.28125 KHz Fs/M 计算得出
每相位抽头数 (K) 36 滤波器复杂度指标
总抽头数 147,456 M × K

这种规模的计算需求无法由单一处理单元承担,必须进行系统分区(System Partitioning),将计算任务合理分配到 AI Engine 和可编程逻辑(PL)之间。

为什么选择 2D IFFT?

对于大点数(4096点)且 SSR > 1 的场景,使用 2D IFFT 算法比传统 1D IFFT 具有更高的整体效率。这类似于将一个大矩阵运算分解为两个较小的矩阵运算,通过中间转置步骤降低计算复杂度。


架构概览

graph TB subgraph "Channelizer 顶层图" CH[channelizer_graph] end subgraph "TDM FIR 滤波器组" FB[firbank_graph
32 tiles, SSR=32] end subgraph "2D IFFT 子系统" IFFT[ifft4096_2d_graph
16 tiles, SSR=8] FRONT[Front FFT + Twiddle] BACK[Back FFT] end subgraph "PL 转置模块" TRANS[Transpose Blocks
BRAM-based] end CH --> FB CH --> IFFT IFFT --> FRONT IFFT --> BACK FRONT --> TRANS TRANS --> BACK

模块组成

本模块包含三个紧密协作的子系统:

  1. channelizer_graph_application —— 完整的信道化器集成图,组合了 FIR 滤波器组和 2D IFFT
  2. ifft4096_2d_graphs_and_characterization —— 4096点二维 IFFT 的实现与性能表征
  3. tdm_fir_filter_bank_graphs_and_characterization —— 时分复用 FIR 滤波器组的实现与优化

核心抽象与心智模型

1. Graph 层次结构

本模块采用 ADF(AI Engine Data Flow)Graph 的分层抽象:

dut_graph (测试封装层)
    └── channelizer_graph (业务逻辑层)
            ├── firbank_graph (FIR 滤波器组)
            │       └── fir_tdm_graph (Vitis 库 IP)
            └── ifft4096_2d_graph (2D IFFT)
                    └── vss_fft_ifft_1d_graph (Vitis 库 IP)

类比理解:想象一个交响乐团 —— dut_graph 是整个音乐厅,channelizer_graph 是指挥,firbank_graphifft4096_2d_graph 分别是弦乐部和管乐部,而底层的 Vitis 库 IP 则是具体的乐器演奏者。

2. SSR(Super Sample Rate)概念

SSR 是本设计中的关键参数,表示超采样率或并行度:

  • TDM FIR: TP_SSR = 32,意味着 32 个并行路径同时处理不同的时隙
  • 2D IFFT: TP_SSR = 8(优化后)或 TP_SSR = 1(表征模式),控制 FFT 的并行分解程度

选择 SSR=8 而非理论最小值 SSR=5 的原因:

  • 2 的幂次简化了 PL 侧的连接逻辑
  • 允许 TDM FIR 输出直接映射到 2D IFFT 输入,无需复杂的状态管理

3. 单缓冲 vs 双缓冲

在存储优化版本中,使用了 single_buffer 约束来替代默认的双缓冲:

single_buffer(dut.tdmfir.m_firKernels[ii].in[0]);

权衡分析

  • 双缓冲(默认):吞吐量更高,但存储需求翻倍
  • 单缓冲:存储减半,但可能引入流水线气泡,降低峰值吞吐

对于存储受限的滤波器组场景,选择单缓冲将资源从 64 tiles 降至 32 tiles,以约 30% 的吞吐损失换取 50% 的资源节省。


数据流详解

端到端数据路径

sequenceDiagram participant PL_IN as PL Input
(DMA Source) participant FIR as TDM FIR
(32 AIE Tiles) participant TRANS1 as Front Transpose
(PL BRAM) participant FFT_FRONT as Front FFT
(8 AIE Tiles) participant TWIDDLE as Twiddle Mult
(与 Front FFT 同 Tile) participant TRANS2 as Back Transpose
(PL BRAM) participant FFT_BACK as Back FFT
(8 AIE Tiles) participant PL_OUT as PL Output
(DMA Sink) PL_IN->>FIR: cint16 数据流
2 GSPS 聚合速率 Note over FIR: 4096 通道 × 36 抽头滤波 FIR->>TRANS1: cint32 输出 Note over TRANS1: 矩阵转置
64×64 数据重排 TRANS1->>FFT_FRONT: 行方向 FFT 输入 Note over FFT_FRONT: 64点 FFT × 64 行 FFT_FRONT->>TWIDDLE: 点乘旋转因子 TWIDDLE->>TRANS2: 中间结果 Note over TRANS2: 再次转置 TRANS2->>FFT_BACK: 列方向 FFT 输入 Note over FFT_BACK: 64点 FFT × 64 列 FFT_BACK->>PL_OUT: cint32 输出
4096点 IFFT 结果

关键数据类型定义

TDM FIR 配置 (firbank_graph.h)

typedef cint16                      TT_DATA;        // 输入数据类型
typedef cint32                      TT_OUT_DATA;    // 输出数据类型
typedef int32                       TT_COEFF;       // 滤波器系数类型
static constexpr unsigned           TP_FIR_LEN = 36;           // 每相位抽头数
static constexpr unsigned           TP_SHIFT = 15;             // 输出移位
static constexpr unsigned           TP_RND = 12;               // 舍入模式
static constexpr unsigned           TP_TDM_CHANNELS = 4096;    // TDM 通道数
static constexpr unsigned           TP_SSR = 32;               // 超采样率
static constexpr unsigned           TP_INPUT_WINDOW_VSIZE = 4096;
static constexpr unsigned           TP_CASC_LEN = 1;           // 级联长度

2D IFFT 配置 (ifft4096_2d_graph.h)

typedef cint32            TT_DATA;        // 数据类型
typedef cint16            TT_TWIDDLE;     // 旋转因子类型
static constexpr unsigned TP_POINT_SIZE = 4096;      // FFT 点数
static constexpr unsigned TP_FFT_NIFFT = 0;          // 0=IFFT, 1=FFT
static constexpr unsigned TP_SHIFT = 0;
static constexpr unsigned TP_API = 0;                // 窗口 API
static constexpr unsigned TP_SSR = 8;                // 超采样率
static constexpr unsigned TP_RND = 12;
static constexpr unsigned TP_SAT = 1;

物理布局与位置约束

AIE Tile 布局策略

在完整的 channelizer_app.cpp 中,内核被精心布置以优化路由:

Col:    0    1   2    3   4   5  6  7
      ------------------------------
Row: 3 | 12  28  14  30  13  29 15 31
     2 |  8  24  10  26   9  25 11 27
     1 |  4  20   6  22   5  21  7 23
     0 |  0  16   2  18   1  17  3 19
      ------------------------------
         ↑ FIR Bank (32 kernels)

设计意图:通过交错放置内核,避免所有分裂/合并单元都连接到同一列造成的路由拥塞。

内存银行分配

每个 FIR 内核的内存布局经过精细规划:

location<kernel>   (dut.firbank.tdmfir.m_firKernels[ii]) = tile(start_fb+FBX[ii], FBY[ii]);
location<stack>    (dut.firbank.tdmfir.m_firKernels[ii]) = bank(start_fb+FBX[ii], FBY[ii], 3);
location<parameter>(dut.firbank.tdmfir.m_firKernels[ii].param[0]) = bank(start_fb+FBX[ii], FBY[ii], 3);
location<parameter>(dut.firbank.tdmfir.m_firKernels[ii].param[1]) = address(start_fb+FBX[ii], FBY[ii], 0x4C00);
location<buffer>   (dut.firbank.tdmfir.m_firKernels[ii].in[0]) = bank(start_fb+FBX[ii], FBY[ii], 0);
location<buffer>   (dut.firbank.tdmfir.m_firKernels[ii].out[0]) = { bank(start_fb+FBX[ii], FBY[ii], 1), 
                                                                    bank(start_fb+FBX[ii], FBY[ii], 3) };

内存映射说明

  • Bank 0: 输入缓冲区
  • Bank 1 & 3: 输出缓冲区(双缓冲或扩展存储)
  • Bank 3: 栈空间和参数存储
  • 0x4C00: 系数表专用地址

IFFT Tile 布局

// 前半部分 SSR (0-3)
location<kernel>(dut.ifft4096_2d.frontFFTGraph[ff].FFTwinproc.m_fftKernels[0]) = tile(34+ff, 4);
location<kernel>(dut.ifft4096_2d.backFFTGraph[ff].FFTwinproc.m_fftKernels[0]) = tile(30+ff, 4);

// 后半部分 SSR (4-7)
location<kernel>(dut.ifft4096_2d.frontFFTGraph[ff].FFTwinproc.m_fftKernels[0]) = tile(34+ff-4, 5);
location<kernel>(dut.ifft4096_2d.backFFTGraph[ff].FFTwinproc.m_fftKernels[0]) = tile(30+ff-4, 5);

关键优化m_fftTwRotKernels(旋转因子乘法)与 frontFFTGraph 的 FFT 内核放置在同一个 tile,减少核间通信延迟。


设计权衡与决策分析

1. 存储 vs 吞吐量权衡

方案 Tiles 数量 吞吐量 适用场景
默认双缓冲 64 ~3270 MSPS 性能优先
单缓冲优化 32 ~2230 MSPS 资源受限

决策依据:在 VE2802 器件上,48 个 AIE tiles 用于计算是可接受的,因此选择单缓冲方案以平衡资源与性能。

2. SSR 选择:8 vs 5

理论上 SSR=5 即可满足 2 GSPS 需求(539 MSPS × 5 ≈ 2695 MSPS),但实际选择 SSR=8:

优势

  • 2 的幂次简化 PL 侧的数据路由
  • TDM FIR (SSR=32) 到 2D IFFT (SSR=8) 的数据映射更直接(32÷8=4,整数倍)

代价

  • 额外 3 个 front FFT 实例(共 8 个 vs 5 个)
  • 更多 AIE tiles 占用(16 vs 10)

3. 2D 分解:64×64

4096 = 64 × 64 的分解选择:

  • 过小的一维(如 16×256):增加转置操作的相对开销
  • 过大的一维(如 128×32):超出 AIE-ML 单核 FFT 的高效处理范围

64 点是 AIE-ML 上 FFT 实现的"甜点"尺寸,平衡了计算效率和资源使用。

4. PL 转置实现

转置操作在 PL 中使用 BRAM 实现 ping-pong 缓冲:

BRAM 需求 = 4096 samples / 512 × 2 ports × 2 (ping-pong) = 32 BRAMs per transpose

潜在优化:若能消除 ping-pong 需求,BRAM 使用量可减半至 16。


跨模块依赖关系

graph LR subgraph "本模块内部" CH[channelizer_graph] FIR[firbank_graph] IFFT[ifft4096_2d_graph] end subgraph "Vitis DSP 库" TDM_LIB[fir_tdm_graph.hpp] FFT_LIB[vss_fft_ifft_1d_graph.hpp] end subgraph "AIE 运行时" ADF[] end subgraph "外部工具链" MAKEFILE[Makefile.graph] end CH --> FIR CH --> IFFT FIR --> TDM_LIB IFFT --> FFT_LIB FIR --> ADF IFFT --> ADF CH -.->|depends_on| MAKEFILE

关键外部依赖

  1. Vitis Libraries DSP: 提供 fir_tdm_graphvss_fft_ifft_1d_graph

  2. ADF 框架: AMD AI Engine 数据流编程基础

  3. 平台创建流程: 依赖 Makefile.graph 进行系统集成


新贡献者注意事项

1. 隐式契约与前置条件

FIR 系数初始化

firbank_graph(void) : dut(TAPS_INIT_0)  // 必须通过构造函数传入系数

系数数组 TAPS_INIT_0 必须在编译期确定,存储于 firbank_init_taps.h

PLIO 命名约定

  • 输入: "PLIO_i_" + std::to_string(index)
  • 输出: "PLIO_o_" + std::to_string(index)
  • IFFT Front: "PLIO_front_in_" + std::to_string(ff)
  • IFFT Back: "PLIO_back_in_" + std::to_string(ff)

2. 仿真与硬件差异

#ifdef AIE_SIM_ONLY
    sig_i[ii] = input_plio::create("PLIO_i_"+std::to_string(ii), plio_64_bits, file_i0);
#else
    sig_i[ii] = input_plio::create("PLIO_i_"+std::to_string(ii), plio_64_bits);
#endif
  • 仿真模式 (AIE_SIM_ONLY): 从文本文件读取输入/写入输出
  • 硬件模式: 使用实际 PL 接口,无文件关联

3. 位置约束的条件编译

#ifndef __X86SIM__
    // 物理位置约束仅在实际 AIE 编译时生效
    location<kernel>(...) = tile(...);
#endif

x86sim(功能仿真)不支持物理位置约束,必须用条件编译包裹。

4. 常见的性能陷阱

问题现象 可能原因 排查方法
吞吐量低于预期 未应用 single_buffer 优化 检查 vitis_analyzer 的缓冲区使用情况
Tile 数量超标 双缓冲导致存储倍增 确认 single_buffer 已应用到所有输入
路由拥塞 内核放置过于集中 参考 FBX/FBY 交错布局模式
IFFT 瓶颈 Front/Back FFT 负载不均衡 分别测量两阶段吞吐

5. 调试建议

  1. 分层验证

    • 先单独验证 tdm_fir_characterize(纯 FIR)
    • 再验证 ifft4096_2d_characterize(纯 IFFT,SSR=1)
    • 最后集成完整 channelizer
  2. 使用 vitis_analyzer

    vitis_analyzer aiesimulator_output/default.aierun_summary
    

    重点关注 Array View(tile 布局)和 Trace View(时序分析)。

  3. 循环 II(Initiation Interval)检查

    python scripts/get_loop_II.py
    

相关模块链接


参考资料

  1. F.J. Harris et al., "Digital Receivers and Transmitter Using Polyphase Filter Banks for Wireless Communications", IEEE Transactions on Microwave Theory and Techniques, Vol. 51, No. 4, April 2003.
  2. Versal Adaptive SoC AIE-ML Architecture Manual (AM020)
  3. AI Engine Kernel and Graph Programming Guide (UG1076)
  4. Versal ACAP Memory Resources Architecture Manual (AM007)
On this page