只读分享解决方案文档原理方案设计📅 2026-09-23🔖 V1.0⏳ 29天有效(至 2026-10-31)
🔧解决方案&应用市场分析 -> 原理方案设计 -> 串行总线系列产品方案 -> eMMC_软件设计规范
生成时间 2026-09-23 14:45:56 有效期至 2026-10-31 23:59:59(29天有效(至 2026-10-31))
同系列 · 原理方案设计

eMMC 软件设计规范

编制日期 2026-09-23

EMMC-SW-SPEC-001 · V1.0
面向嵌入式系统 eMMC(embedded MultiMediaCard,JESD84 / eMMC 5.1)主机侧驱动的设计约束与推荐实现: 覆盖 初始化状态机、EXT_CSD 寄存器体系、分区与启动、读写擦命令序列、 Cache 与掉电保护、BKOPS / HPI 与 健康寿命管理,逐条给出可落地的命令序列、判据与超时预算。

18章节
5附录
512 B扇区访问粒度
CMD6EXT_CSD 唯一写入口
文档编号EMMC-SW-SPEC-001
版本 / 状态V1.0 · 正式发布
适用器件JEDEC eMMC 4.5 / 5.0 / 5.1 器件(BGA-153 / BGA-169 / BGA-100 / BGA-221),容量 4 GB ~ 256 GB
基准器件XTX TLC eMMC 5.1(HS400)XT28EG32GA2YUECGA(32 GB)/ XT28EG64GA2YUECGA(64 GB),FBGA-153 11.5×13×1.0 mm,VCC=3.3 V / VCCQ=1.8 V;文中标注「本器件」的数值均取自该手册 版本号 V1.0
参考标准JEDEC JESD84-B51(eMMC 5.1)· JESD84-B50(5.0)· JESD84-A441(4.41)· XTX eMMC Spec Rev 1.0
配套文档eMMC_电路设计指南.html(原理图 / 双电源 / BGA 扇出 / HS400 布线 / 焊接与生产)

0. 目的与范围

本规范约束 eMMC 器件在嵌入式固件中的主机侧软件行为:初始化状态机、EXT_CSD 配置、分区切换、 读写擦命令序列、Cache 与掉电保护、后台操作与健康管理。凡本文与器件 datasheet 冲突,以 datasheet 为准。

  • 统一初始化路径:CMD0 → CMD1 → CMD2 → CMD3 → CMD7 → CMD8 固化为唯一标准流程,提速必须「先宽度、后时序、切完必验」。
  • 统一配置入口:所有器件配置只走 CMD6 写 EXT_CSD,禁止裸写、禁止批量改字节。
  • 统一 busy 判据:以 DAT0 释放 + CMD13 状态位为准,禁止用固定延时代替。
  • 统一掉电语义:明确哪些操作是原子的、哪些必须 flush、哪些必须提前通知器件。
  • 统一健康策略:寿命字段读什么、读到什么值做什么动作,形成可运维的闭环。
不在本规范范围:硬件原理图、电源设计、PCB 布线与焊接(见《eMMC 电路设计指南》); 文件系统与 FTL 算法选型(eMMC 的 FTL 在器件内部,主机不可干预);RPMB 的密码学协议细节与密钥托管方案。

一句话结论:eMMC 是「把麻烦事都封进 BGA 里的 Managed NAND」—— 坏块、ECC、磨损均衡、映射表全部由器件内部完成,主机只面对 512 B 扇区的块设备接口。 代价是主机必须自己管好三件事:Cache 什么时候 flush、掉电前怎么通知器件、后台操作什么时候让器件做。 这三件事没做好,是 eMMC 项目里 90% 的丢数据与掉速事故的根因。

文档使用说明

  • 本文是《eMMC 电路设计指南》的软件配套篇:硬件篇解决「怎么接、怎么供电、怎么走线」,本文解决「驱动怎么写、协议怎么等、错误怎么救」。
  • 命令码、EXT_CSD 偏移、时序值取自 JESD84-B51。不同厂商、不同固件版本的器件在可选特性上存在差异,移植前必须逐条核对 datasheet。
  • 文中 C 代码为协议骨架伪代码,演示命令序列与判断条件,非特定平台可直接编译的完整实现;mmc_send_cmd()、now_ms() 等为平台相关底层。
  • EXT_CSD 偏移量统一写作 EXT_CSD[179] 形式,方括号内为字节偏移(0~511),不是 bit 位。
贯穿全文的四条铁律

