1.概述
微控制器是一种集成电路,通常由一个或多个中央处理单元(CPU)、内存、输入/输出及其他支持功能组成,内存分为非易失和易失性存储器;微控制器上电先从非易失存储器将可执行程序搬运易失性存储器,然后开始运行;程序运行过程中 CPU 需要不断地从内存(或其他存储器)读取指令和数据,CPU 需要等待指令或数据读取完成,才能进行数据处理,即便 CPU 的速度再快,最后程序运行速度受限于内存(或其他存储器);如果将内存更换为处理速度更快的内存,则会大大增加微控制器的成本;为解决上述问题,微控制器内核引入 Cache 技术,Cache是一段高速的、size 较小的 RAM,Cache 的读、写速度要比内存(或其他存储器)快,作为 CPU 与内存(或其他存储器)之间的桥梁。
2. Cache 分类
Cache 分为一级 Cache 和 二级 Cache,本书仅介绍一级 Cache,一级 Cache 又分为 I-Cache 和 D-Cache,大小一般为 32KB,比如 Cortex-R5 内核,I-Cache 用于存储指令而 D-cache 用于存储数据,I-Cache 与 D-Cache 可以独立的打开和关闭,由于 I-Cache 用于缓存代码本文不予讨论,D-Cache 用于存储数据,D-Cache 使用不当会导致一些隐患,本文只讲解 D-Cache 的使用,且默许 I-Cache 为打开状。
3. Cache 专业术语
命中:CPU 要访问的数据在 Cache 中有缓存;
缺失:CPU 要访问的数据不在 Cache 中,CPU 需要去内存(或其他存储器)读取数据;
Cache size:Cache 可以缓存最大数据的大小;
Cache line:CPU 读取或写入内存(或其他存储器)最小传输字节;
Dirty bit:每一行 Cache line 只有一个 Dirty bit,当 Dirty 位有效时,Cache Line 中的数据不能随意丢失,需要写回内存(或其他存储器);
Valid bit:这个 bit 用来表示 Cache line 中数据是否有效,若被寻址数据在Cache 中命中,但是 Valid bit 无效,也是 Cache 缺失;
Offse、Index:概念见下一章节;
4. Cache 基本原理
Cache是一段高速的、size 较小的 RAM,Cache 的读、写速度要比片内或片外内存快,作为 CPU 与内存(或其他存储器)之间的桥梁;当 CPU 访问数据时,根据被访问数据的地址,确定数据是否在 Cache 中缓存,若在 Cache 中缓存,则命中可直接使用数据,若不在 Cache 中缓存,则需要去内存(或其他存储器)读取。强调一下 Cache 属于内核的一部分,若在多核系统中,多核访问同一片地址的数据,会引起什么问题,如果启动 DMA 技术,同时打开 Cache,会引起什么问题?
直接映射缓存:假设 Cache Size 为 64 byte,Cache Line 为 8 byte,平均为分 8 行;Cache Line 为 8 byte,需要用 3 bit 寻址 Cache Line 中的某一个字节,所以 Offset 需要 3 bit;Cache 总共分为 8 行,需要用 3 bit 寻址 8 行中的某一行,所以 Index 需要 3 bit,其他地址位存放到 Tag 中,直接映射缓存由于可能会引起 Cache 颠簸,接下来引入多路组相连缓存概念;
假设寻址 RAM 地址为 0x0654(0110 0101 0100),数值为 0x12,其中 Offset 为 4,Index 为 2,Tag 为 19。
两路组相连缓存:假设 Cache Size 为 64 byte,平均分为 2 份,每一份为 32 byte,由于 Cache Line 为 8 byte,每一路组为 4 行 Cache Line,其中需要用 2 bit 寻址一组中某一行,所以 Index 需要 2 bit, Cache Line 为 8 byte,需要用 3 bit Cache Line 中的某一个字节,所以 Offset 需要 3 bit,其他地址位存放到 Tag ,当寻址的地址中 Offset、Index 都匹配时,不能准确定位被寻址地址在某一路组,需要将 Tag 数据与被寻址数据再次比较,才能准确定位数据空间,由于此方式增加了比较次数,所以对硬件要求比较高。
假设寻址 RAM 地址为 0x0654(0110 0101 0100),数值为 0x12,其中 Offset 为 4,Index 为 2,Tag 为 0x32。
5. Cache 分配策略
- 写通(write through),CPU 向内存中写数据,首先检查 Cache 中是否有数据备份,如果有则写入到 Cache,之后立即再次写入到内存。
- 写回(write back),CPU 向内存中写数据,首先检查 Cache 中是否有数据备份,如果有则将数据写入到 Cache, 之后并不会立即将数据写入到内存,等待空闲时自动将数据更新到内存。
- 写分配(write alloc),CPU 向内存中写数据时,首先检查 Cache 中是否有备份,如果没有则将会在 Cache 中分配空间,备份写入到内存的数据。
- 读分配(read alloc),与写分配类似,CPU 读内存时首先检查 Cache 中是否有备份,如果没有则在 Cache 中分配空间,备份将要读取的数据。默认情况下读分配是开启的。
- Cache 清理,Cache 清理并不是删除 Cache 中的内容,而是将 Cache 中的内容更新到各自对应的内存中。
- 缓存无效化,Cache 中的内容失效,效果如同删除了 Cache 中的数据备份,CPU 再次读数据时将直接从内存中读取。
6. Cache 一致性
- 多个核心使用同一个变量,一个核心写变量后变量值还在 Cache 中而没进入 IRAM 中(可以通过设置属性 Write-through 解决),导致另一个核心看不到。
- 一个变量的值被 Cache 到某个核心的 Cache 中,如果 IRAM 中的值改变了,此核心直接尝试读此值读到的依然是 Cache 中的旧值。
- 由于 Cache line 是 32 字节,维护 Cache 的最小单位也就是 32 字节,如果一个变量小于 32 字节,当某个核心在维护 Cache 时,可能会导致在此 Cache line 中其它数据也受到影响。因此建议各个 core 的私有数据段(如该数据段 Cacheable)Cache line 对齐,从而避免一个 core 发生 Cache 替换或 Cache 维护时影响另一个 core 数据的情形。
- 在使用一些加速器时,如 DMA 等,需要对 DMA 操作的区域做 Cache 维护操作。
7. SemiDrive Cache 应用
7.1 SSDK Cache
- Cache enable
/**
* @brief Enable ARM I-Cache and D-Cache operations.
*
* If CONFIG_ARCH_WITH_CACHE is defined, ARM I-Cache and D-Cache
* operations are enabled and you can call functions defiend in cache.h to
* manage caches.
*/
#define CONFIG_ARCH_WITH_CACHE 1
/**
* @brief ARM cache line size in bytes.
*
* Cortex R5 cache line length is 8 words (256 bits).
*/
#define CONFIG_ARCH_CACHE_LINE 32
/**
* @brief Enable I-Cache on power up.
*
* If CONFIG_ARCH_EARLY_ENABLE_ICACHE is defined, ARM I-Cache is
* enabled on power up.
*/
#define CONFIG_ARCH_EARLY_ENABLE_ICACHE 1
/**
* @brief Enable D-Cache on power up.
*
* If CONFIG_ARCH_EARLY_ENABLE_DCACHE is defined, ARM D-Cache is enabled on
* power up.
*/
#define CONFIG_ARCH_EARLY_ENABLE_DCACHE 1
- Cache API
/*
* enable caches.
* @flags cache type.
*/
void arch_enable_cache(uint8_t flags);
/*
* disable caches.
* @flags cache type.
*/
void arch_disable_cache(uint8_t flags);
/*
* clean dcache.
* @start start address.
* @len clean data length.
*/
void arch_clean_cache_range(addr_t start, size_t len);
/*
* clean and invalidate dcache.
* @start start address.
* @len clean data length.
*/
void arch_clean_invalidate_cache_range(addr_t start, size_t len);
/*
* clean and invalidate dcache all
*/
void arch_clean_invalidate_dcache_all(void);
/*
* invalidate dcache.
* @start start address.
* @len clean data length.
*/
#define arch_invalidate_cache_range(start, len)
/*
* sync dcache.
* @start start address.
* @len clean data length.
*/
void arch_sync_cache_range(addr_t start, size_t len);
7.2 MCAL Cache
- Cache enable
arch_enable_cache(ICACHE | DCACHE);
- Cache API
void arch_disable_cache(uint32 flags);
void arch_enable_cache(uint32 flags);
void arch_sync_cache_range(unsigned long start, uint32 len); //invalidate icache
void arch_clean_cache_range(const void *start, uint32 len);
void arch_clean_invalidate_cache_range(const void *start, uint32 len);
void arch_invalidate_cache_range(const void *start, uint32 len);
void arch_set_dcache_error_check(void);
8. 参考文档
《AppNote_E3_Boot_and_OTA_Rev01.06》
《E3400_E3600_MCU_TRM_Rev00.13》
欢迎点击此处在原博文下方留言评论,我们会及时回复您的问题。
如有更多需求,欢迎联系大联大世平集团 ATU 部门:atu.sh@wpi-group.com 作者:娜就这么嘀