第 3 章 - Azure RTOS LevelX NAND 支持

NAND 闪存通常用于大数据存储,大数据存储是文件系统的典型特征。 NAND 内存由块组成。 每个 NAND 块中都有一系列页。 NAND 块是可擦除的,这意味着 NAND 块中的所有页会被擦除(设置为全部)。 每个 NAND 块页都有一组备用字节,Azure RTOS LevelX 将其用于簿记、错误块管理和错误检测。 NAND 块页有多种大小可供选择。 最常见的页大小为:

Page Size 备用字节
256 8
512 16
2048 64

NAND 内存与 NOR 内存的区别在于没有直接访问,也就是说,NAND 内存不能像 NOR 内存那样直接从处理器读取。 NAND 内存只能在擦除后写入有限的次数。 这也与 NOR 内存不同,只要写入请求清除设置位,NOR 内存就可以无限次写入。 最后,与每个页相关联的备用字节对于 NAND 闪存来说是唯一的。 典型的备用字节配置如下表中所示。

备用字节 字节数 配置
8 字节 0-2: ECC 字节
字节 3、4、6、7: LevelX 扇区映射
字节 5: 错误的块标志
16 字节 0-3、6-7: ECC 字节
字节 8-11: LevelX 扇区映射
字节 12-15: 未使用
字节 5: 错误的块标志
64 字节 0: 错误的块标志
字节 2-5: LevelX 扇区映射
字节 6-39: 未使用
字节 40-63: ECC 字节

LevelX 利用每个 NAND 页的 4 个备用字节来跟踪映射到物理 NAND 页的逻辑扇区。 这 4 个字节用于实现 LevelX 专有格式的 32 位无符号整数。 32 位字段的高位(位 31)用于指示逻辑扇区到页面的映射有效。 如果此位为 0,则此页中的信息将不再有效。 下一位(位 30)用于指示此页即将过时,正在写入新的扇区。 位 29 用于指示映射项写入完成的时间。 如果位 29 为 0,表示映射条目写入完成。 如果设置了位 29,表示映射项正在写入过程中。 位 30 和 29 用于在更新新的闪存页时从潜在断电中恢复。 最后,低 29 位 (28-0) 包含页的逻辑扇区号。

LevelX 映射条目

含义
31 有效标志。 如果逻辑扇区并非设置为全部,表示映射有效
30 过时标志。 如果清除此项,表示该映射可能已过时或即将过时。
29 如果此位为 0,表示映射条目写入完成
0-28 映射到此物理页面的逻辑扇区(并非全部)。

LevelX 还利用每个 NAND 块的第一页进行块擦除计数,并将其用作块满时的映射页列表。 LevelX 中 NAND 块的第一页格式如下所示:

LevelX 块页 0 格式
[块擦除计数]
[第 1 页扇区映射]
...
[第“n”页扇区映射]
[0xF0F0F0F0]

注意

只有在块已满时(即,块的所有页都已写入),才能写入页映射信息。 这样就可以在运行时更快速地搜索免费页和逻辑扇区。

NAND 错误块支持

NAND 内存也比 NOR 内存更容易出现错误块。 这在很大程度上是因为 NAND 制造商可以通过允许错误块并要求软件绕过这些错误块来提高产量。 LevelX 通过简单地映射错误块来处理 NAND 错误块管理。

LevelX 还为基础 LevelX 驱动程序提供了 256 字节汉明纠错编码 (ECC) 的 API,以用于计算新的 ECC 代码或对页面的每个 256 字节部分中的页面读取执行 1 位纠错。

NAND 驱动程序要求

LevelX 需要基础的 NAND 闪存驱动程序,该驱动程序特定于基础闪存部分和硬件实现。 在初始化期间,通过 API lx_nand_flash_open 将驱动程序指定给 LevelX。 LevelX 驱动程序的原型如下所示。

INT nand_driver_initialize(LX_NAND_FLASH *instance);