① 绝不用固定延时替代 busy 判断——编程/擦除/GC 时间受磨损、温度、随机性影响,波动可达数量级;
② 每个 CMD6 都当成一次「写操作」——它返回 R1b,必须等 DAT0 释放并检查 SWITCH_ERROR;
③ 提速前先确认双方能力——读 DEVICE_TYPE 再决定切哪一档,切完必须读写回环验证并保留回退路径;
④ Cache 是性能开关,也是风险开关——开启前先想清楚掉电时有没有时间 flush,想不清楚就别开。

1 · 器件定位与系统架构

eMMC 不是一颗裸 NAND,而是「NAND 阵列 + 控制器 + FTL + MMC 接口」的完整子系统。 软件设计的第一件事,是认清主机到底能管什么、管不了什么——把器件内部的事当成自己的事,是新手最常见的过度设计。

1.1 器件定位与关键参数

512 B
访问粒度
读写擦一律按扇区(Sector)寻址
6 类
可寻址分区
Boot1/2 · RPMB · GPP1~4 · User
400 MB/s
HS400 峰值
8-bit DDR 200 MHz + DS 选通
4~256 GB
容量范围
典型 8 / 16 / 32 / 64 GB
1.8 V
VCCQ(HS 模式)
本器件仅支持 1.8 V 档(DEVICE_TYPE=57h)
370 ms
通用 CMD6 超时
本器件 GENERIC_CMD6_TIME = 0x25
2550 ms
掉电通知超时
本器件 POWER_OFF_LONG_TIME = 0xFF
≤ 80%
建议容量使用率
给 FTL 留足 GC 与磨损均衡空间

参数取自 JESD84-B51 与主流器件 datasheet;具体数值(Cache 容量、Erase Group 大小、P/E 次数)以所用料号 datasheet 为准。

1.2 系统分层与器件内部架构

eMMC 对外只暴露一组 MMC 总线(CLK / CMD / DAT0~7 / DS / RST_n),且是点对点连接—— 一条总线上只能挂一颗 eMMC,不支持多设备并联(这与 SD 卡的多卡总线是本质区别)。

eMMC 系统分层:主机 / 总线 / 器件内部三层主机 SoC:eMMC Host 控制器块设备层 → MMC 协议层 → 主机控制器驱动(HAL)MMC 总线(点对点,只允许挂一颗器件)CLK · CMD · DAT0~7 · DS(HS400 选通) · RST_neMMC 器件(BGA 封装)MMC 接口 + 协议引擎(命令解析 / 数据 FIFO / CRC)Flash 控制器:FTL 映射 · ECC · 磨损均衡 · 坏块替换 · 垃圾回收NAND 阵列:MLC / TLC + OP 预留空间(对主机完全不可见)主机只看到「512 B 扇区块设备」,坏块 / ECC / 磨损均衡全部由器件内部完成
图 1-1 eMMC 系统分层与内部架构(主机只面对块设备接口)
主机能管的 vs 器件自己管的

主机负责:初始化与提速、分区切换、读写擦命令序列、Cache 开关与 flush、掉电通知、BKOPS 调度、寿命监控、写保护策略。

器件内部负责(主机不可见也不可干预):坏块管理与替换、ECC 纠错、磨损均衡、逻辑到物理地址映射、垃圾回收、OP 预留空间管理。

这意味着:不要再在主机侧实现一套坏块表或磨损均衡——那既无效又浪费 RAM。主机真正要做的是「给器件留出做这些事的时间与空间」。

1.3 地址空间与访问粒度

eMMC 把复杂的 NAND 几何全部藏了起来,主机看到的是一个连续的 32 bit 扇区地址空间。 但有三种粒度在软件里迟早要碰到:

eMMC 地址空间与三种访问粒度主机可见:User Data Area / Boot1 / Boot2 / RPMB / GPP1~4全部按 32 bit 扇区地址(Sector,512 B)线性寻址器件内部(主机不可见)Erase Group擦除粒度 = HC_ERASE_GRP_SIZE × 512 KB;Erase 命令按它对齐Write Protect Group写保护粒度 = HC_WP_GRP_SIZE × Erase Group;写保护按它对齐NAND Page / Block真正的读写擦物理单元,由 FTL 自行映射,主机无从干预软件结论:读写按 512 B 对齐即可,Trim / Erase / 写保护才需要关心内部粒度
图 1-2 eMMC 地址空间与三种访问粒度
  • Sector(512 B):读写的最小单位,也是所有地址参数的单位(容量 > 2 GB 的器件)。读写必须 512 B 对齐。
  • Erase Group:擦除粒度 = HC_ERASE_GRP_SIZE × 512 KB(本器件 = 1 → 512 KB)。CMD35/CMD36 的地址会被器件向下取整到 Erase Group 边界。
  • Write Protect Group:写保护粒度 = HC_WP_GRP_SIZE × HC_ERASE_GRP_SIZE × 512 KB(本器件 = 16 × 512 KB = 8 MB)。写保护只能按它整块施加。
