2. C7x 架构与指令集精要

C7x 是 TI Jacinto 7 系列 SoC 中承担视觉与深度学习加速任务的核心 DSP。它并非传统意义上只做乘加运算的定点 DSP,而是一套由标量控制、矢量并行、矩阵加速、流引擎和分层存储共同构成的异构计算单元。理解 C7x 的关键,不是记住某个指令的编码,而是建立"数据从哪来、由谁算、算完放哪、如何被下一级消费"的整体图景。本节围绕这一图景展开。

一、四类运算单元的分工

C7x 的执行资源可归纳为四类。第一类是 标量单元(Scalar),负责地址计算、循环控制、分支判断和标量算术,是整个内核的"指挥中枢"。第二类是 矢量单元(Vector),C7x 提供 512 位宽的向量寄存器(如 V0–V31),单条指令可同时处理 16 个 32 位整数或 64 个 8 位整数,是图像滤波、色彩转换、特征提取的主力。第三类是 矩阵乘法加速器(MMA),面向深度学习中的卷积与全连接,以脉动阵列方式在单周期内完成大规模乘累加,显著高于用矢量指令逐条模拟的效率。第四类是 流引擎(Streaming Engine, SE),它不执行算术,而是负责在 L2、L1D、向量寄存器之间搬运数据块,并支持地址生成与格式转换,使计算单元不必为取数停顿。

这四者的关系可以概括为:标量发令、矢量并行、MMA 密集、流引擎供数。任何一段高性能 kernel 都是它们协同的结果,而不是单一单元的独角戏。

二、内存层次与数据流

C7x 的存储层次自上而下为:外部 DDR → MSMC/L3 → L2 SRAM → L1D SRAM → 向量寄存器。越靠近计算单元,容量越小、带宽越高、延迟越低。C7x 的 L1D 通常为 32 KB,L2 为 512 KB 量级(具体以器件手册为准),因此大尺寸特征图必须分块(tiling)处理,让每一块在被换出前尽可能被复用。

流引擎在这里扮演关键角色:它通过 SE 指令描述一次数据搬运的源地址、目标地址、步长和块大小,硬件自动完成循环搬运,无需标量逐次寻址。这既降低了指令开销,也让数据预取与计算重叠。下图给出 C7x 的运算单元与存储层次关系,以及一次典型卷积块的数据流向。

C7x 运算单元与存储层次数据流 外部 DDR 大容量 / 高延迟 MSMC / L3 共享缓存 L2 SRAM 约 512 KB L1D SRAM 约 32 KB 流引擎 SE 地址生成 块搬运 / 预取 取数 标量单元 控制 / 地址 / 分支 矢量单元 512-bit V0–V31 MMA 加速器 矩阵乘累加 供数 结果回写 L1D / L2 图例 数据流入 结果回写

三、编程模型:从 C 到内联指令

C7x 的编程模型建立在 C7000 编译器之上。开发者通常先用 C/C++ 描述算法,再通过 #pragma 与内联函数(intrinsics)引导编译器生成矢量或 MMA 指令。核心手段有三:

  • 向量化:使用 __vector 类型或 c7x_ 前缀的内联函数,让编译器把循环展开为 512 位操作。
  • 数据分块:将大张量切成能放进 L1D 的小块,配合流引擎预取,隐藏 DDR 延迟。
  • MMA 调用:对卷积层,将权重与激活按矩阵分块,调用 MMA 内联函数完成 C = A×B + C 的批量计算。

一个典型的矢量加法示例可以写成:

// 假设 a、b、c 均为 32 位整数数组,长度 N 为 16 的倍数
// 使用 C7x 矢量内联函数完成逐元素相加
#include <c7x.h>

void vec_add(const int32_t *a, const int32_t *b, int32_t *c, int n) {
    for (int i = 0; i < n; i += 16) {
        // 一次加载 16 个 32 位整数到向量寄存器
        __vector int32_t va = *(const __vector int32_t *)(a + i);
        __vector int32_t vb = *(const __vector int32_t *)(b + i);
        __vector int32_t vc = va + vb;   // 单条矢量加法指令
        *(__vector int32_t *)(c + i) = vc;
    }
}

这段代码在 C7x 上会被编译为若干条 512 位加载、加法和存储指令,相比标量循环,理论吞吐提升约 16 倍。实际性能还取决于数据是否已在 L1D、是否发生 bank 冲突,以及循环是否被软件流水化。

四、参数与性能判断

评估一段 C7x kernel 是否达到预期,可关注几个可观测指标:单次迭代的周期数(通过 TSCL 或 TSCH 寄存器读取时间戳)、L1D 命中率、流引擎搬运与计算的重叠程度。若发现计算单元利用率低,常见原因是数据未预取、分块过大导致 L1D 颠簸,或 MMA 的矩阵维度未对齐到硬件要求(如 8×8 或 16×16 的倍数)。

实践提示:在 C7x 上做优化时,先确认数据通路是否顺畅,再考虑指令级优化。很多"算得慢"的问题,本质是"等数据"。