MPC5744 Data Flash 仿真 EEPROM

一 、目的

         本文主要是使用 MPC5744 中的 Data Flash 仿真 EEPROM 。之前在 《 MPC5744 烧录一直停留在 98% 的解决方法 》 中也简单介绍了 Flash ,所以在这里直接介绍仿真过程 ,注意这里可以使用仿真的块是 Data Flash ,即标有 EEPROM 的块 。在这里使用的开发软件为 S32DS V2.1 ,使用官方 SDK (S32DS_PA_2.1_UP10.zip) 。


二 、仿真 EEPROM

在仿真过程中 ,可以看到有时已经写入的地方会在没擦除时就写入数据 ,此时这不是说写入 Data Flash ,相应的地址就一定会是写入的数据 ,Flash 只有擦除才能使得 0 -> 1 ,写入数据只能将 1 -> 0 ;另外 ,在 MPC5744 的 Data Flash 没擦除就在已经有数据的地方写入数据 ,同样会有 ECC 错误 ,但不会导致 Flash 或程序运行等出问题 。这里不代表其他 Flash 是同样的性质 。

2.1 概述

2.1.1 块指示

         在这里 ,使用每个块前 32 个字节指示块的状态 ,共分为 4 个 8 字节 ,每 8 个字节指示块其中一种状态 (这样分是因为 Flash 编程是 8 字节对齐 ,即每一次最少写入 8 字节) 。如下表所示 ,其中偏移量是相对当前编程的块首地址 。

                                        表 1 块指示说明

偏移量

指示说明

SDK 操作说明

(1)   0~7

指示块是否为积极块

此值为 0x0000 FFFF FFFF FFFF 为积极块

此值为 0xFFFF FFFF FFFF FFFF 不为积极块

(2)   8~15

指示块擦除的次数

指示块是擦除块还是无效块

如果除了块指示 ,块其余地方读取的皆为 0xFF ,则为擦除块 ,否则为无效块

此值不为 0xFFFF FFFF FFFF FFFF ,则这里值指示 Flash 已经擦除了几次 ,注意 ,使用仿真之前此 Flash 块最多擦除一次 ,否则此值无意义

此值为 0xFFFF FFFF FFFF FFFF ,则指示此块为擦除块还是无效块

(3)   16~23

指示此块是否为死块 (dead)

此值为 0xFFFF FFFF FFFF FFFF 为非死块

此值为 0x0000 FFFF FFFF FFFF 为死块

(4) 23~31

指示是否复制完成

此值为 0xFFFF FFFF FFFF FFFF ,表明复制未完成

此值为 0x0000 FFFF FFFF FFFF ,表明复制完成

         SDK 操作说明里面非 0xFFFF FFFF FFFF FFFF 也可以为别的值 ,这里以 SDK 为准 。

         以表 1 ,要查看块的状态 ,如以下操作 :

  1. 查看 (3) ,此块处于死块的状态或出现 ECC 读取错误 ,若是 ,此块不能使用 。
  2. 查看 (2) ,查看是否处于擦除或无效块 (出现 ECC 错误也是无效块) ,若是 ,返回相应状态 。
  3. 查看 (1) ,不等于 0xFFFF FFFF FFFF FFFF 或出现读取 ECC 错误 ,则返会此块为积极块 。
  4. 查看 (4) ,不等于 0xFFFF FFFF FFFF FFFF 或出现读取 ECC 错误 ,且 (1) 中的值等于 0xFFFF FFFF FFFF FFFF 且读取不出现 ECC 错误 ,则返回复制成功 。
  5. 若 (1) 和 (4) 读取出来的数据都是 0xFFFF FFFF FFFF FFFF 且无出现 ECC 错误 ,则查看此块除了这 32 个字节以外是否都为 0xFF ,如果不都是 0xFF ,则返回块更新 ,若是 0xFF ,则返回块处于可交替状态 ,即块随时可以存储数据 。若都不是这些 ,返回无效状态 。

        所以如果正常运行 ,在更改状态时 ,应当是 :块处于可交替状态 -> 块更新 -> 复制成功 -> 积极块 。当然 ,如果是最初始的时候 ,即数据都没开始写入的时候 ,会指定其中一块为积极块 ,通常都是所选块中编号最小的块 ,或者最开始运行时期根据块状态的积极块数量比不上指定的积极块数量 ,此时也是直接将块从可交替状态变为积极状态 。不说最初的指定 ,其余正常的块指示都如下 :

  1. 从块可交替状态变为块更新 ,此时块指示不变 ,但块指示以外地区已经在记录数据 。
  2. 更新完成之后 ,将 0x0000 FFFF FFFF FFFF 写入 (4) 中 ,指示数据复制完成 。
  3. 将被复制的块从写满数据到擦除整块 ,若擦除失败 ,此块变为死块 ,擦除成功 ,此块变为块交替状态 ,而复制完成的块将 0x0000 FFFF FFFF FFFF 写入 (1) ,使得其变为积极块 。