本器件实测粒度(EXT_CSD)

HC_ERASE_GRP_SIZE[224] = 1 → Erase Group = 512 KB;
HC_WP_GRP_SIZE[221] = 0x10 → Write Protect Group = 8 MB;
SEC_COUNT[215:212] = 07480000h(64 GB 版)→ 用户区扇区总数;BOOT_SIZE_MULT[226] = 0x20 → Boot1/Boot2 各 4096 KB;RPMB_SIZE_MULT[168] = 0x20 → RPMB 4096 KB。
这些值必须运行时从 EXT_CSD 读出,不要按「典型 eMMC」硬编码——换料、换固件版本都可能变。

Erase 的地址会被「悄悄对齐」

给 CMD35 一个未对齐的地址,器件不会报错,而是忽略低于 Erase Group 大小的低位、向下取整。 后果是多擦了相邻区域——如果那里放着有效数据,就静默丢了。正确做法:擦除前自己把起止地址按 Erase Group 大小对齐,并确认范围内的所有数据都不再需要。

1.4 与 SD NAND / SPI NAND / SPI NOR 的关键差异(移植必读)

维度SPI NORSPI NANDSD NANDeMMC
主机接口SPI(1/2/4 线)SPI(1/2/4 线)SDIO / SPIMMC 总线(1/4/8 bit + DS)
访问粒度字节页(2048 B + 备用区)块(512 B)扇区(512 B)
坏块管理无主机必须做(BBT)器件内部器件内部(主机无需参与)
ECC无片内 / 主机侧,须判读器件内部器件内部,主机不可见
擦除Sector/Block 擦Block 擦(64 页)器件内部Erase Group(器件内部调度)
可寻址分区无无无Boot1/2 · RPMB · GPP1~4 · UDA
掉电通知无无无POWER_OFF_NOTIFICATION(独有)
寿命查询无靠自己统计 P/E无DEVICE_LIFE_TIME_EST / PRE_EOL_INFO
Cache无页 Cache 寄存器无写 Cache(需显式 flush)
封装 / 可焊性SOP/WSON,易WSON/LGA,易LGA/WSON,易BGA,需 HDI + X 光检测
从 SPI NAND / SD NAND 移植过来最危险的三个假设
  • 假设「写完就落盘」:裸 NAND 里 10h 后轮询 OIP 结束就是写完了;eMMC 开了 Cache 后,DAT0 释放只代表数据进了 Cache。
  • 假设「要自己管坏块」:SPI NAND 必须有 BBT;eMMC 完全没有坏块概念,实现一套是纯粹的浪费。
  • 假设「掉电就掉电」:eMMC 提供了标准掉电通知机制,不用它就是放弃了一个免费的可靠性提升。

2 · 术语与缩写

本章统一术语口径。eMMC 的术语体系与 NAND 系(页/块/备用区)完全不同,混用会导致文档与代码对不上。

2.1 接口与协议术语

缩写 / 术语全称本文中的含义
eMMCembedded MultiMediaCard板载 Managed NAND 子系统;本文默认 JESD84-B51(5.1)
CMD / RespCommand / Response48 bit 命令帧与响应;R1/R1b/R2/R3 等响应类型
RCARelative Card Address器件相对地址,由 CMD3 分配(常用 0x0001)
OCROperating Conditions Register电压窗口与上电完成标志;CMD1 响应 bit31 = 就绪
CIDCard IDentification128 bit 器件标识:厂商 ID、产品名、序列号、生产日期
CSDCard Specific Data器件基础参数(CMD9 读取)
EXT_CSDExtended CSD512 字节扩展寄存器(CMD8 读 / CMD6 写),eMMC 的核心管理面
R1 / R1b—32 bit 状态响应;R1b 附带 busy 信号(DAT0 拉低)
SWITCH_ERROR—R1 bit7;CMD6 写 EXT_CSD 失败标志

2.2 存储与分区术语

