5.32 Cache设备
5.32 Cache设备
📚 本节导读
学习时长: 约 15 分钟
难度级别: ⭐⭐⭐☆☆
前置知识: C 语言基础、ARM Cortex-M 架构基础知识、5.13 DMA 设备
本节讲解 OneOS 的 Cache 缓存设备。缓存(Cache)是 CPU 与主存之间的高速小容量存储器,它能加速 CPU 访问,但也带来了一个著名的"缓存一致性(Cache Coherence)"问题——尤其在 DMA 场景下,CPU 缓存里的数据与内存里的真实数据可能不一致,导致读写错乱。
本节会带你:
- 理解"为什么会有缓存一致性问题",以及 DMA 场景下它为何会引发 bug;
- 掌握
os_clean_dcache/os_invalid_dcache/os_clean_invalid_dcache三个 API 各自解决什么问题、该在什么时候调用; - 逐个拆解三个 API 的完整源码与底层调用链;
- 通过可运行的示例掌握 DMA 发送/接收时的标准缓存处理顺序。
说明:本节所有源码均取自项目
drivers/cache/cache.h与arch/arm/armv7m/include/arch_cache.h,与项目源码逐字一致(含原始英文注释)。
🎯 学习目标
- 理解 D-Cache 的工作原理与缓存一致性问题
- 掌握三个缓存操作 API(
os_clean_dcache/os_invalid_dcache/os_clean_invalid_dcache)各自的语义 - 掌握 DMA 发送/接收场景下缓存操作的正确时机
- 理解
OS_HW_CACHE_FLUSH/OS_HW_CACHE_INVALIDATE操作码的含义 - 了解非 Cortex-M7 架构下缓存 API 为空操作的适配机制
一、概述
1.1 什么是 Cache 缓存一致性
现代 MCU(如带 D-Cache 的 Cortex-M7)的 CPU 与主存(SRAM/Flash)之间有一层 D-Cache。CPU 读写数据时会优先命中缓存:
CPU ◄────► D-Cache ◄────► 内存(SRAM/外部 Flash)
(高速) (慢速)关键理解 —— 缓存把"写"延迟了:
- CPU 写数据时,可能只把数据写进 D-Cache(标记为 Dirty / 脏),还没写回内存。内存里的旧值暂未更新。
- CPU 读数据时,如果 D-Cache 里有副本,就直接读缓存,不读内存(此时内存里可能是旧值)。
这本身是正常的加速机制,但一旦有其他可以绕过 CPU 缓存直接访问内存的硬件参与,就会出问题——DMA 正是这样的硬件。
1.2 DMA 场景下的缓存一致性问题(为什么要讲"为什么")
DMA 控制器直接读写内存,完全绕过了 CPU 的 D-Cache。这导致两种典型的脏数据 bug:
问题一:DMA 发送时,内存里可能还是旧数据
假设 CPU 往一块缓冲区里填好要发送的数据,然后启动 DMA 把它发出去:
CPU 写缓冲区(数据只进了 D-Cache,没进内存)
│
▼
启动 DMA 发送 ◄── DMA 直接从【内存】读数据
│
▼
DMA 读到的是【内存里的旧数据】← 发错数据!因为 CPU 的数据还"躺在" D-Cache 里没写回内存,DMA 从内存读到的可能是上一次的旧内容。
问题二:DMA 接收时,CPU 读到的是缓存里的旧值
DMA 把外设收到的数据写进内存后,CPU 去读缓冲区,却可能命中 D-Cache 里的旧副本:
DMA 接收数据写入【内存】
│
▼
CPU 读缓冲区 ──► 命中 D-Cache 旧副本 ← 读不到新数据!因为 D-Cache 里有这块缓冲区的旧副本,CPU 读的时候直接用了缓存里的旧值,看不到 DMA 刚写入内存的新数据。
一句话总结:缓存一致性问题的根源是"CPU 通过缓存读写、DMA 直接读写内存,两者看到的数据可能不一致"。
1.3 三种缓存操作各解决什么问题
OneOS 提供三个 API 分别对应三种操作:
| API | 底层操作 | 解决什么 | 典型调用时机 |
|---|---|---|---|
os_clean_dcache | 仅 Clean(清理):把 D-Cache 中的脏数据写回内存 | CPU 写的数据还没进内存 → 先 Clean 让 DMA 能读到 | DMA 发送前 |
os_invalid_dcache | 仅 Invalidate(无效化):丢弃 D-Cache 中的副本 | 内存里有 DMA 写入的新数据 → 丢弃缓存旧副本让 CPU 重读内存 | DMA 接收后 |
os_clean_invalid_dcache | Clean + Invalidate(清理并无效化) | 既要写回、又要丢弃副本 | 共享内存 / 双向通信时 |
1.4 Cache 设备的全局定义与适配机制
来源:drivers/cache/cache.h
#include <os_stddef.h>
#ifdef ARCH_ARM_CORTEX_M7
#include <arch_cache.h>关键理解 —— 为什么只有 ARCH_ARM_CORTEX_M7 才真正实现:
ARCH_ARM_CORTEX_M7是 Cortex-M7 架构的宏。只有 Cortex-M7 有 D-Cache(M0/M3/M4 通常没有 D-Cache)。- 所以代码用
#ifdef ARCH_ARM_CORTEX_M7判断:是 M7 → 包含arch_cache.h并调用底层真正的缓存操作;不是 M7 → 提供三个空函数体(什么也不做)。 - 这样的好处是:应用代码可以无条件调用
os_clean_dcache(...),不需要自己判断架构。在有 D-Cache 的芯片上它真正执行,在没有 D-Cache 的芯片上它是一个空操作(开销为零),应用代码完全可移植。
三种操作的底层操作码:
来源:arch/arm/armv7m/include/arch_cache.h
enum OS_HW_CACHE_OPS
{
OS_HW_CACHE_FLUSH = 0x01,
OS_HW_CACHE_INVALIDATE = 0x02,
};| 操作码 | 值 | 含义 |
|---|---|---|
OS_HW_CACHE_FLUSH | 0x01 | 清理(Flush/Clean):把脏数据写回内存 |
OS_HW_CACHE_INVALIDATE | 0x02 | 无效化(Invalidate):丢弃缓存副本,下次读重新从内存加载 |
后面会看到,
os_clean_invalid_dcache的底层调用OS_HW_CACHE_FLUSH | OS_HW_CACHE_INVALIDATE = 0x03,即两个操作码的位或组合(见 2.1 节)。
1.5 Cache 设备使用场景
1.5.1 一句话核心
Cache 缓存 API 适用于需要让 CPU 缓存与内存保持一致、特别是 DMA 参与的收发场景。它让你"在 DMA 发送前 Clean、在 DMA 接收后 Invalidate",避免 CPU 与 DMA 读到不一致的数据。
1.5.2 典型应用场景
场景一:DMA 发送前的 Clean(发送缓冲区)
主控通过 SPI/UART 的 DMA 发送一段数据。发送缓冲是 CPU 刚填好的,数据可能还在 D-Cache 里。发送前必须 os_clean_dcache(tx_buf, len),把数据写回内存,DMA 才能读到正确内容。
CPU 填好 tx_buf(数据在 D-Cache)
│
├─ os_clean_dcache(tx_buf, len) ← 把数据写回内存
│
▼
DMA 从内存读 tx_buf 并发送 → 数据正确为什么用 Cache API:没有 Clean,DMA 可能把"内存里的旧数据"发出去,这是最难排查的偶发 bug(数据偶尔不对、换优化等级才出现)。
场景二:DMA 接收后的 Invalidate(接收缓冲区)
DMA 把外设收到的数据写入接收缓冲,随后 CPU 去处理。接收后必须 os_invalid_dcache(rx_buf, len),丢弃 D-Cache 里的旧副本,让 CPU 从内存重新读,才能看到 DMA 刚写入的新数据。
DMA 接收数据写入内存 rx_buf
│
├─ os_invalid_dcache(rx_buf, len) ← 丢弃缓存旧副本
│
▼
CPU 读 rx_buf → 从内存读到最新数据 → 数据正确为什么用 Cache API:没有 Invalidate,CPU 可能命中 D-Cache 里的旧副本,读不到 DMA 刚写入的数据,表现为"接收到的数据是旧的/乱的"。
场景三:共享内存 / 双向通信的 Clean + Invalidate
当一块缓冲区既要被 CPU 写又要被 DMA 写(或 CPU 与 DMA 交替读写同一块内存)时,需要在适当节点同时 Clean + Invalidate,保证双向一致。
1.5.3 选型边界表(何时不该用它)
| 场景 | 该用什么 | 为什么 |
|---|---|---|
| 目标芯片没有 D-Cache(如 Cortex-M3/M4) | 无需调用(空操作) | 没有缓存就没有一致性问题,调用这些 API 是零开销的空操作 |
| 只需要清理一段缓冲区 | os_clean_dcache | 单独 Clean 即可,不需要 Invalidate |
| 只需要丢弃一段缓冲区的缓存 | os_invalid_dcache | 单独 Invalidate 即可 |
| 既写回又丢弃 | os_clean_invalid_dcache | 两个操作都要做时用它,一次调用完成 |
注意:只有数据缓冲(D-Cache)才需要这些操作;I-Cache(指令缓存)由系统启动时统一配置,应用层一般不需要手动操作。
二、核心 API 详解
说明:以下三个 API 的完整源码均取自
drivers/cache/cache.h,与项目源码逐字一致(含原始英文注释)。
2.1 os_clean_invalid_dcache —— 清理并无效化 D-Cache
函数签名:
OS_INLINE void os_clean_invalid_dcache(void *addr, int32_t dsize);| 参数 | 说明 |
|---|---|
addr | 要操作的缓冲区起始地址 |
dsize | 要操作的缓冲区大小(字节) |
无返回值(void)。
完整源码(drivers/cache/cache.h,与项目一致):
OS_INLINE void os_clean_invalid_dcache(void *addr, int32_t dsize)
{
os_cpu_dcache_ops(OS_HW_CACHE_FLUSH | OS_HW_CACHE_INVALIDATE, (void *)addr, (int32_t)dsize);
}分步详解:
第 1 步:调用底层缓存操作函数(无条件执行)
os_cpu_dcache_ops(OS_HW_CACHE_FLUSH | OS_HW_CACHE_INVALIDATE, (void *)addr, (int32_t)dsize);详细解释:
os_cpu_dcache_ops(ops, addr, size)是架构层的 D-Cache 操作函数(声明见arch_cache.h),它在汇编/内联汇编里执行真正的缓存操作(如 Cortex-M7 的SCB->DCCIMVAC指令)。- 第一个参数是操作码
OS_HW_CACHE_FLUSH | OS_HW_CACHE_INVALIDATE。
数值演算(操作码组合):
| 计算项 | 代入计算 | 结果 |
|---|---|---|
OS_HW_CACHE_FLUSH | 0x01 | 0000 0001 |
OS_HW_CACHE_INVALIDATE | 0x02 | 0000 0010 |
| 位或组合 | 0x01 | 0x02 | 0x03(0000 0011) |
0x03表示同时执行"清理(写回内存)+ 无效化(丢弃副本)"两种操作,所以本函数叫"clean invalid"。
条件走向总结:
os_clean_invalid_dcache() 条件走向总结:
├── (Cortex-M7)调用 os_cpu_dcache_ops(FLUSH|INVALIDATE, addr, size) → 真正执行
└── (非 M7) 函数体为空 → 什么都不做(由 #ifdef 选择不同实现)2.2 os_clean_dcache —— 清理 D-Cache
函数签名:
OS_INLINE void os_clean_dcache(void *addr, int32_t dsize);| 参数 | 说明 |
|---|---|
addr | 要操作的缓冲区起始地址 |
dsize | 要操作的缓冲区大小(字节) |
无返回值(void)。
完整源码(drivers/cache/cache.h,与项目一致):
OS_INLINE void os_clean_dcache(void *addr, int32_t dsize)
{
os_cpu_dcache_ops(OS_HW_CACHE_FLUSH, (void *)addr, (int32_t)dsize);
}分步详解:
第 1 步:调用底层清理操作(无条件执行)
os_cpu_dcache_ops(OS_HW_CACHE_FLUSH, (void *)addr, (int32_t)dsize);详细解释:
- 只传
OS_HW_CACHE_FLUSH(0x01),即仅清理:把addr起始的dsize字节中,标记为脏(Dirty)的缓存行写回内存。 - 为什么 DMA 发送前要用它:CPU 写缓冲区后,数据可能在 D-Cache 里还没写回内存。Clean 强制写回,DMA 从内存才能读到正确数据。
条件走向总结:
os_clean_dcache() 条件走向总结:
├── (Cortex-M7)调用 os_cpu_dcache_ops(FLUSH, addr, size) → 真正执行清理
└── (非 M7) 函数体为空 → 什么都不做2.3 os_invalid_dcache —— 无效化 D-Cache
函数签名:
OS_INLINE void os_invalid_dcache(void *addr, int32_t dsize);| 参数 | 说明 |
|---|---|
addr | 要操作的缓冲区起始地址 |
dsize | 要操作的缓冲区大小(字节) |
无返回值(void)。
完整源码(drivers/cache/cache.h,与项目一致):
OS_INLINE void os_invalid_dcache(void *addr, int32_t dsize)
{
os_cpu_dcache_ops(OS_HW_CACHE_INVALIDATE, (void *)addr, (int32_t)dsize);
}分步详解:
第 1 步:调用底层无效化操作(无条件执行)
os_cpu_dcache_ops(OS_HW_CACHE_INVALIDATE, (void *)addr, (int32_t)dsize);详细解释:
- 只传
OS_HW_CACHE_INVALIDATE(0x02),即仅无效化:把addr起始的dsize字节对应的缓存副本标记为无效,下次 CPU 读取时会重新从内存加载。 - 为什么 DMA 接收后要用它:DMA 把新数据写进内存后,D-Cache 里可能还留着这块缓冲区的旧副本。Invalidate 丢弃旧副本,CPU 再读就能从内存拿到 DMA 写入的新数据。
条件走向总结:
os_invalid_dcache() 条件走向总结:
├── (Cortex-M7)调用 os_cpu_dcache_ops(INVALIDATE, addr, size) → 真正执行无效化
└── (非 M7) 函数体为空 → 什么都不做2.4 三个 API 的对比与底层调用链
| API | 操作码 | 作用 | 典型时机 |
|---|---|---|---|
os_clean_dcache | OS_HW_CACHE_FLUSH(0x01) | 脏数据写回内存 | DMA 发送前 |
os_invalid_dcache | OS_HW_CACHE_INVALIDATE(0x02) | 丢弃缓存旧副本 | DMA 接收后 |
os_clean_invalid_dcache | FLUSH | INVALIDATE(0x03) | 既写回又丢弃 | 共享/双向内存 |
完整调用链(以 os_clean_dcache 为例):
应用调用 os_clean_dcache(tx_buf, len)
│
▼
cache.h: os_clean_dcache → os_cpu_dcache_ops(OS_HW_CACHE_FLUSH, tx_buf, len)
│
▼
arch_cache.h 声明 os_cpu_dcache_ops(架构层)
│
▼
arch_cache.c(汇编/内联汇编,如 SCB->DCCMVAC 写回指令)
│
▼
真正把 D-Cache 脏数据写回内存关键理解:
cache.h的三个 API 只是薄封装,真正操作缓存的指令在架构层os_cpu_dcache_ops里。cache.h通过#ifdef ARCH_ARM_CORTEX_M7在编译期决定是"调用架构层"还是"空实现",所以应用层代码无需条件编译即可跨平台使用。
底层接口声明(arch/arm/armv7m/include/arch_cache.h,与项目逐字一致):
extern void os_cpu_icache_enable(void);
extern void os_cpu_icache_disable(void);
extern os_base_t os_cpu_icache_status(void);
extern void os_cpu_icache_ops(int32_t ops, void *addr, int32_t size);
extern void os_cpu_dcache_enable(void);
extern void os_cpu_dcache_disable(void);
extern os_base_t os_cpu_dcache_status(void);
extern void os_cpu_dcache_ops(int32_t ops, void *addr, int32_t size);接口详解(源码无注释,下表补充说明,不属于源码):
| 接口 | 含义 |
|---|---|
os_cpu_dcache_ops(ops, addr, size) | D-Cache 操作:按 ops(FLUSH/INVALIDATE/组合)对 addr 起 size 字节操作,架构层汇编实现(Cortex-M7 用 SCB->DCCMVAC/DCCIMVAC/DCIMVAC 指令) |
os_cpu_dcache_enable/disable/status | 使能/禁用/查询 D-Cache(启动阶段调用,应用一般不用) |
os_cpu_icache_* | I-Cache(指令缓存)操作,应用层一般不用(系统启动统一配置) |
关键理解 —— 架构层负责真正执行:
os_cpu_dcache_ops的实现在arch/arm/armv7m/arch_cache.c(或内联汇编),用 ARM 缓存操作指令按缓存行逐行处理addr~addr+size。不同架构(RISC-V 等)需提供自己的os_cpu_dcache_ops实现(见第四章适配)。
三、完整使用示例
示例 1:DMA 发送前清理、接收后无效化(标准流程)
#include <cache.h>
#include <os_stddef.h>
/* 收发缓冲区(DMA 直接访问内存,需保证缓存一致) */
#define BUF_LEN 128
static uint8_t tx_buf[BUF_LEN];
static uint8_t rx_buf[BUF_LEN];
/* 模拟启动 DMA 发送的函数(实际驱动中由具体外设提供) */
extern void start_dma_tx(uint8_t *buf, int len);
/* 模拟等待 DMA 接收完成的函数 */
extern void wait_dma_rx_done(void);
void dma_transfer_demo(void)
{
int i;
/* 1. 填充发送数据(数据会进入 D-Cache,尚未写回内存) */
for (i = 0; i < BUF_LEN; i++)
{
tx_buf[i] = (uint8_t)i;
}
/* 2. 【关键】DMA 发送前必须 Clean:把 D-Cache 脏数据写回内存 */
os_clean_dcache(tx_buf, BUF_LEN);
/* 3. 启动 DMA 发送,DMA 直接从内存读,读到正确数据 */
start_dma_tx(tx_buf, BUF_LEN);
/* 4. 等待 DMA 接收完成(DMA 把数据写进内存 rx_buf) */
wait_dma_rx_done();
/* 5. 【关键】DMA 接收后必须 Invalidate:丢弃 D-Cache 旧副本,
* 让 CPU 从内存重新读取 DMA 刚写入的新数据 */
os_invalid_dcache(rx_buf, BUF_LEN);
/* 6. 现在 CPU 读 rx_buf 能拿到 DMA 写入的最新数据 */
for (i = 0; i < BUF_LEN; i++)
{
/* 处理接收数据 ... */
}
}说明:示例展示了 DMA 收发缓存处理的标准顺序——发送前 Clean、接收后 Invalidate。实际效果:没有这两步,DMA 可能发旧数据、CPU 可能读旧数据;加了这两步,缓存与内存保持一致,数据正确。
示例 2:共享内存的双向同步(Clean + Invalidate)
#include <cache.h>
/* 与另一主设备(如协处理器/FPGA)共享的缓冲区 */
static uint8_t shared_buf[64];
void shared_mem_sync_demo(void)
{
/* 1. 把 CPU 写入共享缓冲区的内容先写回内存,另一主设备才能读到 */
os_clean_invalid_dcache(shared_buf, sizeof(shared_buf));
/* 2. 另一主设备写入后,丢弃本 CPU 的旧副本,重新读内存里的最新值 */
os_clean_invalid_dcache(shared_buf, sizeof(shared_buf));
/* 3. 现在读 shared_buf 是双向一致的最新数据 */
}说明:共享内存场景通常同时需要"把 CPU 的脏数据写回"和"丢弃旧副本重读",所以用
os_clean_invalid_dcache一次完成。实际效果:保证 CPU 与另一主设备读写同一块内存时不互相看到旧值。
四、Cache 的适配(为带 D-Cache 的新架构实现底层操作)
4.1 先认识适配层:Cache 框架怎么"长"到新架构上
drivers/cache/cache.h 是跨平台统一封装(三个 OS_INLINE API),通过 #ifdef ARCH_ARM_CORTEX_M7 在编译期选择实现。真正操作缓存的是架构层 os_cpu_dcache_ops 等接口(Cortex-M7 实现在 arch/arm/armv7m/arch_cache.c,用 SCB->DCCMVAC 等指令)。接入带 D-Cache 的新架构(如某 RISC-V),就是实现 arch_cache.h 声明的一组接口。
适配全景图:
┌──────────────────────────────────────────────────────────┐
│ 应用层(os_clean/invalid_dcache) │
└──────────────────────────────┬───────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ 通用封装层(drivers/cache/cache.h,通用不改) │
│ #ifdef ARCH_ARM_CORTEX_M7:调用架构层 / 空实现 │
└───────────────┬──────────────────────────┬────────────────┘
│ ①实现 os_cpu_dcache_ops │ ②宏开关
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ 架构层(arch/xxx/arch_cache.c)★需实现★ │
│ os_cpu_dcache_ops(ops, addr, size) 按缓存行操作 │
│ enable/disable/status(启动阶段调用) │
└───────────────┬──────────────────────────┬────────────────┘
│ 缓存操作指令(汇编) │
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ 硬件(带 D-Cache 的 CPU 核) │
└──────────────────────────────────────────────────────────┘适配的关键洞察:接入新架构做两件事:① 实现 arch_cache.h 声明的接口(核心是 os_cpu_dcache_ops,用该架构的缓存操作指令/CSR);② 在 cache.h(或架构头)使能对应宏,让应用能调到架构实现。cache.h 的应用层 API 不用改。
4.2 接口契约:新架构需要实现什么
| 接口 | 职责 | 说明 |
|---|---|---|
os_cpu_dcache_ops(ops, addr, size) | 按 ops 对 addr+size 做 FLUSH/INVALIDATE | 核心,用缓存行逐行操作 |
os_cpu_dcache_enable/disable/status | 使能/禁用/查询 D-Cache | 启动阶段调用 |
os_cpu_icache_* | I-Cache 操作 | 启动统一配置,应用一般不用 |
ARCH_ARM_CORTEX_M7(或对应宏) | 让 cache.h 走架构实现 | 编译期开关 |
4.3 适配新架构的标准流程
第 1 步:确认架构是否有 D-Cache——无 D-Cache 则无需适配(应用层空操作即可);有则查手册确认缓存操作指令/CSR。
第 2 步:实现 os_cpu_dcache_ops——在 arch/xxx/arch_cache.c 实现,解析 ops(FLUSH/INVALIDATE),用该架构的缓存操作指令按缓存行处理 addr~addr+size(注意按缓存行对齐、处理首尾不满行)。
第 3 步:实现 enable/disable/status——D-Cache 使能/禁用/查询(启动早期调用)。
第 4 步:接入宏开关——让 cache.h 的 #ifdef 能命中架构实现(定义对应宏或调整 #ifdef 条件)。
第 5 步:测试——用 DMA 收发示例验证缓存一致性(发送前 Clean、接收后 Invalidate),确认数据正确。
4.4 适配检查清单(快速自查)
| 检查项 | 说明 |
|---|---|
| 按缓存行对齐处理? | 未按行对齐(如 32 字节)会漏掉部分缓存行 |
ops 解析正确? | FLUSH/INVALIDATE/组合(0x01/0x02/0x03)要分别处理 |
| 首尾不满行处理? | 边界地址不满整行也要操作,否则边界数据不一致 |
| 使能/禁用配对? | 启动 enable 后不应随意 disable,否则缓存失效 |
| 宏开关能命中? | #ifdef ARCH_ARM_CORTEX_M7 条件不符则走空实现 |
📝 本节小结
核心要点回顾
本节围绕 Cache 缓存设备展开,核心要点如下:
1. 缓存一致性问题
- CPU 通过 D-Cache 读写,DMA 直接读写内存,两者看到的数据可能不一致。
- DMA 发送时数据可能还在缓存里(内存旧)→ 发旧数据;DMA 接收后 CPU 可能命中缓存旧副本 → 读旧数据。
2. 三种缓存操作
| 操作 | 语义 | 时机 |
|---|---|---|
Clean(os_clean_dcache) | 脏数据写回内存 | DMA 发送前 |
Invalidate(os_invalid_dcache) | 丢弃缓存旧副本 | DMA 接收后 |
Clean+Invalidate(os_clean_invalid_dcache) | 两者都做 | 共享/双向内存 |
3. 适配机制
- 三个 API 都是
OS_INLINE内联函数,薄封装到架构层os_cpu_dcache_ops。 #ifdef ARCH_ARM_CORTEX_M7:M7 才真正执行;非 M7 为空操作,应用代码无需条件编译。
4. 操作码
OS_HW_CACHE_FLUSH(0x01)、OS_HW_CACHE_INVALIDATE(0x02),Clean+Invalidate 是0x03。
5. 硬件适配
- 接入带 D-Cache 的新架构 = 实现
os_cpu_dcache_ops(按缓存行 FLUSH/INVALIDATE)+ enable/disable/status + 宏开关,cache.h应用层 API 不用改。 - 无 D-Cache 架构无需适配(应用层空操作)。
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
| DMA 发送前忘了 Clean | 发送前必须 os_clean_dcache(tx_buf, len),否则 DMA 可能读到内存里的旧数据 |
| DMA 接收后忘了 Invalidate | 接收后必须 os_invalid_dcache(rx_buf, len),否则 CPU 可能命中缓存旧副本 |
| 混淆 Clean 与 Invalidate | Clean 是把缓存写回内存(发送用);Invalidate 是丢弃缓存重读内存(接收用),方向相反 |
| 对没有 D-Cache 的芯片强行担心 | 非 M7 芯片这三个 API 是空操作,无需特殊处理,直接调用即可 |
| 忽略地址对齐要求 | 底层缓存操作通常按缓存行(如 32 字节)对齐,缓冲区应尽量按行对齐以获得最优效果 |
| 只对部分区域操作 | 三个 API 都按 addr + dsize 精确操作,参数必须覆盖完整目标缓冲区 |
| 底层未按缓存行对齐 | os_cpu_dcache_ops 按缓存行(如 32B)处理,未对齐会漏掉部分行 |
新架构未实现 os_cpu_dcache_ops | 带 D-Cache 架构不实现则缓存操作无效,DMA 一致性 bug 仍存在 |
下一步
掌握了 Cache 缓存设备后,接下来学习 Boot 启动设备:
- 5.33 Boot设备:了解 OneOS 启动向量表的配置、中断重映射与芯片启动流程。