[信息] STM32H7 中的Cache Level1配置相关话题

[复制链接]
247|10
STM新闻官 发表于 2025-11-2 09:01 | 显示全部楼层 |阅读模式
1. Cache 的不正确使用很容易造成代码执行问题 在协助客户处理问题时经常发现客户在使能指令和数据cache后,不注意cache的使用而造成很多代码的运行问题。

2. 曾经遇到过的一个案例 客户在外部使用了3个相同的外设,使用SPI模式进行通讯,调试发现最后一个初始化过的外设总是工作不正常,检查发现对最后这个外设的初始化配置没有生效,造成工作不正常。 经过调试发现客户在使能Cache后,没有进行任何操作,没有配置MPU,也没有对cache 进行冲刷。造成最后一个设备初始化的配置一直没有生效。

3. Cache 的使用概念和MPU配置
3.1. Cache 的使用概念


619456906aaef74cc0.png
144826906ab0866b23.png
667226906ab1637e3c.png
698006906ab225d21e.png
Cache 的读策略: 对于cacheable的传输,读首先要在cache中查找。 如果Hit: 则CPU直接从Cache中读取数据即可。 如果Miss,有下面两种处理方式: Read allocate:将读回的数据在cache中分配一个cache line进行存储,再从Cache 中读数据。占用Cache。 Read through(也有称为Read non-allocate):将读回的数据直接给master使用,不在cache中缓存。避免Cache占用,但是对统一内容连续读效率很低。 默认情况下都是使用read allocate(无论是write-back还是write-through,他们的读取策略是一样的)。 Cache 的写策略与write allocate / write non-allocate Hit 时: Write-through:将写的数据更新到cacheline,同时将这个数据写到内存中。此模式的优点是操作简单,保证数据一致性;缺点是因为数据修改需要同时写入存储,数据写入速度较慢,所需带宽更大。 Write-back:直接将数据更新到cacheline,不写到内存,在这个cacheline被驱逐出cache 时(flush),才写入到内存中。 优点是写入速度非常快,多次到Cache的写只需一次到Main Memory的写,所需带宽更小。缺点是Main Memory数据和Cache中不一致,另外Cache替换的读操作会将dirty块写入Main Memory。 Miss 时要考虑allocate配置: Write allocate 配置时:先在cache中分配一个cacheline,将数据读到cache中,然后依照write-back和write-through 回写数据(具有write-through策略和write allocate 的缓存在缓存Miss时从内存中读取整个块(cacheline),并仅将更新的项写入存储的内存中。驱逐不需要写入内存)。

Write non-allocate 配置:无论write-back 还是 write-through 都直接将数据写到内存中,不会被加载到Cache中。只有读操作会缓存。

3.2. Cache 的使用图解 CPU读Cache时:Hit时,直接从cache中读取。如果miss,则Read through或Read allocate。 CPU写时:Hit 时使用Write Through或Write-back。如果miss:使用Write allocate 或是 No write allocate,同时配合Write Through 或Write-back 策略。 Write 策略组合: Write Through (hit) + Write Allocate(miss) Write Through (hit) + No Write Acllocate(miss) Write Back (hit) + Write Allocate (miss) Write Back(hit) + No Write Allocate (miss) 在有cache的单机系统中,通常有两种写策略:write through和write back。这两种写策略都是针对写命中(write hit)情况而言的:write through是既写cache也写main memory;write back 是只写cache,并使用dirty标志位记录cache的修改,直到被修改的cache 块被替换时,才把修改的内容写回main memory。那么在写失效(write miss)时,即所要写的地址不在cache中,该怎么办呢?一种办法就是把要写的内容直接写回main memory,这种办法叫做no write allocate policy;另一种办法就是把要写的地址所在的块先从main memory调入cache中,然后写cache,这种办法叫做write allocate policy。  • 读命中(Hit):  
466486906ab4f6ffc5.png
• Read Allocate(读分配, 先把数据读到cache中,再从cache中读到CPU。cache使能时,默认都是使用read allocate):
174676906ab60bdd52.png
• Write Through(WT,写通,透写,直接写,Hit时把数据同时写到cache和内存中):
536566906ab7351ba4.png
写数据时同时更新缓存和二级存储,缓存行不被标记为“dirty。这样,这对于数据一致性来说更安全,但它需要更多的总线访问。当某一个 cache line 需要替换时,就不必将其中的数据写到主存储器中去了,新调入的块可以立即把这一块覆盖掉。 尽管由于总线流量的增加,Write-Through缓存策略使用更多的能量并且速度更慢,但与使用回写缓存策略相比,它们有两个优点: •Write-Through cache policies reduce the need for software cache maintenance.  •Write-Through cache policies avoid the extra decrease in determinism because there is never any dirty data in the cache. • Write Back(hit时): 写数据时,只更新缓存,然后将 cache line 标记为“dirty”,当这个缓存行被新的缓存行替换,或者手动 clean 的时候,再将数据写到存储器中.
949856906ab8e00dfb.png
• Write Allocate(WA,写分配,Miss时考虑): 当进行数据写操作时,如果cache未命中,cache系统将会进行cache内容预取,从主存中将相应的块读取到cache中相应的位置,并执行写操作,把数据写入到 cache 中。对于写通类型的 cache,数据将会同时被写入到主存中,对于写回类型的 cache 数据将在合适的时候写回到主存中(写在此时只发生在cache中)。  由于写操作分配cache增加了cache内容预取的次数,它增加了写操作的开销,但同时可能提高cache的命中率,因此这种技术对于系统的整体性能的影响与程序中读操作和写操作数量有关。
507656906aba9387bd.png
Write non-Allocate(Miss 时考虑): Cache miss 时,无论是write through 还是write back,都是直接将数据写入内存中。不加载到cache。

