2. eMMC 内部架构:NAND Flash 与控制器集成、总线接口、寄存器映射

聊到 eMMC 的内部结构,我其实最怕学员一开始就去死磕每个模块的电路图。咱们换个角度,从它的本职工作入手——它本质上就是个「黑盒子」,一头是 Host(比如你的手机 SoC),另一头是真正的存储介质 NAND Flash。盒子里最关键的三个东西就是:控制器、NAND Flash 阵列、还有那个帮你完成所有配置的寄存器映射。这一章我们就掰开这个盒子,看看里面到底怎么协同工作的。

2.1 NAND Flash 与控制器集成:为什么非得绑在一起?

你想想看,早期的闪存卡(比如 SD 卡)是分开卖的,你得买个读卡器,然后插卡。但 eMMC 不这么干,它直接把 NAND Flash 芯片和一颗 专用控制器 封装在一个 BGA 小颗粒里。这么做的原因非常实际:NAND Flash 本身非常「娇气」——有坏块,需要 ECC 纠错,读写前还要先擦除,时序还特别复杂。

我个人的经验:在调试第一款 eMMC 驱动时,我误以为可以直接像操作 SPI Flash 那样逐字节读写。结果数据全乱码。后来才发现,控制器帮我们完成了所有 坏块管理 (BBM) 和 垃圾回收 (GC)。你发的 LBA 地址,Controller 内部会映射成 PBA(物理块地址),这个过程对 Host 是完全透明的。

所以,咱们不需要操心 NAND 的内部磨损均衡算法。Host 只管发命令,剩下的脏活累活,全交给这个集成好的控制器去处理。

eMMC 内部架构逻辑图 Host (SoC) CPU / DMA / MMU 发送 CLK/CMD/DAT eMMC 控制器 命令译码器 DMA / FIFO ECC 引擎 坏块 / 磨损均衡 寄存器映射 OCR / CID / CSD / RCA Extended CSD NAND Flash 块 0 ~ 块 N 页 (Page) 结构 备用区域 (OOB) 坏块标记 数据存储区 说明:黄色箭头表示控制通路,实线表示数据传输通路。 Host 只管发命令,所有坏块管理、ECC 纠错、磨损均衡均由 eMMC 控制器内部透明完成。

2.2 总线接口:三条线撑起天地(CLK / CMD / DAT)

eMMC 通信靠三条基本的信号线。说白了,就是把控制、时钟、数据分离设计,这样并行处理效率才高。

2.2.1 CLK:一切活动的基准

CLK 用于同步所有操作。在 HS400 模式下,时钟频率可以达到 200MHz(DDR 传输模式相当于 400MT/s)。需要注意的是,eMMC 在初始化阶段使用低速时钟(比如 400kHz),然后通过 CMD0、CMD8 等命令协商切换到高速模式。如果时序配置不对,数据传输就会出现毛刺。

我曾经见过一个坑:有位同事没注意 CLK 的占空比要求,在 200MHz 下占空比偏到了 40%。结果数据在上升沿和下降沿采样时老是跑飞。HS400 对时钟抖动的容忍度很低,建议在画 PCB 时给 CLK 信号走包地处理,并严格控制等长。

2.2.2 CMD:双向的命令线

CMD 是双向的。Host 通过它发送命令,eMMC 通过它回应响应。单根线通过开漏(Open Drain)或推挽(Push-Pull)方式工作。我记得在初始化阶段,CMD 是开漏模式,允许总线仲裁(比如一次只应答一个设备)。一旦进入数据传输模式,就会切换到推挽,提升速度。

2.2.3 DAT[7:0]:数据高速公路

DAT 线有 8 位。在 HS400 模式下,DAT 采用 DDR(双倍数据速率) 传输,即 CLK 的每个上升沿和下降沿都采样一次。所以 8 条线同时工作,理论上带宽能到 400MB/s(200MHz * 8bit * 2边沿)。