缩写 / 术语全称本文中的含义
Sector—512 B,读写与寻址的最小单位
Erase Group—擦除粒度 = HC_ERASE_GRP_SIZE × 512 KB
Write Protect Group—写保护粒度,通常远大于 Erase Group
UDAUser Data Area用户数据区,默认承载全部容量,文件系统主要用这里
Boot 1 / Boot 2Boot Area Partition启动分区,容量 = 128 KB × BOOT_SIZE_MULT
RPMBReplay Protected Memory Block带 HMAC-SHA256 认证与防重放的分区;密钥 / 计数器放这里
GPP 1~4General Purpose Partition通用分区,可做增强属性与独立写保护,用于系统 / 数据 / 日志隔离
Enhanced / pSLCEnhanced User Data Area把部分区域配置为 pseudo-SLC:容量换寿命、性能与数据保持
OPOver-Provisioning器件内部预留空间(主机不可见)+ 用户主动留出的空闲容量
FTLFlash Translation Layer器件内部的逻辑→物理映射层,主机不可干预

2.3 特性与机制术语

缩写 / 术语全称本文中的含义
HS200 / HS400—高速时序档位;HS200 = SDR 200 MHz,HS400 = DDR 200 MHz + DS 选通
DSData StrobeHS400 模式下由器件输出的选通信号,用于主机采样
Tuning—HS200/HS400 下用 CMD21 找最佳采样相位的过程
Cache—器件内部写缓存;开则快、掉电则丢,需 FLUSH_CACHE 落盘
Reliable Write—CMD23 bit31;要求该次写入原子完成(要么新要么旧)
Forced Programming—CMD23 bit24;仅 Cache 开启时有效,强制立即写入 NAND
BKOPSBackground Operations器件内部垃圾回收与磨损均衡等后台任务
HPIHigh Priority Interrupt高优先级中断;打断器件长时间操作(CMD12/CMD13 带 HPI 标志)
Trim / Discard—标记数据无效;Trim 读出值确定,Discard 不确定
Sanitize—物理清除所有未映射数据,用于出厂恢复与报废清密
FFUField Firmware Update器件固件在线升级;过程掉电可能变砖
POWER_OFF_NOTIFICATION—EXT_CSD[34];主机断电前通知器件的标准机制

2.4 本文书写约定

  • 命令写作 CMD13 形式(不加 h 后缀),与 JEDEC 文本一致。
  • EXT_CSD 字段写作 EXT_CSD[179],方括号内为字节偏移;位域写作 EXT_CSD[179][2:0]。
  • 数值:十六进制用 0x 前缀;位序 bit0 为最低位。
  • 时序:「典型值 / 最大值」成对给出时,软件超时一律按最大值预算;typ 值只用于安排轮询节奏。
  • 代码示例为 C 伪代码,省略主机控制器寄存器细节,只表达命令序列与判据。

3 · 驱动架构与初始化

eMMC 的初始化是一条不能跳步的状态机:每一步都把器件推到下一个状态,跳步的直接后果是「命令发出去没响应」。 提速则是初始化之后单独的一段,必须「先慢后快、切完必验」。

3.1 分层职责

层职责禁止做的事
文件系统 / 块设备层提交 bio / request;下发 flush、discard 语义直接拼 MMC 命令、直接算扇区边界之外的地址
MMC 核心协议层初始化状态机、EXT_CSD 解析、分区切换、BKOPS 调度绕过状态检查直接发读写命令
命令封装层CMD17/18/24/25、CMD23、CMD13、CMD6、CMD35/36/38省略 busy 等待、忽略 SWITCH_ERROR
主机控制器驱动(HAL)时钟、总线宽度、DMA、CRC、tuning 引擎、超时在数据传输中途改时钟频率或总线宽度

3.2 上电初始化流程

完整的识别流程只有一条标准路径,六步缺一不可:

