NAND MCP 软件设计规范
编制日期 2026-09-25 | 版本号 V1.1
面向 XTX XT61M4G8D2TA-B8Bxx / XT61M2G8D2TA-B8Bxx 系列(SLC NAND die + LPDDR2 die 合封,BGA-162)的固件驱动设计约定 —— 覆盖 异步 25 ns 命令组帧、5 周期地址映射、双 plane(2-plane) 并行与 Cache 流水、主机 8 bit/544 B ECC、 坏块判定与 BBT、LPDDR2 初始化与刷新、掉电数据完整性,逐条给出可落地的命令序列、判据与核查清单。
| 文档编号 | NAND-MCP-SW-SPEC-001 · V1.1 | 版本 | V1.1(依手册 A1.9 / A1.6 校订) |
|---|---|---|---|
| 适用器件 | XT61M4G8D2TA-B8Bxx(4 Gb + 2 Gb) XT61M2G8D2TA-B8Bxx(2 Gb + 2 Gb) | 参考手册 | XTX nMCP SPEC A1.9(4G2G,2025-12-06) XTX nMCP SPEC A1.6(2G2G,2024-09-24) |
| NAND 接口 | 异步 x8,tRC ≥ 25 ns,无同步模式 | 配套文档 | NAND MCP 电路设计指南(硬件侧) |
目的与范围
本文规定 XTX nMCP 中 NAND Flash 部分与 LPDDR2 部分的软件设计约束:命令组帧、地址映射、读写擦流程、主机 ECC、坏块管理、性能手段、DRAM 初始化与电源管理。
本文以手册 Table 3 命令表为唯一命令真值来源,以 Table 6 状态输出表为状态判据,所有时序数值均取自手册 AC / Program & Erase 特性表。
不覆盖范围:硬件原理图与布线(见电路设计指南);文件系统与 FTL 算法选型(本文只给约束,不指定实现);LPDDR2 控制器 IP 的内部实现(本文只给初始化与刷新约束);量产测试流程。
这颗器件不是 ONFI 同步器件,也没有多 CE 交错能力。把 eMMC 或 ONFI 2.x/3.x 的经验直接套过来,是本文档要拦住的第一类错误。
+ 2 Gb LPDDR2
/ 544 B
~ 2048
文档使用说明
- 没有内部控制器。ECC、坏块管理、磨损均衡、掉电保护全部由主机软件承担;SoC 必须具备硬件 ECC 引擎(8 bit/544 B)。
- 只有异步模式。手册命令表里没有 ECh / EFh / EEh,也没有 NV-DDR2/3 同步时序模式——不存在「Set Features 切同步」这一步。
- 只有 1 个 CE#、1 个 R/B#。因此不存在多 CE interleave。并行只能靠「双 plane(2-plane) 同时操作」与「Cache 流水」两种手段。
命令 用十六进制 + h 表示;危险 表示会导致数据丢失或器件损坏;建议 表示工程经验而非硬性约束。所有「典型值」在未标注时均指 25 ℃、VCC = 1.8 V 条件。1 · 器件定位与合封架构
XTX nMCP 把一颗 SLC NAND die 与一颗 LPDDR2 die 封进同一个 BGA-162。两者电气上完全独立、可各自操作,共用的是封装、电源轨与上电时序。软件必须先认清「这是异步 Raw NAND,不是 ONFI 同步器件」,否则后面所有配置都会做无用功。
1.1 器件定位与关键参数
| 项目 | XT61M4G8D2TA-B8Bxx | XT61M2G8D2TA-B8Bxx | 软件影响 |
|---|---|---|---|
| NAND 容量 / 位宽 | 4 Gb(512 M × 8) | 2 Gb(256 M × 8) | 均为 x8,列地址 2 周期 |
| 页大小 | (4096 + 256) B | (2048 + 128) B | spare 区不同,OOB 布局参数必须按料号取 |
| 块大小 | (256 K + 16 K) B = 64 页 | (128 K + 8 K) B = 64 页 | 都是 64 页/块 → 块地址 = 页地址 >> 6 |
| 块数 | 2048 | 2048 | 行地址 PA6~PA16 共 11 bit |
| District(Plane) | 2 个,各 1024 块 | 2 个,各 1024 块 | 见 1.3,是唯一的片内并行手段 |
| 内部寄存器 | 2 × 4352 B | 2 × 2176 B | Page Buffer + Data Cache,Cache 流水的基础 |
| 接口模式 | 异步 Only,串行读周期 25 ns(Min) | 无同步模式、无 Set Features | |
| ECC 要求 | 8 bit / 544 B | 硬件 ECC 引擎的硬指标 | |
| P/E 次数 | 50 000(配 8 bit/544 B ECC) | 磨损均衡的预算基数 | |
| 数据保持 | 10 年(Typ) | 巡检周期设定依据 | |
| 有效块 NVB | 2008(Min)~ 2048(Max) | 最多 40 个出厂坏块 ≈ 2% | |
| NAND 电源 | VCC 1.7 ~ 1.95 V(单电源) | 没有 VCCQ,见第 11 章 | |
| LPDDR2 | 2 Gb(8 Banks × 8 M × 32),1066 Mbps | 两料号 DRAM 侧完全一致 | |
| 封装 | 162 ball BGA 8 × 10.5 × 0.9 mm | 162 ball BGA 8 × 10.5 × 0.95 mm | 仅硬件选型相关 |
NAND 侧没有片内 ECC,手册明确要求 8 bit/544 B 由主机完成。若 SoC 只有 NAND 接口而无硬件 ECC 引擎,软件实现的 BCH 解码耗时远大于 tR(25 µs),性能与实时性都无法满足——不要选这颗 MCP。
块 0 保证有效。手册承诺出厂时 Block 0 是好块,因此 boot 代码放在块 0 是安全的。但请注意:BBT 不要存在块 0——很多 SoC 的 boot ROM 强制从块 0 读取,把元数据放在那里会互相干扰。
1.2 合封架构与两套总线
1.3 阵列组织与 District
手册把 NAND 阵列划分为 2 个 District(等价常见说法的「双 plane / 2-plane」),这是这颗器件唯一的片内并行能力:
| 层级 | 划分 | 数量 |
|---|---|---|
| District | District 0 = 偶数块(Block 0/2/4 … 2046) District 1 = 奇数块(Block 1/3/5 … 2047) | 2 |
| Block | 每个 District 1024 块 | 2048 |
| Page | 每块 64 页 | 131 072 |
| Byte | 每页 4352 B(4 Gb 料号)/ 2176 B(2 Gb 料号) | — |
1.4 地址映射与 ID 识别
地址固定 5 个周期(列 2 + 行 3),手册 Table 1 定义如下:
| 周期 | I/O7 | I/O6 | I/O5 | I/O4 | I/O3 | I/O2 | I/O1 | I/O0 |
|---|---|---|---|---|---|---|---|---|
| 第 1 周期 | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 |
| 第 2 周期 | L | L | L | CA12 | CA11 | CA10 | CA9 | CA8 |
| 第 3 周期 | PA7 | PA6 | PA5 | PA4 | PA3 | PA2 | PA1 | PA0 |
| 第 4 周期 | PA15 | PA14 | PA13 | PA12 | PA11 | PA10 | PA9 | PA8 |
| 第 5 周期 | L | L | L | L | L | L | L | PA16 |
位域含义:CA0~CA12 列地址(页内偏移 + spare);PA0~PA16 页地址;其中 PA6~PA16 是块地址(11 bit),PA0~PA5 是块内页号(6 bit,64 页)。
因此:页地址 = (块号 << 6) | 块内页号;擦除时行地址 = 块号 << 6。手册同时说明第 6 个地址周期会被芯片忽略,不要多发。
上电后通过 90h ID Read 读回 5 字节识别码,两个料号的取值:
| 字节 | 含义 | XT61M4G8D2TA | XT61M2G8D2TA |
|---|---|---|---|
| 1st | Maker Code | 98h | 98h |
| 2nd | Device Code | ACh | AAh |
| 3rd | Chip Number / Cell Type | 90h(1 chip,SLC) | 90h(1 chip,SLC) |
| 4th | 页大小 / 块大小 / 位宽 | 26h | 15h |
| 5th | Plane(District)Number | 76h | 76h |
识别表必须收录两个 Device Code。同封装不同容量的料号可能在产线上互换,驱动只认一个码会直接返回 MCP_ENODEV 导致整机不开机。建议识别流程:先校验 Maker Code = 98h,再按 Device Code 查表取几何参数;未知码一律报错,不做「猜配置」。
2 · 术语与缩写
先统一语言。尤其注意 District / Page Buffer / Data Cache 这三个词——它们来自 XTX 手册,与 ONFI 规范里的 Plane / Cache Register 不是同一套说法,混用会导致对命令流程的误解。
2.1 术语表
| 术语 | 全称 / 对应 | 本文含义 |
|---|---|---|
| MCP / nMCP | Multi-Chip Package | 本文特指 XTX 的 NAND + LPDDR2 合封器件 |
| District | 手册用词(≈ Plane) | 片内两个可独立操作的子阵列;D0 = 偶块,D1 = 奇块 |
| Page Buffer | 页缓冲寄存器 | 4352 B(2 Gb 料号 2176 B)静态寄存器之一,阵列与 Data Cache 之间的中转 |
| Data Cache | 数据缓存寄存器 | 另一个同级寄存器,主机串行读写的直接对象 |
| Page | 页 | 编程 / 读出的最小单位:4096 B 数据 + 256 B spare |
| Block | 块 | 擦除的最小单位:64 页 |
| Spare / OOB | 冗余区 | 页尾额外区域,放 ECC 校验位与元数据 |
| NVB | Number of Valid Blocks | 有效块数,2008(Min)~ 2048(Max) |
| CLE / ALE | Command / Address Latch Enable | 区分当前周期是命令还是地址 |
| R/B# | Ready/Busy | 开漏输出,低 = 忙,需外部上拉 |
| tR | Cell array to register | 阵列→寄存器时间,典型 25 µs / 最大 60 µs |
| tPROG | 编程时间 | 300 µs(Typ)/ 700 µs(Max) |
| tBERASE | 块擦除时间 | 3.5 ms(Typ)/ 10 ms(Max) |
| tDCBSYR1 | Read Cache 忙时间 | 31h / 3Fh 之后,25 µs(Max) |
| tDCBSYW1/2 | Write Cache 忙时间 | 11h 后 10 µs(Max);15h 后 700 µs(Max) |
| NOP | Number of Partial Program Cycles | 同一页可分段编程的次数,Max 4 |
| MRW / MRR | Mode Register Write / Read | LPDDR2 的模式寄存器读写命令 |
| DAI | Device Auto-Initialization | LPDDR2 上电自检完成标志,轮询 MR0.DAI 获知 |
| PASR / ATCSR | 部分阵列自刷新 / 温补自刷新 | LPDDR2 的低功耗特性 |
| BBT | Bad Block Table | 主机维护的坏块表,需持久化到 NAND |
| UECC | Uncorrectable ECC | 超出纠错能力的不可纠正错误 |
2.2 书写约定
- 命令码一律写成
00h/30h形式,与手册 Table 3 保持一致。 - 地址周期按手册 Table 1 的
CAn/PAn位域描述,不写「第几个字节」这类含糊说法。 - 时序数值标注 Typ / Max:软件超时预算取 Max,性能估算取 Typ。
- 状态位按手册 Table 6 的
I/O0~I/O7编号,不采用 ONFI 的 FAIL / ARDY / RDY 命名。 - 凡是本文给出但手册未明确规定的做法(如 BBT 结构、错误码),均标注为建议。
为什么不用 ONFI 术语:这颗器件的命令表里没有 ECh(Parameter Page)、EFh/EEh(Set/Get Features)、78h(Read Status Enhanced),也没有同步时序模式。它是异步 Legacy 接口的 SLC NAND,几何参数靠 90h ID 与料号识别表获得,而不是读参数页。用 ONFI 的命名去描述它,会让人误以为可以发这些命令。
3 · 驱动架构与初始化
初始化要解决三件事:让 LPDDR2 起来、让 NAND 进入已知状态、拿到正确的几何参数。三者有严格的先后顺序,且 NAND 侧在上电内部初始化期间只接受 FFh 与 70h——发别的命令一律无效。
3.1 软件分层
| 层级 | 职责 | 是否本文范围 |
|---|---|---|
| 文件系统 / FTL | 映射表、磨损均衡、垃圾回收 | 仅给约束 |
| NAND 抽象层 | 坏块表 BBT、替换池、ECC 分级处置、掉电保护 | 是 |
| NAND 驱动 | 命令组帧、地址映射、R/B# 等待、状态判定 | 是 |
| 硬件 ECC 引擎 | 8 bit / 544 B 编解码,返回纠正位数 | 约束 |
| NAND 控制器 | 异步时序产生、CE#/CLE/ALE/WE#/RE# 控制 | 约束 |
| LPDDR2 控制器 | 初始化、MRW/MRR、刷新、自刷新 | 是 |
| 电源管理 | 上电/掉电时序、PFO 检测 | 是 |
3.2 上电初始化流程
3.3 器件识别与几何参数建立
这颗器件没有参数页可读,几何参数来自两条路径:90h ID 识别表 + 料号常量表。推荐做法是「ID 校验 + 表驱动」:
/* 伪代码:识别并建立几何参数 */
static const struct mcp_geom {
uint8_t dev_code; /* 90h 读回的第 2 字节 */
const char *name;
uint32_t page_data; /* 数据区字节数 */
uint32_t page_spare;/* spare 区字节数 */
uint32_t pages_per_block;
uint32_t blocks;
} geom_table[] = {
{ 0xAC, "XT61M4G8D2TA", 4096, 256, 64, 2048 },
{ 0xAA, "XT61M2G8D2TA", 2048, 128, 64, 2048 },
};
int mcp_nand_probe(void)
{
uint8_t id[5];
nand_reset(); /* FFh,等待 R/B# 回高 */
nand_read_id(id); /* 90h + 地址 00h,读 5 字节 */
if (id[0] != 0x98) /* Maker Code 必须为 98h */
return MCP_ENODEV;
for (i = 0; i < NR(geom_table); i++)
if (geom_table[i].dev_code == id[1])
return apply_geom(&geom_table[i]);
return MCP_ENODEV; /* 未知器件码:报错,绝不猜配置 */
}
三个识别陷阱:
- 只比对 Device Code 而忽略 Maker Code,可能把别家的同码器件认进来;
- 用页大小去反推料号,而不是用 ID——一旦同容量新料号上市就会误判;
- 识别失败后仍用默认几何参数继续跑,这是最危险的一种:读写地址全错,且症状是「有时对有时错」。
3.4 上电与掉电时序约束
- 上电期间 WP# 拉低:手册指出 WP# 可用于上电/掉电期间防止数据被意外改写;初始化完成后再拉高。
- NAND 内部初始化期只发 FFh / 70h:发其它命令无效且可能损坏数据。
- 掉电前不得进行写/擦:手册明确要求「写/擦完成前不要断电」,电量低时应主动停止写入。
- PFO 中断处理:收到掉电预警立即停止新操作,完成在途写,刷写 BBT 与映射表。
- 电源爬升 ≤ 20 ms(tINIT0):超出该上限需检查 PMIC 配置与软启动。
boot 阶段的特殊路径:若 SoC 的 boot ROM 直接从 NAND 读取启动代码,此时 DRAM 尚未初始化,驱动不能依赖任何缓存。建议实现一条裸读路径:只用栈上小缓冲、只发 00h-30h 顺序读、不做 ECC 之外的任何处理,等 DRAM 起来后再切到完整驱动。
4 · 命令集与组帧
本章所有命令逐条取自手册 Table 3。这颗器件不支持 ECh(读参数页)、EFh/EEh(Set/Get Features)、78h(增强状态读)与任何同步模式命令——如果你在别处看到这些命令的用法,那是 ONFI 同步器件,不适用这里。
4.1 命令表(手册 Table 3 真值)
| 功能 | 第一周期 | 第二周期 | Busy 期间可接受 | 软件要点 |
|---|---|---|---|---|
| 串行数据输入 | 80h | — | — | 编程序列的起始,后接地址与数据 |
| 读(Read) | 00h | 30h | — | 00h 后发 5 周期地址,30h 启动阵列搬运 |
| 随机读出列地址变更 | 05h | E0h | — | 读 spare / 跳读页内位置,后面再跟 2 周期列地址 |
| 带 Cache 读 | 31h | — | — | 把下一页从 Page Buffer 搬到 Data Cache |
| Cache 读最后一页启动 | 3Fh | — | — | 结束 Cache 读序列,漏发会停在 Cache 模式 |
| 自动页编程 | 80h | 10h | — | 10h 后才真正写入阵列 |
| 随机写入列地址变更 | 85h | — | — | 分段编程,受 NOP ≤ 4 约束 |
| 带 Cache 自动编程 | 80h | 15h | — | Cache Program,掉电风险见 6.3 |
| 多页编程(无 Cache) | 80h | 11h | — | 两个 District 同时编程 |
| 多页编程(最后一页) | 81h | 10h | — | 第二个 District 用 81h 起始 |
| 多页带 Cache 编程 | 81h | 15h | — | 与 80h-15h 配对使用 |
| 页拷贝读出 | 00h | 3Ah | — | Copy-Back 的读阶段 |
| 页拷贝带 Cache 编程 | 8Ch | 15h | — | Copy-Back 的写阶段 |
| 页拷贝最后页编程 | 8Ch | 10h | — | 结束 Copy-Back 序列 |
| 自动块擦除 | 60h | D0h | — | 两周期设计用于防误擦 |
| ID 读取 | 90h | — | — | 地址 00h,读 5 字节 |
| 状态读取 | 70h | — | ○ | 单页 / 单块操作用它 |
| 多页编程 / 多块擦除状态读取 | 71h | — | ○ | Multi 操作结束必须用它 |
| 复位 | FFh | — | ○ | 终止当前操作 |
手册明确规定:器件处于 Busy 状态时,除状态读与复位外不得输入任何命令。这条约束可以用来简化驱动设计——忙时不需要考虑命令仲裁,只管判状态。
80h 之后的命令白名单。一旦发出 80h,后续只允许 85h(换列)、10h(编程)、11h(多页)、15h(Cache 编程)、FFh(复位)。输入其它命令会导致本次编程不执行,且器件切换到该命令指定的模式——表现为「写成功但数据没进去」,极难定位。
4.2 命令-地址-数据组帧
4.3 异步时序约束
器件只有异步模式,以下参数决定控制器时序配置(Ta = −40 ~ 85 ℃,VCC = 1.7 ~ 1.95 V):
| 符号 | 参数 | Min | Max | 单位 |
|---|---|---|---|---|
| tRC | 读周期时间 | 25 | — | ns |
| tWC | 写周期时间 | 25 | — | ns |
| tREA | RE# 访问时间 | — | 20 | ns |
| tRP | 读脉冲宽度 | 12 | — | ns |
| tWP | 写脉冲宽度 | 12 | — | ns |
| tCS | CE# 建立时间 | 20 | — | ns |
| tCLS / tALS | CLE / ALE 建立时间 | 12 | — | ns |
| tDS / tDH | 数据建立 / 保持 | 12 / 5 | — | ns |
| tWB | WE# 高到 Busy | — | 100 | ns |
| tRR | Ready 到 RE# 下降沿 | 20 | — | ns |
| tWHR | WE# 高到 RE# 低 | 60 | — | ns |
| tRHW | RE# 高到 WE# 低 | 30 | — | ns |
| tWW | WP# 高到 WE# 低 | 100 | — | ns |
两条手册注释:① tCLS 与 tALS 不得短于 tWP;② tCS 应长于 tWP + 8 ns。控制器配置时把这两条作为约束检查项,而不是只看最小值。
4.4 状态读取与判据(手册 Table 6)
/* 状态位定义(手册 Table 6) */
#define SR_FAIL 0x01 /* I/O0:1 = 失败 */
#define SR_CS2 0x02 /* I/O1:70h = Cache 编程的上一页;71h = District 0 CS1(两 District 的 OR) */
#define SR_D1_CS1 0x04 /* I/O2:71h = District 1 CS1;70h 下 Not used 恒 0 */
#define SR_D0_CS2 0x08 /* I/O3:71h = District 0 CS2;70h 下 Not used 恒 0 */
#define SR_D1_CS2 0x10 /* I/O4:71h = District 1 CS2;70h 下 Not used 恒 0 */
#define SR_PB_READY 0x20 /* I/O5:1 = Page buffer 就绪 */
#define SR_DC_READY 0x40 /* I/O6:1 = Data cache 就绪 */
#define SR_WP_N 0x80 /* I/O7:0 = 处于写保护 */
int nand_wait_ready(uint32_t timeout_us)
{
uint8_t st;
while (!rb_is_high()) { /* 优先判引脚,最快 */
if (--timeout_us == 0)
return MCP_ETIMEOUT;
delay_1us();
}
st = nand_status(0x70);
if (!(st & SR_WP_N)) /* 写保护生效 */
return MCP_EPROGFAIL;
if (st & SR_FAIL) /* 本页失败 */
return MCP_EPROGFAIL;
return MCP_OK;
}
I/O0 = 1是失败(与「1 表示成功」的直觉相反);I/O5 / I/O6 = 1是就绪,与 R/B# 引脚「高 = 就绪」方向一致,但一个是位值一个是引脚电平,不要混用同一套变量名;I/O7 = 0是写保护生效——读到 0 时写/擦根本不会执行。
70h 与 71h 的分工:普通单页编程 / 单块擦除用 70h;多页编程(80h-11h、81h-10h、81h-15h)与多块擦除必须用 71h。因为 71h 下 I/O1 ~ I/O4 分别给出两个 District 的 Chip status 1 / 2(其中 I/O1 还是两个 District 的 OR),能定位到具体 District;而 70h 只有 I/O1 会置位表示「有任一页失败」,定位不到——这就是 Multi 结束必须用 71h 的原因。若确实只读了 70h,就应判定两个块都要读回校验,不能只处理其中一个。
4.5 复位的正确用法
| 复位时机 | 手册说明 | 软件处理 |
|---|---|---|
| Read 中 | tRST ≤ 5 µs | 复位后可直接发新命令 |
| Program 中 | tRST ≤ 10 µs | 被中断页内容不可信,需重新擦除 |
| Erase 中 | tRST ≤ 500 µs | 等待时间比其它状态长两个数量级,别按 5 µs 写延时 |
| Ready 中 | tRST ≤ 5 µs | 正常 |
| Cache 编程中 | 可能连前一页编程一起停止 | 受影响的页都要重新处理 |
连续发两次 FFh 是允许的,手册专门列出了这种情况。但复位后不要立刻假设器件状态——先发 70h 确认回到 Ready,再继续后续操作。