3.3. Cache 控制寄存器
992756906abc838fa0.png
477836906abd9408f2.png
599236906abe6f3078.png
SIWT=0: 所有的shared 位置被认为是Non-cacheable的(例如Dcache),配置的内部可缓存性属性将被忽略掉。(share了的就不能cache,这是共享内存的默认操作模式。cache对这些位置的软件是透明的,因此不需要软件维护来保持一致性。) SIWT=1:普通的、可缓存的、共享的位置被视为WT(write-through),配置的其他属性不受CACR.SIWT的影响,仍然是WT特性。对于I-cache:共享(shared)位置被视为不可缓存(non-cacheable)。

3.4. MPU的使用概念 在STM32H7 的PM0253编程手册中MPU 配置:
437416906ac1c02b9f.png
164576906ac1fd8ec6.png
上表中关于 Cache 策略的设置 AA/BB 定义如下: 00 Non-cacheable  01 Write-back, write and read allocate
691236906ac3725ee7.png
10 Write-through, no write allocate  11 Write-back, no write allocate 概念:TEX,C,B, S, memory type,shareability, attributes. Write Through,Write Back,Write Allocate,No Write Allocate, Read Allocate 内存类型:Normal Memory, Device Memory, Strongly Ordered Memory。 Device 和 Strongly-ordered 类型的存储器(可以理解为外设)区域,都是 Noncacheable 的。见上表 “Other attributes”栏。 缓存策略cacheability:Non-Cacheable(主控不使用Cache,直接与存储器交互,这样不会带来内存一致性问题,但是效率不高) 和 Cacheable (Cacheabe 又分为Write-Through cacheable 透写 和Write-Back cacheable 回写) 共享属性shareability:共享Shareable(比如多核 CPU 中,UART 外设地址空间是对所有 CPU 共享的),不共享Non- shareable(当被配置成为 Non-Shareable 的时候,意味着在多核系统中,它只能被一个核心访问) Normal Memory:  通常,用于程序代码和数据存储的内存是普通内存。使用普通内存技术的例子有: Programmed Flash ROM   /*在编程过程中,闪存可以比普通存储器更严格地排序*/ROM、 SRAM、DRAM 和DDR memory Device Memory: Peripherals and I/O,通常是系统外设(I/O),Device内存类型属性定义了内存位置,在这些位置上,对这些位置的访问可能会导致副作用,或者为一个负载返回的值可能会根据执行的负载数量而变化。内存映射外设和I/O位置是通常标记为设备的内存区域的示例: memory-mapped 外设(比如I/O,访问会造成一些影响。),内存控制配置寄存器,中断控制寄存器,FIFO。 Strongly Ordered Memory: 比 Normal Memory 的访问规则更严格;Peripherals and I/O,通常是系统外设(I/O),对于强顺序内存,所有内存访问都严格按照内存访问指令的程序顺序进行排序:读和写都会对其他方面造成一定影响;访问不能重复;必须维护访问的数量、顺序和大小。具有Shareable特性。

