
3. Cache 一致性问题 3.1 什么是 cache 一致性问题 所谓的 Cache 一致性问题, 主要指的是由于 D-cache 存在时,表现在有多个 Host(典型的如 MCU 的 Core, DMA 等)访问同一块内存时, 由于数据会缓存在 D-cache 中而没有更新实际的物理内存。 在实际应用中,有以下两种情况: 第一种情况是当有写物理内存的指令时,Core 会先去更新相应的 cache-line(Write-back 策略),在没有 clean 的情况下,会导致其对应的实际物理内存中的数据并没有被更新,如果这个时候有其它的 Host(如 DMA)访问这段内存时,就会出现问题(由于实际物理内存并未被更新,和 D-cache 中的不一致),这就是所谓的 cache 一致性的问题。 ![]() 图3.1 Cache 一致性问题 第1种情况 第二种情况是 DMA 更新了某段物理内存(DMA 和 cache 直接没有直接通道),而这个时候 Core 再读取这段内存的时候,由于相对应地址的 cache-line 没有被 invalidate,导致 Core 读到的是 cache-line 中的数据,而非被 DMA 更新过的实际物理内存的数据。 ![]() 图3.2 Cache 一致性问题 第2种情况 3.2 如何处理 cache 一致性问题 我们知道,Cache 机制是为了提高存储系统的平均读写性能而设计的,但是这种机制带来了数据一致性问题,然而,却没有对一致性的硬件支持。 因此为了解决一致性问题,一个办法就是禁用 Cache(cache 都禁用了,肯定不会有 cache 一致性的问题啦~)。但是如果你选择使用 STM32F7 这样高性能的微控制器,又不使用其带来的高性能特性,那你为什么要用 F7 呢,用 F3、F4 不就得了么?所以为了提高性能,还是使能 cache,并积极解决 cache 一致性问题吧。 好吧,解决 STM32F7 的 cache 一致性问题,有两种可选方案: 所有的共享存储器都定义为共享属性 这些区域将默认不被缓存到 D-Cache。 所有的操作都直接针对二级存储器(内部Flash,外部存储器),性能降低。 因为缓存对这些区域是透明的,写软件更容易。 通过软件进行cache的维护 (1)Cortex-M7 的写操作要是全局可见的 使用透写属性(通过 MPU 设置)。 使用 SIWT@CACR(Shared = Write Through)。 通过指令清 D-cache,然后所有更新位置禁止 D-Cache操作。 (2)其他主设备的写操作要对 Cortex-M7 可见 比如作废 Cortex-M7 Dache 中数据。 3.3 示例 3.3.1 程序描述 (1)首先将地址 0x20020000(SRAM1)处开始的 128 字节初始化为 0x55。 (2)将 Flash 中的 128 字节的常量数组 aSRC_Const_Buffer 拷贝到 SRAM1 地址 0x20020000(pBuffer)。 (3)配置并使能 DMA,通过 DMA 将数据从 SRAM1 的地址 0x20020000 处拷贝到 DTCM RAM 中的数组 aDST_Buffer 中。 (4)将 Flash 中的数组 aSRC_Const_Buffer 与 DMA 读出的数组 aDST_Buffer 进行比较。 显然,这个例子中的 cache 一致性问题, 展示的是上面(图3.1)的第一种情况。也就是在 Write-back 策略下,CPU 先去更新相应的 cache-line,然后 DMA 去访问对应的内存,从而导致数据不一致的现象。 程数据的传输流程和路径如下图所示: ![]() 图3.3 Cache 示例数据传输框图 3.3.2 复现 cache 一致性问题 我们先来按照示例要求编写代码,复现 cache 一致性问题。有些人可能会疑惑,变量数据怎么放到 Flash、SRAM1、DTCM?实际上,可以通过一些相关的配置文件进行设置,比如 icf 文件、scatter 文件等,当然,这跟所使用开发环境和编译工具链有关。 本文所使用的环境是 IAR,其链接文件 *.icf 如下: ![]() 然后将 aSRC_Const_Buffer 数组定义为常量,即可分配到 RO 区域,aDST_Buffer 定义为普通的全局变量或静态变量即可,因为内存区域从 0x20000000 开始,也就是 DTCM RAM。 好了,代码主体部分如下:
为了确保 aSRC_Const_Buffer 在 Flash,aDST_Buffer 在 DTCM,我们可以在编译完之后查看 *.map 文件,如下: ![]() 图3.4 检查常量和变量分配情况 下载到 STM32F769I-DISCO 板子上,显然,由于此时开启了 D-Cache,会出现数据不一致的现象,执行结果如下所示: ![]() 图3.5 Cache 数据不一致 3.3.3 解决方案 (1)不启动 D-Cache 注释掉 SCB_EnableDCache(); 不启动 D-Cache,当然也就没有了 Cache 数据不一致的问题啦~ (2)将 SRAM1 相应区域设置为 shareable 通过 MPU 将 SRAM1 相应区域设置为 shareable,MPU_Config() 函数处理如下:
(3)DMA 访问 SRAM1 前先 Clean cache 在启动 DMA 访问之前,程序员需要在合适的地方将 D-Cache 数据回写到主内存中,也就是 Clean 的操作。 在本示例中,可以在 DMA_Config(); 前调用:
(4)将 SRAM1 相应区域设置为 Write-through 策略 通过 MPU 将 SRAM1 相应区域设置为透写模式(Write-through),MPU_Config() 函数处理如下:
(5)将所有 cacheable 的空间全部强制 Write-though 通过 cache 控制寄存器,将所有 cacheable 的空间全部强制 Write-though 模式。 ![]() 图3.6 CACR 寄存器(来自 PM0253) 在初始化的时候进行设置:
宏定义为:
以上这是都是较为常用的方法,在实际的开发过程中,为了提高性能,一般都会开启 cache,同时将其配置为 WB 策略,这就需要开发者在使用时特别小心! 值得一提的是:对于第二种情况(图3.2),就不是 clean 操作了,而是 invalidate。需要先调用 SCB_InvalidateDCache() 或 SCB_InvalidateDCache_by_Addr() 去 invalidate 相应的 cache-line, 这样当 CPU 在读取时,会忽略 D-cache 中的内容,去真实的物理地址读取对应的数据。 ![]() 图3.7 Cache 数据一致 好啦,通过上述几种方法,就可以解决 cache 数据一致性问题。当然,除了我这里提供的,还有其他方案,各种方案各有利弊,要根据实际应用场景去衡量,这就是嵌入式程序员展示才华的时候啦~ |
【实战经验】基于STM32F7的网络时间同步例程
STM32硬件结构学习
STM32中BOOT的作用
【STM32F769I-DISC1】开发板刷入Micropython并完成点灯、读取内部温度测试
【STM32F769I-DISC1】测评01:创建STM32cube IDE 工程,点个灯
【STM32F769】创建deepseek本地服务,并实现http请求
汇编浮点库qfplib移植STM32F769I-DISCO开发板与硬件浮点运算性能测试对比
coremark移植到STM32F769I-DISCO开发板的两种方法
【GUI板免费申请活动】【圣诞GUI】使用F746-DISO基于TouchGFX的圣诞树
刘氓兔的杂谈【001】-片上USB 高速PHY