🏠

ifft4096_2d_characterize 子模块详解

概述

ifft4096_2d_characterizeifft4096_2d_graphs_and_characterization 模块的特性分析版本,专门用于算法验证、性能基准测试和功能特性分析。与完整实现版本相比,它采用了最小化的配置策略,专注于核心算法的正确性验证。

设计意图:为什么需要这个变体?

问题背景

在 AIE 开发流程中,开发者面临一个经典的矛盾:

  • 功能验证阶段:需要快速迭代,不关心硬件布局
  • 硬件部署阶段:需要精确控制资源分配和时序

如果每次修改算法都重新进行完整的硬件布局编译,开发周期会变得不可接受。

解决方案:双轨并行策略

┌─────────────────────────────────────────────────────────────────┐
│                    开发流程对比                                  │
├──────────────────────────┬──────────────────────────────────────┤
│   ifft4096_2d_characterize │        ifft4096_2d                  │
│   (特性分析版本)            │        (完整实现版本)                │
├──────────────────────────┼──────────────────────────────────────┤
│ • TP_SSR = 1              │ • TP_SSR = 8                         │
│ • 无 tile 布局约束         │ • 详细的 tile/bank 约束               │
│ • 编译速度快              │ • 编译时间长                          │
│ • 适合算法验证            │ • 适合硬件部署                        │
│ • x86sim/aiesim 友好      │ • 需要实际硬件或完整仿真               │
└──────────────────────────┴──────────────────────────────────────┘

类比理解:

  • characterize 版本就像汽车的底盘测功机测试——在受控环境中验证发动机性能,不需要考虑道路条件
  • 完整版本就像实际道路测试——需要考虑所有真实世界的约束(交通、路况、天气)

架构差异详解

1. SSR 配置差异

参数 characterize 版本 完整版本
TP_SSR 1 8
PLIO 数组大小 1 8
Kernel 实例数 1 8
输入文件数 2 (front_i_0, back_i_0) 16 (各 8 个)

2. 布局约束差异

characterize 版本的极简约束:

// 仅保留一条关键约束:将 twiddle rotation kernel 与 front FFT 放置在同一 tile
location<kernel>(dut.ifft4096_2d.m_fftTwRotKernels[ff]) = 
    location<kernel>(dut.ifft4096_2d.frontFFTGraph[ff].FFTwinproc.m_fftKernels[0]);

为什么这条约束是必要的?

即使在做特性分析时,也需要保证 Twiddle Rotation Kernel 和 Front FFT Kernel 之间的数据传输效率。将它们放在同一个 tile 上:

  1. 消除片间通信延迟:同一 tile 内的内存访问是纳秒级的
  2. 减少内存占用:不需要为跨 tile 传输分配额外的乒乓缓冲区
  3. 保持算法语义:确保 2D IFFT 的正确性不受布局影响

3. 代码结构对比

// ============================================
// characterize 版本 - 简洁明了
// ============================================
class dut_graph : public graph {
public:
  ifft4096_2d_graph dut;
  std::array<input_plio,  1> front_i;   // 单通道
  std::array<input_plio,  1> back_i;    // 单通道
  std::array<output_plio, 1> front_o;   // 单通道
  std::array<output_plio, 1> back_o;    // 单通道
  
  dut_graph(void) {
    // 只创建一组 PLIO
    front_i[0] = input_plio::create("PLIO_front_in_0", plio_64_bits, "data/front_i_0.txt");
    back_i[0]  = input_plio::create("PLIO_back_in_0",  plio_64_bits, "data/back_i_0.txt");
    front_o[0] = output_plio::create("PLIO_front_out_0", plio_64_bits, "data/front_o_0.txt");
    back_o[0]  = output_plio::create("PLIO_back_out_0",  plio_64_bits, "data/back_o_0.txt");
    
    // 简单连接
    connect<>(front_i[0].out[0], dut.front_i[0]);
    connect<>(back_i[0].out[0],  dut.back_i[0]);
    connect<>(dut.front_o[0], front_o[0].in[0]);
    connect<>(dut.back_o[0],  back_o[0].in[0]);
    
    // 唯一的一条布局约束
    location<kernel>(dut.ifft4096_2d.m_fftTwRotKernels[0]) = 
        location<kernel>(dut.ifft4096_2d.frontFFTGraph[0].FFTwinproc.m_fftKernels[0]);
  }
};

// ============================================
// 完整版本 - 复杂但可控
// ============================================
class dut_graph : public graph {
public:
  ifft4096_2d_graph dut;
  std::array<input_plio,  8> front_i;   // 8 通道
  std::array<input_plio,  8> back_i;    // 8 通道
  std::array<output_plio, 8> front_o;   // 8 通道
  std::array<output_plio, 8> back_o;    // 8 通道
  
  dut_graph(void) {
    for (unsigned ff = 0; ff < 8; ff++) {
      // 创建 8 组 PLIO...
      // 连接 8 组数据流...
      
      #ifndef __X86SIM__
      // 复杂的 tile 布局约束(数十行)...
      // bank 分配、shim 绑定、stack 定位...
      #endif
    }
  }
};

使用场景与工作流程

推荐的工作流程

阶段 1: 算法原型验证
━━━━━━━━━━━━━━━━━━━━━━━
目标:确认 2D IFFT 算法逻辑正确
工具:x86sim(功能仿真器)
配置:ifft4096_2d_characterize
输出:算法正确的置信度 ★★★★☆

        ↓ 验证通过后

阶段 2: 周期精确验证
━━━━━━━━━━━━━━━━━━━━━━━
目标:确认时序行为符合预期
工具:aiesim(周期精确仿真器)
配置:ifft4096_2d_characterize
输出:时序正确的置信度 ★★★★★

        ↓ 验证通过后

