AUTOSAR Memory Stack之Nvm模块

AUTOSAR Memory Stack之Nvm模块

img

前言

​ 此文章参考大佬“小猫爪”的文章,以及AUTOSAR官方文档,前文大部分为摘录,详情可参考:【小猫爪】AUTOSAR学习笔记16-Memory Stack之Nvm模块_autosar memstack-CSDN博客,后文则是自己对AUTOSAR的API功能函数的部分理解,仅为总结记录所用,如有不足欢迎指正。再次感谢小猫爪大佬文章对我的帮助!!!

1.Memory Stack结构

Memory Stack有服务层的NvM模块,有ECU抽象层的Fee模块和Ea模块,还有MCAL层的驱动。其中NVRAM Manager模块是整个Memory Stack的控制器,它更多的关注于数据本身的,对数据进行校验,读写操作的发起,而根据最终存储器类型的不同,则最终负责数据存储和读写操作的则是Fee模块(FLASH)和Ea模块(EEPROM)。而在MCAL层则根据是内部FLASH,外部FLASH,外部EEPROM的各种情况而灵活变化。

2.NVM模块

NvM 模块主要功能如下:

1. 将不同类型的存储器(Flash、EEPROM 等)进行统一管理,提供AUTOSAR 标准服务接口
  2. 提供以 Block 为单位的读、写等服务
  3. 基于优先级的数据块访问机制,提高用户访问的灵活性和实时性
  4. 提供访问任务的队列
  5. 工作请求异步处理
  6. 提供 CRC 机制,保障数据的一致性
  7. 提供数据故障恢复机制
  8. 提供任务完成通知机制

3.BLOCK

这里的Block是一种软件层面上的玩意,它是NvM操作的数据基本单元,也就是说,NvM模块里面操作的对象就是Block,所以在AUTOSAR里想要存储数据,那么就要提前给数据分配Block。

NvM 支持三种类型的 Block,分别为:

1. Native Block:最常用的 Block,用于一般数据的管理;
  2. Redundant Block:该 Block 支持数据冗余,可用于对一些安全性要求较高的数据进行管理,其实就是可以对其进行备份的Block。
  3. Dataset Block:该 Block 支持同一类型的多套数据。用户可以根据当前的运行状态,选择使用多套数据中的一套。

每个 Block,又由以下几个部分组成:

1. NV Block:位于掉电不丢失的设备,如 Flash,用于正常的存储数据;
  2. RAM Block:程序运行时,用户的应用程序直接访问的 Block,有点类似于缓存,当条件满足时会将其更新至NV Block。临时 RAM Block:在运行时,用户通过接口将 RAM 的地址传递给 NvM;永久 RAM Block:用户配置给该 Block 的固定的 RAM 地址,运行时不可改变。
  3. ROM Block:存储该 Block 的默认值,位于掉电不丢失的设备,当 NV Block 的数据异常时,可恢复为默认值;
  4. Administrative Block:管理 Block,一般位于 RAM 中,用于记录和存储程序运行时 Block的状态,如地址、CRC 结果、Block 状态等。Administrative Block 由 NvM 内部实现,不用关心。

不同类型Block的组成情况如下表所示:

类型NVRAMAdministrativeROM
NATIVE1110-1
REDUNDANT2110-1
DATASET1~256110~256

此外NvM 内部还会预留 2 个特殊 Block,Block0 和Block_CfgID。其中Block0 不存储任何实际数据信息,仅用于记录 WriteAll 和 ReadAll 的工作结果,Block_CfgID 则用于存储当前内存的配置信息。用户实际使用的 Block 从第 3 个 Block 开始。

在NvM模块的下层模块Fee和Ea模块中,它们的基本数据存储单元也叫做Block,NvM模块的Block数据来源于Fee和Ea的Block,前面提到了NvM的三种Block由若干个NV Block组成,所以这两种Block是一对多的情况。重点:NvM的Block编号叫做Block Handle,Fee和Ea的Block编号叫做Block ID,也就是说APP通过Block Handle(类似于Linux中的文件描述符FD,比较抽象)来索引NvM的数据,而NvM则是通过Block ID来索引Fee和Block的Block数据。