3.5. ST 的参考文件
AN4839:Level 1 cache on STM32F7 Series and STM32H7 Series AN4838:Introduction to memory protection unit management on STM32 MCUs 下图是MPU没使能,并且没有对某些特殊寄存器进行配置,那么存储器的映射地址及其属性就可以参考下图。 其中,WT 表示 Write-through(透写),WB 表示 Write-back(回写),WA 表示 Write-allocate(写分配),没有明确标注 WA 的就是 RA(读分配)。
904916906ac696ad7c.png
请参考AN4839文件末尾的cache和MPU使用示例(下图):PC 将Flash中的数据拷贝到RAM,DMA将SRAM中的数据拷贝到DTCM中,最后比较DTCM和Flash中的原始数据来理解Cache、MPU和数据一致性。
683286906ac859ee47.png
数据传输中如果配置为write-back,即写数据时,只更新缓存,然后将 cache line 标记为“dirty”,当这个缓存行被新的缓存行替换,或者手动 clean 的时候,再将数据写到存储器中,这样容易造成数据的不一致。 要避免这种问题,需要:
343246906aca18bc83.png
Note:MPU配置为shareable,相当于没使用cache的效果。 如果系统对执行速度要求比较高,建议使用 cache,然后通过 cache 操作函数维护数据的一致性。 AN4838: MPU 寄存器的使用信息,可以参考 AN4838 文件中提到的 Programmer manual:
423996906acb74c903.png
具体配置和示例代码 在AN4838中有如下示例代码:

图2. Example of setting up the MPU
427806906acd0f1461.png
615276906acde3657c.png
最高性能配置:MPU enable, MPU_TEX_Level1, bufferable, cacheable, not shareable

3.6. MPU配置和cache相关函数 使能cache的函数: SCB_EnableDCache(); SCB_EnableICache(); 使能cache后,要配合MPU的正确配置来使系统性能最佳。

3.6.1. MPU配置示例:

内部Flash:仅运行指令,最低性能的配置,读写都不使用cache:MPU_ACCESS_NOT_BUFFERABLE, MPU_ACCESS_NOT_CACHEABLE, MPU_ACCESS_SHAREABLE,   MPU_TEX_LEVEL1. TEX=0b001, C=0, B=0, S=0 or 1. 最高性能的配置: MPU_ACCESS_BUFFERABLE, MPU_ACCESS_CACHEABLE,  MPU_ACCESS_NOT_SHAREABLE, MPU_TEX_LEVEL1。TEX=0b001, C=1, B=1, S=0, shareability =Not shareable, -> outer and inner write-back, write and read allocate. Flash 高性能配置*/ MPU_InitStruct.Enable = MPU_REGION_ENABLE;  //使能该保护区域 MPU_InitStruct.BaseAddress = 0x08000000;   //设置基地址 MPU_InitStruct.Size = MPU_REGION_SIZE_2M;  //保护区域的大小 MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; //设置访问权限 MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;     // 允许缓冲 buffer MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;   //允许 cache  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;    // 不共用 not share MPU_InitStruct.Number = MPU_REGION_NUMBER0;   //设置包含区域 MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;  //设置类型扩展域 MPU_InitStruct.SubRegionDisable = 0x00;  //禁止子区域 MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; //允许读取指令 HAL_MPU_ConfigRegion(&MPU_InitStruct); /*DTCM 和ITCM,和CPU主频一样,不需要配置MPU和cache。 AXI SRAM:  配置为 Normal, cache 读写都开启;性能最强,缺点:由于开启了读cache 和写 cache,需要用户调用函数 SCB_CleanInvalidataDCache(),管理数据一致性,需要注意,只有多主控操作(包括DMA)此空间需要如此处理。*/ MPU_InitStruct.Enable = MPU_REGION_ENABLE;  MPU_InitStruct.BaseAddress = 0x24000000;  
  MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;  MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;  MPU_InitStruct.Number = MPU_REGION_NUMBER1;  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;  MPU_InitStruct.SubRegionDisable = 0x00;  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;  HAL_MPU_ConfigRegion(&MPU_InitStruct);