2.1.2 记录指示

         记录即指块指示之下的内容 ,每一条写入的内容都被称为一条记录 。记录的结构如表 3 所示 。表 2 说明的是记录状态指示的含义 ,每一条记录的状态指示都是其前 8 个字节 ,最开始都是 0x FFFF FFFF FFFF FFFF 。

                                                                      表 2 记录指示说明

记录指示的数据

记录指示的数据说明

0xFFFF FFFF FFFF FFFF

指示这里还没记录

0xFFFF 0000 FFFF FFFF

指示这里是有效记录 ,即存在有效数据

0x0000 0000 FFFF FFFF

指示此记录无效 ,即这里的数据不可用

记录指示的数据获取之后 ,将按照以下分析 :

  1. 若为 0xFFFF FFFF FFFF FFFF ,则先检测该记录的记录指示后的 8 个字节 ,即 ID + 数据长度 +数据 ,若 8 字节全为 0xFF ,则此时该记录还未记录数据 ;否则 ,则获取数据长度 ,算出记录长度之后 ,将空白地址的指针加上这个记录长度 。
  2. 若为 0xFFFF 0000 FFFF FFFF ,则该记录为有效数据 。
  3. 若为 0x0000 0000 FFFF FFFF ,则该记录为无效数据 。

         若是更改记录的状态 ,则 :

         将其余数据写入完成后 ,将记录指示更改为 0xFFFF 0000 FFFF FFFF ,在将数据删除之后 ,则记录指示更改为 0x0000 0000 FFFF FFFF 。

2.2 仿真结构

         如表 3 是 Flash 仿真 EEPROM 的结构 。

 

           如表 3 所示 ,块的前 32 个字节为块指示状态 。之后就是记录 。记录由 记录指示+ID+size+数据 组成 ,其中 size 若是可变的 ,就如上表 3 所示 ,若是固定的 ,则这里不用 size ,数据即是我们实际要写入的数据 。

          需要注意的是 ,如表 3 所示 ,其中红的标定的 ID 中 ,为了更改其中一个数据 (0xF0->0xF1) ,需要将整个记录重新写 ,虽然两个的记录指示可能都指示其为有效数据 ,但这里是以最后一个记录为有效记录 。

2.3 读取数据

         如图 1 ,是读取的流程 。其中缓存表在这里面就是用数组 ,在此 SDK 中 ,数组名用 table  ,存放 ID 对应的有效 Flash 地址 ,相当于建立了一个查找表 ,如 table[0] 代表 ID=0 所在的有效 Flash 地址 , table[1] 代表的是 ID=1 的有效地址 ,依此类推 。

         需要注意的是 ,如果在这里没有找到对应的 ID ,即如果我们有写入 ID=0 和 ID=2 的记录 ,但没有 ID=1 的记录 ,此时就不会从 Flash 读取数据 ,这个应当在此流程有手动处理 。在此 SDK 中 ,返回 STATUS_EEE_ERROR_DATA_NOT_FOUND 表明没有找到该 ID 的有效记录 ,返回 STATUS_EEE_HVOP_INPROGRESS 表明有一个任务不能挂起 。
                                                                              

                                                                         图 1 读取流程

 

2.4 写入数据

