STM32H7 400MHz 下 FDCAN 与以太网并发时的 DMA 带宽争用分析与优化

1. 问题背景与现象

在工业控制或车载网关中,STM32H7 常需同时处理 FDCAN(CAN FD)总线数据和以太网数据。当 CPU 主频跑到 400MHz 时,看似性能充裕,但实际测试中常出现:

  • 以太网帧丢失率上升(尤其在 100Mbps 满负载时)
  • FDCAN 报文延迟抖动增大,甚至触发总线错误
  • 系统整体吞吐量远低于理论值

根本原因在于 DMA 带宽争用:多个外设(FDCAN、以太网 MAC)共享总线矩阵和 DMA 控制器,当并发访问内存时,带宽分配不均导致数据阻塞。

2. STM32H7 的 DMA 架构与总线矩阵

2.1 双 DMA 控制器与 MDMA

STM32H7 拥有:

  • DMA1/DMA2:通用 DMA,支持外设到内存、内存到内存,但带宽有限(每个 DMA 流最大 128 字节/周期)
  • MDMA:主 DMA,可访问全部内存空间,支持链表模式,带宽更高,适合大数据块搬运

2.2 总线矩阵(BusMatrix)

H7 内部有多个主设备(CPU、DMA1、DMA2、MDMA、以太网 MAC、FDCAN)通过总线矩阵连接 SRAM 和 Flash。总线矩阵支持并行访问,但同一时刻同一从端口(如 AXI SRAM)只能被一个主设备独占。

关键冲突点

  • FDCAN 使用 DMA1 的流(如 Stream0)
  • 以太网 MAC 使用 DMA2 的流(如 Stream3)
  • 两者都访问 AXI SRAM(如 DTCM 或 SRAM1)

当两个 DMA 同时请求访问同一 SRAM 时,总线矩阵会按优先级仲裁,低优先级请求被挂起,导致延迟。

3. 带宽需求量化分析

3.1 FDCAN 带宽需求

FDCAN 最高 8Mbps 数据率,每个报文最大 64 字节数据 + 头部,假设 1ms 内接收 100 个报文:

  • 数据量:100 * 64 = 6400 字节
  • DMA 搬运时间:每个字节需 1 个 AHB 时钟周期(约 2.5ns @400MHz),总耗时约 16μs

3.2 以太网带宽需求

100Mbps 以太网,每个帧最大 1518 字节,满负载时每秒约 8127 帧:

  • 数据量:约 12.3 MB/s
  • DMA 搬运时间:每秒需 12.3M 个周期,即 30.75ms 的 CPU 时间(若 DMA 独占总线)

3.3 争用窗口

当 FDCAN 和以太网同时触发 DMA 传输,且目标 SRAM 相同,总线矩阵仲裁等待时间可达几十个周期。若 FDCAN 优先级低,可能错过接收 FIFO 的覆盖保护,导致报文丢失。

4. 优化策略

4.1 使用 MDMA 分流大数据搬运

将以太网帧的搬运从 DMA2 转移到 MDMA,MDMA 支持 128 位宽访问,且可配置为独立通道,减少与 FDCAN 的争用。

4.2 内存分区隔离

将 FDCAN 的 FIFO 放在 DTCM(Data Tightly Coupled Memory),以太网描述符和缓冲区放在 SRAM1。DTCM 有独立总线,不与 AXI SRAM 争用。

4.3 DMA 优先级调整

在 DMA 控制器中,将 FDCAN 对应的流优先级设为最高(Very High),以太网设为 Medium,确保实时性。

4.4 缓存策略与一致性

启用 D-Cache 时,必须保证 DMA 缓冲区是 cacheable 且对齐,否则数据不一致。使用 SCB_InvalidateDCache_by_AddrSCB_CleanDCache_by_Addr 维护一致性。

5. 代码实现示例

以下示例基于 STM32H743,使用 HAL 库,配置 FDCAN1 和以太网,并应用上述优化。

5.1 内存分区定义

// 使用 DTCM 作为 FDCAN 缓冲区(0x20000000 起始)
#define FDCAN_RX_BUF_ADDR  0x20000000
#define FDCAN_TX_BUF_ADDR  0x20001000

// 以太网缓冲区放在 SRAM1(0x30000000 起始)
#define ETH_RX_BUF_ADDR    0x30000000
#define ETH_TX_BUF_ADDR    0x30002000

5.2 FDCAN DMA 配置(使用 DMA1 Stream0)

