news 2026/10/19 2:10:49

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flash 缓存没刷新读旧值——写了缓存未回写,写后 invalidate/回写

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

文章标签:#嵌入式开发 #单片机 #Flash #OTA升级 #STM32 #扇区 #擦写 #缓存 #回写

一、开篇:那个"写进去了却读不出"的缓存

做 STM32F7/H7(带 DCache/ICache/ART 预取)的设备,往 Flash 写完参数后立刻读回校验,结果读到的还是修改前的旧值。排查半天才发现:CPU 读 Flash 走的是数据缓存(DCache),我写的是 Flash 物理介质,但缓存里那份旧的没失效——读命中缓存旧副本,当然"看不见"新写的内容。复现率 100%(带缓存系列),是"写后读"经典坑。

如果你在带缓存的 MCU 上"写 Flash 后马上读",这一讲能帮你搞清楚缓存一致性。我会从缓存原理讲起,给你 invalidate/回写代码,并附实测对比。

全文导航:入门认知(为什么有缓存会读到旧值)→ 进阶原理(写直达 vs 写回 + 缓存行)→ 高级应用(错误写法复现 + 失效代码 + 实测对比)。

二、入门认知:CPU 看的是"缓存",不是 Flash 本体

带缓存的 MCU(F7/H7 有 L1 DCache/ICache,F4 有 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 共享也有类似一致性问题。

三、进阶原理:为什么"写完要 invalidate"

Flash 编程属于"非缓存参与的写"(程序直接操作 Flash 控制器寄存器),缓存对此无感知。两种修复:

  1. Invalidate(失效):丢弃缓存里这段旧副本,下次读强制回 Flash 取新值——用于"我刚写了,缓存旧了"。
  2. Clean/Write-back(回写):把缓存里的新值刷回物理介质——用于"CPU 改了缓存,要落盘"。

这里场景是"Flash 物理已新、缓存旧",所以用Invalidate。

易混淆点对比:

操作用途本场景
Invalidate(失效)缓存旧,物理新✓ 写 Flash 后读前
Clean(回写)缓存新,物理旧✗ 不适用
Clean+Invalidate都处理通用但多余

[坑] 高频翻车:Invalidate 的地址必须按缓存行对齐(F7/H7 缓存行 32 字节),且长度要覆盖整行。若只失效 4 字节但落在某 32 字节行里,需把整行失效,否则相邻数据也被误失效(虽无害但需对齐计算)。用SCB_InvalidateDCache_by_Addr会自动按行处理。

四、高级应用:复现与根治

错误写法演示(F7/H7)

voidsave_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 缓存)

#include"stm32f7xx_hal.h"// 或 stm32h7xx_hal.h#include<string.h>voidsave_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 上被"缓存读了旧值"坑过吗?排查了多久?评论区见。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 2:10:10

联调时我当了太久人肉快递员,最后让两个 agent 自己聊上了

开发最耗人的地方&#xff0c;我一直觉得不是写代码。 前几天有个活&#xff1a;一部分功能跑在无影云上&#xff0c;手机插在本地这台 PC 上。做起来是这样——云上跑一遍&#xff0c;看日志&#xff0c;复制结论&#xff1b;切到本地&#xff0c;接上手机&#xff0c;再跑一遍…

作者头像 李华
网站建设 2026/10/19 2:10:07

处理超时未支付订单:两套轻量化方案,附 PHP 极简实现

电商交易类业务&#xff0c;「待支付订单超时自动取消」是个绕不开的需求。用户占着库存、优惠券、活动名额迟迟不付款&#xff0c;不及时释放就是实打实的资损。很多技术人第一反应就是上延迟队列、分布式事务、各种中间件&#xff0c;好像不用点 “高大上” 的技术就不算做架…

作者头像 李华
网站建设 2026/10/19 2:09:29

Qwen3.8-27B 对上 Nemotron 3.5 Bolt、Gemma 4:一张 24GB 显卡该装谁

封面&#xff1a;大数字海报 纸白&#xff0c;数字为 Hugging Face 显示的 Qwen3.8-27B 上月下载量&#xff0c;条形为三家官方公布的 GPQA Diamond 分数&#xff0c;由本号封面脚本生成。 Hugging Face 上赞数最多的千问&#xff0c;不是几百 B 的大模型&#xff0c;而是一个…

作者头像 李华
网站建设 2026/10/19 2:08:28

DMA读旧数据?三招解决Cache一致性问题的核心原理与实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/19 2:07:53

告别ChatGPT赚快钱时代,真正聪明人早已换上AI高阶玩法!

本文指出&#xff0c;虽然AI技术仍在发展&#xff0c;但最初的“赚快钱”模式&#xff08;如写Prompt、AI绘画&#xff09;已逐渐过时。真正有远见的人已转向利用AI创造实际价值&#xff0c;如为企业提供自动化解决方案、开发系统等。文章强调&#xff0c;AI正成为基础工具&…

作者头像 李华
网站建设 2026/10/19 2:06:54

三篇自荐信揭秘程序员求职成功秘诀

我对于您在非常繁忙的工作间隙, 愿意抽出时间来仔细阅读我的这封自我推荐信件, 感到非常感激。与公司建立联系是一种双向选择的性质, 而对我个人而言, 这则是一次非常难得的发展机遇。在那数以千计的、内容看起来都差不多的求职材料里面, 我非常希望您手中的这一份能够让您感到…

作者头像 李华