# 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 拆分,开发者可有效规避带宽陷阱。建议在项目初期就进行总线负载分析,避免后期调试的噩梦。