上电初始化:MMC 协议状态机推进顺序CMD0GO_IDLE_STATE→ IdleCMD1SEND_OP_COND轮询 OCR bit31→ ReadyCMD2ALL_SEND_CID→ IdentCMD3SET_RELATIVE_ADDR→ StbyCMD7SELECT_CARDRCA ≪ 16→ TranCMD8SEND_EXT_CSD读回 512 B→ Tran① CMD1 必须循环重发并设超时(典型 1 s),不可用固定延时代替轮询② CMD8 只能在 Transfer 态发送;EXT_CSD 未读到则无从判断能力提速:必须「先慢后快、先宽度后时序」,切完必验配总线宽度CMD6 → BUS_WIDTH[183]1→4→8 bit切时序CMD6 → HS_TIMING[185]HS / HS200HS200 TuningCMD21 采样相位找到采样窗口切 HS400CMD6 → HS_TIMING=3DS 选通 + DDR⑤ 切换后必须做读写回环校验;失败要能逐级回退到低速模式HS400 不允许直接从 Legacy/HS 切入,必须先经 HS200 完成 tuning
图 3-1 上电初始化状态机与提速路径
int mmc_init(void)
{
    uint32_t resp, rca = 0x0001;
    uint8_t  ext_csd[512];

    /* ① 电源与复位就绪:VCC/VCCQ 稳定、RST_n 释放、输出 400 kHz 以下时钟 */
    host_set_clock(400000);              /* 识别阶段必须低速 */
    host_set_width(1);

    /* ② CMD0:复位到 Idle */
    send_cmd(CMD0, 0x00000000, RSP_NONE);     /* GO_IDLE_STATE → Idle(0xF0F0F0F0 是 GO_PRE_IDLE_STATE,用于复位后重引导 boot) */
    delay_us(200);

    /* ③ CMD1:电压协商 + 等待上电完成(bit31 = 1) */
    if (wait_cmd1_ready(1000) != 0)      /* 超时 1 s,循环重发 */
        return -E_MMC_NO_RESPONSE;

    /* ④ CMD2 / CMD3:读 CID、分配 RCA */
    send_cmd(CMD2, 0, RSP_R2);           /* 128 bit CID */
    send_cmd(CMD3, rca << 16, RSP_R1);   /* → Standby */

    /* ⑤ CMD7:选中,进入 Transfer 态 */
    send_cmd(CMD7, rca << 16, RSP_R1);

    /* ⑥ CMD8:读 512 B EXT_CSD(只能在 Transfer 态发) */
    if (mmc_send_ext_csd(ext_csd) != 0)
        return -E_MMC_EXT_CSD;

    return mmc_setup_from_ext_csd(ext_csd);   /* 提速 + 分区(见 3.4 / 第 5 章) */
}

/* CMD1 必须循环重发并设超时 —— 固定延时必然在某些器件上翻车 */
static int wait_cmd1_ready(uint32_t timeout_ms)
{
    uint32_t t0 = now_ms(), resp;
    do {
        send_cmd(CMD1, 0x40FF8080, RSP_R3);
        resp = host_last_resp();
        if (resp & 0x80000000u)          /* bit31 = 上电完成 */
            return 0;
        delay_ms(10);
    } while (now_ms() - t0 < timeout_ms);
    return -E_TIMEOUT;
}
CMD1 用固定延时代替轮询,是「某些批次起不来」的经典原因

器件的上电就绪时间随容量、温度、内部初始化任务(尤其是掉电恢复)波动,可以从几十毫秒到几百毫秒。 写死一个 delay_ms(100) 在实验室样机上没问题,量产时换个批次或低温冷启动就起不来。

正确做法:循环发送 CMD1 并检查 OCR bit31,总超时设 1 s。这是 JEDEC 明确要求的做法,没有捷径。

3.3 器件识别与能力协商

初始化完成后,器件的全部能力都在 CID / CSD / EXT_CSD 里。驱动必须从寄存器读,不许硬编码:

信息来源关键字段软件用途
厂商 / 型号 / 序列号CID(CMD2)MID、PNM、PSN日志、追溯、产线记录
eMMC 版本EXT_CSD[192] REV0x07 = 5.0,0x08 = 5.1决定可用特性集(5.1 才有 pSLC 增强等)
用户区容量EXT_CSD[215:212] SEC_COUNT容量 = SEC_COUNT × 512 B唯一的容量来源,不要用 CSD 推算
支持的速度档位EXT_CSD[196] DEVICE_TYPE位域:HS200/HS400 及对应电压决定能切到哪一档
Cache 容量EXT_CSD[252:249] CACHE_SIZE0 = 无 Cache;否则 × 1 KB是否值得开 Cache
可靠写扇区数EXT_CSD[222] REL_WR_SEC_C单次可靠写的最大扇区数Reliable Write 的分批依据
Erase / Trim 粒度EXT_CSD[224] / [232]HC_ERASE_GRP_SIZE、TRIM_MULT擦除对齐与超时预算
分区切换时间EXT_CSD[199]PARTITION_SWITCH_TIME切分区后的等待时间
寿命与预警EXT_CSD[267..269]PRE_EOL_INFO、LIFE_TIME_A/B健康管理(第 12 章)
/* 能力解析:全部从 EXT_CSD 取,禁止硬编码容量与几何 */
static int mmc_setup_from_ext_csd(const uint8_t *e)
{
    uint32_t sec   = le32(e + 212);       /* SEC_COUNT[215:212] */
    uint64_t cap   = (uint64_t)sec * 512; /* 用户区字节容量 */
    uint8_t  rev   = e[192];              /* EXT_CSD_REV      */
    uint8_t  dtype = e[196];              /* DEVICE_TYPE      */
    uint32_t cache = le32(e + 249);       /* CACHE_SIZE[252:249] */

    mmc.capacity   = cap;
    mmc.rev        = rev;
    mmc.hs200_1v8  = !!(dtype & 0x02);    /* HS200 @ 1.8 V */
    mmc.hs400_1v8  = !!(dtype & 0x04);    /* HS400 @ 1.8 V */
    mmc.cache_kb   = cache;               /* 0 = 无 Cache */

    log_i("eMMC rev=%d cap=%llu MB cache=%u KB hs400=%d",
          rev, cap >> 20, cache, mmc.hs400_1v8);
    return 0;
}

