1、概述
NVM:非易失性存储
NVM主要提供抽象数据存储,在上电读取下电存储数据,支持Immediately存储数据,将NV data在ROM和RAM之间建立关联;MemIf实现存储数据Block在内部Flash或者外部EEP的分离操作;FEE实现对Flash的数据Block的抽象和动态数据的Layout;Flash Driver提供操作Flash的接口;EA定义外部EEPROM的存储抽象和数据的Layout;EEP提供外部EEPROM的操作接口和驱动。
NvM模块负责管理和从非易失性内存中读写数据。在系统启动和关闭阶段,同步应用程序RAM区的数据。此模块还提供其他服务,例如用于上层数据保护的冗余数据单元。同时,RTE提供了简单灵活和接口(NvData Interface)用于非易失内存的数据处理。
至于NVM的块关系,再之前的文章里面已经有描述,此处贴图规范,作为一个空间形式的理解。
不同块类型的逻辑结构如下图:
在AuoSAR架构中NvM的主要功能可以概述为以下几点:
·提供了三种Block的管理类型(Native、Redundant、DataSet)
·支持16bit和32bit的CRC校验
·支持数据操作的优先级机制,支持Immediately写操作
·给APP提供服务接口,NV Data类型的数据接口在APP层可以直接操作
·NvM与APP的同步机制
·用于DCM诊断的数据操作
·支持操作数据读写操作的完成以及错误的回调通知
·可以配置的ID处理
在AutoSAR中NvM与多个模块存在关联。RTE提供与APP交互的接口;CRCLib用于NV Data存储时CRC校验;SchM用于NvM API调度;MemIf提供下层的内部还是外部存储的区分;BswM在上、下电阶段调用ReadAll和WriteAll接口;Dem负责相关DTC等诊断数据的存储;Det用于检测开发过程中相关的错误。
NVM的数据格式如下:
NVM中各Blcok存储的基本内容分为三部分:Block Header + Block Data + CRC Data,其中Block Header(Block ID,size等信息)及CRC校验信息为可配置内容。
在NVM读写时会对CRC进行校验:
在读取时如CRC校验不过,会采用默认值,如无特殊默认值将默认为0;
如在写入时CRC校验一致即认为需要存入NVM的数据没有发生改变,则跳过写操作请求,CRC校验不一致则将数据及CRC一起写入DFLASH中,该CRC值将参与下一轮比较。
永久性RAM
RAM块的地址是固定的,存储在NVM的配置中,可以由一个应用程序访问NV块,也可以让多个应用层序访问同一个NV块。
非永久性RAM
每个应用层序使用自己的RAM块,在这种情况下,RAM块称为非永久RAM块,由于RAM地址没有被存储,可能存在变化,因此必须给出一个指针来读写非永久块。
2、NVM同步机制(显式与隐式)
个人理解的同步机制:使RAM数据与NV Block的数据保持一致的过程叫NVM数据同步。
2.1、隐式同步
在隐式同步中,Application和NvM可以同时访问公共RAM块。应用程序通过调用NvM API将数据写入RAM或从RAM读取数据。
在这种情况下,一个NvM Block的 RAM 块被映射到一个SW-C,不推荐共享RAM。此外,为了保证数据的一致性,当SWC调用NVRAM接口进行读写操作时,SWC不能改变RAM中的值,直到NvM完成正在进行的操作,在此期间,应用程序可以使用轮询来获取请求NVM的状态,也可以通过回调函数异步通知。
适用场景: 适用于下电写的Block,即在程序RUN的过程中不存,下电前将RAM中最后的值存进去。
NvM默认使用的就是隐性同步,如果有多个应用使用同一块RAM区域,那么这些应用之间需要自行保证进行Nvm操作过程中还有对RAM区域的访问。
隐式数据流如下:
1、应用SWC通过RTE调用NV Interface类型接口触发NvSwc请求进行写(读)操作事件,传递RAMBlock参数给NVM用户数据写入(或从NVM读取)。
2.、NvSwc调用 NvM_WriteBlock((或NvM_ReadBlock)),请求把RAM Block中的数据写入 NvBlock (或从NvBlockEE) 。
3、从发起请求开始,到NvM模块读写完成为止,其他SwC不能更改RAM Block中的值,(虽可读取,为便于管理此期间一般禁止其他SWC读写操作)
4、SWC可以用轮询的方式周期性检查NvM写入(读取)操作是否完成, NvM也可以用回调函数的方式来通知SwC操作完成。
5、当NvM操作完成后,各SWC即可再次对Block进行读写操作了。
2.2、显式同步
在显示同步中,NVM会再定义一个RAM Mirror(RAM镜像)与应用层进行数据交换(注意这块多了一个RAM空间),App在调用NVM实时写接口时将应用层RAM数据写到RAM Mirror中,应用层可以随意更改应用层RAM,仅需要在发起写入请求时,将应用层RAM拷贝到RAM Mirror中即可修改应用层RAM而无需等待NVM读写完成。
与隐式同步相比,在显式同步中,应用层RAM可以由多SWC共享,缺点同样由于增加了RAM Mirror会增加Ram资源的消耗量。
适用场景: 适用于实时存储的Block,应用层可以更新RAM Block中的值,等上一轮写完再调用NVM接口进行写入。
选择显性同步时,数据在镜像区域和RAM块之间地拷贝,都需要基于用户自定义的回调函数进行。
比如Dem需要使用的Default Data Block,在Nvm里设置了显性同步,下电执行NvM_WriteAll期间在其回调函数Dem_NvMWriteCopyEventStatusBlock中,Dem将数据拷贝到Nvm的镜像当中。
描述一下显式同步简介
·ASW调用NvM_WriteBlock时候写数据到RAM Block,此时数据仍然能被修改。
·此时数据没有到最终的NV Block,调用NVM数据操作NvMWriteRamBlockToNvM的时候数据被Copy到内部的mirror镜像中,最终将数据写到NV Block
·NVM在读的时候调用API从NvM_ReadBlock读取数据,在NvM调用了NvMReadRamBlockFromNvM后数据才真正的从RAM Mirror中Copy到了RAM Block。NvM提供了双向的控制Callback的路由,实现APP数据的传输。
显式数据流如下
1、SwC通过RTE调用NV Interface类型接口触发NvSwc进行写(读)操作事件,若为写操作同时将数据传给RAMBlock.
2.、NvSWC检测到写(读)操作事件,调用NvM_WriteBlock(或NvM_ReadBlock),发起一个写入(读)数据请求。读写函数数据指针为空,目的是发起一个写入(读)数据的请求。
3. NvM模块检测到读写数据请求会调用WriteRamBlockToNvCallback (或ReadRamBlockFromNvCallback)这里配置的回调函数,在调用这个回调函数之前APP都可以修改RAM Block中的值。在Write回调中,将用户Ram到NVM的mirror RAM (read则方向相反) ,进而调用NVM底层协议栈完成真正写操作(Read操作理解应为先执行协议栈再Copy数据)。
4、NvM模块调用读写Callback时,NV Block状态自动置为Busy,不允许其他SWC请求操作。
5、复制过程中出现问题,NvM协议栈会自动调用回调函数重试。
6、建议NvM用回调函数的方式通知APP操作结果。(轮询方式可能没有触发pending,导致read ok的结果是上一次的值!)
7、以上执行完成后,各SWC就可以再次对Block进行读写操作了。
在应用程序发出NvM_WriteBlock后,只有两个回调函复制数据阶段(真正读写数据)不能使用RAM Block.其余时间均可访问。
2.3、显式与隐式的差异点
名称 | 隐式 | 显式 |
RAM开销 | 无需其他RAM | RAM镜像开销 |
共享RAM | 不建议使用共享RAM | 多SWC使用共享RAM访问NV Block |
数据一致性 | SWC确保数据一致性 | 镜像机制确保数据一致性 |
操作时间 | 较短,只有在读写 Nv Callback 拷贝数据真正操作 Nv其间不允许访问 NV Block. | 较短,只有在读写 Nv Callback 拷贝数据真正操作 Nv其间不允许访问 NV Block. |
3、Block队列及优先级
NvM 对Block进行读写操作,对于存储介质来说都是异步的。因此对于NvM是可以同时发起多个Block的读写操作(NvM_ReadAll/ NvM_WriteAll)。但是对于存储介质来说,当前操作的Block没有完成,无法接收下一个操作任务。因此NvM通过任务队列,同时结合回调函数或者轮询当前操作Job的状态(可配置),完成对多Block的任务管理。
NvM 提供两种任务队列, 一种为 FCFS(First Come First Serve ) 队列即先发出的请求会先被执行, 另一种为优先级队列即高优先级的请求会先被执行。启用 NvMJobPrioritization 时, NvM 使用优先级队列。Block 的优先级范围为 0…255,数值越小优先级越高。禁用 NvMJobPrioritization 时, NvM 使用 FCFS 队列。
NvM Block 可以通过将优先级设置成 Immdieta,该Block 可以越过FCFS 原则,进行优先处理。
4、Block虚拟地址映射
NvM 通过 32 位的虚拟地址访问 Fee 及 Ea 模块, 16 位为 Block 地址偏移, 16 位为 Block Number,如下图所示:
Block Number 分为 Block Base Number 及 Data Index 两部分, 二者的位数占比由配置 NvMDatasetSelectionBits 决定。Block Base Number 为(16–NvMDatasetSelectionBits)12位, Data Index 为 NvMDatasetSelectionBits 4 位。即NvMDatasetSelectionBits 配置为 4 时, Block Number 如下图所示。
用户通过 Block ID 访问 NvM 的 Block, 而 NvM 通过下列计算方式访问 Fee/Ea 的 Block:
FEE/EA_BLOCK_NUMBER = (NvMNvBlockBaseNumber << NvMDatasetSelectionBits)
+ DataIndex.下图展示了在 NvMDatasetSelectionBits 配置为 2 时, NvM Block 及 Fee Block 的关联关系及 Fee Block在 NV Memory 上的排布。
不管是FEE还是EA,NvM的Block的地址映射都是NvM Block ID与 Fee/EA Block ID的映射,实际的写入地址都是由FEE/EA决定的
Fee Block在NV Memory 的排布顺序,与Block ID无关,只与写入的先后顺序有关
Fee Block ID不一定是连续的,但是NvM Block ID是连续的,因为两者的ID映射关系,因此允许Fee Block ID不连续。
规范里面的例子很详细,
5、数据修复方式
5.1、Read Retry
NvM配置参数中NVM_MAX_NUM_OF_READ_RETRIES 定义了最大的尝试次数。
5.2、读取Redunant Block的备份数据
如果Block 属性是Redunant Block,那么当Read Block发生错误时,将会从备份Block读取数据恢复数据
5.3、Rom Block
当以上措施都失效时,Rom Block中的默认数据,可以将数据恢复到默认状态。
6、宏定义知识点
看一个C语言的宏用法
注意一下:LIST_ID_JOB(LIST_ENTRY_ID_JOB) 中
NvM_Prv_idJob_ LIST_ENTRY_ID_JOB _e是不存在的,但是
NvM_Prv_idJob_Read_e是存在的
中LIST_ID_JOB(LIST_ENTRY_ID_JOB)只是表示枚举里面有Count的元素,注意一下NvM_Prv_idJob_Count_e声明在LIST_ID_JOB(LIST_ENTRY_ID_JOB)中。