实例参数指定 LevelX NAND 控制块。 驱动程序初始化函数负责为关联的 LevelX 实例设置所有其他的驱动程序级服务。 以下列表显示了每个 LevelX NAND 实例所需的服务。

  • 阅读页
  • 写入页
  • 块擦除
  • 验证擦除的块
  • 验证擦除的页
  • 获取块状态
  • 设置块状态
  • 获取块额外字节
  • 设置块额外字节
  • 系统错误处理程序

驱动程序初始化

这些服务是通过在驱动程序初始化函数内的 LX_NAND_FLASH 实例中设置函数指针来设置的。 驱动程序初始化函数还指定块总数、每个块的页数、每页的字节数,以及足够将一个页读入内存的 RAM 区域。 在返回 LX_SUCCESS 之前,驱动程序初始化函数可能还执行其他特定于设备和/或实现的初始化任务。

驱动程序读取页

LevelX NAND 驱动程序“读取页”服务负责读取 NAND 闪存的特定块中的特定页面。 所有错误检查和纠正逻辑都由驱动程序服务负责。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“读取页”服务的原型如下所示。

INT nand_driver_read_page(
    ULONG block,
    ULONG page,
    ULONG *destination, 
    ULONG words);

其中块和页标识要读取的页,目标和字指定要放置页内容的位置以及要读取的 32 位字的数量 。

驱动程序写入页

LevelX NAND 驱动程序“写入页”服务负责将特定页面写入 NAND 闪存的指定块。 所有错误检查和 ECC 计算都由驱动程序服务负责。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“写入页”服务的原型如下所示。

INT nand_driver_write_page(
    ULONG block, 
    ULONG page,
    ULONG *source, 
    ULONG words);

其中块和页标识要写入的页,源和字指定写入的源以及要写入的 32 位字的数量 。

注意

LevelX 在写入闪存页时依赖驱动程序进行低级错误检测,这通常涉及读回页并与写入缓冲区进行比较,以确保写入成功。

驱动程序块擦除

LevelX NAND 驱动程序“块擦除”服务负责擦除 NAND 闪存的指定块。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“块擦除”服务的原型如下所示。

INT nand_driver_block_erase(ULONG block,  
    ULONG erase_count);

其中块标识要清除的块。 出于诊断目的提供了参数 erase_count。 例如,当擦除计数超过特定阈值时,驱动程序可能需要警告应用程序软件的另一部分。

注意

LevelX 在删除块时依赖驱动程序进行低级错误检测,这通常涉及确保块的所有页都是一整页。

验证擦除的驱动程序块

LevelX NAND 驱动程序“验证擦除的块”服务负责验证 NAND 闪存的指定块是否被擦除。 如果已擦除,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未擦除该块,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“验证块擦除”服务的原型如下所示:

INT nand_driver_block_erased_verify(ULONG block);

其中块指定要验证是否被擦除的块。

注意

LevelX 依赖驱动程序检查所有页面以及每页的所有字节(包括备用字节和数据字节),以确保将其擦除(包含所有字节)。

验证擦除的驱动程序页

LevelX NAND 驱动程序“验证擦除的页”服务负责验证 NAND 闪存的指定块的指定页是否被擦除。 如果已擦除,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未擦除该页,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“验证页擦除”服务的原型如下所示:

INT nand_driver_page_erased_verify(
    ULONG block,  
    ULONG page);

其中块指定是哪个块,页指定要验证是否已擦除的页 。

注意

LevelX 依赖驱动程序检查指定页面的所有字节(包括备用字节和数据字节),以确保将其擦除(包含所有字节)。

获取驱动程序块状态

LevelX NAND 驱动程序“获取块状态”服务负责检索 NAND 闪存的指定块的错误块标志。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“获取块状态”服务的原型如下所示。

INT nand_driver_block_status_get(
    ULONG block,  
    UCHAR *bad_block_byte);

其中块指定是哪个块,bad_block_byte 指定错误块标志的目标 。

设置驱动程序块状态

LevelX NAND 驱动程序“设置块状态”服务负责设置 NAND 闪存的指定块的错误块标志。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“设置块状态”服务的原型如下所示:

INT nand_driver_block_status_set(
    ULONG block,
    UCHAR bad_block_byte);

其中块指定是哪个块,bad_block_byte 指定错误块标志的值 。

获取驱动程序块额外字节

LevelX NAND 驱动程序“获取块额外字节”服务负责检索与 NAND 闪存的特定块的特定页相关的额外字节。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“获取块额外字节”服务的原型如下所示:

INT nand_driver_block_extra_bytes_get(
    ULONG block,  
    ULONG page, 
    UCHAR *destination, 
    UINT size);

其中块指定是哪个块,页指定特定的页,目标指定额外字节的目标 。 参数大小指定要获取的额外字节数。

设置驱动程序块额外字节

LevelX NAND 驱动程序“设置块额外字节”服务负责在 NAND 闪存的特定块的特定页中设置额外字节。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“设置块额外字节”服务的原型如下所示:

INT nand_driver_block_extra_bytes_set(
    ULONG block,  
    ULONG page, 
    UCHAR *source, 
    UINT size);

其中块指定是哪个块,页指定特定的页,源指定额外字节的源 。 参数大小指定要设置的额外字节数。

驱动程序系统错误

LevelX NAND 驱动程序“系统错误处理程序”服务负责设置 LevelX 检测到的处理系统错误。 此例程中的处理依赖于应用程序。 如果成功,LevelX NAND 驱动程序将返回 LX_SUCCESS。 如果未成功,LevelX NAND 驱动程序将返回 LX_ERROR。 LevelX NAND 驱动程序“系统错误”服务的原型如下所示:

INT nand_driver_system_error(
    UINT error_code,  
    ULONG block, 
    ULONG page);

其中块指定是哪个块,页指定发生 error_code 表示的错误的特定页 。

NAND 模拟驱动程序

LevelX 提供模拟的 NAND 闪存驱动程序,该驱动程序只使用 RAM 来模拟 NAND 闪存部件的操作。 默认情况下,NAND 模拟驱动程序提供 8 个 NAND 闪存块,每个块 16 页,每页 2048 字节。

模拟的 NAND 闪存驱动程序初始化函数为 lx_nand_flash_simulator_initialize,在 lx_nand_flash_simulator.c 中定义。 这个驱动程序还为编写特定的 NAND 闪存驱动程序提供了一个很好的模板。

NAND FileX 集成

如前文所述,LevelX 不依赖于 FileX 进行操作。 应用程序软件可以直接调用所有 LevelX API,以将原始数据存储/检索到 LevelX 提供的逻辑扇区。 但是,LevelX 也支持 FileX。

文件 fx_nand_flash_simulated_driver.c 包含用于 NAND 闪存模拟的示例 FileX 驱动程序。 该驱动程序有趣的一点是,它将通常由 FileX 使用的 512 字节逻辑扇区合并为使用 2048 字节页面对 LevelX 模拟器的单个逻辑扇区读/写请求。 这样可以更高效地使用 NAND 闪存。 LevelX 的 NAND 闪存 FileX 驱动程序为编写自定义 FileX 驱动程序提供了一个很好的起点。

注意

FileX NAND 闪存格式应该是小于 NAND 闪存提供的扇区的一个完整块大小。 这将有助于确保在损耗均衡处理期间获得最佳性能。 在 LevelX 损耗均衡算法中提高写入性能的其他技巧包含以下内容。

  1. 确保所有写入大小都恰好是一个或多个群集,并且在确切的群集边界上启动。
  2. 通过 API 的 FileX fx_file_allocate 类执行大文件写入操作之前,请先预分配群集
  3. 确保已启用 FileX 驱动程序以接收释放扇区信息,并通过调用 lx_nor_flash_sector_release 在驱动程序中处理对驱动程序发出的释放扇区的请求
  4. 定期使用 lx_nand_flash_defragment 释放尽可能多的 NAND 块,从而提高写入性能
  5. 利用 lx_nand_flash_extended_cache_enable API 提供各种 NAND 块资源的 RAM 缓存,以提高性能