3.4 提速:先宽度、后时序、切完必验

上电后器件默认1-bit + Legacy(最高 26 MHz),所有高速档位都要主机主动切换。顺序错了会直接通信失败。

  1. 确认 VCCQ 已切到 1.8 V:HS200/HS400 只在低电压档位下保证,3.3 V 下不许切(本器件 DEVICE_TYPE=0x57 只开放 1.8 V 档的 HS200/HS400,不支持 1.2 V)。
  2. 先配总线宽度:CMD6 写 EXT_CSD[183] BUS_WIDTH —— 0 = 1-bit,1 = 4-bit,2 = 8-bit,5 = 4-bit DDR,6 = 8-bit DDR。
  3. 再切时序:CMD6 写 EXT_CSD[185] HS_TIMING —— 0 = Legacy,1 = High Speed,2 = HS200,3 = HS400,4 = HS400ES。
  4. HS200 必须 tuning:反复发 CMD21,主机在不同采样相位上采集并选出有效窗口。
  5. HS400 必须先经 HS200:完成 HS200 tuning 后,写 HS_TIMING = 3 并启用 DS 选通。
  6. 切完必验:做一次已知数据的写入 → 读回 → 比对,失败则逐级回退(HS400 → HS200 → HS → Legacy)。
/* 提速:每一步都等 busy、每一步都检查 SWITCH_ERROR */
static int mmc_switch(uint8_t index, uint8_t value, uint32_t timeout_ms)
{
    uint32_t arg = (0x03u << 24)          /* Access = Write Byte */
                 | ((uint32_t)index << 16)
                 | ((uint32_t)value << 8)
                 | 0x00u;                /* CmdSet = 标准命令集 */
    uint32_t st;

    send_cmd(CMD6, arg, RSP_R1b);
    if (wait_dat0_release(timeout_ms))   /* 关键:CMD6 也带 busy */
        return -E_TIMEOUT;
    send_cmd(CMD13, mmc.rca << 16, RSP_R1);
    st = host_last_resp();
    if (st & (1u << 7))                  /* SWITCH_ERROR */
        return -E_SWITCH_ERR;
    return 0;
}

int mmc_upgrade_to_hs400(void)
{
    mmc_switch(183, 2,  500);            /* BUS_WIDTH  = 8-bit      */
    mmc_switch(185, 2,  500);            /* HS_TIMING  = HS200      */
    if (host_hs200_tuning())             /* CMD21 采样相位调谐       */
        return -E_TUNING;
    mmc_switch(183, 6,  500);            /* 8-bit DDR               */
    mmc_switch(185, 3,  500);            /* HS_TIMING  = HS400      */
    return mmc_speed_verify();           /* 读写回环校验,失败即回退 */
}
超时别写 100 ms:本器件通用 CMD6 超时是 370 ms

JEDEC 给的通用默认 GENERIC_CMD6_TIME = 0x0A(10 × 10 ms = 100 ms),但本器件实测为 0x25 = 37 → 370 ms。 按 100 ms 判超时,在 Cache 回收、GC 撞车、分区切换等场景下会误判失败,进而触发本不必要的复位与回退。

正确做法:超时值从 GENERIC_CMD6_TIME 读出再乘单位(10 ms)算出来,再加 30% 余量;只有那些自带更长超时字段的操作(Sanitize、掉电通知、分区切换、初始化后第一次分区生效)才改用对应字段。

跳过 tuning 的后果是「偶发数据错误」,不是「连不上」

HS200/HS400 下如果不做 tuning,器件能连上、能跑、大部分时间也对, 只在特定温度、电压、老化程度下偶尔出现 CRC 错误或数据位错。这类问题在实验室几乎抓不到,量产时才爆发。

