🏠

MUSIC 算法 MM2S/S2MM 系统集成模块

概述

本模块是 MUSIC (Multiple Signal Classification) 方向到达估计算法 的硬件系统集成层,负责在 Versal ACAP 设备(VC1902)上构建完整的异构计算流水线。它解决了如何将复杂的信号处理算法从 MATLAB 仿真模型高效地映射到实际硬件的关键问题。

核心使命:桥接理论与硬件

想象你正在设计一个雷达或通信系统的"数字耳朵"——这个系统需要实时分析来自8个天线阵元的信号,判断信号从哪个方向传来。MUSIC 算法是这个"耳朵"的大脑,而本模块则是将这个大脑安装到 FPGA 硬件中的"神经系统"。

具体来说,本模块解决三个核心问题:

  1. 数据供给问题:如何以足够快的速度(1μs/快照)将 128×8 的快照矩阵从 DDR 内存输送到 AI Engine 阵列?
  2. 结果回收问题:如何将 AI Engine 计算出的空间谱和 DOA 估计结果高效地写回 DDR?
  3. 系统集成问题:如何在 PL (Programmable Logic)、AI Engine 和 PS (Processing System) 之间建立正确的数据通路和时钟域协调?

为什么采用这种架构?

本设计采用了 "双输入、单输出" 的数据搬运策略:

  • 两个 MM2S (Memory-to-Stream) 内核:因为单个 PLIO 接口的带宽不足以在 1μs 内传输完整的 128×8 矩阵。通过两条独立的 64-bit @ 625MHz 数据流,实现所需的聚合带宽。
  • 一个 S2MM (Stream-to-Memory) 内核:输出数据量相对较小(256 个频谱 bin + 32 个标签),单个数据流即可满足需求。

这种设计与传统的 "DMA 乒乓缓冲" 方案不同——它不是简单地搬运数据块,而是作为 AI Engine 数据流水线的"前端泵"和"后端收集器",确保计算单元永远不会因数据饥饿而停滞。


架构概览

