有人使用STM32H743做产品开发, DMA 传输待发送的数据到 UART 发送寄存器做后续UART通信。在开启D-Cache的情况下,发现UART没法发送更新过的数据。
具体应用场景是这样的,源数据放在STM32H743片内D1域的AXI-SRAM区,数据会不定期地被CPU修改,然后让DMA将数据传输到USART3的发送寄存器进行后续UART通信。结合手册可以查得USART3位于D2域。[下面截图来自STM32H7芯片参考手册]
目前开启了D-Cache/I-Cache。我基于现有场景写了一段简单的如下测试代码【编译环境使用STM32CubeIDE】:
__attribute__((section(".Source"))) uint8_t Source[5];
uint32_t TimeOut;
uint8_t Variable=0;
基于上面测试代码,也重现了相同现象。即尽管CPU在不停修改源端数据,可目的端UART3的TDR寄存器的数据总保持0不变。【注:我这里的DMA使用的Memory to Memory方式,并非要一定这样操作。你完全可以基于UART事件使用Memory to Peripheral的方式。】
这里排除了其它方面的原因,该现象是因为开启了D-Cache并使用write back策略而导致的不同主设备访问同一内存而产生的数据不一致的问题。
现在CPU不时修改AXI-SRAM1指定区域的数据,DMA到同一位置读取数据送到UART发送寄存器。画个图示意下:
对于STM32H743片内AXI-SRAM1区域,其默认的存储属性为write back及writeallocate。【下图来自STM32H7参考手册】
此时CPU对该区域进行写操作发生Cache分配,数据会先写到Cache里。要等到Cache重分配或手动刷新Cache时才会将Cache里的新数据写到RAM内存。
这里有三种方案可选用来解决这个问题:
第一种方案就是,CPU做数据更新操作后,对相应存储区执行Cache清除操作,让Cache的新数据及时写到RAM内存,即添加下面打红勾的代码。
第二种方案就是针对CPU修改的数据存储区进行MPU设置,配置为write through或关闭该区域Cacheable特性。下面将其配置为Writethrough属性。【下面截图来自ARM相关技术手册。C:Cacheable,B:Bufferable,S:Shareable】
使用STM32图形化配置工具CubeMx进行MPU相关配置【参见下图】:
第三种方案,简单粗暴且有效,那就是关闭芯片D-Cache的使用。如果对开启D-Cache不在乎或者只是前期功能调试先关掉无妨,后面再去调整也可以。
上面简单介绍了在开启D-Cache情况下,CPU不定期修改Cacheable内存数据,DMA读取相应内存而发生的数据不一致问题的解决方案,以供参考。
最后提醒下,当我们使用SCB_CleanDCache_by_Addr()函数清除Cache时,需注意给定地址要遵循32字节对齐的原则。【注:上面截图来自STM32H7Cube库。】