ARM Cortex-M7 (STM32F7) MCU 上的 DMA 操作存在数据损坏问题

问题描述 投票:0回答:1

我使用 ARM Cortex-M7 微控制器(特别是 STM32F767ZG)通过 4 个 USART(配置为异步发送器/接收器,并使用 DMA 处理传输)与外部设备进行通信。在测试(裸机)代码时,我注意到数据损坏的问题,可能与 ARM 和/或编译器处理缓存和 RAM 中的变量的方式有关。请参阅以下测试代码:

volatile char buffer[3];

// USART & DMA initialization code
// ...

buffer[0] = 0x11;   //
buffer[1] = 0x22;   // Buffer initial values
buffer[2] = 0x33;   //

// Some other code
// ...

buffer[0] = 0xAA;   //
buffer[1] = 0xBB;   // Buffer updated values
buffer[2] = 0xCC;   //

// DMA stream starts here
// ...

执行上面的代码,USART出来的数据如下:

0x11   (OLD value of buffer[0])
0x22   (OLD value of buffer[1])
0xCC   (NEW value of buffer[2])

我怀疑这与 ARM 和/或编译器如何处理变量及其在缓存和 RAM 中的存储有关。看来

buffer[]
的内容需要一些时间才能到达实际 RAM,因此 DMA 会拾取 old 值。请注意,对于前两个字节,USART Tx 寄存器立即释放(由于 USART 的内部缓冲),因此 DMA 几乎立即读取前两个字节(
buffer[0]
buffer[1]
)。对于第三个字节,有 1 字节的传输延迟(在 9600 bps 下,仅超过 1 毫秒),因此在这种情况下,MCU 有足够的时间来更新 RAM,因此 buffer[2]new
由 DMA 读取。

只需在启动 DMA 流之前添加仅 1 微秒的非常小的延迟即可消除此问题,如下所示:

... Delay_us(1); // DMA stream starts here // ...
在这种情况下,USART 发送以下(预期)数据:

0xAA (NEW value of buffer[0]) 0xBB (NEW value of buffer[1]) 0xCC (NEW value of buffer[2])
事实上,上面的延迟是可以微调的(纳秒范围内),这样只有第一个字节是旧的,接下来的两个字节是新的(即USART发送

0x11, 0xBB, 0xCC

)。

我的问题是,如何才能绝对确定实际 RAM 内容(由 DMA 读取)反映了我在代码中设置的缓冲区值?在启动 DMA 流之前添加延迟似乎是一个非常粗糙且不确定的解决方案。有没有一种明确的方法(C 语言的技术,甚至汇编命令)来刷新 MCU 缓存并将其内容传输到 RAM,以便 RAM 中的缓冲区数据不会损坏?

arm cortex-m cpu-cache dma stm32f7
1个回答
0
投票
在我对真实的硬件进行调查和实验室实验之后,我正在发布我自己问题的答案。事实证明,DMA 数据损坏问题确实与 MCU 的 1 级 (L1) 缓存有关。通常,编译器分配给

buffer[]

 的 RAM 区域是 
可缓存,这意味着来自/到 buffer[]
 的任何读/写访问都会通过 L1 缓存。然而,正如预期的那样,DMA 总是直接访问物理 RAM,而不是缓存,这可能会导致数据损坏,因为代码和/或外设实际上可能正在访问缓冲区数据的旧版本。这种情况称为
失去一致性 可以采用三种标准方法来解决此问题: 在启动 DMA Tx 传输之前,使用特殊指令“清理”缓存,强制 MCU 将缓存内容写入 RAM(以便 DMA 读取)。同样,在访问来自 DMA Rx 传输的传入数据之前,使用特殊指令“使缓存无效”,强制 MCU 从 RAM 读取新数据,而不是缓存中的旧数据。

使用 MCU 的内存保护单元 (MPU) 将 RAM 的特定区域配置为“不可缓存”。这确保了对该区域的所有访问将始终直接对 RAM 进行,并且永远不会通过缓存。我发现 MPU 方法是最优雅的,因为它可以保证一致性,而无需在每个 DMA 事务之前/之后使用

CleanDCache()
    /
  1. InvalidateDCache() 指令。 在“已经不可缓存”的 RAM 区域内分配 DMA 缓冲区。在我使用的 ARM Cortex-M7 MCU (STM32F767xx) 中,TCM 接口(紧耦合内存接口)上有一个称为 DTCM-RAM 的 SRAM 区域,映射在地址范围 0x20000000 ~ 0x2001FFFF
  2. (128 KB)。该区域不可缓存。这是最快的解决方案,只需将
  3. buffer[] 分配在不可缓存区域内的任何位置即可应用,如下所示:
    volatile char buffer[100] absolute 0x20000000;
    
    在我的代码中添加这一行已经完全解决了我所有的 DMA 数据损坏问题!
  4. 有关更多详细信息,请参阅此帖子
    及其中的链接。另请参阅意法半导体提供的以下资源:
    PM0253 — STM32F7 系列和 STM32H7 系列 Cortex®-M7 处理器编程手册
AN4839 — STM32F7 系列和 STM32H7 系列上的 1 级缓存


AN4838 — STM32 MCU 上的内存保护单元管理简介

© www.soinside.com 2019 - 2024. All rights reserved.