STM32H7 缓存一致性引发小BUG记录

/ 0评 / 0

最近在调试一个SDMMC外设,他的IDMA只能通过AXI SRAM,所以开启缓存是最优化性能选择.

都知道缓存是有一致性问题的,我们看看官方参考代码是怎么处理读取问题的.

https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H743I-EVAL/Applications/USB_Host/AUDIO_Standalone/Src/sd_diskio_dma.c

当读取回调完成,标记当前区域的缓存为无效,使得内存需要重新读取.

我举个例子这里会产生什么BUG,假设现在需要传输的buff位于0x2400_0444地址,长度40,通过计算,SCB_InvalidateDCache_by_Addr需要从0x2400_0440开始,直到0x2400_046C部分需要标记无效,直接丢弃.

假设我们之前写入了0x2400_0440 = AA,0x2400_0441 = BB,由于这个区域默认为写回,所以,实际这些内容还没写到内存(如果你缓存没满),现在我却直接标记Invalid了,那么这两个变量就被永久破坏了.

怎么解决,在内存Invalid之前,先把需要写到主存的内容写回去.

SCB_CleanDCache_by_Addr((uint32_t *)alignedAddr, count * DEV_MMC_DEFAULT_BLOCK_SIZE + 32);
SCB_InvalidateDCache_by_Addr((uint32_t *)alignedAddr, count * DEV_MMC_DEFAULT_BLOCK_SIZE + ((uint32_t)buff - alignedAddr));

为什么不可以用SCB_CleanInvalidateDCache_by_Addr呢,因为这样的话相当于Flush了更大范围到主存,而有一些并不是我们想覆盖的范围.如果在IDMA读取后再执行SCB_CleanInvalidateDCache_by_Addr,那IDMA就执行了个寂寞了,因为你把缓存写到主存,IDMA内容被覆盖了.

为什么要在Read执行前做呢,因为Cacheline的大小是32字节的,这是size的处理最小单位,我们在IDMA后再做,就会导致部分IDMA内容被覆盖了,这也是为什么覆盖刷新到缓存的内容要完全包裹无效掉的地址,前面会不小心被覆盖,后面的也会.

当然优化选择你可以把他拆成两段进行CleanCache,毕竟部分内容确实不需要刷到主存.

SCB_CleanDCache_by_Addr((uint32_t *)alignedAddr, __SCB_DCACHE_LINE_SIZE);
SCB_CleanDCache_by_Addr((uint32_t *)alignedAddr + count * DEV_MMC_DEFAULT_BLOCK_SIZE, __SCB_DCACHE_LINE_SIZE);

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注