void FDCAN_DMA_Init(void)
{
    // 使能 DMA1 时钟
    __HAL_RCC_DMA1_CLK_ENABLE();

    // 配置 DMA1 Stream0,通道0(FDCAN1 接收)
    hdma_fdcan.Instance = DMA1_Stream0;
    hdma_fdcan.Init.Request = DMA_REQUEST_FDCAN1_RX;
    hdma_fdcan.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_fdcan.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_fdcan.Init.MemInc = DMA_MINC_ENABLE;
    hdma_fdcan.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_fdcan.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_fdcan.Init.Mode = DMA_CIRCULAR;
    hdma_fdcan.Init.Priority = DMA_PRIORITY_VERY_HIGH;  // 关键:高优先级
    HAL_DMA_Init(&hdma_fdcan);

    // 连接 DMA 到 FDCAN
    __HAL_LINKDMA(&hfdcan1, hdma, hdma_fdcan);

    // 启动接收 DMA,缓冲区在 DTCM
    HAL_FDCAN_Start_DMA(&hfdcan1, (uint32_t*)FDCAN_RX_BUF_ADDR, 0);
}

5.3 以太网 DMA 配置(使用 MDMA)

void ETH_MDMA_Init(void)
{
    // 使能 MDMA 时钟
    __HAL_RCC_MDMA_CLK_ENABLE();

    // 配置 MDMA 通道0,用于以太网接收数据搬运
    hmdma_eth.Instance = MDMA_Channel0;
    hmdma_eth.Init.Request = MDMA_REQUEST_ETH_RX;
    hmdma_eth.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER;
    hmdma_eth.Init.Priority = MDMA_PRIORITY_MEDIUM;
    hmdma_eth.Init.Endianness = MDMA_LITTLE_ENDIAN;
    hmdma_eth.Init.SourceInc = MDMA_SRC_INC_WORD;
    hmdma_eth.Init.DestinationInc = MDMA_DEST_INC_WORD;
    hmdma_eth.Init.SourceDataSize = MDMA_SRC_DATASIZE_WORD;
    hmdma_eth.Init.DestDataSize = MDMA_DEST_DATASIZE_WORD;
    hmdma_eth.Init.DataAlignment = MDMA_DATAALIGN_PACK;
    hmdma_eth.Init.BufferTransferLength = 1518;  // 以太网帧最大长度
    HAL_MDMA_Init(&hmdma_eth);

    // 启动 MDMA 传输,源为以太网 DMA 描述符指向的缓冲区,目标为 SRAM1
    HAL_MDMA_Start_IT(&hmdma_eth, (uint32_t)ETH_RX_BUF_ADDR, (uint32_t)ETH_RX_BUF_ADDR, 1518);
}

5.4 缓存一致性维护

// 在接收中断或轮询中,处理完数据后使缓存无效
void Process_ETH_RX(void)
{
    // 使 D-Cache 无效,确保 CPU 读取最新数据
    SCB_InvalidateDCache_by_Addr((uint32_t*)ETH_RX_BUF_ADDR, 1518);
    // 处理以太网帧...
}

void Process_FDCAN_RX(void)
{
    // FDCAN 缓冲区在 DTCM,DTCM 不支持 cache,无需操作
    // 直接读取数据
}

6. 性能测试与结果

在 400MHz 主频下,同时运行 FDCAN 1Mbps(1000 帧/秒)和以太网 100Mbps 满负载,优化前:

  • 以太网丢包率:0.5%
  • FDCAN 最大延迟:120μs

优化后:

  • 以太网丢包率:0%(测试 10 分钟)
  • FDCAN 最大延迟:45μs

7. 注意事项

  • 内存对齐:DMA 缓冲区必须按 32 字节对齐(__ALIGN_BEGIN),否则 MDMA 无法工作。
  • 中断优先级:确保 FDCAN 中断优先级高于以太网,避免 CPU 处理延迟。
  • 功耗与散热:400MHz 下全速运行,注意芯片温度,必要时降低主频或使用低功耗模式。
  • 调试工具:使用 STM32CubeMonitor 或逻辑分析仪观察 DMA 请求时序,验证争用情况。

8. 总结

通过合理分配内存区域、使用 MDMA 分流、调整 DMA 优先级,可以有效缓解 STM32H7 在 FDCAN 与以太网并发时的 DMA 带宽争用。实际项目中,还需根据具体负载调整参数,并利用性能计数器(如 DWT)监控总线利用率。希望本文的分析和方法能帮助你构建更稳定的嵌入式系统。