/*对于Device 或 Strongly order 的外设,读写 cache 都要关闭,比如寄存器,FMC扩展IO,FMC驱动的一些strongly order外设,LCD,并口NOR Flash,并口SRAM,NAND Flash 等。 能保证严格按照程序代码执行,缺点是不支持非对齐访问。· 示例代码如下:*/ MPU_InitStruct.Enable = MPU_REGION_ENABLE;  MPU_InitStruct.BaseAddress = 0x60000000;  MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;  MPU_InitStruct.Number = MPU_REGION_NUMBER2;  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;  MPU_InitStruct.SubRegionDisable = 0x00;  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;  HAL_MPU_ConfigRegion(&MPU_InitStruct); /*配置为Normal,关闭写Cache,开启读cache, 特点:保证数据直接写到SDRAM,保证读性能最佳 缺点:由于开启了读 cache,需要用户调用函数 SCB_CleanInvalidataDCache(),管理数据一致性问题,这里需要注意,只有多主控操作此空间需要处理(包括DMA)。*/ MPU_InitStruct.Enable = MPU_REGION_ENABLE;  MPU_InitStruct.BaseAddress = 0xC0000000;  MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;  MPU_InitStruct.Number = MPU_REGION_NUMBER3;  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;  MPU_InitStruct.SubRegionDisable = 0x00;  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;  HAL_MPU_ConfigRegion(&MPU_InitStruct); /*配置为最低性能Normal,关闭cache的读写,normal里性能最差*/ MPU_InitStruct.Enable = MPU_REGION_ENABLE;  MPU_InitStruct.BaseAddress = 0x24000000;  MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;  MPU_InitStruct.Number = MPU_REGION_NUMBER5;     //根据具体配置Number号 MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;  MPU_InitStruct.SubRegionDisable = 0x00;  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;  HAL_MPU_ConfigRegion(&MPU_InitStruct); MPU 的详细内容,请参考ARM使用手册DUI0646C_cortex_m7_dgug(Arm CortexM7 Devices Generic User Guide r1p2,下面是 Cortex-M7 MPU 的寄存器:
975616906ad2c229a1.png
3.6.2. Cache 相关函数的使用说明 CMSIS软件包的core_cm7.h文件为Cache的配置提供了11个函数: SCB_EnableICache SCB_DisableICache SCB_InvalidateICache SCB_EnableDCache SCB_DisableDCache SCB_InvalidateDCache SCB_CleanDCache SCB_CleanInvalidateDCache SCB_InvalidateDCache_by_Addr SCB_CleanDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr
SCB_EnableICache(void) 使能 I cache SCB_DisableICache(void) 失能 I cache SCB_InvalidateICache(void) 使 I cache line 标记为无效,等同于删除操作。当读取指令时,会理解成cache-line中没有对应指令,转而去真实的物理地址获取指令。 SCB_EnableDCache(void) 使能 D cache SCB_DisableDCache(void) 失能 D cache SCB_InvalidateDCache(void) 使 D cache 标记为无效,等同于删除操作,当读取数据时,会理解成cache-line中没有对应数据,转而去真实物理地址获取数据。 SCB_CleanDCache(void) Clean 所有的 cache-line,即将标记为dirty 的 cache-line 数据全部写到 cache line 对应的真实的物理地址中。(比如write-back,cache-line 中有新的数据,并且标记为 dirty,但真实物理地址中可能是旧的数据。这个 clean,会把数据回写到真实memory中。) SCB_CleanInvlaidateDCache(void) 是 前 面 两 个 函 数 SCB_InvalidateDCache 和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。Cache空间就都腾出来,可以加载新的数据。 SCB_InvalidateDCache_by_Addr() 根据地址信息无效其对应的 cache-line。  SCB_CleanDCache_by_Addr() 根据地址信息 clean 其对应的 cache-line。 SCB_CleanInvalidateDCache_by_Addr() 根据地址信息 clean 并 invalidate 其对应的 cache-line。 Cortex-M7 内核的L1 Cache由多行内存区组成,每行有32字节, 请注意cache addr操作的地址一定要32字节对齐的。dsize一定要是32字节的整数倍。(Cortex m7 cache line 是8 words ,32bytes。对应的如果其他系统是64bytes cache line对应的就是64bytes) 3.6.3. 使用DMA时软件维护一致性 启用 Cache 通常可以带来程序运行速度的提升,不过,当在代码中使用到 DMA 时,我们可能需要根据DMA的不同传输方向,采取相应Cache维护策略措施以保持多主访问时的数据一致性。 在启用cache并遵循32字节对齐的条件下: 如果DMA负责从I/O读取数据到内存(DMA Buffer)中,那么在 DMA 传输之前,可以invalid DMA Buffer 地址范围的高速缓存。在 DMA 传输完成后,程序读取数据不会由于cache hit 导致读取过时的数据。 如果DMA负责把内存(DMA Buffer)数据发送到I/O设备,那么在DMA传输之前,可以clean DMA Buffer 地址范围的高速缓存,clean的作用是写回cache中修改的数据(数据准备阶段用到的cache line)。在DMA传输时,就不会把主存中的过时数据发送到I/O设备。

