3. STM32N6内存体系与图像缓冲区规划

STM32N6 的图像预处理链路能否跑满带宽,往往不取决于 CPU 主频,而取决于“数据放在哪块内存、谁在访问它、访问路径是否冲突”。STM32N6 内部包含多块用途不同的 SRAM,外部可挂 HyperRAM/PSRAM,再加上 Cache 与 DMA 的可见性问题,构成了一个必须提前规划的存储拓扑。本节围绕“把哪块缓冲区放在哪里、为什么、怎么验证”展开。

一、先看清各存储区的角色分工

STM32N6 的片上存储大致可分为三类角色:

  • DTCM(Data Tightly-Coupled Memory):紧耦合数据内存,通常为 128KB 级别,直连内核,零等待、无 Cache 一致性问题,适合存放栈、频繁访问的标量变量、小型查找表、DMA 描述符等。它不经过系统总线矩阵,因此不适合作为大图像缓冲。
  • ITCM(Instruction Tightly-Coupled Memory):紧耦合指令内存,用于存放对实时性要求极高的中断服务程序或热点代码,保证取指不被总线竞争拖慢。图像数据不应放在这里。
  • AXI SRAM:挂在 AXI 总线矩阵上的通用 SRAM,容量较大(数百 KB 到 MB 级),可被 CPU、DMA、NPU、显示控制器等多主设备访问,是图像行缓冲、中间特征图、DMA 目标缓冲的主力区域。代价是访问需要经过总线仲裁,且若开启 D-Cache 就存在一致性维护成本。
  • 外部 HyperRAM / PSRAM:通过 OCTOSPI/XSPI 接口扩展,容量可达几十 MB,适合存放整帧图像、多帧环形缓冲、模型权重等大块数据。带宽和延迟明显劣于片上 SRAM,且访问是串行的,随机小访问效率低,必须靠突发传输和 Cache 掩盖延迟。

一个常见的误区是把整帧图像直接放进 AXI SRAM,结果发现同时还要放模型中间层和显示缓冲,很快耗尽。正确做法是按“访问频率 × 数据量”分层:高频小数据进 DTCM,中频中等数据进 AXI SRAM,低频大数据进 HyperRAM。

二、Cache 与 DMA 可见性:最容易踩的坑

STM32N6 的 Cortex-M55 带 D-Cache。当 CPU 写图像缓冲、DMA 再去读同一块内存时,如果数据还停留在 Cache 里没写回,DMA 读到的是旧数据;反过来 DMA 写入新数据后,CPU 若命中旧 Cache 行,读到的也是旧值。这就是一致性问题。

处理方式有三种,按场景选择:

  1. 把 DMA 缓冲放在非 Cache 区或配置为 Write-Through/Non-Cacheable:最省心,适合小缓冲和描述符,但会牺牲 CPU 访问速度。
  2. 显式维护 Cache:CPU 写完后调用 SCB_CleanDCache_by_Addr() 写回,DMA 写完后调用 SCB_InvalidateDCache_by_Addr() 失效,再让 CPU 读。注意地址和长度必须按 Cache 行(通常 32 字节)对齐,否则会误伤相邻数据。
  3. 利用 MPU 分区属性:把图像缓冲所在区域配置为 Normal Non-Cacheable 或 Device 属性,从根上避免一致性问题,代价是该区域 CPU 访问变慢。
失效(Invalidate)操作前必须确保 CPU 没有未写回该区域的脏数据,否则失效会直接丢弃尚未写回的内容,造成数据损坏。安全顺序是:先 Clean 再 Invalidate,或对纯 DMA 写入区只做 Invalidate。

三、多缓冲区分配策略

图像流水线通常需要至少三级缓冲:采集缓冲(DMA 写入)、处理缓冲(CPU/NPU 读取并处理)、输出缓冲(送显示或下一级)。若只有一块缓冲,采集和处理必须串行,帧率会被腰斩。因此推荐使用双缓冲或三缓冲 + 乒乓切换。

以 640×480、RGB565 为例,单帧约 600KB。若放在 HyperRAM,可开三块共 1.8MB 做环形队列;若放在 AXI SRAM,则通常只够放行缓冲或降分辨率后的中间图。下面这张图给出一个典型的分层缓冲拓扑与数据流向。

STM32N6 图像缓冲分层拓扑与数据流 图像传感器 DCMI/CSI 接口 DMA 控制器 突发写入 外部 HyperRAM Buf A(采集中) Buf B(处理中) Buf C(输出中) AXI SRAM 行缓冲 / 中间特征图 NPU 输入张量 显示输出缓冲 DTCM DMA 描述符 / 栈 查找表 / 标量参数 ITCM 热点 ISR / 预处理内核 零等待取指 乒乓切换 Cache 一致性维护点 CPU 写后 Clean,DMA 写后 Invalidate 地址长度按 32 字节 Cache 行对齐 带宽层级(由快到慢) DTCM/ITCM 零等待 → AXI SRAM 总线仲裁 → HyperRAM 串行突发(需 Cache 掩盖延迟)

四、一个可落地的分配示例

假设系统需求为:OV5640 输出 640×480 RGB565,帧率 30fps,NPU 做轻量检测,同时送 LCD 显示。单帧 614400 字节,30fps 即约 18.4MB/s 的采集带宽。规划如下:

缓冲区位置大小属性用途
Frame[3]HyperRAM3×600KBNon-Cacheable 或 Clean/InvalidateDMA 采集环形缓冲
LineBufAXI SRAM2×1280BCacheable逐行预处理输入
TensorInAXI SRAM224×224×3Cacheable + CleanNPU 输入张量
DispBufAXI SRAM2×300KBNon-CacheableLCD 双缓冲输出
Desc/ParamDTCM<8KB—DMA 描述符、标量

关键判断依据:HyperRAM 上的三帧缓冲必须保证 DMA 写入与 CPU 读取不在同一帧上,通过帧完成中断切换索引;AXI SRAM 上的 TensorIn 在交给 NPU 前执行一次 Clean,NPU 写回结果后执行 Invalidate;DispBuf 设为 Non-Cacheable,避免显示控制器持续读取时反复维护 Cache。

验证方法:在帧切换中断里翻转一个 GPIO,用示波器测周期是否稳定在 33.3ms;若抖动大,多半是 HyperRAM 突发被其他主设备打断,可尝试提高 OCTOSPI 时钟或增大突发长度。同时用 DWT 计数器统计 Cache 维护耗时,若单帧超过 1ms,说明缓冲划分需要调整。

最后强调一个原则:先定访问模式,再定内存位置。凡是“DMA 写、CPU 读”的缓冲,优先考虑 Non-Cacheable 或严格对齐的 Clean/Invalidate;凡是“CPU 高频小访问”的数据,优先放 DTCM;凡是“大块、低频、可突发”的数据,放 HyperRAM 并用 Cache 掩盖延迟。按这个顺序规划,STM32N6 的图像流水线才能在有限的内存资源下稳定跑起来。