NvM 升级后读出历史残留的数据?可能是你对"抗变"的理解错了

软件升级之后,有个 NvM Block 读出来的数据不是改版前存好的旧值:有时是默认值,有时竟是一串早就以为没有的旧数据。你八成也想过:我都勾了抗变,不该读出改版前存好的旧值吗?

讲清楚这件事之前,先说三个词:抗变,就是NvM Layout配置变了,NvM 还照旧去读 NV 里的数据,对应NvM的参数 NvMResistantToChangedSw;NV, 指 EEP存储的数据,掉电不丢;默认值,是编译进程序里预先写好的那份数据(ROM 里的值,不能修改也不丢失)。

再借个比喻,帮你把这些名词对上号:NvM Block 是相框,数据是照片,长度是相框尺寸。RAM 是桌上的相框(掉电就丢失),EEP 是档案柜里存的照片(只要不擦除EEP,就一直存着数据),默认值是相框自带的底图(天生自带,不会丢失,也不能被修改)。NvM 是管理员,配置 ID (Compiled Configuration ID)是整个档案柜共用的一张版本贴纸。你换了新尺寸的相框,没人替你把旧照片裁好塞进去。读出来的,要么是底图,要么是档案柜里一张尺寸对得上的旧照(这里事先假设永远不会存在能读到半截照片的情况,要么读出来一张尺寸刚好的旧照,要么读出来底图,没有第三种情况)。

图片

图1:相框、档案柜、底图和管理员的对应关系

比喻和真实术语的对照,先记在这儿,后面忘记了回到这里看:

  • 相框 = NvM Block;相框尺寸 = 块长度

  • 桌上的相框 = RAM 块;档案柜里的照片 = EEP(NV)里存的数据

  • 底图 = 默认值(编译进程序、放在 ROM 里的那份)

  • 管理员 = NvM 模块,上电读块的动作是 NvM_ReadAll

  • 版本贴纸 = 配置 ID(Compiled Configuration ID)

  • 勾了抗变 = 块的 NvMResistantToChangedSw 为 True

  • 三道门 = 三个判断:配置 ID、NvMDynamicConfiguration、NvMResistantToChangedSw

后文为了不绕,关键位置我会把比喻和真实术语放在一起写。

一、升级后读 NV 之前,NvM 要过三道门

配置工具里那几个开关,谁管谁,很容易混。本文只追一件事:勾了抗变之后,读出来的旧值到底是哪一版的。

先看 NvM_ReadAll(管理员)的判断顺序,见图2。

图片

图2:NvM_ReadAll 升级后读 NV 的三道门(判定流程)

  • 第一道门:配置 ID(柜子的版本贴纸)对得上吗?对上,照常把 EEP 里的值读进 RAM;对不上,进第二道门。

  • 第二道门:NvMDynamicConfiguration(动态配置)开着吗?False,不读 NV,用默认值(底图);True,进第三道门。

  • 第三道门:这个块的 NvMResistantToChangedSw(抗变)勾了吗?没勾(False),忽略 NV,用默认值;勾了(True),把 NV 里的数据装进 RAM。

另外,配置 ID 不自增,NvM 就认为配置没变,不会走后两道门。

二、真相:长度变了,抗变取不回改版前的值

勾了抗变,NvM 就无视“配置变了”这件事,像没变过一样照常去 EEP(档案柜里)翻。

翻出来的,不是改版前写进去的旧值。实验是这样的:块 A 长度由小变大,配置 ID 变了,勾了抗变,EEP 从没擦过。EEP 里要是有一份“同样长度、写过值”的历史数据(柜里的旧照),读到的就是它;没有,就读到默认值(底图)。大变小同理。这就是“脏数据”的来路。

NvM 为什么偏偏认长度对得上的那份?我的推断是存储按长度匹配,这只是推断,没验证过机制(当然,如果NvM底层用的Fee来自不同的供应商,那么实现机制可能会有差异,建议仿造我的方法测试)。

图3 把这个现象画成时间线,数据用 X1、X2、X3代替:

图片

图3:三个版本的时间线,升级后读到第 1 版的值

具体是这样:第 1 版长度 2,写入 X1X1;第 2 版长度 1,写入 X2;当前第 3 版长度又回到 2,默认值是 X3X3。升级后读到的是 X1X1,既不是 X2,也不是默认值 X3X3。

读到 X1X1的那一刻,你大概会怀疑是不是自己改错了配置。未必,多半是 EEP 里真有这份旧数据。