信号 方向 HS400 下关键特性 我的备注
CLK Host → Device 200MHz,占空比 45% ~ 55% 差分信号处理? 不,这里单端就够,但回流地要稳
CMD 双向 带 CRC 校验,总线仲裁 启动阶段开漏,防止多个 Device 冲突
DAT[7:0] 双向 8 位并行,DDR 采样,每通路带 CRC HS400 模式下不需要 CRC 状态?不,是 CRC 必须关闭?嗯,是 CRC 校验由控制器硬件自动完成,Host 不需要手动处理。

2.3 寄存器映射:eMMC 的「大脑」配置区

eMMC 内部有一块寄存器映射区,Host 通过 CMD 和 DAT 线读写这些寄存器,来获取设备能力、设置工作模式、监控状态。我习惯把它们分成两组理解:基础识别寄存器 和 扩展控制寄存器。

2.3.1 基础寄存器:OCR / CID / CSD / RCA

  • OCR (Operating Conditions Register):告诉你这个 eMMC 能工作在什么电压(比如 1.8V / 3.3V),还有它的初始化状态。Host 发 CMD1 就是读它。
  • CID (Card Identification Register):唯一的身份证号,包括厂商ID、产品名、序列号。这个一般在产线上用,用来追踪批次。
  • CSD (Card Specific Data Register):存着设备容量、读写速度等级、块大小等死参数。我在做驱动移植时,就踩过 CSD 里不同版本容量计算方式不同的坑。
  • RCA (Relative Card Address):Host 在初始化阶段给每个 eMMC 分配一个短地址,后续通信就用这个 16 位地址,避免每次发完整 CID。
特别注意:HS400 模式必须在 Extended CSD 中使能 HS400 功能位(比如 HS_TIMING),并且还需要验证 DEVICE_TYPE 字段是否支持 HS400。如果只配置 HS200 就以为能跑 400MT/s,这步跳过去的话,总线会直接挂死。

2.3.2 Extended CSD(ECSD):真正的大本营

ECSD 有 512 个字节,里面塞了几百个控制字段。比如 SECTOR_SIZE、BOOT_SIZE_MULT、以及我前面提到的 HS_TIMING 寄存器。这些字段很多是需要通过 SWITCH (CMD6) 命令来修改的,不能直接写。

// 伪代码示例:切换至 HS400 模式
// 1. 先发 CMD6 设置 EXT_CSD[HS_TIMING] = 0x02 (HS400)
// 2. 再发 CMD6 设置 EXT_CSD[BUS_WIDTH] = 0x06 (8-bit DDR)
// 3. 等待 eMMC busy 完成
CMD6(Set(HS_TIMING, 0x02));
CMD6(Set(BUS_WIDTH, 0x06));
WaitBusy();

这一步不能省。我最初做测试时,直接发 CMD6 改了 HS_TIMING,忘了同步改 BUS_WIDTH,结果 Deice 认为你在用 4-bit 跑 DDR,数据对不上,整块数据全是 0x00。嗯,这里要注意,EXT_CSD 大部分字节是只读的,写之前先查对应 BIT 是不是 W(可写)。

2.4 避坑总结:寄存器和时序的配合

关于寄存器映射,我还有一个经验分享:很多新手觉得配置好了 HS400 就万事大吉。但其实,eMMC 内部有一个 Tuning 流程(CMD21),它利用寄存器中的 TUNING_BLOCK 数据来自动修正采样相位。如果跳过了 Tuning,总线在温度或电压变化时会出现窗口漂移,导致 Bit Error。

警告:ESD 或总线干扰很容易让寄存器值意外跳变。我建议在关键数据流传输前,通过 CMD13 获取 CURRENT_STATE 状态,检查 Device 是否真的身处 TRAN(传输态),而不是 PROG(编程态)。否则你发写命令进去,数据还没写进 NAND,主机却以为已经写好了。

好了,这就是 eMMC 内部架构的三个核心要素。理解了这个架构,下一环节我们就能安心去讨论总线协议层的具体命令握手流程了。记住这个图,它会帮你把控制器硬件和寄存器配置串起来。