问题描述
车辆的行驶信息记录不下来,重新上电后行驶里程等相关信息被清零,低概率出现,低于千分之一。
问题复现
只要连续操作就会复现没有写入内存的问题,复现概率百分之一百。NVM模块配置了48个block,都是配置为标准优先级,里面有些block是Native Block,有些是Redundant Block,但无论连续写哪个block都会出现,所以排除不同block之间的区别。
功能设计
代码设计是使用NvM_WriteBlock接口把存储信息存储到FLASH当中。
分析问题
这个功能是内存管理功能,涉及应用层的逻辑,BSW层的NVM、MEMIF、FEE,MCAL层的FLS模块。
从上层开始排查,只要显示出行驶里程为非0就代表行驶里程的信息传递到了底层,根据客户反馈的现象排除了应用层的原因。
BSW层的NVM是最可能导致原因的模块,MEMIF和FEE配置过于简单,并不会导致问题产生,所以接下来重点排查NVM模块。
分析问题点1:Block类型
三种类型的block:Native Block、Redundant Block、 Dataset Block,三种都试过了,问题没有改善,跟复现出来的现象是对应的。
分析问题点2:JOB优先级
只有两个优先级:标准优先级和立即优先级。尝试改成立即优先级,也没有改善。
分析问题点3:取消校验和改变CRC类型
由于都是配置为有校验的,所以当数据跟之前一样的话就不能写入,看到网上也是建议取消这个功能,但isolar工具对代码没有改变,所以没有改善。
CRC类型有8/16/32位校验,均尝试过没有改善。
分析问题点4:同步机制
有显式同步和隐式同步,怀疑是使用显式同步的时候因为多了一步,数据同步卡在RAM block中了,导致在同步过程当中没有同步到最后的内存当中。
分析问题点5:通过测试代码触发
仅进行读写
编写测试代码,在10ms周期函数里面分开两步,并且循环执行
1.第1个10ms周期里面,调用NvM_WriteBlock接口,写入一个跟之前内容不同的数组。
2.第2个10ms周期里面,调用NvM_ReadBlock接口,并且对比读取出来和写进去的内容是否一致。
if(第1个10ms周期)
{
创建一个内容跟之前内容都不一样的写入数组;
NvM_WriteBlock(NvM_Block名称,&写入数组);
}
else if(第2个10ms周期)
{
NvM_ReadBlock(NvM_Block名称,&读取数组);
比较写入数组和读取数组内容是否一致并返回比较结果
}
每次运行一个循环就不会有问题,只要连续运行多次循环就会写着写着写不进去了,哪怕再怎么调用NvM_WriteBlock接口也无济于事。
添加写入完成标志位
在写入操作前添加读取完成标志位,如果没读取完成就等待下一个10ms,读取操作也是同理。
怀疑是因为多次循环的时候的时候,操作的速度太快了,导致JOB执行堵塞,为了监控操作执行是否结束需要找到有用的标志位。尝试过使用NvM_GetErrorStatus和NvM_Rb_GetStatus接口,返回的结果虽然正确但无法对本问题起到帮助。
在NvMSingleBlockCallback配置项当中添加回调函数。这个回调函数入参有ServiceId NVM服务ID和JobResult JOB执行结果,会在JOB执行完毕后进入,当“NVM服务ID为NVM_SERVICE_ID_WRITE_BLOCK”和“JOB执行结果为0”的时候就是写入完成。可以在里面判断,做一个写入完成标志位,读出完成也是同理。添加的时候注意isolar工具里面会对函数名称进行限制,我们需要根据规范来进行配置。
if(第1个10ms周期)
{
if(读取完成)
{
创建一个内容跟之前内容都不一样的写入数组;
NvM_WriteBlock(NvM_Block名称,&写入数组);
}
}
else if(第2个10ms周期)
{
if(写入完成)
{
NvM_ReadBlock(NvM_Block名称,&读取数组);
比较写入数组和读取数组内容是否一致并返回比较结果
}
}
这样就解决了写着写着写不进去的问题。
增加代码鲁棒性
原来的block是20字节的,加长到1024字节。循环执行操作的次数也从10次增加到1000次。
新的问题出现了:读取出来和写进去的内容不一致的时候,经过代码检查,发现JOB队列设计有问题,哪怕上一个JOB已经执行完成,新的JOB也排不进队列里面,返回NOT_OK,于是增加判断条件,当读写返回值为NOT_OK的时候,重新调用读写接口。
if(第1个10ms周期)
{
if(读取完成 或者 写入失败)
{
创建一个内容跟之前内容都不一样的写入数组;
写入返回值 = NvM_WriteBlock(NvM_Block名称,&写入数组);
if(写入失败)
{
下一个10ms执行第1个10ms周期
}
}
}
else if(第2个10ms周期)
{
if(写入完成 或者 读取失败)
{
读取返回值 = NvM_ReadBlock(NvM_Block名称,&读取数组);
比较写入数组和读取数组内容是否一致;
if(读取失败 或者 内容不一致)
{
下一个10ms执行第2个10ms周期
}
}
}
经过测试,问题被完美解决。