STM32H7 系列 MDMA 与 DMA 并发访问 AXI SRAM 的带宽仲裁陷阱与规避策略
👁 1 阅读 · 2026-08-27 · 嵌入式
STM32H7 系列凭借双核与高主频成为高性能嵌入式首选,但其 AXI SRAM 在 MDMA 与 DMA 并发访问时存在带宽仲裁陷阱,导致数据延迟或总线死锁。本文深入剖析 AXI 总线矩阵的仲裁机制,揭示优先级反转与带宽饥饿的根因,并提供寄存器配置、缓冲策略及代码级规避方案,帮助开发者榨干 H7 内存带宽,避免隐性故障。
# STM32H7 系列 MDMA 与 DMA 并发访问 AXI SRAM 的带宽仲裁陷阱与规避策略
## 一、AXI SRAM 的仲裁架构与冲突本质
STM32H7 的 AXI SRAM(如 AXI SRAM1/2/3)挂载于 AXI 总线矩阵上,该矩阵采用多层交叉开关(Crossbar)设计,支持多个主设备(如 Cortex-M7、MDMA、DMA1/2、以太网 MAC)同时访问不同从设备。然而,当多个主设备并发访问**同一块 AXI SRAM 区域**时,仲裁器(Arbiter)介入,按固定优先级或轮询策略分配带宽。
关键陷阱在于:
- **优先级反转**:MDMA 默认优先级高于 DMA,但若 DMA 持续占用低优先级通道,MDMA 可能被无限期阻塞,造成实时任务超时。
- **带宽饥饿**:当 MDMA 进行大块突发传输(Burst=16/32)时,会连续占用总线多个周期,DMA 的分散传输(Scatter-Gather)被迫等待,导致外设 FIFO 溢出。
- **死锁风险**:若 MDMA 与 DMA 同时访问同一 SRAM 地址且开启写响应(Write Response),仲裁器可能因等待响应而暂停所有请求,形成总线锁死。
## 二、仲裁机制详解:从优先级到 QoS
STM32H7 的 AXI 矩阵仲裁器支持两种模式:
1. **固定优先级**(Fixed Priority):主设备编号越小优先级越高(如 MDMA=0,DMA1=1,DMA2=2)。高优先级请求总是先被服务,低优先级可能饿死。
2. **轮询优先级**(Round-Robin):所有主设备轮流获得服务,公平但延迟不确定。
此外,每个 AXI 主设备接口还提供 **QoS(Quality of Service)信号**(如 AxQOS[3:0]),可动态调整仲裁权重。例如,将 DMA 的 QoS 设为最高,可临时抢占 MDMA 的带宽。但 QoS 仅在支持 QoS 的从设备(如 AXI SRAM)上生效,且需在 RCC 中使能 QoS 时钟。
**仲裁粒度**:仲裁器以“传输事务”(Transaction)为单位切换,而非单个数据节拍(Beat)。因此,一个长突发(Burst Length=16)会独占总线直到完成,这解释了为何 MDMA 大块传输会阻塞 DMA。
## 三、规避策略:从配置到代码
### 1. 合理分配存储区域
将 MDMA 与 DMA 的数据缓冲放置于**不同的物理 SRAM**(如 MDMA 用 AXI SRAM1,DMA 用 SRAM4),从根源避免仲裁冲突。若必须共用 AXI SRAM,则使用**地址交错**(Interleaving)——将数据分散到不同 Bank,但 H7 的 AXI SRAM 不支持硬件交错,需手动分区。
### 2. 调整突发长度与优先级
- 降低 MDMA 的突发长度(如从 32 降至 8),减少单次占用总线时间,给 DMA 留出窗口。
- 设置 DMA 通道优先级高于 MDMA(通过 DMA_SxCR 的 PL 位),但注意 MDMA 的优先级固定,需用 QoS 覆盖。
**代码示例:配置 DMA 优先级与 QoS**
```c
// 使能 QoS 时钟(RCC_AHB3ENR 的 QOSEN 位)
RCC->AHB3ENR |= RCC_AHB3ENR_QOSEN;
// 配置 DMA1 通道 0 优先级为非常高(PL=11)
DMA1_Channel0->CCR |= DMA_CCR_PL_1 | DMA_CCR_PL_0;
// 设置 DMA1 的 QoS 值为 0xF(最高)
// 注意:QoS 寄存器在 AXI 主设备接口中,需通过 AHB3 外设访问
DMA1->QOSR = (DMA1->QOSR & ~DMA_QOSR_QOS_MASK) | (0xF << DMA_QOSR_QOS_Pos);
```
### 3. 使用 MDMA 的 Linked List 与中断同步
MDMA 支持 Linked List 描述符,可将大块传输拆分为多个小任务,并在每个任务完成后触发中断,让 DMA 有机会插入。同时,利用 MDMA 的 Transfer Complete 中断信号(如 MDMA_TCIF)与 DMA 的 Half Transfer 中断进行软件握手,避免同时发起访问。
**代码示例:MDMA 拆分传输并同步**
```c
// 定义两个描述符,每个传输 1KB(突发长度 8)
MDMA_LinkNodeTypeDef node1, node2;
MDMA_InitTypeDef mdma_init;
// 初始化 MDMA 为 Linked List 模式
mdma_init.Request = MDMA_REQUEST_DMA1_TC; // 由 DMA 完成触发
mdma_init.Endianness = MDMA_LITTLE_ENDIAN;
mdma_init.Bufferable = MDMA_BUFFERABLE_ENABLE;
mdma_init.SourceInc = MDMA_SRC_INC_WORD;
mdma_init.DestInc = MDMA_DEST_INC_WORD;
mdma_init.SourceDataSize = MDMA_SRC_DATASIZE_WORD;
mdma_init.DestDataSize = MDMA_DEST_DATASIZE_WORD;
mdma_init.DataAlignment = MDMA_DATAALIGN_PACK;
mdma_init.BufferTransferLength = 1024; // 1KB
mdma_init.SourceBurst = MDMA_SOURCE_BURST_8; // 突发 8
mdma_init.DestBurst = MDMA_DEST_BURST_8;
// 配置节点 1
HAL_MDMA_LinkedListNode_Init(&node1, src_addr, dst_addr, &mdma_init);
// 配置节点 2,并链接到节点 1
HAL_MDMA_LinkedListNode_Init(&node2, src_addr+1024, dst_addr+1024, &mdma_init);
node1.NextNode = &node2;
// 启动 MDMA,并在每个节点完成中断中释放 DMA 信号量
HAL_MDMA_Start_IT(&mdma_handle, src_addr, dst_addr, 1024, &node1);
```
### 4. 使用 AXI 总线监视器(可选)
STM32H7 内置 AXI 总线监视器(AXI-BM),可实时统计各主设备带宽占用。通过调试器读取 AXI_BM 寄存器,定位冲突热点,动态调整策略。
## 四、注意事项
- **不要随意修改 QoS 寄存器**:QoS 值过高可能导致其他外设(如以太网)延迟,影响实时性。建议先测量基线带宽。
- **MDMA 的 Linked List 模式需注意描述符对齐**:描述符必须 32 字节对齐,否则触发 HardFault。
- **中断优先级设置**:MDMA 和 DMA 的中断优先级应低于实时任务(如 SysTick),避免中断风暴。
- **测试覆盖**:在不同时钟频率(如 400MHz vs 480MHz)下验证,因为仲裁延迟与时钟相关。
## 五、总结
STM32H7 的 AXI SRAM 仲裁机制是双刃剑:合理利用可提升吞吐,忽视则可能引发隐蔽故障。通过存储分区、突发长度调整、优先级/QoS 配置以及 Linked List 拆分,开发者可有效规避带宽陷阱。建议在项目初期就进行总线负载分析,避免后期调试的噩梦。