请注意,在DMA 传输没有完成期间CPU不要访问DMA Buffer(如果需要访问 DMA buffer,请注意保证数据的一致性)。 在速度要求不太严格的时候,保险起见(比如数据没有对齐,或是客户需要使用简单方法保证数据一致性),建议 DMA 传输前,对 DMA 要访问的对应地址的 cache 空间进行先clean 后 invalid 操作(先 clean 不会损坏其他有用数据,后 invalid 操作可以理解为删除操作)。在DMA传输完成,并搬移DMA buffer内容到其他空间后,对对应地址的cache空间进行先clean 后invalid操作。当然,本建议可根据具体应用灵活处理。 cache addr 操作的地址一定要 32 字节对齐的。dsize 一定要是32字节的整数倍。这点同样适用于可以Cacheable 的DMA buffer(dsize 为 32byts 整数倍,当 buffer 大小不是32bytes 的整数倍的情况下有一定的空间浪费)。32  bytes是根据cache line的最小粒度来确定的(Cortex m7 cache line 是 8 words ,32bytes。对应的如果其他系统是 64bytes cache line 对应的就是64bytes)。如果DMA Buffer的安排也遵守32字节对齐,就可以避免同一Cacheline出现两种应用场景的数据,否则不方便针对Cacheline做数据维护操作。 Cache 的使用能否提高效率需要综合考虑,因为维护数据一致性有一定开销:对于32bytes,16bytes,8bytes,1bytes 的一些常用变量,不使用 cache 但把他们放在 0 延时等待的DTCM空间效率会更好。对与512bytes,1024bytes等,空间需求较大,DTCM放置栈、堆及常用变量后可能空间比较紧张了,这些需求量大的buffer可以放在AXI SRAM, SRAM1,SRAM2 等空间,使用 cache 效率会更高,但需要地址和 size 大小对齐。配置外设、同外设进行通讯、DMA操作时也需要维护一致性或是配合MPU来使用。

4. 小结
在使用cache时要配合使用MPU,并且做好数据一致性的处理。
534046906ad6489250.png
763656906ad7410b55.png
磨砂 发表于 2025-11-3 11:44 | 显示全部楼层
其L1 Cache的配置需结合MPU实现
晓伍 发表于 2025-11-3 13:04 | 显示全部楼层
通过SCB寄存器直接使能I-Cache和D-Cache
八层楼 发表于 2025-11-3 15:17 | 显示全部楼层
在CubeMX中禁用“Speculation default mode”会自动禁用Cache和MPU
观海 发表于 2025-11-3 17:57 | 显示全部楼层
64KB ITCM和128KB DTCM,无需配置MPU,可直接使用且速率高达480MHz
guanjiaer 发表于 2025-11-3 20:11 | 显示全部楼层
AXI SRAM(512KB):映射到0x24000000,默认速率240MHz,需通过MPU配置为可缓存/缓冲模式以提升访问速度
heimaojingzhang 发表于 2025-11-3 22:31 | 显示全部楼层
控制模式:背景区域设为特权访问,并启用硬件错误和NMI中断
keaibukelian 发表于 2025-11-4 08:52 | 显示全部楼层
STM32H7支持最多16个内存区域,优先级从高到低排列
paotangsan 发表于 2025-11-4 11:04 | 显示全部楼层
当DMA操作涉及缓存区域时,需手动清理或无效化缓存
renzheshengui 发表于 2025-11-4 13:28 | 显示全部楼层
多总线访问同一内存时,需正确配置S位,避免数据竞争
wowu 发表于 2025-11-4 15:58 | 显示全部楼层
Cortex-M7采用伪随机替换算法,频繁访问的缓存行应尽量固定位置
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:意法半导体(中国)投资有限公司
简介:您的嵌入式应用将得益于意法半导体领先的产品架构、技术、多源产地和全方位支持。意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器。

1428

主题

1758

帖子

25

粉丝
快速回复 在线客服 返回列表 返回顶部