Block三种组成成分Block的存储数据格式如下:
其中 RAM 和 NV Block,都有可选的 Header 和 CRC 区域。Header 可用于存储 Block 的Block ID 信息,固定占用两个字节的信息,CRC 区域用于存储 Block 的 CRC,根据 CRC 的类型,占用 1、2 或 4 个字节。ROM Block 仅存储默认数据,不存储 Header 和 CRC 内容。用户在读取和写入数据的时候,不需要关心 Header 和 CRC,NVM 模块将自动对 Header 和 CRC进行填充、计算、校验等工作。

以下为对NVM的API函数的一些个人总结,尽量按照函数调用关系来介绍

4.函数API功能及其实现要求(不断更新中)

注:官方文档只摘录重要的几点,具体细节还有很多,辛苦一下自己查阅吧!

4.1 NvM_Init

void NvM_Init (const NvM_ConfigType* ConfigPtr)

官方文档中对此函数的说明有主要几点:

  1. ConfigPtr 应该总是为 NULL_PTR 值。因为在目前情况下 ConfigPtr 并未被使用。
  2. NvM_Init 应该初始化所有内部变量,例如队列、请求标志、状态机等全部需要初始化为默认值,同时在内部发送信号INIT DONE去使能工作进程和状态机。

**个人理解:**其实 NvM_Init 函数里面可以囊括 Fls_Init 和 Fee_Init 两个函数,因为 NVM 和 FEE FSL 是逐级调用关系。想要对底层代码进行一个读写,那么必须要先初始化 FLS 和 FEE 层(注意,FLS 和 FEE 层初始化是有先后顺序的)。初始化完成以后就可以在函数末尾执行 Nvm_ReadAll函数了

4.2 Nvm_ReadAll

 void Nvm_ReadAll(void)

官方文档中对此函数的说明有主要几点:

  1. 初始化所有 NVRAM 块的管理数据。
  2. 将数据复制到永久 RAM 块或为相应配置的 NVRAM 块调用显式同步回调(NvM_ReadRamBlockFromNvm)。
  3. 函数 NvM_ReadAll 将向NvM模块发出请求信号并返回。NVRAM 管理器应延迟处理请求 ReadAll,直到所有单块作业队列为空。(所以说为什么把 READALL 函数放到 FEE 和 FLS 初始化函数后面)。

**个人理解:**此函数在实现时需要考虑底层 FEE 及 FLS 工作是否完成,同时可以设置一个处理的超时时间。在 FEE 层空闲之后便可以开始数据的读取了。

注:在对块进行读取的过程中需要时刻检测 FEE 层的 MemIf_StatusType 是否为空闲,一般读取一个块重置一遍超时时间和检测一遍 FEE 工作状态。也可以做检测,若块从未被写过,可以给数据区读成默认层。也可以做数据备份之类的操作,这就看你自己想怎么设计了。

4.3 Nvm_MainFunction

void Nvm_MainFunction(void)

官方文档中对此函数的说明有主要几点:

  1. Nvm_MainFunction 函数主要实现对 NVM 的一个管理,也就是各种读写任务之间的调度。
  2. Nvm_MainFunction 应该立即返回若没有接下来的工作安排。
  3. 必须在 NVM 初始化以后才能执行接下来的操作(可以设置一个初始化状态标志位来实现状态判定,若初始化成功则设置成 INITDONE)。

**个人理解:**Nvm_MainFunction 是会被放到一个 TASK 任务调度的,因此一些周期性的写 BLOCK 操作可以放在这里面进行调度。为什么读操作不放到这里面呢?因为 NVM 最重要的是存储!读函数基本上可以进行立即读等一些操作,但是写 BLOCK 的话则有立即写,延迟写,下电写等操作(后面在写 BLOCK 操作时会介绍),像延时写这种操作一般就是放在这种函数里面进行周期性统一执行的。

