Cortex-M7 Cache and MPU 简介
-
介绍
YTM32B1HA01 是云途第一款搭载 Cortex-M7 的芯片,开始引入 Cache。本文旨在简单介绍 Cache,以及 MPU。
Cache
随着工艺和设计的演进,CPU 计算性能其实发生了翻天覆地的变化,但是DRAM存储性能的发展没有那么快。
所以造成了一个问题,存储限制了计算的发展。
容量与速度不可兼得。
如何解决这个问题呢?可以从计算访问数据的规律入手。
我们随便贴段代码:
for (j = 0; j < 100; j = j + 1) for( i = 0; i < 5000; i = i + 1) x[i][j] = 2 * x[i][j];
可以看到,由于大量循环的存在,我们访问的数据其实在内存中的位置是相近的。
换句专业点的话说,我们访问的数据有局部性。
我们只需要将这些数据放入一个小而快的存储中,这样就可以快速访问相关数据了。
总结起来,Cache是为了给CPU提供高速存储访问,利用数据局部性而设计的小存储单元。
Cache 按照数据类型可划分为 I-Cache 与 D-Cache。其中I-Cache负责放置指令,D-Cache负责放置数据。两者最大的不同是D-Cache里的数据可以写回,I-Cache是只读的。
Cache 工作原理
CPU 和 Cache 是 word 传输的,而 Cache 到主存是以块传输的,一块大约 64Byte 。其中 I-Cache 缓存指令,D-Cache 缓存数据。
当 CPU 访问Cache过的 Memory 时,由于 Cache 可以缓存 Memory 的内容,这样 CPU 访问 Memory 就会变成访问 Cache,CPU 访问 Cache 的速度远大于访问 Memory 的速度,以此提成 CPU 的性能。
Cache 找数据
在讲 Cache 的读取或写入之前,先谈一下 Cache 的数据。
Cache 的数据由 Tag 与 Memory 内的数据组成,当 CPU 希望通过 Cache 访问 Memory 时,会先比对 Cache 中的 Tag。
若成功,又称命中 (hit),CPU 会直接操作 Cache 中的数据;
若失败,又称缺失 (miss),则 Cache 会缓存这块 Memory。
例如,当 CPU 第一次访问 Memory 时,Cache 内未缓存该 Memory,则 CPU miss,此时 Cache 会缓存 Memory;当 CPU 紧接着又访问该 Memory 时,Cache 内正好缓存着 Memory,则 CPU hit。
Cache 写数据
Cache 毕竟是个临时缓存,如果 CPU 往 Cache 中写数据,则会造成 Cache 中数据与 Memory 数据不匹配,为保证写数据的统一,通常会设置 Memory 的 Cache Write 属性。
- 若 CPU hit:
- Write through: CPU 向 Cache 中写入数据时,同时向 Memory 也写一份,使 Cache 和 Memory 的数据保持一致。优点是简单,缺点是每次都要访问 Memory,速度比较慢。
- Write back: CPU 更新 Cache 时,只是把更新的 Cache 区标记一下,并不同步更新 Memory 。只是在 Cache 区要被新进入的数据取代时,才更新 Memory。这样做的原因是考虑到很多时候 Cache 存入的是中间结果,没有必要同步更新 Memory。优点是CPU执行的效率提高,缺点是实现起来技术比较复杂。
- 若 CPU miss:
- Write allocate: 先把要写的数据载入到 Cache 中,写 Cache,然后再通过flush方式写入到 Memory 中。
- No write allocate:直接把要写的数据写入到 Memory 中。
Cache 读数据
CPU 读取 Cache 中数据时,也分 hit 与 miss 的情况。
- 若 CPU hit:
- CPU 直接从 Cache 中读取数据。
- 若 CPU miss:
- Read through: CPU 直接从 Memory 中读取数据。
- Read allocate: Cache 先缓存当前 Memory,CPU 再从 Cache 中读取数据。
Cache 示例
下面以一个简单的例子来展示 Cache 的工作原理。
uint16_t x[8] = {0}; uint8_t i = 0; uint8_t j = 0; for (j = 0; j < 50; j = j + 1) for( i = 0; i < 8; i = i + 1) x[i] = x[i] + j;
- 当没有 Cache 时,x 数组置于 SRAM 上,每次修改 x[i] 时,CPU 会先访问 x[i] 所在的地址,并读取该值,做完加法运算后,把运算结果回写至 x[i] 所在的地址,至此完成一次 x[i] 的赋值,共需执行400次这样的操作。
- 当有 Cache 时,x 数组置于 SRAM 上。
- CPU 第一次去访问 x[i] 时,Cache 内没有 x 数组的内容,此时会缓存 x 数组所在的 SRAM,当前 Cache 内 x 数组值为全零。
- 每次修改 x[i] 时,会读取 Cache 中 x[i] 的值,做完加法后,若此时为 Write-Back 策略,则CPU将结果再写回 Cache 中,不会同步更新到 x[i] 所在的真实 SRAM 地址的值。
- 中间的运算以及访问都会在 Cache 中,当 Cache 中内容要被新数据取代时,Cache 会将计算结果回写至 x[i] 所在的真实 SRAM 地址。
Cache 操作
Invalidate
Invalidate 操作主要是针对 cache line中的 valid bit(有效位)进行操作。通过对有效位进行清零,从而使cache line中的数据失效,达到清除数据的目的。比如在上电或者重置操作后,cache 中的内容是未定义的,此时它的valid bit为0。
Clean
Clean 操作主要针对cache line 中的 dirty bit(脏位)
某个cache line中的dirty bit为1,说明该line中的数据与内存中的数据不一致,我们就可以对该cache line描述为变“脏”了。
清除cache或cache line意味着将标记为“脏”的cache line内容写入下一级缓存或主内存,并清除cache line中的脏位。这使得当前的cache line中的数据与下一级缓存,或主存中的的内容相同。此操作仅适用于写回策略 (WB)的数据缓存 (data-cache)。
Flush
每条cache line 先clean,再invalidate.
Cache APIs
Enable Cache
void SCB_EnableICache (void); void SCB_EnableDCache (void);
Disable Cache
void SCB_DisableICache (void); void SCB_DisableDCache (void);
Invalidate Cache
void SCB_InvalidateICache (void); void SCB_InvalidateICache_by_Addr (volatile void *addr, int32_t isize); void SCB_InvalidateDCache (void); void SCB_InvalidateDCache_by_Addr (volatile void *addr, int32_t dsize);
Clean Cache
void SCB_CleanDCache (void); void SCB_CleanInvalidateDCache (void); void SCB_CleanDCache_by_Addr (volatile void *addr, int32_t dsize); void SCB_CleanInvalidateDCache_by_Addr (volatile void *addr, int32_t dsize);
Cache Tips
Cache 一致性是 Cache 中遇到的比较麻烦的一个问题。
实际使用过程中,Memory 不仅仅只有 CPU 或 Cache 访问,一些外设也可以去读写 Memory,例如 DMA,下面列举了常见的 Cache 使用场景。
CPU访问,外设改写
如果CPU先读了一块内存,接下来要DMA/其他硬件要访问这块内存,无需clean. DMA/其他硬件改写了这段DDR后,要将这段cache置为invalid,这样后续如果CPU访问这段内存,就不会使用cache中的数据。
外设访问,CPU改写
如果CPU先写了一块内存,接下来要DMA/其他硬件要访问这块内存,要先clean,确保DMA/其他硬件访问到的DDR数据是准确的。DMA/其他硬件改写了这段DDR后,要将这段cache置为invalid,这样后续如果CPU访问这段内存,就不会使用cache中的数据。
MPU
YTM32B1HA01 使用的是 ARM-v7 架构,MPU 版本也是 ARM-v7 的 MPU。
ARM-v7 的 MPU 与 ARM-v8 的 MPU 实现的功能基本类似,本篇主要介绍 MPU 的属性配置。
Region 地址与大小
MPU 设置的每个 Region 为基地址与 region size。其中基地址要求 region size 对齐,即要求 Base_address % Region_size == 0。Region size 要求为 2 的幂次,最小为 32 Bytes,最大为 4GB。
MPU 属性
XN
XN 为 Execute Never encoding 的缩写,表示当前 Region 能否执行指令。
XN Description 0 Execution of an instruction fetched from this region permitted 1 Execution of an instruction fetched from this region not permitted AP
AP 为 Access permissions field encoding 的缩写,表示当前 Region 的访问权限。
AP[2:0] Privileged access Unprivileged access Notes 000 No access No access Any access generates a permission fault 001 Read/write No access Privileged access only 010 Read/write Read-only Any unprivileged write generates a permission fault 011 Read/write Read/write Full access 100 UNPREDICTABLE UNPREDICTABLE Reserved 101 Read-only No access Privileged read-only 110 Read-only Read-only Privileged and unprivileged read-only 111 Read-only Read-only Privileged and unprivileged read-only 其中 UNPREDICTABLE 为不可预知的,不能设置成该属性。
TEX,S,C,B
TEX 与 C,B 共同控制 Memory 属性,S 表示 Shareable,根据不同的内存属性有着不同的作用域。
设置shareable表示该存储块可以被多个主设备访问,当配置 Shareable 时,
该区域的数据是不会使用cache的,效果跟write through一样。如果 Non-shareable,一般指被本地CPU访问。
TEX C B Memory type Description, or Normal region Cacheability Shareable? 000 0 0 Strongly-ordered Strongly ordered Shareable 000 0 1 Device Shared device Shareable 000 1 0 Normal Outer and inner Write-Through, no write allocate S bit 000 1 1 Normal Outer and inner write-back, no write allocate S bit 001 0 0 Normal Outer and inner Non-cacheable S bit 001 0 1 Reserved Reserved Reserved 001 1 0 IMPLEMENTATION DEFINED IMPLEMENTATION DEFINED IMPLEMENTATION DEFINED 001 1 1 Normal Outer and inner write-back; write and read allocate S bit 010 0 0 Device Non-shared device Non-shareable 010 0 1 Reserved Reserved Reserved 010 1 X Reserved Reserved Reserved 011 X X Reserved Reserved Reserved 100 0 0 Normal Non-cacheable S bit 101 0 1 Normal Write-Back, write and read allocate S bit 110 1 0 Normal Write-Through, no write allocate S bit 111 1 1 Normal Write-Back, no write allocate S bit S bit 表示当前配置下,S=0 为 Non-Shareable,S=1 为 Shareable
SRD
SRD 为 Subregion Disable 的缩写。
一个 Region 可以分成8个 sub region,每个 sub region 大小为母 region 的 1/8。
8块 Sub region 由8个比特单独控制,设 0 表示使能 sub region,设 1 表示禁止 sub region。例如,若设置 MPU->RASR.SRD = 0b10101010,表示当前 Region 的第 0 块 Sub region 使能,第 1 块 Sub region 禁止......第 6 块 Sub region 使能,第 7 块 Sub region 禁止。
若禁止某块 Sub region,则该块 Sub region 与母 Region 属性不一致,其为默认属性。
优先级
当两个 Region 的区域重叠时,这块儿重叠区域的属性是后面的 Region,例如某一块 Memory 既在 Region 10 内,又在 Region 8 内,此时这块 Memory 属性为 Region 10 中所设置的属性。(注:ARM-V8 不支持 Region 区域重叠)
MPU 控制
PRIVDEFENA
MPU->CTRL 里有 PRIVDEFENA 比特位:
- 当设置成 0 时,禁止默认的 Memory Map,访问未被 MPU 定义的区域会产生错误
- 当设置成 1 时,使能默认的 Memory Map,但仅特权模式 (privileged) 能访问未被 MPU 定义的区域。且默认的 Memory Map 可当作 Region 为 -1,即优先级为最低。
ARMv7 默认的 Memory Map 属性如下
Address Name Device type XN? Cache Description 0x00000000-0x1FFFFFFF Code Normal - WT Typically ROM or flash memory. 0x20000000-0x3FFFFFFF SRAM Normal - WBWA SRAM region typically used for on-chip RAM. 0x40000000-0x5FFFFFFF Peripheral Device XN - On-chip peripheral address space. 0x60000000-0x7FFFFFFF RAM Normal - WBWA Memory with write-back, write allocate cache attribute for L2/L3 cache support. 0x80000000-0x9FFFFFFF RAM Normal - WT Memory with Write-Through cache attribute. 0xA0000000-0xBFFFFFFF Device Device, shareable XN - Shared device space. 0xC0000000-0xDFFFFFFF Device Device, Non-shareable XN - Non-shared device space. 0xE0000000-0xFFFFFFFF System - XN - System segment for the PPB and vendor system peripherals 注:
- WB:Write back
- WT:Write through
- WA:Write allocate
- 该 Memory Map 为 ARM 定义的 Memory Map,芯片实际的 Memory Map 会不太一样,但默认属性是一致的。
HFNMIENA
Core 内部有一些异常中断,例如 Hard Fault,Bus Fault,Usage Fault,NMI...这些均为不可屏蔽的中断。
MPU->CTRL 里有 HFNMIENA 比特位:
- 当设置成 0 时,产生类似 Hard Fault,NMI 错误,或开启 FAULTMASK 后,MPU 设置的属性会失效
- 当设置成 1 时,Memory 在上述情况下仍使用 MPU 配置的属性
注:当未使能这个比特时,产生 HardFault 后,MPU 对 Memory 配置的属性会失效,例如对 RAM 配置的 non-cacheable 属性,会恢复为默认 cacheable,此时需要注意 Cache 的同步问题;或者使用 “cpsid if” 来关闭全局中断后,MPU 的属性也会失效。("cpsid i" 不影响 MPU)
FAQ
下面会介绍,在 YTM32B1HA0 上可能遇到的与 Cache 一致性有关的应用场景。
Cache 与 Flash 的读写擦
Flash 无法被 Cache 擦写,主要是 Flash 的读操作。
若 Flash 被 Cache 缓存了,当用户操作 EFM 模块修改 Flash 内容后,例如擦除某个扇区,或某块空 Memory 进行编程,并不会修改 Cache 中的内容,若此时 CPU 去读取这段被修改的 Memory,则会读取 Cache 中的结果,即被修改前的 Memory 的值。
所以需要在用户修改完 Flash 中的内容后,对 Cache 进行 Invalidate 操作,让 Cache 重新读取同步 Flash。
当前 SDK 有对该场景进行配置,用户调用 SDK 操作 Flash 是正常的。
同样的,Fee(EEPROM) 也需要类似的操作。
Cache 与 DMA
Cache 与 DMA 均无法修改 Flash 的值,但均可以修改 SRAM 里的值。
当 SRAM 被 Cache 缓存后,
- 若 DMA 去修改 SRAM 里的值,则需要对 Cache 进行 Invalidate 操作,让 Cache 重新读取同步 SRAM;
- 若 CPU 通过 Cache 去修改 SRAM,而 DMA 需要搬运这块 SRAM,那么在 DMA 搬运之前,需要对 Cache 进行 Clean 操作,把 Cache 中的数据回写到 SRAM 中。
其余能操作 SRAM 的外设均类似。
Cache 与调试 Debug
YTM32B1HA0 调试总线是经过 Cache,所以在调试时,用户添加在 Watch 窗口的值大多为 Cache 中的值,当 DMA 修改完这个值后,调试窗口里的值不一定会改变。
在调试过程中,当出现调试窗口里的值不是预期的结果,有可能会是这个问题。
当然,同样的,如果在调试过程中,用户通过 Memory 窗口,修改 SRAM 值,DMA 再去搬运,有可能搬运的是修改前的值。
调试时,用户对 SRAM 进行的读写操作与 CPU 类似,可参考上一条。
HardFault or FAULTMASK
MPU->CTRL 的 HFNMIENA 未使能时,开启 FAULTMASK 或 HardFault 等异常置起时,需要注意数据同步。
while(1) { A++; __asm volatile ("cpsid if"); B = A; __asm volatile ("cpsie if"); }
例如上述代码,A,B 变量均在 MPU 配置的 non-cache 的 RAM 上,A++ 对A变量的读取以及回写不会经过D-Cache,开启 FAULTMASK 后,MPU配置属性失效,A与B变量所在的 RAM 恢复为 cacheable,第一次 B 读取 A 变量会先过 D-Cache,由于 D-Cache 未 hit 过 A,所以会从 RAM 中加载真实的值。关闭 FAULTMASK 后,MPU 配置会重新生效,A,B 恢复为 non-cache 。A++ 对A变量的读取以及回写依旧不会经过D-Cache;第二次 B 读取 A 变量,由于 A 变量已经被 D-Cache hit 过,所以会直接从 Cache 中读取 A 变量。但 A 变量的变化不经过 Cache,直接刷写 RAM,会导致 Cache 与 RAM 数据不同步,显示 B 的值始终保持不变。
解决方法可以使用 "cpsid i" and "cpsie i" 来关闭全局中断,与恢复全局中断,这个不会影响 MPU 。
- 若 CPU hit:
帮助没办法联网的电脑使用YCT
帮助改进和优化YT CONFIG TOOL,有机会抽取YTM32B1ME0 EVB哦...