缓存(Cache)
概述
各个 CPU 的缓存描述如下:
CPU |
类型 |
大小 |
Way |
缓冲行长度 |
---|---|---|---|---|
KM4 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
|
KM0 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
CPU |
类型 |
大小 |
Way |
缓冲行长度 |
---|---|---|---|---|
KM4 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
|
KR4 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
CPU |
类型 |
大小 |
Way |
缓冲行长度 |
---|---|---|---|---|
KM4 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
|
KR4 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
|
DSP |
I-Cache |
32KB |
4 |
128B |
D-Cache |
48KB |
3 |
128B |
CPU |
类型 |
大小 |
Way |
缓冲行长度 |
---|---|---|---|---|
KM4 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
|
KR4 |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
|
DSP |
I-Cache |
32KB |
4 |
128B |
D-Cache |
48KB |
3 |
128B |
CPU |
类型 |
大小 |
Way |
缓冲行长度 |
---|---|---|---|---|
KM4 |
I-Cache |
64KB |
4 |
32B |
D-Cache |
32KB |
4 |
32B |
|
KM0 |
I-Cache |
16KB |
2 |
32B |
D-Cache |
8KB |
2 |
32B |
|
CA32 |
L1 I-Cache |
32KB |
2 |
64B |
L1 D-Cache |
32KB |
4 |
64B |
|
L2 Cache |
256KB |
8 |
64B |
CPU |
Type |
Size |
Way |
Cache line length |
---|---|---|---|---|
KM4TZ |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
|
KM4NS |
I-Cache |
16KB |
4 |
32B |
D-Cache |
16KB |
4 |
32B |
片内缓存支持 Enable/Disable、Flush 和 Clean 操作。在 main()
函数之前,各个 CPU 均已调用 Cache_Enable()
启用了缓存。
操作 |
描述 |
I-Cache |
D-Cache |
---|---|---|---|
Enable/Disable |
启用或禁用缓存 |
√ |
√ |
Flush (Invalidate) |
|
√ |
√ |
Clean |
|
x |
√ |
备注
CA32 清空某个地址时,只有处于 clean 状态的缓存行才会执行清空操作,处于 dirty 状态的缓存行会执行 Clean & Invalidate 操作。
CA32 和 DSP 具有自动数据预取功能,当 CPU 预测未来将需要访问的数据时,CPU 会在后台执行行填充操作,自动将数据加载到缓存中。
对于 CA32 和 DSP,
DCache_Clean()
/DCache_CleanInvalidate()
操作会将整个缓存行写入内存。当两个 CPU(具有不同的缓存行大小)使用共享内存通信时,共享内存必须与较大的缓存行对齐,以避免数据覆盖问题。例如:如果共享内存只有 32 字节,缓存行大小为 32 字节的 CPU0 每次清理时只会写入 32 字节,而缓存行大小为 64 字节的 CPU1 每次清理时会写入 64 字节,可能会覆盖 CPU0 的其他数据。
API 参考
ICache_Enable
void ICache_Enable(void)
启用 I-Cache
ICache_Disable
void ICache_Disable(void)
禁用 I-Cache
ICache_Invalidate
void ICache_Invalidate(void)
清空 I-Cache
DCache_IsEnabled
u32 DCache_IsEnabled(void)
确认 D-Cache 是否启用,返回值:
1:启用
0:禁用
DCache_Enable
void DCache_Enable(void)
启用 D-Cache
DCache_Disable
void DCache_Disable(void)
禁用 D-Cache
DCache_Invalidate
void DCache_Invalidate(u32 Address, u32 Bytes)
通过地址清空 D-Cache,参数说明:
- Address:
失效地址(按缓存行长度对齐)
- Bytes:
内存块大小(单位:字节)
DCache_Clean
void DCache_Clean(u32 Address, u32 Bytes)
通过地址清理 D-Cache,参数说明:
- Address:
清理地址(按缓存行长度对齐)
- Bytes:
内存块大小(单位:字节)
DCache_CleanInvalidate
void DCache_CleanInvalidate(u32 Address, u32 Bytes)
通过地址清理和清空 D-Cache,参数说明:
- Address:
需清理并失效的地址(按缓存行长度对齐)
- Bytes:
内存块大小(单位:字节)
备注
Address
和Bytes
同时为 0xFFFFFFFF,表示清理或者清空全部 D-Cache。Address
和Bytes
必须按缓存行长度对齐。如果不对齐,例如缓存行长度为 32 字节,Address
为 0x20000003C,Bytes
为 0x00000008 时,那么操作的地址范围跨越了 2 个缓存行,实际操作的地址范围为0x200000020 ~ 0x20000003F
和0x200000040 ~ 0x20000005F
,此时会导致预期外的问题。
如何定义非缓存数据缓冲区
在缓冲区定义前添加宏 SRAM_NOCACHE_DATA_SECTION,可定义具有非缓存属性(Non-Cacheable)的数据缓冲区。
SRAM_NOCACHE_DATA_SECTION u8 noncache_buffer[DATA_BUFFER_SIZE];
在缓冲区定义前添加宏 SRAM_NOCACHE_DATA_SECTION,可定义具有非缓存属性(Non-Cacheable)的数据缓冲区。
SRAM_NOCACHE_DATA_SECTION u8 noncache_buffer[DATA_BUFFER_SIZE];
备注
对于 KR4:非缓存属性仅能通过 MCCA 寄存器 配置,因此无法直接通过宏定义实现非缓存缓冲区。
在缓冲区定义前添加宏 SRAM_NOCACHE_DATA_SECTION,可定义具有非缓存属性(Non-Cacheable)的数据缓冲区。
SRAM_NOCACHE_DATA_SECTION u8 noncache_buffer[DATA_BUFFER_SIZE];
备注
对于 KR4:非缓存属性仅能通过 MCCA 寄存器 配置,因此无法直接通过宏定义实现非缓存缓冲区。
对于 DSP:若需操作 DSP 缓存,请参考 Xtensa LX7 Microprocessor Data Book 和 Xtensa System Software Reference Manual 获取详细信息。
在缓冲区定义前添加宏 SRAM_NOCACHE_DATA_SECTION,可定义具有非缓存属性(Non-Cacheable)的数据缓冲区。
SRAM_NOCACHE_DATA_SECTION u8 noncache_buffer[DATA_BUFFER_SIZE];
备注
对于 KR4:非缓存属性仅能通过 MCCA 寄存器 配置,因此无法直接通过宏定义实现非缓存缓冲区。
对于 DSP:若需操作 DSP 缓存,请参考 Xtensa LX7 Microprocessor Data Book 和 Xtensa System Software Reference Manual 获取详细信息。
在缓冲区定义前添加宏 SRAM_NOCACHE_DATA_SECTION,可定义具有非缓存属性(Non-Cacheable)的数据缓冲区。
SRAM_NOCACHE_DATA_SECTION u8 noncache_buffer[DATA_BUFFER_SIZE];
备注
对于 CA32:非缓存属性通过 MMU 配置,配置的区间至少是 4KB 的整数倍。
在缓冲区定义前添加宏 SRAM_NOCACHE_DATA_SECTION,可定义具有非缓存属性(Non-Cacheable)的数据缓冲区。
SRAM_NOCACHE_DATA_SECTION u8 noncache_buffer[DATA_BUFFER_SIZE];
使用 DMA 时的缓存一致性
当使用 DMA 在内存缓冲区之间迁移数据时,缓冲区的起始地址和结束地址必须与缓存行对齐,以避免缓存数据与内存数据不一致。
例如:若某缓冲区的起始地址位于缓存行的中间位置,且前半部分已被其他程序占用,当其他程序对缓存行执行 Invalidate 或 Clean 操作时,该操作将影响整个缓存行,导致当前缓冲区的缓存数据与内存数据不一致。
备注
DMA 操作地址需独占一个完整的缓存行。可通过以下方式之一定义缓冲区:
malloc()
:该函数会返回一个起始地址为缓存行对齐,并且缓冲区长度也是缓存行对齐的地址。ALIGNMTO(CACHE_LINE_SIZE) u8 op_buffer[CACHE_LINE_ALIGMENT(op_buffer_size)]
:ALIGNMTO(CACHE_LINE_SIZE)
保证起始地址为缓存行对齐,CACHE_LINE_ALIGMENT(op_buffer_size)
保证长度也是缓存行对齐。
DMA Tx 流程
CPU 分配 Tx 缓冲区
CPU 写入 Tx 缓冲区
建议操作:调用
DCache_Clean()
清理数据缓存配置 DMA Tx 参数
DMA 发送中断处理
DMA Rx 流程
CPU 分配 Rx 缓冲区
执行
DCache_Clean()
确保 Rx 缓冲区处于 clean 状态(如果 Rx 缓冲区处于 clean 状态,可跳过此步骤)小心
此步骤执行的原因是:
对于 Cortex-A32,如果 Cache 中的 Rx 缓冲区处于 dirty 状态,执行第 5 步
DCache_Invalidate()
将同时执行 clean 和 invalidate 操作,可能会导致意外写入行为。如果 Cache 中的 Rx 缓冲区处于 dirty 状态,当 CPU 的 D-Cache 满了,CPU 可能会将 Rx 缓冲区中的脏数据写回内存,覆盖 DMA 已经写入的内容。
配置 DMA Rx 参数
DMA Rx 中断处理
执行
DCache_Invalidate()
确保 Cache 没有残留旧的 Rx 缓冲区数据。
小心
此步骤必须执行,原因如下:
对于具有自动数据预取功能的 CPU(如 Cortex-A32 和 DSP),当 CPU 读取 Rx 缓冲区相邻地址的内容时,CPU 会在后台执行行填充操作,自动将 Rx 缓冲区的旧值重新加载到 Cache 中。
防止 CPU 在 DMA 处理期间将旧值读入 Cache。
CPU 读取 Rx 缓冲区(DMA Rx 返回的值)