另一类常见错误是HS400 直接从 Legacy 切入:协议要求先到 HS200 完成 tuning,跳过这一步的器件行为未定义。

3.5 初始化自检清单

  • 上电时钟 ≤ 400 kHz,CMD1 循环轮询 bit31 且超时 ≥ 1 s
  • CMD8 在 Transfer 态发送,已成功读回 512 B EXT_CSD
  • 容量从 SEC_COUNT 计算,未硬编码;已与预期容量对账
  • 提速前已确认 DEVICE_TYPE 与 VCCQ 电压档位匹配
  • 总线宽度与 HS_TIMING 的写入顺序正确(宽度在前)
  • HS200/HS400 已完成 tuning,并有读写回环验证
  • 提速失败有逐级回退路径,且回退后功能可用
  • 初始化日志记录了 REV、容量、速度档位、Cache 容量,便于现场追溯

4 · EXT_CSD 寄存器体系

EXT_CSD 是 eMMC 的管理面:512 字节,包罗万象。 所有配置只有一个入口——CMD6。理解它的分段、写入规则与生效时机,是写对 eMMC 驱动的前提。

4.1 512 字节的分段

EXT_CSD:512 字节,CMD8 读 / CMD6 写Properties Segment [0..191]只读:容量(SEC_COUNT)、版本(REV)、能力(DEVICE_TYPE)、寿命、时序Mode Segment [192..511]可读写:总线宽度、HS_TIMING、分区配置、Cache、BKOPS、写保护访问方式:CMD8 读整块(512 B) / CMD6 改单个字节写后一律返回 R1b:必须等 DAT0 释放,并检查 SWITCH_ERROR(R1 bit7)常见误解:EXT_CSD 不是「寄存器数组」,每次 CMD6 只能改一个字节,且部分字段要断电/复位才生效
图 4-1 EXT_CSD 512 字节分段与访问方式
  • Properties Segment(0~191):只读。容量、版本、能力、时序、寿命都在这里。
  • Mode Segment(192~511):可读写。总线宽度、时序、分区、Cache、BKOPS、写保护都在这里。

4.2 CMD6:唯一的写入口

CMD6(SWITCH)32 bit 参数位域31:26置 025:24Access23:16Index15:8Value7:3置 02:0CmdSetAccess:00 = 命令集切换 01 = 置位 10 = 清位 11(0x3) = 写字节(最常用)Index = EXT_CSD 字节偏移(0~511) Value = 写入值 CmdSet = 0(标准命令集)例:切 HS400 → arg = (0x03≪24) | (185≪16) | (0x03≪8) | 0x00例:开 Cache → arg = (0x03≪24) | (33≪16) | (0x01≪8) | 0x00
图 4-2 CMD6(SWITCH)32 bit 参数位域
CMD6 是 R1b 命令,自带 busy

这一点最容易被忽略:CMD6 不是「写个寄存器就完事」,它可能触发器件内部的重新配置、分区重算甚至内部擦除。 返回类型是 R1b,器件会把 DAT0 拉低直到操作结束。不等 busy 就发下一条命令,会得到不可预期的结果。

超时由 GENERIC_CMD6_TIME 给出,单位 10 ms:本器件 = 0x25 → 370 ms(JEDEC 通用默认 0x0A → 100 ms,切勿照抄); 个别操作(Sanitize、掉电通知、分区切换、初始化后第一次分区生效)有自己的更长超时字段,见附录 C 与附录 F。

4.3 写 EXT_CSD 六步准则

  1. 先读后写:用本地缓存的 512 B EXT_CSD 确认当前值,避免无意义的改写。
  2. 一次只改一个字节:CMD6 的 Index 是单字节偏移,没有批量写。
  3. 用 Write Byte 模式:Access = 0x3(写字节);置位/清位模式只在少数场景需要。
  4. 等 busy 释放:轮询 DAT0 或用主机控制器的 R1b 完成中断,超时按 GENERIC_CMD6_TIME 预算。
  5. 检查 SWITCH_ERROR:发 CMD13 读状态,R1 bit7 = 1 表示这次写入被拒绝。
  6. 回读确认:关键配置(总线宽度、时序、分区)写完后重新 CMD8 读回比对。

4.4 生效时机:三种,别搞混

