2. eMMC 内部架构:NAND Flash 与控制器集成、总线接口、寄存器映射
聊到 eMMC 的内部结构,我其实最怕学员一开始就去死磕每个模块的电路图。咱们换个角度,从它的本职工作入手——它本质上就是个「黑盒子」,一头是 Host(比如你的手机 SoC),另一头是真正的存储介质 NAND Flash。盒子里最关键的三个东西就是:控制器、NAND Flash 阵列、还有那个帮你完成所有配置的寄存器映射。这一章我们就掰开这个盒子,看看里面到底怎么协同工作的。
2.1 NAND Flash 与控制器集成:为什么非得绑在一起?
你想想看,早期的闪存卡(比如 SD 卡)是分开卖的,你得买个读卡器,然后插卡。但 eMMC 不这么干,它直接把 NAND Flash 芯片和一颗 专用控制器 封装在一个 BGA 小颗粒里。这么做的原因非常实际:NAND Flash 本身非常「娇气」——有坏块,需要 ECC 纠错,读写前还要先擦除,时序还特别复杂。
所以,咱们不需要操心 NAND 的内部磨损均衡算法。Host 只管发命令,剩下的脏活累活,全交给这个集成好的控制器去处理。
2.2 总线接口:三条线撑起天地(CLK / CMD / DAT)
eMMC 通信靠三条基本的信号线。说白了,就是把控制、时钟、数据分离设计,这样并行处理效率才高。
2.2.1 CLK:一切活动的基准
CLK 用于同步所有操作。在 HS400 模式下,时钟频率可以达到 200MHz(DDR 传输模式相当于 400MT/s)。需要注意的是,eMMC 在初始化阶段使用低速时钟(比如 400kHz),然后通过 CMD0、CMD8 等命令协商切换到高速模式。如果时序配置不对,数据传输就会出现毛刺。
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。
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。
好了,这就是 eMMC 内部架构的三个核心要素。理解了这个架构,下一环节我们就能安心去讨论总线协议层的具体命令握手流程了。记住这个图,它会帮你把控制器硬件和寄存器配置串起来。