2.4.1 写入流程图

         如图 2 ,是写入的流程图 。在这里面 ,主要是 (1) 和 (2) 。注意 :积极块索引指向我们正在操作的块 。

         对于 (1) ,具体操作如下 :

  1. 根据积极块索引 ,读取当前积极块的状态 ,
  2. 当前积极块的状态是复制完成状态 (此时是交换块后 ,旧的积极块没有被擦除 ,新的块就还未被改为积极状态) ,若此块剩余的空间此时能存储此次的数据 ,则返回 EEE_WRITE_ON_COPY_DONE ,否则返回 EEE_WRITE_NO_ENOUGH_SPACE ,表明没有足够空间写入数据 。
  3. 当前积极块不是复制完成状态 ,则若有足够空间存储此次的数据 ,返回 EEE_WRITE_NORMAL ;否则 ,读取每个块存在块前 32 字节的状态 ,计算积极块的数量 ,若此数量与规定的积极块数量一致 ,则返回 EEE_WRITE_SWAP ,意思是需要进行交换块 ,将旧的积极块擦除作为可交替的块 ,将可交替的块作为新的积极块 ;否则 (数量不一致) ,返回 EEE_WRITE_ON_NEW_ACTIVE ,即意思是需要从可交替块中找出一块作为积极块 ,旧的积极块不必擦除 。

                                                             


                                                                     图 2 写入流程

         对于 (2) ,具体操作如下 :

  1. 从 (1) 中获取的写入操作中 ,如果为 EEE_WRITE_ON_NEW_ACTIVE ,则更新当前积极块索引 , 然后将当前块的块状态更改为积极状态 ,最后更新 blank addr 位为 32 字节之后的地址 。
  2. 此时 ,(1) 中获取的写入操作 ,如果为 EEE_WRITE_NORMAL 、EEE_WRITE_ON_NEW_ACTIVE 和 EEE_WRITE_ON_COPY_DONE ,则根据积极块索引获取当前块的 blank addr ,然后将数据根据格式写入 ,如果有使能缓存表 ,则更新缓存表 。
  3. 若 (1) 中的状态为 EEE_WRITE_SWAP ,则将数据写入到下一个块中 (即写入下一个要成为积极块的块中 ,在擦除时或初始化时已经将 blank addr 设置为 32 个字节之后 ,注意此时积极块索引还没更新) ,如果写入成功 ,启动块交换 。
  4. 若不是以上写入操作 ,则返回 EEE_WRITE_NO_ENOUGH_SPACE 。

2.4.2 块交换

         根据当前积极块索引找出下一个积极块索引和当前最久的积极块 (即当前积极块中存在时间最久的 ,注意 :是可以有多个积极块的和可交替块的) 。

         若使用缓存表 ,则检索缓存表内所有 ID 的地址是否在最久的积极块中 ,若是 ,读取 ID 和 size 之后 ,然后用这个作为参数 ,将这些有效数据写入到下一个积极块中 。

         为防止有些 ID 太大 ,超出在缓存表允许的最大 ID ,此时使用交替表 (是一个 uint32_t 的变量) 。此时先以缓存表最大 ID + 1 的 ID (在后文就称为 searchID) 在最久的积极块中搜索 ,此时同时获取超过大于 searchID 中最小的 ID (在后文称为 nextID) :

  1. 查找后 ,若 searchID 和 nextID 都没有 ,则搜索完毕 ;
  2. 查找后 ,若 searchID 存在而 nextID 不存在 ,则在其他积极块和下一个要成为积极块的块 (复制最久的积极块中有效记录的块) 搜索 searchID 有效记录是否存在 ,如果不存在这些块中 ,将该记录进行复制 ,存在就不必 ,如果出现复制失败退出复制数据的过程 ;这过程完成后搜索完毕 。
  3. 查找后 ,若 searchID 不存在而 nextID 存在 ,则将 nextID 作为 searchID ,继续 2 或 4 过程 。
  4. 查找后 ,若 searchID 不存在而 nextID ,则在其他积极块和下一个要成为积极块的块 (复制最久的积极块中有效记录的块) 搜索 searchID 有效记录是否存在 ,如果不存在这些块中 ,将该记录进行复制 ,存在就不必 ,如果出现复制失败退出复制数据的过程 ,然后将nextID 作为 searchID ,继续 2或 4 过程 。

         完成后 ,将下一个要成为积极块的块状态变为复制完成状态 ,然后更改积极块索引至该块 。对于最久的积极块 ,先读出其擦除次数并加一 ,然后执行擦除操作 ,等待擦除成功之后将擦除次数写入最久的积极块当中 ,即该块就是变为可交替块 ,并将 blank addr 改到正确的地方 (块首地址偏移 32 的地方) ,然后可以将刚刚改为复制完成状态的块改为积极块状态 。

         在这里需要注意的是 ,仿真块最好用那些块大小一致的 EEPROM 块 ,因为假设交换的块是从 32KB 的块将有效内容复制至 16KB 的块 ,且有效内容超过 16KB ,此时此 SDK 只会返回 EEE_WRITE_NO_ENOUGH_SPACE ,此时是需要我们进行处理 ,虽然也可以处理 ,就是将要编程积极块的这个块重新擦除 ,并将它的编号和块大小更大的块的编号进行交换 ,很明显 ,要尽量避免这些操作 。