注:因为必须在 NVM 初始化以后才能执行接下来的操作(可以设置一个初始化状态标志位来实现状态判定),因此可以在函数前对状态标志位进行判断,若未初始化则重新执行一遍 NvM_Init 的操作。其中会调用一些 Fee_GetStatus、Fee_MainFunction、Fls_MainFunction、Fee_Write 等函数来判断 FEE 抽象层的状态,这个在 FEE 及 FLS 文章中会详细介绍。

4.4 NvM_WriteBlock

Std_ReturnType NvM_WriteBlock (NvM_BlockIdType BlockId,const void* NvM_SrcPtr)
    				/*Std_ReturnType    E_OK: request has been accepted
					E_NOT_OK: request has not been accepted*/

官方文档中对此函数的说明有主要几点:

  1. NvM_WriteBlock 函数的任务是将RAM块中的数据复制到相应的 NV 块中。
  2. 如果传递的 BlockId 引用了配置为具有立即优先级的 NVRAM 块,则 NvM_WriteBlock 函数将以破坏性的方式立即取消挂起的作业。当前作业应该是下一个要处理的活动作业。
  3. 如果被处理的 NVRAM 块的 NVRAM 块管理类型为 NVM_BLOCK_REDUNDANT,则 NvM_WriteBlock 函数的作业需要将该RAM块的数据内容拷贝到对应的两个NV块中

**个人理解:**在块中可以设置块的写方式是立即写、延迟写、还是下电写。NvM_WriteBlock 函数执行的主要任务就是立即写的操作,其次就是对块的写数据状态未的设置,若为延迟写或者下电写则设置为相应状态位。如前文所示,延迟写可以放在 Nvm_MainFunction 中进行统一处理。而在这里下电写则就是放在 NvM_WriteAll 函数里面了,至于为啥,请看下文。

4.5 Nvm_WriteAll

void Nvm_WriteAll(void)

官方文档中对此函数的说明有主要几点:

  1. NvM_WriteAll函数的任务应该将永久RAM块的内容同步到相应的NV块,或者在关闭时调用显式同步回调(NvM_WriteRamBlockToNvm)。
  2. 如果 NVRAM 块ID 1(包含内存布局的配置ID)被标记为“在 NvM_WriteAll 期间写入”,则 NvM_WriteAll 函数的作业将在最后一步(最后一次写操作)中写入该块,以防止在写操作过程中掉电失败时内存布局不匹配。

**个人理解:**NvM_WriteRamBlockToNvm 其实是官方定义的回调函数,其具体解释为:块特定的回调例程,为了让应用程序将数据从 RAM 块复制到 NvM 模块的镜像,应该调用该例程。可以在下电时触发这个函数,关闭耗电外设同事进行关键数据的存储。之所以把下电写操作函数放在 Nvm_WriteAll 函数是因为函数本身是多个块的写操作,也就没必要再去实现 NvM_WriteRamBlockToNvm 函数了。

4.6 NvM_ReadBlock

Std_ReturnType NvM_ReadBlock (NvM_BlockIdType BlockId,void* NvM_DstPtr)

官方文档中对此函数的说明有主要几点:

  1. NvM_ReadBlock 函数的任务将把 NV BLOCK 的数据复制到相应的 RAM BLOCK 中

  2. NvM_ReadBlock函数的任务应该在第二个NV块上发起读取尝试,如果传递的块引用了一个类型为NVRAM的块

    NVM_BLOCK_REDUNDANT。

**个人理解:**实际上此函数的功能主要为对 NV 块的读取,给上层做接口调用,比如我想知道某个诊断情况之类的,就需要读取FEE了。

总结:

​ 其实 NVM 模块主要是对底层 FEE 及 FLS 的一个统筹管理,提供给上层读写 FEE 的接口,在状态机主功能函数及读写函数中也会调用下层的 FEE 及 FLS 接口函数,相当于第一层 API 接口,第二层就是 FEE 接口,最后一层就是 FLS 接口,层层调用实现我们对EEPROM 的管理以及内存块的读写,从而更好的为应用层服务。

END

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值