Flash 缓存没刷新读旧值——写了缓存未回写,写后 invalidate/回写

📅 发布时间:2026/10/19 2:10:51
Flash 缓存没刷新读旧值——写了缓存未回写,写后 invalidate/回写
Flash 缓存没刷新读旧值——写了缓存未回写写后 invalidate/回写文章标签#嵌入式开发 #单片机 #Flash #OTA升级 #STM32 #扇区 #擦写 #缓存 #回写一、开篇那个写进去了却读不出的缓存做 STM32F7/H7带 DCache/ICache/ART 预取的设备往 Flash 写完参数后立刻读回校验结果读到的还是修改前的旧值。排查半天才发现CPU 读 Flash 走的是数据缓存DCache我写的是 Flash 物理介质但缓存里那份旧的没失效——读命中缓存旧副本当然看不见新写的内容。复现率 100%带缓存系列是写后读经典坑。如果你在带缓存的 MCU 上写 Flash 后马上读,这一讲能帮你搞清楚缓存一致性。我会从缓存原理讲起给你 invalidate/回写代码并附实测对比。全文导航入门认知为什么有缓存会读到旧值→ 进阶原理写直达 vs 写回 缓存行→ 高级应用错误写法复现 失效代码 实测对比。二、入门认知CPU 看的是缓存不是 Flash 本体带缓存的 MCUF7/H7 有 L1 DCache/ICacheF4 有 ART 指令预取加速器CPU 访问 Flash 时先查缓存。缓存里存着Flash 某段的副本。当你绕过缓存直接写 Flash 物理地址Flash 编程控制器就是这么干的缓存不知道你改了里面还是旧副本。下次 CPU 读这段命中缓存旧值——“写了像没写”。类比图书馆有书架Flash和阅览桌上的复印件缓存。你趁闭馆把书架上的书换了写 Flash但读者桌上的复印件没换缓存读者看复印件还以为书没变。如图 1 展示写 Flash 后缓存未失效导致的读旧值是否Flash 物理介质:已写新值DCache:仍存旧副本CPU 读 addr命中缓存?返回旧值(错!)回 Flash 取新值(对)图 1写 Flash 后 DCache 未失效读命中旧缓存适用边界仅带缓存/预取的系列F4 ART、F7/H7 D/I Cache需处理F1/F0 无缓存无此问题。SRAM 的 DMA 与 CPU 共享也有类似一致性问题。三、进阶原理为什么写完要 invalidateFlash 编程属于非缓存参与的写程序直接操作 Flash 控制器寄存器缓存对此无感知。两种修复Invalidate失效丢弃缓存里这段旧副本下次读强制回 Flash 取新值——用于我刚写了缓存旧了。Clean/Write-back回写把缓存里的新值刷回物理介质——用于CPU 改了缓存要落盘。这里场景是Flash 物理已新、缓存旧所以用Invalidate。易混淆点对比操作用途本场景Invalidate(失效)缓存旧,物理新✓ 写 Flash 后读前Clean(回写)缓存新,物理旧✗ 不适用CleanInvalidate都处理通用但多余[坑] 高频翻车Invalidate 的地址必须按缓存行对齐F7/H7 缓存行 32 字节且长度要覆盖整行。若只失效 4 字节但落在某 32 字节行里需把整行失效否则相邻数据也被误失效虽无害但需对齐计算。用SCB_InvalidateDCache_by_Addr会自动按行处理。四、高级应用复现与根治错误写法演示F7/H7voidsave_bad(uint32_taddr,uint32_t*data,intn){flash_erase_and_program(addr,data,n);uint32_trd;flash_read(addr,rd,4);if(rd!data[0]){/* 必触发!读到的是 DCache 旧副本 */}}正确完整代码写后 Invalidate 缓存#includestm32f7xx_hal.h// 或 stm32h7xx_hal.h#includestring.hvoidsave_good(uint32_taddr,uint32_t*data,intn){/* 1. 写 Flash 前,若 data 在可缓存 RAM,先清缓存保证源是对的(本例 data 已就绪可省) */flash_erase_and_program(addr,data,n);/* 2. 写后让 DCache 失效,迫使 CPU 回 Flash 取新值 */SCB_InvalidateDCache_by_Addr((void*)addr,n*4);/* 3. 现在读到的就是物理新值 */uint32_trd;flash_read(addr,rd,4);// rd data[0] ✓}/* 若启用 D-Cache 且 Flash 运行代码也会被改(OTA 写 App 区),还要失效 I-Cache */voidafter_ota_write(uint32_taddr,intlen){SCB_InvalidateDCache_by_Addr((void*)addr,len);SCB_InvalidateICache();// App 区代码变了,指令缓存也要清}F4 系列用 ART 预取加速器写 Flash 后一般无需手动失效ART 不缓存数据但若开了数据缓存相关特性按手册处理。F1/F0 无缓存跳过本讲。改前 vs 改后实测对比指标写后不失效写后 Invalidate写后读回值旧值(错)新值(对)校验通过率0%100%受影响系列F4/F7/H7同上,修复后正常[提示] 读 Flash 频繁且数据不变时保留缓存能加速只有刚写过这块才需失效。切忌每次读都全量SCB_InvalidateDCache()清全部缓存性能差用by_Addr精准失效。五、收尾要点复盘与最佳实践核心要点① 带缓存 MCU写 Flash 后缓存旧副本导致读旧值② 写后必须SCB_InvalidateDCache_by_Addr③ 改 App 代码区还要失效 I-Cache④ 失效按行对齐。最佳实践清单可收藏F7/H7 写 Flash 后读前必SCB_InvalidateDCache_by_Addr(地址, 长度)。OTA 写 App 区后额外SCB_InvalidateICache()。用by_Addr精准失效别每次全量清缓存。确认芯片是否带缓存F1/F0 无无需处理。DMA 与 CPU 共享 SRAM 同样有缓存一致性按需 Clean/Invalidate。进阶延伸若把 Flash 映射到可缓存属性MPU 配置务必设成写透(WT)或不可缓存避免一致性麻烦RT 系列/带 Cache 的 M7 务必仔细配 MPU。互动你在 F7/H7 上被缓存读了旧值坑过吗排查了多久评论区见。