AUTOSAR实战干货:NVM模块Block属性配置全解析
前言
本文思维大纲如下:
继小T之前所写到的AUTOSAR NVM模块详细文章《AUTOSAR技术干货:CP NVM介绍与实战经验分享》, 小T今天将基于NVM模块中每个Block的重要属性配置一次性讲解清楚,希望能给大家工作带来帮助,如果觉得不错,也欢迎大家多多转发分享!!!
Device ID选择
我们知道NVM顾名思义就是非易失性存储器,对于非易失性存储器,有多种不同的物理存储介质,比如常见的Flash,EEPROM等。
如下图所示为整个NVM存储协议栈的软件模块架构图,在该图中可以看到NVM可以通过Memif接口来实现对底层硬件Fls,EEP两类非易失性设备进行访问。
以ETAS工具链为例,Device ID的配置项如下:
- 如果该Device ID为0 ,则表示底层存储硬件使用的是Flash设备,通过FEE机制来实现数据存储;
- 如果该Device ID为1,则表示底层存储硬件使用的是Eeprom设备,通过EA抽象层来实现数据存储;
- 一般AUTOSAR中定义的存储设备主要是Flash跟EEPROM这两大类,其他的暂时没有遇到。
Block配置类型
在之前的NVM文章中我们已经知道BLock的配置类型可分为三种:Native Block,Redundant Block以及Dataset Block。
本文将重点介绍每种类型的主要特点以及应用场景:
以ETAS工具链为例,如下为Block配置类型选项:
Native Block
最为常见普通的Block类型,这类的Native Block有且仅有1个NV Block,有且仅有一个RAM Block,ROM Block 最多1个或者没有,Administrator Block有且仅有1个。
Redundant Block
对于重要的数据需要双备份时一般选用Redundant Block。这类的Block有且仅有2个NV Block,有且仅有一个RAM Block,ROM Block 最多1个或者没有,Administrator Block有且仅有1个。
Dataset Block
对于存在多组相同类型的非易失性数据选择时,比如我们的配置字便可以使用该类Block。这类的Block NV Block至少1个,最大255个,RAM Block有且仅有1个,ROM Block为至少0个,至多N个,Administrator Block有且仅有1个。
Block基础属性
BlockBaseNumber
该数值每个Block均需要配置且唯一,且该数值会与FEE模块或者EA模块中的Block Number 有一定的定量关系,具体关系如何需要参考各自的供应商具体实现方案,仅需明白一点就是该BaseNumber会按照定量关系决定FEE或者EA中的Block Number,否则的话NVM就无法找到正确的FEE或者EA Block进行写入。
Block Length
该参数主要用于定义该Block的实际长度,该长度就按照应用层实际需要写入的长度来定即可,比如该Block的长度为4,那么就配置成4几即可,以ETAS工具链为例,如下图:
Block优先级
对于NVM模块来说,每个Block可以分配其优先级,但是默认情况下该优先级均不起作用,基本上按照FIFO原则进行处理,因为NVM模块本身处理都是异步行为,但是如果碰到一些高优先级BLock数据存储,如Crash Data,那么就需要用到立即写队列来实现。
NVM支持普通队列写入以及立即写队列两种,以ETAS工具链为例,该BLock优先级配置参数如下:
普通队列
普通队列就是按照“先进先出”原则处理来自上层应用的NVM写入请求,且该请求作为异步请求,会在NVM_Mainfunction函数周期调用中入队出队。
此时上述的Job Priority配置可以忽略,不存在优先级判断逻辑,且JobPriority的配置为FALSE,如下图:
普通队列大小通过如下参数配置:
立即队列
如果对于Crash Data需要使能立即数据的存储,那么便可以设置上述参数为TRUE,表示使能NVM队列优先级。
一旦使能了NVM 队列优先级机制,那么此时NVM一般会存在如下两种立即处理队列:
- 立即写队列:专门存储立即写相关的队列,并按照Block对应的优先级进行优先处理;
- 立即读/擦队列:主要用于进行Block的读/擦动作,也是按照BLock对应的优先级进行处理;
- 一般来讲,配置成立即写的Block优先级会高于立即读/擦优先级 的Block请求;
Block优先级特性使能如下:
每个BLock对应的优先级通过如下参数设置:
立即队列大小通过如下参数配置:
Block同步机制
对于Block同步机制,首先通过如下配置可以区分当前NVM采用的是显式同步还是隐式同步,以ETAS工具链参考为例:
- 当上述参数为FALSE, 表示使用的是隐式同步机制;
- 当上述参数为TRUE, 表示使用的是显式同步机制;
隐式同步
隐式同步的交互图如下图所示:
Single Write Request:
- S1:应用层写入数据至上述RAM block,以便RAM数据能给被传入至NVM模块进行写入操作;
- S2: 当应用层更新完数据之后,调用NVM函数接口NvM_WriteBlock 或者NvM_WritePRAMBlock 此时便会将控制权传递至NVM模块,NVM模块将从RAM Block数据拷贝至NV Block中,在这个过程中RAM Block数据不应该被再次写入,否则可能会出现数据不一致性问题;
- S3:应用层可以通过此时可以通过Polling 方式去轮询当前NVM写入的状态或者通过Callback的方式进行异步通知;
- S4:当NVM完成上述写入操作之后,RAM Block便可以被再次更改。
Multi-Write Request:
- 通过BswM模块或者EcuM模块调用NvM_WriteAll 进行多个Block写入操作,此时控制权便会移交至NVM模块;
- BswM模块或者EcuM模块可以通过Polling方式或者Callback的方式来获取当前NVM Block的写入状态。
显式同步
显式同步的交互图如下图所示:
在显式同步过程中,NVM模块定义了一个RAM Mirror,该Block则用于与应用层进行数据交换,应用层通过写入数据至RAM Block之后调用NVM写入API之后,NVM中就会将RAM Block数据拷贝至RAM Mirror中,最终写入到NV Block中。
在上述第一步步骤中的读写操作均是通过Callout方式来实现。
优点:就是应用层只需要通过调用函数 ReadRamBlockFromNvM / WriteRamBlockToNvM 的过程中保证数据的一致性即可,然后之后便可以再次更改RAM Block;
缺点:需要增加一个与最大NVRAM Block 长度相等的RAMMirror Block,用于实现RAM Block与RAM Mirror Block之间的数据同步。
注意点:在执行调用函数 ReadRamBlockFromNvM / WriteRamBlockToNvM 的过程中务必注意临界区保护,避免出现在RAM Block拷贝数据至RAM Mirror的过程中发生数据不一致问题,如下图所示:
Single Write Request:
- S1:应用层更新数据至RAM Block;
- S2:应用层调用NVM接口NvM_WriteBlock或者NvM_WritePRAMBlock触发NVM写入请求;
- S3:NVM在处理该Block并且执行NvMWriteRamBlockToNvM之前均可以修改RAM Block的值,但是在执行该函数中的数据拷贝动作时则不允许进行更改,该临界保护由应用层自行负责;
- S4: 在此之后,应用层 便可以再次读写RAM Block数据;
- S5:应用层此时可通过NVM函数polling该Block请求的最终状态或者通过配置Callback 的方式来得到是否完成。
Multi-Write Request:
- S1:BswM或者EcuM可以调用函数接口NvM_WriteAll 触发Multi-Block写入请求;
- S2: NVM模块在执行上述请求时,凡是使能WriteAll属性的Block若配置了NvM_WriteRamBlockToNvM ,在此函数执行过程中数据拷贝的一致性由应用层来决定;
- S3:在此之后,应用层 便可以再次读写RAM Block数据;
- S4:应用层此时可通过NVM函数polling该Block请求的最终状态或者通过配置Callback 的方式来得到是否完成。
Block Data Address配置
NvmRamBlockDataAddress
- 对于NVM Block中的RAM Block,可分配成Temporary RAM Block 或者Permanent RAM Block,前者则是使用应用层的Buffer,后者则是使用NvmRamBlockDataAddress对应的地址变量;
- 若使用Temporary RAM Block,NvmRamBlockDataAddress 可以不进行任何配置,调用NVM_WriteBlock传入的数据地址为应用层全局变量地址;
- 若使用Permanent RAM Block,那么应用层调用NVM_WriteBlock传入的数据地址可以为空,NVM内部将自动使用NvmRamBlockDataAddress 配置的地址,也可以直接传入NvmRamBlockDataAddress 配置的地址;
//Using Temporary RAM Block且NvmRamBlockDataAddress == NULL_PTR
uint32 g_APP_TempDataAddress;
g_APP_TempDataAddress = 5;
NVM_WriteBlock(BlockID, &g_APP_DataAddress);
//Using Permanent Block 且 NvmRamBlockDataAddress == g_APP!= NULL_PTR
uint32 g_APP_PermaDataAddress;
g_APP_PermaDataAddress = 8;
//可直接传入Permanent BLock地址或者传入地址为空均可
NVM_WriteBlock(BlockID, &g_APP_PermaDataAddress) 或者NVM_WriteBlock(BlockID, NULL_PTR);
该参数相关配置如下:
上述参数的地址一般会在其他头文件进行声明,为了保证编译通过,NVM会提供一个包含上述变量声明的头文件配置如下所示:
NvmRomBlockDataAddress
同理,对于参数NvmRomBlockDataAddress的参数配置也不是必须的,不过其具体数量则根据上述Block配置类型而有所差异,
该参数一般用于如果当NVM读取某BLock数据出现错误时,一般是CRC错误,那么便会自动赋值默认值,即从NvmRomBlockDataAddress的地址拷贝数据至RAM Block,以防止意外情况发生。
该配置参数如下图:
类似的作用配置参数NvM_InitBlockCallback如下:
NvM_InitBlockCallback 参数的作用类似,就是发生block数据破坏,如CRC不正确,那么就会触发该初始化函数回调进行处理。
注意点:
- 虽然参数NvM_InitBlockCallback 与NvmRomBlockDataAddress 功能作用基本相同,但是也有其各自的适用场景,对于简单赋值的操作可直接采用NvmRomBlockDataAddress 即可,对于其他复杂操作,那么就需要适用NvM_InitBlockCallback回调函数中完成;
- 两个参数任意选择一个即可, 无需两个同时配置;
CRC比较机制
NVM模块可以通过使能参数NvMBlockUseCRCCompMechanism来实现针对相同数据的忽视写入,减少不必要的Flash资源消耗;
- 若参数NvMBlockUseCRCCompMechanism == TRUE, 那么就会通过计算两次数据的CRC是否相等,如果相等则忽视最新一次的写入,如果不相等则正常处理;
- 若参数NvMBlockUseCRCCompMechanism == FALSE, 那么无论数据是否发生变化,NVM均会依次处理所有的写入请求;
注意点: 由于采用的CRC比较,CRC可选为8/16/32, 可能会存在即使是不同数据可能CRC还是相同的情况,因此也需要考虑这种机制造成数据丢失的可能性。
相关配置参数如下:
Write Protection机制
NVM Block的Write Protection功能由参数NvMBlockWriteProt与NvMWriteBlockOnce共同来决定。
基本规则如下:
- 若参数NvMWriteBlockOnce == FALSE, 那么应用层可通过调用函数接口NvM_SetBlockProtection来动态实现Block的保护;
- 若参数NvMWriteBlockOnce == TRUE且不关心 NvMBlockWriteProt的值, 那么应用层调用函数接口NvM_SetBlockProtection来动态保护是不被允许的;
- 若参数 NvMBlockWriteProt == TRUE, 则该Block默认开启了写保护,不允许被写入,可通过调用函数接口NvM_SetBlockProtection来关闭保护;
- 对于参数NvMWriteBlockOnce == TRUE的Block仅允许被写入一次,一般用于非常重要且仅被允许写一次的Block;
Write Verification机制
写验证主要用于实现在写的过程中将写入的数据立即读取上来检查是否跟当前RAM Block数据一致,如果不一致,则会自动上报故障 NVM_E_VERIFY_FAILED至 DEM模块。
其配置参数如下:
一般默认不开启,写进去然后重读验证机制需要消耗更多的时间,除非针对特别重要的Block才会采用该机制;
Block读写Retry机制
写重试
NVM模块通过设置参数NvMMaxNumOfWriteRetries来决定当调用NVM_WriteAll或者NVM_WriteBlock接口返回结果失败可以重试的次数,以便增加系统写入成功的准确性,对于非常重要的Block可以使能。
读重试
NVM模块通过设置参数NvMMaxNumOfReadRetries 来决定调用NVM_ReadBlock或者NVM_ReadBlock接口返回结果失败可以重读的次数,以确保系统读取的稳定性,对于非常重要的Block可以使能。
静态Block ID检查
NVM模块为了防止在读取过程中出现硬件原因导致读取数据失败,因此可以使能在每个NV Block的Header中添加Static Block ID
检查,具体检查过程如下:
S1:写入Block时,会将当前写入的Block ID一并添加在NV Block的Header一并写入到存储设备;
S2:每次读取该Block时,都会去校验下从NV Memory中读取到的Block ID是否与当前请求的Block ID一致,如果一致则正常读取,如果不一致,那么就会想DEM模块上报NVM_E_WRONG_BLOCK_ID故障,在此情况下还可尝试采用上述的读重试机制进行恢复;
注意点:在进行Block配置过程中务必确保每个Block对应的Block ID是唯一的。
Block回调函数配置
NvmSingleBlockCallback
对于参数NvmSingleBlockCallback 一般用来当某个Block针对物理存储设备操作完成(包括读/写/擦)之后进行回调通知到上层的函数,以便应用层能给做后置处理。
参数设置如下图:
NvMMultiBlockCallback
同理,对于Multi-Block的相关操作如果需要知道其是否完成,可通过设置Callback函数通知应用层做后置处理,如NVM_WriteAll, NVM_ReadAll等,该参数位于NvMCommon Container下面:
错误恢复机制
关于错误恢复机制在上述内容的讲解过程中已有提及,因此在这里做个系统性总结:
- 如果NVRAM Block的管理类型为NATIVE或者REDUNDANT,发生CRC错误时便会主动通过NvMRomBlockDataAddress或者NvMInitBlockCallback来赋值其默认值;
- 对于所有类型的block,均可手动调用NvM_RestoreBlockDefaults进行赋默认值,如果NVRAM Block的管理类型为DATASET,则需要手动提前设置对应的Data Index,其他没有差别;
- 如果NVRAM Block的管理类型为REDUNDANT,每次写入均会写入两份,如果读取的过程中发现第一份数据错误,就会自动读取第二份,如果第二份还是错误,才会采用上述赋值默认值方式;
- 上述写重试或者读重试也是一种通用的错误恢复方式,不关心具体的NVRAM Block的管理类型。
NDANT,发生CRC错误时便会主动通过NvMRomBlockDataAddress或者NvMInitBlockCallback来赋值其默认值;
- 对于所有类型的block,均可手动调用NvM_RestoreBlockDefaults进行赋默认值,如果NVRAM Block的管理类型为DATASET,则需要手动提前设置对应的Data Index,其他没有差别;
- 如果NVRAM Block的管理类型为REDUNDANT,每次写入均会写入两份,如果读取的过程中发现第一份数据错误,就会自动读取第二份,如果第二份还是错误,才会采用上述赋值默认值方式;
- 上述写重试或者读重试也是一种通用的错误恢复方式,不关心具体的NVRAM Block的管理类型。
更多精彩内容,欢迎大家多多关注如下公号:“ADAS与ECU之吾见”!!!