2.4.3 死块

         一般可以用于如真实擦除次数大于最小 Flash 擦除次数或者块擦除失败时 ,具体过程如下 :

  1. 将 0x0000 FFFF FFFF FFFF 写入 Dead indicator 。
  2. 如果成功写入 ,增加死块的数量 ,其余非死块索引大于此死块索引 (每个块都有索引 ,所以积极块索引才能找到对应的块) 的索引都减一 ,死块索引移动至最后 ,即索引最大的数是死块索引 。块数量减一 。
  3. 积极块索引大于没改变前死块索引的 ,积极块索引减一 ,这是因为对应的块索引减一了 。
  4. 检查积极块数量是否大于等于块总体数量 ,如果是就返回STATUS_EEE_ERROR _NO_ENOUGH_BLOCK ,因为此时没有可交替的块进行块交换 。

2.5 数据恢复

         首先 ,先计算所有积极块数量 ,复制完成块数量 ,更新块数量 ,擦除块数量 ,有效块数量和无效块数量 。这是通过读取块状态来决定的 ,然后设置积极块索引 ,并尝试了解掉电之前运行的状态 :

  1. 如果积极快和复制完成块的数量都为 0 ,或者擦除块数量是总块数减一且第一块状态是复制完成块或积极块状态 ,则这可能是第一次运行或上次运行时没完成初始化 (第一次运行时会将所有块的擦除次数默认为 1 写入) ,不涉及其他块 。
  2. 如果不是 1 中条件 ,如果第一块 (索引为零) 是积极块 ,且擦除次数为 1 ,则若第一块是无任何记录 ,且其余块是可交替块 ,可交替块擦除次数为 1 ,此时是正常运行 ;若交替块数量没有那么多或擦除次数不为 1 ,则若块数量只有 2 或 擦除数量大于 0 或第二块是可交替块 ,则这是第一次运行没完成初始化 。
  3. 如果不是以上条件 ,则若积极块数量达到预定的积极块数量且更新块数量不为 0 ,则表明上次运行状态在块更新状态 。
  4. 如果不是以上条件 ,如果复制完成示例大于等于 1 ,上次运行状态在复制完成状态 。
  5. 如果不是以上条件 ,则正常运行 。

         在这个过程 ,积极块索引已经设置好 ,是从索引 0 开始往后第一次积极块索引相连的最后一个索引 (如块 0 、1 是积极块状态 ,块 3 、4 、5 、6 是可交替块 ,块 7 、8 是积极块状态 ,则积极块索引为 1) ,但如果有复制完成块 ,就用已经给的复制完成块索引作为积极块索引 (复制完成块一般是未在块首地址更新未积极块 ,当确实当前要操作的块 ,必须等到旧块擦除之后才在块首地址正式更新) ,复制完成块索引规则是如 :从索引 0 开始 ,第一个确定为复制完成块的索引确定为复制完成块索引 (这是在仅有一个复制完成块的情况下) ,但如果之后有上一个块是积极块的复制完成块出现 ,复制完成块索引更新为此复制完成块的索引 ,再遇到上一个块同样是复制完成块且预定的积极块数量只有 1 时 ,复制完成块索引更新为此复制完成块的索引 。

          完成掉电前积极块索引和运行状态的确定后 ,开始恢复 :

          如果是第一次运行或第一次运行未完成初始化 ,则将擦除次数 (默认 1) 给所有没写上的块写上 ,然后将索引 0 的块标为积极块 ,

          如果掉电前运行状态是有块更新 ,则重新启动块交换 。

         如果掉电前是有复制完成块 ,则检查被复制的块 (即最久的积极块) 的状态 ,如果是可交替状态 ,则找出最久积极块索引 + 1 的块 (如果索引超出索引最大数则该索引为零) 的擦除次数 ,通过对比擦除次数 ,如果次数不正确 ,则重新擦除 ;如果不是可交替或是擦除块状态 ,则擦除最久积极块 。然后最久积极块擦除的要重新写入擦除次数 ,然后让复制完成块变为积极块状态 。

三 、参考资料

①  NXP 官网 :《MPC5744PRM》

下载链接 :

https://www.nxp.com/products/processors-and-microcontrollers/power-architecture/mpc55xx-5xxx-mcus/ultra-reliable-mpc57xx-mcus/ultra-reliable-mpc574xp-mcu-for-automotive-industrial-safety-applications:MPC574xP?tab=Documentation_Tab

 

  • NXP 官网 :《AN4868》

下载链接 :

https://www.nxp.com.cn/docs/en/application-note/AN4868.pdf

 

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论