graph TB subgraph Host["Host PC (MATLAB)"] TCP["TCP/IP Client"] end subgraph VCK190["VCK190 Board"] subgraph PS["Processing System"] Server["TCP Server"] XRT["XRT Runtime"] end subgraph PL["Programmable Logic (PL)"] subgraph DataMovers["HLS Data Movers"] MM2S_1["mm2s_1
312.5MHz, 128-bit"] MM2S_2["mm2s_2
312.5MHz, 128-bit"] S2MM["s2mm
312.5MHz, 128-bit"] end CDC["Clock Domain Crossing
& Data Width Converter"] end subgraph AIE["AI Engine Array"] IOAdapter["IO Adapter Subgraph"] QRD["QRD Subgraph
(36 tiles)"] SVD["SVD Subgraph
(38 tiles)"] DOA["DOA Subgraph
(64 tiles)"] Scanner["Scanner Subgraph
(2 tiles)"] Finder["Finder Subgraph
(16 tiles)"] end DDR[(DDR4 Memory)] end TCP <-->|"Snapshots & Results"| Server Server <-->|"Frame Processing"| XRT XRT -->|"Configure & Trigger"| DataMovers XRT -->|"Graph Control"| AIE DDR <-->|"Read 128x8 matrix"| MM2S_1 DDR <-->|"Read 128x8 matrix"| MM2S_2 DDR <-->|"Write results"| S2MM MM2S_1 -->|"PLIO_i_0
64-bit @ 625MHz"| CDC MM2S_2 -->|"PLIO_i_1
64-bit @ 625MHz"| CDC CDC --> IOAdapter IOAdapter --> QRD --> SVD --> DOA --> Scanner --> Finder Finder -->|"PLIO_o"| CDC --> S2MM

数据流详解

整个系统的数据流可以分为三个阶段:

阶段一:主机到 DDR(软件层面)

  1. MATLAB 生成合成快照数据(模拟移动目标的雷达回波)
  2. 数据通过 TCP/IP 传输到 VCK190 的 PS 端
  3. PS 应用将数据帧解析后写入 DDR4 的特定缓冲区

阶段二:DDR 到 AI Engine(硬件加速)

这是本模块的核心职责所在:

DDR Buffer → mm2s_1/mm2s_2 → CDC/DWC → PLIO → AI Engine Pipeline → PLIO → s2mm → DDR
  • MM2S 内核:每个时钟周期从 DDR 读取 128-bit 数据(通过 m_axi 接口),转换为 AXI4-Stream 输出
  • 时钟域转换:Vitis 自动插入的 IP 将 312.5MHz/128-bit 转换为 625MHz/64-bit 以匹配 PLIO
  • AI Engine 流水线:6 个子图级联,执行完整的 MUSIC 算法(详见下文)
  • S2MM 内核:将 AXI4-Stream 结果写回 DDR

阶段三:DDR 到主机(结果返回)

  1. PS 检测 DMA 完成中断
  2. 从 DDR 读取计算结果
  3. 封装为 TCP 帧返回给 MATLAB
  4. MATLAB 可视化对比参考结果与硬件结果

核心组件详解

HLS 数据搬运内核

本模块包含两个对称的 MM2S 内核和一个 S2MM 内核,均使用 Vitis HLS 实现。

MM2S 内核 (mm2s.cpp)

void mm2s(ap_int<128>* mem, hls::stream<ap_int<128>>& s, int size) {
    #pragma HLS INTERFACE m_axi port=mem offset=slave bundle=gmem
    #pragma HLS interface axis port=s
    #pragma HLS INTERFACE s_axilite port=mem bundle=control
    #pragma HLS INTERFACE s_axilite port=size bundle=control
    #pragma HLS interface s_axilite port=return bundle=control

    for(int i = 0; i < size; i++) {
        #pragma HLS PIPELINE II=1
        ap_int<128> x = mem[i];
        s.write(x);
    }
}

关键设计决策分析:

特性 设计选择 理由
数据宽度 128-bit 最大化 DDR 带宽利用率,同时保持合理的资源开销
时钟频率 312.5 MHz 与 AI Engine PLIO 的 625MHz 形成 2:1 比例,简化 CDC 设计
II (Initiation Interval) 1 每个时钟周期输出一个样本,达到理论最大吞吐
接口协议 m_axi + axis + s_axilite 标准的三接口模式:数据读、流输出、控制寄存器

为何不使用更宽的数据总线? 虽然 256-bit 或更宽的总线可以提供更高带宽,但会增加:

  • 布线复杂度(特别是在 PL 侧)
  • 与 AI Engine PLIO 64-bit 接口的转换开销
  • 内存对齐约束

128-bit 是一个平衡点,配合双实例化正好满足带宽需求。

S2MM 内核 (s2mm.cpp)

void s2mm(ap_int<128>* mem, hls::stream<ap_int<128>>& s, int size) {
    // ... 接口定义与 MM2S 类似 ...
    for(int i = 0; i < size; i++) {
        #pragma HLS PIPELINE II=1
        ap_int<128> x = s.read();
        mem[i] = x;
    }
}

S2MM 是 MM2S 的镜像操作,设计参数保持一致以确保时序对称性。

系统集成配置 (system.cfg)

[connectivity]
nk=mm2s:2:mm2s_1,mm2s_2      # 实例化 2 个 MM2S 内核
nk=s2mm:1:s2mm                 # 实例化 1 个 S2MM 内核
sp=mm2s_1.mem:DDR              # 绑定到 DDR 内存端口
sp=mm2s_2.mem:DDR
sc=mm2s_1.s:ai_engine_0.PLIO_i_0   # 连接到 AI Engine 输入
sc=mm2s_2.s:ai_engine_0.PLIO_i_1
sc=ai_engine_0.PLIO_o:s2mm.s       # 连接 AI Engine 输出到 S2MM

[clock]
freqHz=312500000:mm2s_1        # 统一时钟域
freqHz=312500000:mm2s_2
freqHz=312500000:s2mm

[debug]
chipscope=s2mm:s               # 启用 ILA 调试探针
chipscope=mm2s_2:s
chipscope=mm2s_1:s

配置解读:

  • nk (num kernels):显式声明内核实例数量,这是 Vitis 链接器的必需信息
  • sp (scatter/gather port):指定 DDR 内存 bank 分配,避免访问冲突
  • sc (stream connection):定义数据流拓扑,注意方向性(: 左侧是源,右侧是目标)
  • 时钟一致性:所有数据搬运器使用相同频率,简化时序收敛

AI Engine 流水线:MUSIC 算法的硬件实现

本模块服务的 AI Engine 图包含六个子图,构成完整的信号处理流水线:

flowchart LR Input["sig_i[0]
sig_i[1]"] --> IOAdapter["IO Adapter
(1 tile)"] IOAdapter --> QRD["QRD
(36 tiles)"] QRD --> SVD["SVD
(38 tiles)"] SVD --> DOA["DOA
(64 tiles)"] DOA --> Scanner["Scanner
(2 tiles)"] Scanner --> Finder["Finder
(16 tiles)"] Finder --> Output["sig_o"]

各子图功能简述

子图 功能 算法 资源占用
IO Adapter 合并双 PLIO 流到内部缓冲区 数据重排 1 tile
QRD QR 分解 Modified Gram-Schmidt 36 tiles
SVD 奇异值分解 One-sided Jacobi (4 iter) 38 tiles
DOA 空间谱估计 Steering vector projection 64 tiles
Scanner 粗粒度峰值搜索 Threshold-based tagging 2 tiles
Finder 精确定位 DOA Gradient change detection 16 tiles

总计:157 个 AI Engine tiles

为何选择 QRD+SVD 而非特征值分解?

传统 MUSIC 算法通常直接对协方差矩阵进行特征值分解 (EVD)。本设计采用 QRD→SVD 的两步方法,原因如下:

  1. 数据局部性:QRD 直接在 128×8 的快照矩阵上操作,无需显式计算协方差矩阵
  2. 并行友好:Modified Gram-Schmidt 和 Jacobi SVD 都具有规则的数据流模式,适合 AI Engine 的 SIMD 架构
  3. 数值稳定性:对于病态矩阵,QR-SVD 路径通常比直接 EVD 更稳定

代价是需要更多的计算步骤,但通过足够的并行度(157 tiles)来弥补。


设计权衡与决策分析

1. 带宽 vs. 复杂度:为什么选择双 MM2S?

备选方案:使用单个更宽的 PLIO 或更高的时钟频率

决策逻辑

  • AI Engine PLIO 物理限制为 64-bit @ 625MHz(每方向 4GB/s)
  • 需要的带宽:128 × 8 samples × 8 bytes/sample / 1μs = 8.19 GB/s
  • 单 PLIO 只能提供 4GB/s,因此必须双路并行
  • 保持 312.5MHz 而非提升到 625MHz 简化了 HLS 设计的时序收敛

2. 内存接口策略:连续 burst vs. 随机访问

MM2S/S2MM 使用 m_axi 接口的默认行为是 burst 传输。这对于 MUSIC 算法是理想的,因为:

  • 快照矩阵在 DDR 中是连续存储的
  • 顺序访问允许内存控制器预取,最大化 DRAM 效率
  • 无需复杂的地址计算逻辑

3. 调试基础设施:ChipScope 探针的取舍

system.cfg 中为所有数据流接口启用了 ChipScope 调试。这在开发阶段非常有价值,但增加了:

  • 额外的 LUT 资源消耗(约 5-10%)
  • 潜在的时序压力
  • 比特流文件增大

建议:在产品化构建中移除 [debug] 段以提高资源效率。

4. 错误处理策略

当前实现采用 "fail-silent" 策略:

  • HLS 内核没有显式的错误返回值
  • 依赖上层软件(PS 应用)监控 DMA 完成状态
  • 超时或数据量不匹配由主机端检测

这是一种务实的选择,因为:

  • 数据搬运器逻辑简单,出错概率低
  • 硬件错误(如总线挂死)通常需要系统级复位
  • 添加全面的错误检查会增加逻辑复杂度和延迟

跨模块依赖关系

本模块位于系统集成的最顶层,依赖以下模块提供的功能:

上游依赖(被本模块使用)

模块 依赖内容 关系说明
music_direction_of_arrival_pipeline AI Engine 子图实现 本模块的 system.cfg 引用 ai_engine_0,即该模块定义的图实例
baseline_aie_pl_integration_examples HLS 数据搬运器模板 MM2S/S2MM 内核的设计模式继承自此模块

下游依赖(使用本模块)

本模块是顶层集成模块,无下游依赖。


新贡献者注意事项

1. 隐式契约与假设

数据格式假设

  • MM2S 期望输入数据为 128-bit 对齐的连续内存块
  • 数据类型解释由 AI Engine 端决定(cfloat16 复数数组)
  • 大小参数 size 以 128-bit 字为单位,而非样本数

时序假设

  • 两个 MM2S 实例必须同步启动,否则 AI Engine 可能陷入等待
  • PS 应用负责确保 DDR 缓冲区在 DMA 启动前已准备好

2. 常见陷阱

陷阱 1:内存 Bank 冲突 如果两个 MM2S 实例绑定到同一个 DDR bank,高负载下可能出现争用。system.cfg 中应明确指定不同的 bank 或使用智能调度。

陷阱 2:时钟域越界 虽然 Vitis 自动插入 CDC,但如果修改了时钟频率,需要验证:

  • FIFO 深度是否足以吸收 transient 突发
  • 握手信号是否正确同步

陷阱 3:Size 参数单位混淆 mm2s()s2mm()size 参数是以 128-bit 为单位的。如果误以为是样本数(cfloat),会导致数据传输不完整。

3. 调试技巧

使用 ChipScope 波形: 启用 [debug] 段后,可以在 Vivado 中捕获以下信号:

  • mm2s_1.s_tvalid/tready:观察数据流反压情况
  • s2mm.s_tvalid/tready:确认 AI Engine 输出速率

性能分析

# 查看实际吞吐率
grep "Average Sweep Time" ps_apps/output.log

4. 扩展点

如果需要支持更大的阵列(如 16 元素 ULA):

  1. 增加 MM2S 实例数量(可能需要 4 个)
  2. 修改 system.cfg 中的连接关系
  3. 调整 AI Engine 图的输入端口配置
  4. 重新评估带宽需求和时钟频率
On this page