生效时机典型字段软件要注意什么
立即生效HS_TIMING、BUS_WIDTH、CACHE_CTRL、PARTITION_ACCESS写完等 busy 结束即可使用新配置
复位后生效PARTITION_CONFIG 的 BOOT 相关位、部分写保护设置写完需 CMD0 或 RST_n 才生效
断电 / 硬件复位后生效BOOT_SIZE_MULT、RPMB_SIZE_MULT、GP_SIZE_MULT、ENH_SIZE_MULT分区容量类配置必须 power cycle,只复位不一定够,以 datasheet 为准
分区容量配置写完没断电,是「分区大小不对」的头号原因

典型事故:主机写完 GP_SIZE_MULT 后只做了软件复位,然后立刻按新分区大小去读写—— 器件还停留在旧的分区布局上,轻则读不到数据,重则写到错误的分区(比如把文件系统镜像写进了 Boot 区)。

产线流程必须把「配置分区 → 断电 → 重新上电 → 校验分区布局」做成不可跳过的固定步骤。

4.5 高频字段速查

偏移字段R/W取值与含义用途
34POWER_OFF_NOTIFICATIONR/W0x00 不支持 / 0x01 POWERED_ON / 0x02 SHORT / 0x03 LONG / 0x04 SLEEP掉电通知(第 10 章)
32FLUSH_CACHEWbit0 FLUSH = 1 触发回写Cache 落盘(第 7 章)
33CACHE_CTRLR/Wbit0 Cache_EN:0 关 / 1 开Cache 开关
161HPI_MGMTR/Wbit0 HPI_EN使能 HPI(第 11 章)
163BKOPS_ENR/Wbit0 MANUAL_EN,bit1 AUTO_EN一次性可编程,不可回退
164BKOPS_STARTW0x01 手动触发一次后台操作空闲时主动 GC
165SANITIZE_STARTW0x01 启动物理清除出厂恢复 / 报废清密
175ERASE_GROUP_DEFR/W0 = 用 CSD 定义 / 1 = 用 EXT_CSD 定义高密度器件必须置 1
179PARTITION_CONFIGR/W[7] BOOT_ACK / [5:3] BOOT_PARTITION_ENABLE / [2:0] PARTITION_ACCESS分区切换与启动配置
183BUS_WIDTHR/W0/1/2 = 1/4/8-bit;5/6 = 4/8-bit DDR总线宽度
185HS_TIMINGR/W0/1/2/3/4 = Legacy/HS/HS200/HS400/HS400ES速度档位
192REVR0x08 = eMMC 5.1版本判断
196DEVICE_TYPER位域:HS200 / HS400 及各电压档能力协商
212SEC_COUNTR4 字节小端;容量 = SEC_COUNT × 512 B容量唯一来源
246BKOPS_STATUSR0 = 无需求;1~3 = 需求等级递增是否该让器件做 GC
267PRE_EOL_INFOR0 Normal / 1 Warning / 2 Urgent寿命终期预警
268DEVICE_LIFE_TIME_EST_AR0x01 0–10% … 0x0A 90–100%,0x0B 超期常规模式寿命
269DEVICE_LIFE_TIME_EST_BR同 A 的分档规则pSLC / SLC 模式寿命

完整字段定义见 JESD84-B51 第 7 章;厂商自定义字段以器件 datasheet 为准。本器件(XT28EG32/64GA2YUECGA)上电默认实测值见附录 F.1——其中 RST_n_FUNCTION[162] = 0(硬件复位默认未使能)、HPI_MGMT[161] = 0、BKOPS_EN[163] = 0、CACHE_CTRL[33] = 0、CMQ_MODE_EN[15] = 0 都是「支持但默认关」,必须由主机显式打开。

4.6 常见陷阱

陷阱现象对策
不等 CMD6 的 busy后续命令无响应或返回错乱状态把 CMD6 一律当 R1b 处理,等 DAT0 释放
不查 SWITCH_ERROR配置静默失败,实际仍在旧档位每次 CMD6 后 CMD13 查 bit7
用 3.3 V VCCQ 切 HS400高速下大量 CRC 错误切高速前先把 VCCQ 切到 1.8 V(本器件不支持 1.2 V 档)
照抄 JEDEC 默认超时Cache 回收 / GC 撞车时误判 CMD6 失败超时一律从 GENERIC_CMD6_TIME 读出算,本器件 370 ms
分区配置后未断电分区大小不对,甚至写错分区配置 → 断电 → 上电 → 校验,固化为产线步骤
把 BKOPS_EN 当普通开关写入后无法回退(一次性可编程)只在确认需要时写入,并留记录
改 ERASE_GROUP_DEF 后不重算几何擦除范围算错置 1 后从 EXT_CSD 重新读取所有几何参数