前提都是块 A 长度已变、EEP 没擦,三种情形各读到什么:

情形 读到的值
小→大,勾抗变,配置 I D 变 EEP 里有同长度的旧数据→读到那份历史值(如 X₁X₁);没有→默认值(如 X₃X₃)
大→小,勾抗变,配置 I D 变 同上
不勾抗变 默认值(如 X₃X₃)

抗变不是备份

,它只管“照常去 EEP 里读”,不管读出来的是哪一版的数据。

三、相邻块没事,RAM 和 NV 对不上才出事

相邻块。三个块按 BlockId 排:块 B(2)、块 A(3)、块 C(4)。块 A 不管变长还是变短,B 和 C 照样读到 EEP 里原来的值(设为 Xb、Xc)。我理解这里说的 Layout 指块在存储里的排布,它会受影响,但不影响读到旧值。就像相邻的相框各挂各的,你把中间那个换大了,别的还挂在原处。

图片

图4:块 A 变长,块 B、块 C 仍读到原来的值

边界先说清:本实验配置 ID自增(因为块A的长度变了,NvM Layout自然就变),块B和块C的抗变属性为True,块A的抗变属性为False(当然,本实验中实验对象是块B和块C,块A的抗变属性不重要)。

RAM 对不上 NV。这个坑更阴。RAM 块(桌上的相框)的长度 N,和 EEP 里那份(档案柜里的照片)的长度 M 对不上,在升级过程中一直没擦 EEP 的情况下,会被判定为异常。

要求是两边一样长,并打开 Block Size Check(块长度检查)选项。之所以建议打开Block Size Check,是因为打开后会在代码编译阶段报错,提示你当前NvM Block的配置出错了,而不是等软件装车了再给你暴雷。

图片

图5:RAM 长度 N、NV 长度 M,两边对不上

四、正解:升级前 5 条自查

长度变了的 NvM Block,别想当然勾抗变。不勾抗变→读默认值;勾抗变→取决于 EEP 里有没有同长度的旧数据。想让结果可控,要么不勾抗变,要么升级前擦 EEP。

NvM Layout 升级前,下面 5 条全查一遍。第 3、4 条直接决定会不会读到脏数据,优先。

  1. 配置 ID。

    在配置工具里搜 Compiled Configuration ID。长度变了而 ID 没自增,容易栽。配置变更就自增。按流程图推演,自增后不勾抗变的块都会回到默认值,连长度没变的也一样。

  2. NvMDynamicConfiguration。

    看取值,再去你用的 NvM 源码里看 NvM_ReadAll 怎么处理它。取 False 且配置 ID 已变时,流程图所示是读默认值,方向以你的源码为准。看不懂分支,就做一次升级前后对比。

  3. 抗变(优先)。

    逐个看块的 NvMResistantToChangedSw。长度变了还勾着,NvM 可能从 EEP 里读出同长度的旧数据。你可以不勾,或者先确认 EEP 里没有同长度的历史数据。

  4. EEP 擦没擦(优先)。

    以产线/刷写流程为准,别只靠读 EEP 判断。流程不擦、历史上又有过同长度版本,就是隐患。

要擦,先想清代价:擦等于清空整个 EEP,所有 NvM Block 回到默认值,可能包括 VIN、里程等,具体看你的配置。时机上,应赶在第一次 NvM_ReadAll 之前。要保留旧数据,常见思路是应用层加版本号做迁移,本文不展开。

  1. RAM 与 NV 长度。

    对比两个长度数字,不一样就改一致,有长度检查选项就打开。

这 5 项过一遍,下次升级能少赌一些运气。最后,如果你的项目分支、基线、车型特别复杂、特别多,为了避免因为跨基线升级导致NvM数据丢失,给你一个保命建议:无论多少基线、分支软件版本,全局只设计一张NvM Layout。(这样如论怎么升级,NvM Layout始终是兼容的,不会丢数据)

勾了抗变,不等于旧值受保护。

升级前先问一句:EEP 里有没有同长度的旧数据(柜里有没有同尺寸的旧照)?

你在项目里遇到过哪些跟NvM升级丢数据相关的坑?评论区聊聊。

本文由 [公众号:自学汽车软件] 原创首发,欢迎分享与转发。媒体或公众号如需转载,请在文末或醒目位置注明文章出处及作者。

posted on 2026-10-23 11:16  编号0927  阅读(4)  评论(0)    收藏  举报