二、Nand flash硬件基础

1.1 基本存储单元与层次结构

!请添加图片描述(https://i-blog.csdnimg.cn/direct/44b9b78c6edb413c937ecb2c97de601d.png

参数 GD5F4GM8 实例 说明
总容量 512 MiB (0x20000000) bit 厂商标 4Gbit
Page Size 2048 bytes 最小读/写单位(主数据区)
OOB Size 128 bytes 存放 ECC、坏块标记、元数据
Block Size 128 KiB (64 pages) 最小擦除单位
擦写寿命 ~10 万次/块 需坏块管理和磨损均衡

地址计算方式:

c 复制代码
物理地址 = Block 编号 × Block Size + Page 编号 × Page Size + Column 偏移
例:Block 0, Page 0 → 0x0000_0000
   Block 1, Page 0 → 0x0002_0000  (128KiB = 0x20000)
   Block 0, Page 1 → 0x0000_0800  (2KiB = 0x800)

1.2 关键操作特性

NAND Flash 的三种基本操作有严格约束,是驱动设计和使用的前提:

操作 单位 方向 约束
读(Read) Page 任意 无约束,可随机读
写(Program) Page 1 → 0 只能将 bit 1 写为 0,不能反向;写前必须擦除
擦(Erase) Block 全部复位 0xFF 以 Block 为单位,整块擦除

1.3 OOB 区域与坏块管理

OOB(Out-Of-Band)是每个 Page 附带的备用字节区域,用于存储元数据:

c 复制代码
GD5F4GM8 OOB 布局(128 bytes)

Byte  0     : 坏块标记(Bad Block Marker,BBM)
              非 0xFF → 该 Block 为坏块,驱动应跳过
Byte  1-3   : 保留
Byte  4-63  : 用户可用 / 文件系统元数据(UBI EC/VID header 可存于此)
Byte 64-127 : On-die ECC 校验数据(由芯片硬件自动写入/校验)

坏块两种类型:

类型 产生时间 标记位置 处理方式
出厂坏块(Factory Bad Block) 芯片出厂前 Block 0 或 1 的 OOB byte 0 非 0xFF 驱动扫描时跳过
运行时坏块(Runtime Bad Block) 使用过程中 擦写失败后由驱动标记 UBI 重映射到备用 PEB

坏块扫描流程(MTD 驱动初始化时):

plain 复制代码
spinand_init()
  → 扫描所有 Block 的 OOB byte 0
  → 非 0xFF 的标记为坏块
  → 注册到 MTD 的 bbt(bad block table)
  → UBI 挂载时读取 bbt,跳过坏块 PEB

工作中注意事项:

  • 不要手动擦除 Block 0(通常存储 U-Boot,且含出厂坏块标记)

  • UBI 会自动管理坏块,无需上层显式处理

  • nanddump --oob 可查看每个 Block 的 OOB 坏块标记

1.4 SPI NAND vs 并行NAND

对比项 SPI NAND 并行 NAND (Raw NAND)
接口 SPI / QSPI(4 线) 8/16 bit 并行总线 + CLE/ALE/CE
引脚数 少(6-8 根) 多(20+ 根)
内置 ECC 通常有(on-die ECC) 通常需外部 ECC 引擎
驱动复杂度 较低(spinand 子系统) 较高(需处理时序/ECC)
最高速度 较低(受 SPI 总线限制) 较高(并行带宽大)
典型场景 IoT、车载、消费类 大容量、高性能存储
本项目 GD5F4GM8 via QSPI ---

SPI NAND 命令流程

1.5 ECC 错误纠正码

ECC 用于检测和纠正存储单元因衰老、辐射等导致的 bit 翻转(bit flip)。

GD5F4GM8 on-die ECC 规格:

参数
ECC 算法 BCH
纠错能力 8 bits per 512 bytes
ECC 数据位置 OOB 区域(由芯片硬件维护)
状态寄存器 STATUS5:4 → ECCS bits

ECC 状态码:

plain 复制代码
ECCS[1:0] = 00 → 无错误
ECCS[1:0] = 01 → 有错误已纠正(bit flip ≤ 8)
ECCS[1:0] = 10 → 有错误已纠正但接近阈值(≥ 8 flip,需关注!)
ECCS[1:0] = 11 → 不可纠正错误(UE),该页数据不可信

驱动处理逻辑(gigadevice.c ecc_get_status):

c 复制代码
static int gd5fxgq4_variant2_ecc_get_status(struct spinand_device *spinand,
                                             u8 status)
{
    switch (status & GD5FXGQ4_STATUS_ECC_MASK) {
    case GD5FXGQ4_STATUS_ECC_NO_BITFLIPS:
        return 0;                          // 正常
    case GD5FXGQ4_STATUS_ECC_1_7_BITFLIPS:
        return 7;                          // 有翻转,已纠正
    case GD5FXGQ4_STATUS_ECC_8_BITFLIPS:
        return 8;                          // 接近极限
    case GD5FXGQ4_STATUS_ECC_UNCOR_ERROR:
        return -EBADMSG;                   // 不可纠正,MTD 返回 EIO
    }
}

工作中注意:

  • ECC 接近阈值(如返回 7~8)不是立即故障,但 UBI 会将该 PEB 标记为疑似坏块并搬迁数据

  • 系统日志出现大量 mtd: corrected X bit errors 说明 Flash 老化,需关注

1.6 Flash 可靠性与寿命

工作中影响 NAND 可靠性的几个重要概念:

磨损均衡(Wear Leveling):

NAND 每个 Block 有约 10 万次擦写寿命。如果某个区域被频繁写入(如日志),会提前损坏。UBI 通过磨损均衡算法(Wear Leveling)在所有可用 Block 上均匀分配写操作。

plain 复制代码
UBI 维护每个 PEB 的 EC(Erase Count):
- 写操作时选择 EC 最小的空闲 PEB
- 静态数据也会定期迁移(static wear leveling)
- EC 差值超过阈值(默认 4096)时强制迁移

写放大(Write Amplification):

由于 NAND 必须先读-修改-写(如修改一个字节需读整页→修改→擦整块→写整页),实际写入量远大于逻辑写入量。UBIFS 通过日志结构化写入尽量减少写放大。

数据保持(Data Retention):

  • NAND 存储电荷会随时间泄漏,通常保证 1~10 年(取决于温度和擦写次数)

  • 高温加速衰减,长期不通电设备存在数据丢失风险

  • ECC 可以纠正少量 bit flip,UBI 的 scrubbing 机制会定期重写数据

Read Disturb:

读取一个 Page 时,同一 Block 内其他 Page 的存储单元会受到轻微干扰(高压影响)。多次读取后可能导致 bit flip。UBI 的 scrubbing 可以检测并纠正这种问题。