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 具有更高的整体效率。这类似于将一个大矩阵运算分解为两个较小的矩阵运算,通过中间转置步骤降低计算复杂度。
架构概览
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
模块组成
本模块包含三个紧密协作的子系统:
- channelizer_graph_application —— 完整的信道化器集成图,组合了 FIR 滤波器组和 2D IFFT
- ifft4096_2d_graphs_and_characterization —— 4096点二维 IFFT 的实现与性能表征
- 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_graph 和 ifft4096_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% 的资源节省。
数据流详解
端到端数据路径
(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。
跨模块依赖关系
关键外部依赖
-
Vitis Libraries DSP: 提供
fir_tdm_graph和vss_fft_ifft_1d_graph- 文档参考:TDM FIR Graph
- 文档参考:2D FFT/IFFT Vitis Subsystem
-
ADF 框架: AMD AI Engine 数据流编程基础
-
平台创建流程: 依赖
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. 调试建议
-
分层验证:
- 先单独验证
tdm_fir_characterize(纯 FIR) - 再验证
ifft4096_2d_characterize(纯 IFFT,SSR=1) - 最后集成完整
channelizer
- 先单独验证
-
使用 vitis_analyzer:
vitis_analyzer aiesimulator_output/default.aierun_summary重点关注 Array View(tile 布局)和 Trace View(时序分析)。
-
循环 II(Initiation Interval)检查:
python scripts/get_loop_II.py
相关模块链接
- channelizer_graph_application —— 完整信道化器实现细节
- ifft4096_2d_graphs_and_characterization —— 2D IFFT 设计与优化
- tdm_fir_filter_bank_graphs_and_characterization —— TDM FIR 滤波器组实现
- prime_factor_fft_pipeline_graphs —— 其他 FFT 实现参考
- farrow_filter_design_variants —— 滤波器设计变体
参考资料
- 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.
- Versal Adaptive SoC AIE-ML Architecture Manual (AM020)
- AI Engine Kernel and Graph Programming Guide (UG1076)
- Versal ACAP Memory Resources Architecture Manual (AM007)