MemStack 以NvM (Non-Volatile RAM Manager)模块向User提供各种非易失存储器的数据管理,NvM模块自身是独立于硬件的,但所有的功能都是直接访问硬件的,包括内部/外部的 EEP,或者是内部/外部的Flash 模拟的EEP。NVRAM 管理器处理对非易失性数据的并发访问,并提供可靠性机制,如单个数据元素的校验和保护。
MemService 由NvM MemIf EA Eep Fee Fls组成,支持对Flash 和Eeprom的数据集成管理,本文主要通过介绍NvM MemIf Fee EA 等模块来认识MemStack。
NvM 模块
Memory Service 通过将数据元素抽象成Block 进行统一管理,NvM所有的功能都是以Block为单元。NvM管理的Block包含:
- Ram Block : 位于 RAM 中, 存放应用待读/待写的数据
- Rom Block : 位于 PFlash 中,存放 Block 默认数据, 用于数据读取失败后恢复默认数据
- Administrative Block:位于 RAM 中,保存所有Data Block 的错误/状态/Crc 等信息
- NV Block: 位于非易失性存储器中, 用于保存非易失数据
1. Block 分类
NvM 包 含 3 种 Block 类 型 :
- NVM_BLOCK_NATIVE
- NVM_BLOCK_REDUNDANT
- NVM_BLOCK_DATASET。
每种类型的 Block 都由 NV Block、 RAM Block、 ROM Block 及 Administrative Block 等基本存储对象组成。
NVM_BLOCK_REDUNDANT 类型的 Block 包含两个 NV Block, 且两 NV Block 中保存的数据互为备份, 提高了数据安全性。
NVM_BLOCK_DATASET 类型的 Block 可根据需求配置多个 NV Block 或 ROM Block 并通过索引选择操作对象, 提高了系统的灵活性。
各类型 Block 存储结构如下图
2. Block 数据结构
虽然NvM中存在这么多类型的Block,但是在存储介质中实际存在的只有NV Block, Ram Block 和Rom 都不参与实际的写入过程。
一般来说NV Block 由Block Header + Block Data +Block CRC组成,其中Block Header会包含Block ID,用于写入/读取时校验
3. Block 任务分发
NvM 对NV Block的操作是独立于硬件,在NvM模块中无法直接写入对应的存储介质中。NvM通过识别Block的配置参数,确定当前操作的Block的下属模块,再将任务下发给MemIf模块。
当前操作的Block完成Job后,即可进行下一个Job操作。下面以NvM 操作Fee的Block为例
3.1. Block 任务队列以及Block 优先级
3.1.1. 任务队列
NvM 对Block进行读写操作,对于存储介质来说都是异步的。因此对于NvM是可以同时发起多个Block的读写操作(NvM_ReadAll/ NvM_WriteAll)。但是对于存储介质来说,当前操作的Block没有完成,无法接收下一个操作任务。因此NvM通过任务队列,同时结合回调函数或者轮询当前操作Job的状态(可配置),完成对多Block的任务管理。
3.1.2. 优先级 & Immdieta Job
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)位, Data Index 为 NvMDatasetSelectionBits 位。当NvMDatasetSelectionBits 配置为 2 时, Block Number 如下图所示。
用户通过 Block ID 访问 NvM 的 Block, 而 NvM 通过下列计算方式访问 Fee/Ea 的 Block:
NvM Block Base Number = NvM Block ID << NvMDatasetSelectionBits
Ea Block Number/Fee Block Number = NvM Block Base Number + Data Index
下图展示了在 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. 数据校验&Redunant
NvM 提供了多种校验方式
- 校验Block Header中的Block ID
- 校验 根据Block Header 以及Block Data 计算的CRC
当数据校验发生错误时,NvM存在以下几种方式去修复故障
- Read Retry
NvM配置参数中NVM_MAX_NUM_OF_READ_RETRIES 定义了最大的尝试次数
- 读取Redunant Block的备份数据
如果Block 属性是Redunant Block,那么当Read Block发生错误时,将会从备份Block读取数据恢复数据
- Rom Block
当以上措施都失效时,Rom Block中的默认数据,可以将数据恢复到默认状态
MemIf 模块
mory Abstraction Interface (MemIf)作为接口抽象层, 为 NvM 提供访问 Fee/Ea 模块的函数,NvM借此抽象成硬件无关的模块。
MemIf 通过Device Id将Fee 与EA 的相关接口抽象成MemIf接口供NvM访问
1. Device & Api管理
MemIf 可以同时管理EA 和FEE,通过NvM Block 的 DeviceId 配置属性,进行识别,在访问对应的Device Api
EA 的Device ID 0
Fee 的Device ID 1
Fee 模块
Fee(Flash Eeprom Emulation), 顾名思义就是Flash 模拟Eeprom. 那么为何不直接使用Eeprom,或者说Flash为什么需要模拟Eeprom.
Flash 与 EEP 都属于非易失存储器(Non-Valatile Memory),主要存在以下几点区别
- 成本
同样的存储空间,EEP比Flash贵上许多,因此一般MCU上Flash空间要比EEPROM 空间大
- 擦写方式
Flash 以Page为单位,擦除的时候 必须按照Page 为单位擦住,同时写入的时候,只能写1,也就是由0->1,因此如果要改写某个Page的一个Byte,那么必须先将Page的内容全部读到Ram,然后改写Ram中对应的变量,然后擦除该Page,最后把Ram修改后的值写入到Page,也就是需要以下三步:
读->改->写
Eeprom 支持按照Byte直接修改Fee,就是将Flash的写入操作,通过软件的模拟成EEP.
1. 模拟EEP写入方式
Fee 通过 Fls 模块操作 DataFlash, DataFlash 具有如下物理特性:
- 按页写入, 写数据需为物理页的整数倍, 否则需做字节补齐。
- 写入前需确保此段空间未被写入过数据, 否则需要擦除后再写入数据。
- 最小擦除单位为整个物理扇区。
Fee 的写入单元是Block,Fee Block 是继承自NvM Block,在NvM Block 数据的基础上加上Fee Block Header数据,形成Fee Block。
Fee 写入Block 按照先写入,地址就靠前的原则,下次写入Block 就依次往后排。这样确保每次写入都不需要擦除。
2. Fee翻页机制
Fee 通过对Flash空间整体划分为两个逻辑Sector, 一个作为Active Sector,一个作为Inactive Sector。这两个Sector 由一个或者多个物理Sector组成。
在任意时间,只能有一个Logical Sector在作为 Active Sector只有当Active Sector,如何写入下一个Block,或者达到配置的剩余空间,那么就会进行翻页操作。翻页操作一般会经历如下几个步骤
- 将所有Block的最新数据写入到Inactive Sertor
- 擦除Active,设置Inactive Sector为Active
翻页前
翻页后
注意,这里翻页操作时,并没有将所有的数据都由Active Sector搬运到Inactive Sector,而是将所有Block的最新数据搬运到Inactive Sector
3. Fee 初始化读取地址
Fee 提供地址管理机制,Fee 会在Ram中实施记录所有Block的当前地址和状态,在Block写入新的数据时,又会更新这个地址。这些地址主要用于Fee读取Block数据。
在Fee进行初始化之前,这些Block地址是未知的,因此需要在Fee 初始化阶段,需要从Flash 遍历得到当前Blcok的地址。
由下图可知,Fee写入是在Active Sector 中依次往下排布的,因此在最新的Block 数据往往在Active Sector的底部。
4. Fee 写入Block 管理
通过 Block 头信息中的有效标识位字段可以判断当前的扇区及 Block 是否有效。
在写任务中, 首先写入除有效标识位之外的 Block 头, 随后写入数据段, 最后写入 Block 有效标识位。
若在写任务中异常下电, Block 头中的有效标识位未成功写入, 重新上电执行完扫描任务后, 认为该 Block无效, 无法成功读取该 Block 的数据, Fee 向上层返回的结果为 MEMIF_BLOCK_INCONSISTENT。
EA 模块
由于EEP 是可以支持直接写入的,因此存储在Eep的Block地址都是固定的,Ea 模块为
EEPROM 硬件抽象层, 通过访问下层 Eep 模块提供的 API 操作 EEPROM 硬件, EEPROM 具有如下物理特性:
- 可按字节写入。
- 写入前不需执行数据擦除操作。
由于 EEPROM 具有如上特性, Ea 可为每个 Block 确定具体的硬件存储地址并划分存储空间, NvM 在执行读/写等服务时, 通过 Ea 完成相应 Block 的地址映射即可操作 EEPROM 硬件中数据
1. EA 地址管理
通过获取 NvM Block 的类型、 长度等配置, Ea 按照各 Block 的长度为其分配地址空间, 从 0 地址起依次向后排列。
2. EA Block 写入管理
Ea Block 中包含块状态标识位, 该标识位会存入 Eeprom 中, 通过该字段可以判断当前 Block 是否有效。在读任务若发现标识位为无效的状态, 则直接向上层返回 MEMIF_BLOCK_INVALID 失败状态;若发现标识位不为有效/无效值, 则直接向上层返回 MEMIF_BLOCK_INCONSISTENT 失败状态。
作者: Archieeeeee
来源:https://mp.weixin.qq.com/s/VhCG6AogZpecdO8FRbV4ug
微信公众号:
推荐阅读:
更多汽车电子干货请关注汽车电子与软件专栏。