author: xiexun
tags: Raw NAND, ONFI, SEMC, BootROM, OOB, ECC
Raw NAND 驱动库学习
代码学习:NXP i.MX RT BootROM 风格的并行 NAND 驱动库ONFD(GitHub - JayHeng/RT-RNAND: An ONFI-compliant Raw NAND flash universal driver | 一款基于ONFI标准的并行NAND Flash通用驱动,适配多款MCU硬件接口,目前主要适配i.MXRT的SEMC接口 · GitHub)
核心概念
Raw NAND :主机直接面对 Page/Block 命令接口;ECC、坏块、地址映射由主机侧完成。
Managed NAND(eMMC/UFS 等):主机多使用 LBA;ECC/坏块/磨损由片内 FTL 完成,OOB 对主机不可见。
ONFI(Open NAND Flash Interface):并行 Raw NAND 规范集合,覆盖命令集、时序、Parameter Page、存储组织等,不仅是软件协议名。
text
Device → Plane/LUN → Block → Page → Main + Spare(OOB)
| 单位 | 含义 |
|---|---|
| Page | Read / Program 粒度 |
| Block | Erase 粒度 |
| Plane/LUN | 并行/多逻辑单元组织 |
Main / Spare(OOB):同一 Page 内主数据区与冗余区。Spare 存放:
| 内容 | 作用 |
|---|---|
| ECC | 校验/纠正 Main(或 ECC chunk)bit 错误 |
| 坏块标记 | 标识 Block 是否禁用(位置以 datasheet 为准,常见在特定 Page 的 Spare) |
| 元数据 | 逻辑页号、有效标志等管理信息(是否使用取决于软件) |
寻址:
| 视图 | 含义 |
|---|---|
| 线性字节地址 | mem_read/write(addr) 的入参;软件抽象 |
pageIndex + column |
Flash 层单位;addr/pageSize、addr%pageSize |
| Column (CA) / Row (RA) | 器件周期;由 SA = pageIndex × pageSize 按 COL 位宽拆分 |
操作约束:
-
擦后为
0xFF;Program 将目标 bit 置 0,不可按 RAM 原地改写 -
阵列操作后必须等 Ready(R/B# 或 Status
0x70) -
必须处理 ECC 与坏块
薄地址抽象对比FTL:
memory层做线性换算、页缓存、DBBT 整块跳过;无磨损均衡/GC/精细 LPN 映射。
上层:RT BootROM、Flashloader下载工具;并非通用文件系统块层。
【控制器 IP 行为】
SEMC(fsl_semc)职责:把软件意图变成命令/地址/数据周期,硬件自动翻译成 ONFI 8080 总线时序波形,并完成与 NAND 芯片的硬件交互、等待响应、数据收发闭环。
- 命令编码 :
get_nand_code打包 NAND 命令字节与地址/命令模式(CMD+ADDR、HOLD、READ/WRITE),硬件行为输出时序:CLE 拉高、ALE 拉低、WE# 脉冲,将命令字节锁存进 NAND - 地址拆分 :软件提供字节地址
SA;IP 按columnAddressWidth生成 CA/RA(x8/x16 规则不同)。Spare 访问时 COL 可能多扩 1 bit(convert_nand_address) ,硬件行为输出时序:ALE 拉高、CLE 拉低、WE# 连续脉冲,逐周期发送地址。 - 数据通路:命令完成后经 IP Bus 或 AXI 搬 Main;Ready 走 R/B# 或 Status。
【软件驱动逻辑】
text
BootROM / Flashloader
→ semc_nand_memory # 线性地址、页缓存、DBBT、FCB/DBBT
→ semc_nand_flash # ONFI 页/块、Parameter、ECC
→ fsl_semc # IP 事务
→ NAND
| 层 | 对外接口 | 职责 |
|---|---|---|
fsl_semc |
IP 命令事务 | 地址转换、发令、搬数 |
semc_nand_flash |
pageIndex / blockIndex |
命令序列、ECC |
semc_nand_memory |
字节 address |
换算、缓存、跳坏块、Boot 控制块 |
Boot 相关数据 :FCB(含 nandConfig、固件副本表、DBBT 搜索区);DBBT(坏块列表)。mem_init:默认配置拉起 → 搜 FCB → 按 FCB 重 init → 搜 DBBT。
生命周期:片内 BootROM 极简路径加载下一级后交权;完整驱动在 Flashloader/应用侧接力,一般不同时占用同一套 SEMC+NAND 软件栈。本 archive 即 Boot/下载链路风格实现。
关键代码片段
配置字段(semc_nand_flash.h):
c
uint32_t bytesInPageDataArea; /* Main */
uint32_t bytesInPageSpareArea; /* Spare/OOB */
uint32_t pagesInBlock;
uint32_t blocksInPlane;
uint32_t planesInDevice;
页读:页号 → SA → SEMC → issue (semc_nand_flash_read_page):
c
ipgCmdAddr = pageIndex * config->bytesInPageDataArea; /* 字节 SA */
ipgCmdAddr = semc_ipg_command_convert_nand_address(&config->memConfig, ipgCmdAddr);
status = semc_nand_issue_read_page(config, ipgCmdAddr); /* 0x00/0x30 + wait */
/* 再 IP/AXI 搬 Main,并按配置做 Device/Software ECC */
Spare 路径地址整形 (semc_ipg_command_convert_nand_address):
c
/* 输入始终为 byte address;Main 路径通常直接使用 SA */
if (columnAddressOption == SpareAreaAccess) {
converted = (sa >> col) << (col + 1);
converted |= sa & ((1u << col) - 1u);
}
线性读 (semc_nand_mem_read):
c
pageIndex = address / pageSize;
pageColumnAddress = address % pageSize;
semc_nand_skip_bad_blocks(&pageIndex); /* DBBT:逻辑连续,物理可跳块 */
/* cache miss → flash_read_page;再按 column 切片拷出 */
调用链:
text
mem_read → pageIndex/column + skip BB → flash_read_page
→ convert_nand_address + issue_read_page + 搬数/ECC