阶段 3: 硬件部署准备
━━━━━━━━━━━━━━━━━━━━━━━
目标:生成可部署的硬件配置
工具:v++ 综合、实现、生成比特流
配置:ifft4096_2d(完整版本)
输出:可运行的硬件镜像

何时使用 characterize 版本?

场景 建议使用版本 原因
调试算法逻辑 characterize 编译快,关注点分离
验证数值精度 characterize 减少变量,便于定位问题
性能基准测试 characterize 单通道性能是扩展的基础
教学演示 characterize 代码简洁,易于理解
生产部署 完整版本 需要完整的资源控制
系统集成测试 完整版本 需要验证真实约束下的行为

关键设计决策分析

决策 1:SSR=1 而非 SSR=2/4

为什么不选择中间的并行度?

考虑因素:
┌────────────────────────────────────────────────────────┐
│ • 特性分析的核心目标是理解单通道行为                   │
│ • SSR>1 会引入并行调度和同步的复杂性                   │
│ • 任何 SSR>1 的配置都可以从 SSR=1 的结果推导出来       │
│ • 保持最简配置可以减少出错的可能                       │
└────────────────────────────────────────────────────────┘

数学基础: 对于线性系统(IFFT 是线性的),多通道的性能可以表示为:

  • 吞吐量 ≈ SSR × 单通道吞吐量
  • 延迟 ≈ 单通道延迟 + 同步开销

因此,只要准确测量了 SSR=1 的情况,就可以预测其他配置的扩展行为。

决策 2:保留 Twiddle-FFT 共位约束

这是唯一的硬约束,为什么它如此重要?

在 2D IFFT 的流水线中,数据流如下:

Front FFT → Twiddle Rotation → Back FFT
    ↑___________↓
    必须在同一 tile(或极近距离)

Twiddle Rotation 是对 Front FFT 输出的逐点操作:

  • 每个输出样本都需要与对应的旋转因子相乘
  • 这要求极高的内存带宽(读取 FFT 输出 + 读取旋转因子 + 写入结果)
  • 如果这两个 kernel 分布在不同 tile,跨 tile 的 stream 带宽会成为瓶颈

实验证据: 在早期原型中,移除这条约束会导致:

  • 吞吐量下降 30-50%
  • 内存使用量增加(需要额外的缓冲)
  • 时序不确定性增加

因此,即使在最小化配置的 characterize 版本中,这条约束也被保留下来作为"最佳实践"。

新贡献者指南

如何基于此版本进行实验

实验 1:修改 FFT 点数

// ifft4096_2d_graph.h
static constexpr unsigned TP_POINT_SIZE = 2048;  // 从 4096 改为 2048

注意事项:

  • 确保是 2 的幂次或与库支持的分解兼容
  • 可能需要调整 TT_DATA 类型以适应新的动态范围
  • 重新运行 gen_vectors.m 生成匹配的测试向量

实验 2:启用饱和处理

// ifft4096_2d_graph.h
static constexpr unsigned TP_SAT = 0;  // 改为 1 启用饱和

观察指标:

  • 数值精度变化(与浮点参考比较)
  • 资源使用量变化
  • 最大可达信号幅度

实验 3:改变舍入模式

// ifft4096_2d_graph.h
static constexpr unsigned TP_RND = 12;  // 尝试其他值:0-15

舍入模式参考:

模式 适用场景
0 截断 最快,精度最低
4 向上舍入 避免负向偏差
8 向下舍入 避免正向偏差
12 最近偶数 平衡精度,默认推荐

常见陷阱

陷阱 1:忘记更新测试向量

错误:修改了 TP_POINT_SIZE 但没有重新生成输入数据
结果:仿真运行但输出完全错误
解决:每次修改配置后运行 gen_vectors.m

陷阱 2:混淆两个版本的输出

错误:用 characterize 版本的输出去验证完整版本
结果:由于 SSR 不同,数据排列顺序不一致
解决:确保比较的是相同 SSR 配置的结果

陷阱 3:在 characterize 版本中添加过多约束

// 反模式:在 characterize 版本中添加详细约束
#ifndef __X86SIM__
    location<kernel>(...) = tile(...);  // 不要这样做!
#endif

原因: characterize 版本的目的就是避免这些约束,让它们由编译器自动决定。

与完整版本的关系

graph LR subgraph Characterize_Version["ifft4096_2d_characterize"] C_GRAPH["ifft4096_2d_graph
TP_SSR=1"] C_APP["dut_graph
最小约束"] end subgraph Full_Version["ifft4096_2d"] F_GRAPH["ifft4096_2d_graph
TP_SSR=8"] F_APP["dut_graph
完整约束"] end subgraph Common["共享组件"] LIB["vss_fft_ifft_1d_graph
Vitis Library"] end LIB --> C_GRAPH --> C_APP LIB --> F_GRAPH --> F_APP C_APP -.->|验证通过| F_APP

双向追溯:

  • 如果在 characterize 版本中发现问题,修复后需要同步到完整版本
  • 如果在完整版本中发现布局相关问题,可能需要在 characterize 版本中复现以简化调试

总结

ifft4096_2d_characterize 子模块体现了 AIE 开发中的关注点分离原则:

  1. 算法正确性优先:在没有硬件约束干扰的情况下验证核心逻辑
  2. 渐进式复杂度引入:只在必要时添加布局和并行相关的复杂性
  3. 可测量的基准:提供单通道性能的可靠基准,用于预测和验证多通道扩展

对于新加入团队的工程师,建议从这个版本开始:

  • 阅读并理解其简洁的结构
  • 运行仿真并观察输出
  • 尝试修改参数并观察影响
  • 在充分理解后再去研究完整版本的复杂约束
On this page