首页 / 资讯中心 / 文章详情

eMMC从物理结构到u-boot管理:EXT_CSD、分区切换与调试实战

eMMC从物理结构到u-boot管理:EXT_CSD、分区切换与调试实战 ★ FEATURED ARTICLE
eMMC这颗芯片只要你做过嵌入式尤其是消费电子、工业控制、车机或者国产化平台基本绕不开它。它不像UFS那样高速也不像裸NAND那样需要你操心坏块管理和ECC但它内部的那些寄存器、分区、命令一旦出问题排查起来能让人抓狂。我见过太多人卡在EXT_CSD某个bit上或者u-boot里mmc dev能识别但读写报错最后发现是分区配置没对上。这篇内容就把eMMC从物理结构到u-boot管理这条链路完整捋一遍重点讲清楚那些文档里一笔带过、但实际调试中一定会碰到的细节。不管你是刚接触eMMC的新手还是已经调过几颗但想系统梳理的老手应该都能从中找到能直接用的东西。1. eMMC的物理与逻辑存储区域到底怎么划分很多人拿到eMMC第一反应是这不就是个BGA封装的存储芯片吗但它的内部结构比想象中复杂得多。eMMC全称embedded MultiMediaCard是在MMC协议基础上发展出来的嵌入式存储方案。它把NAND Flash颗粒、闪存控制器、ECC校验、坏块管理全部封装在一个BGA芯片里对外只暴露标准的MMC接口。这个控制器存储的架构决定了你操作eMMC的方式和操作裸NAND完全不同——你面对的是一个有自己逻辑的黑盒而不是一片可以直接寻址的存储阵列。1.1 从NAND到eMMC控制器屏蔽了什么裸NAND的使用者需要自己处理几件事坏块标记、ECC纠错、磨损均衡、垃圾回收。这些工作在eMMC里全部由内部控制器完成。你写入一个逻辑地址控制器负责映射到实际的物理页处理该页是否坏块、是否需要搬移数据。这意味着eMMC的寿命管理和数据可靠性很大程度上取决于控制器固件的策略而不是你的驱动代码。但这也带来一个副作用你无法直接感知底层NAND的健康状况。eMMC通过EXT_CSD寄存器里的生命周期估算字段DEVICE_LIFE_TIME_EST来间接反映磨损程度这个值分档表示精度有限。实际项目中如果要做寿命预测不能只依赖这个字段还得结合写入量统计和厂商提供的工具。1.2 用户数据区与Boot分区的关系eMMC的存储空间在逻辑上被划分为几个独立区域这是理解后续所有操作的基础。最核心的是User Data Area也就是你平时挂载成块设备、格式化文件系统的那块空间。它的容量就是芯片标称容量减去其他专用分区占用的部分。除了用户区eMMC还定义了Boot Area Partition 1和Boot Area Partition 2这两个分区是专门给SoC上电启动用的。很多应用处理器比如i.MX系列、瑞芯微系列支持从eMMC的Boot分区直接加载启动代码不需要额外的SPI Flash。Boot分区的大小通常是固定的早期芯片是128KB每个后来有512KB、1MB甚至更大的配置具体取决于eMMC的版本和厂商实现。还有一个**RPMBReplay Protected Memory Block**分区用于存储需要防重放攻击的安全数据比如指纹模板、DRM密钥。RPMB的读写需要经过HMAC-SHA256认证普通读写命令访问不了。这个分区在手机和支付类设备上用得很多工业场景下如果涉及安全启动链也会用到。1.3 分区容量的配置机制Boot分区和RPMB分区的容量不是随便定的它们通过EXT_CSD寄存器里的BOOT_SIZE_MULT和RPMB_SIZE_MULT字段来配置。BOOT_SIZE_MULT是一个乘数实际Boot分区大小 BOOT_SIZE_MULT× 128KB。比如这个字段值为0x2032那每个Boot分区就是32×128KB4MB。RPMB类似RPMB_SIZE_MULT× 128KB。这里有个实际调试中容易踩的坑Boot分区的容量配置是一次性的还是可改的取决于eMMC的具体实现。有些芯片允许通过WRITE_SECURE_REGISTER命令重新配置有些则在出厂时熔丝固化。如果你在u-boot里发现Boot分区大小和预期不符先查EXT_CSD的179字节也就是BOOT_SIZE_MULT所在的偏移确认配置值再判断是配置问题还是硬件固化问题。2. EXT_CSD[179]这个字节为什么值得单独拿出来讲EXT_CSD是eMMC的扩展卡特定数据寄存器总共512字节偏移0到511。它承载了eMMC几乎所有的可配置参数和状态信息。在这512字节里第179字节从0开始计数对应的是BOOT_SIZE_MULT但为什么偏偏这个字节被反复提及因为它是Boot分区配置的核心而Boot分区又直接关系到系统能不能正常启动。2.1 EXT_CSD的访问方式访问EXT_CSD不能用普通的读命令。它需要通过CMD8SEND_EXT_CSD来读取或者通过CMD6SWITCH来写入。CMD8的响应是512字节的数据块直接映射整个EXT_CSD空间。在u-boot里你可以用mmc extcsd read命令把整个EXT_CSD dump出来然后对照JEDEC标准文档逐字节分析。写入EXT_CSD则要谨慎得多。CMD6的参数字段里bit[31:26]是访问模式bit[25:24]是索引对于EXT_CSD写操作索引固定为0x03bit[23:16]是要写的字节偏移bit[15:8]是要写入的值。这个命令的响应里会返回当前状态如果目标字节是只读的或者当前不允许写会返回错误。2.2 BOOT_SIZE_MULT的读取与修改实操在u-boot命令行下读取BOOT_SIZE_MULT最直接的方式是mmc dev 0 mmc extcsd read /dev/mmcblk0 | grep -i boot_size如果系统里没有mmc命令的extcsd子命令也可以自己写一段代码发送CMD8后取返回数据的第179字节。用C语言描述大概是uint8_t ext_csd[512]; // 发送CMD8接收512字节到ext_csd send_cmd8(ext_csd); uint8_t boot_size_mult ext_csd[179]; uint32_t boot_partition_size boot_size_mult * 128 * 1024; // 单位字节修改这个值需要发送CMD6把偏移179、目标值写进去。但要注意修改Boot分区大小会重新划分存储空间原有Boot分区内的数据会丢失。而且不是所有eMMC都允许在运行时修改有些需要先进入特定的配置模式。2.3 与179字节相关的其他关键字段虽然179字节是BOOT_SIZE_MULT但在实际调试中你往往需要同时关注它周围的几个字段。比如偏移177的RPMB_SIZE_MULT偏移181的BOOT_INFO指示Boot分区是否使能、总线宽度等偏移173的PARTITION_CONFIG控制从哪个分区启动、访问哪个分区。这几个字段经常需要联动配置。举个例子你要让系统从Boot Partition 1启动同时把User Data Area作为普通存储访问那么PARTITION_CONFIG的bit[5:3]BOOT_PARTITION_ENABLE要设为1bit[2:0]PARTITION_ACCESS要设为0。如果这两个值配错可能出现能识别eMMC但读不到启动数据或者启动后找不到用户分区的怪现象。3. eMMC各分区的访问切换与实战配置eMMC的分区不是同时全部可见的。在任意时刻主机只能访问一个分区User Data Area、Boot Partition 1、Boot Partition 2、RPMB中的一个。切换访问目标靠的是PARTITION_CONFIG字段里的PARTITION_ACCESS位。这个机制和很多人的直觉不同——不是像普通块设备那样所有分区同时挂载而是需要显式切换。3.1 PARTITION_CONFIG字段的位域拆解PARTITION_CONFIG位于EXT_CSD偏移173是一个8位字段位域定义如下位域名称含义bit[7]BOOT_PARTITION_ENABLE的bit2保留或扩展bit[6:5]BOOT_PARTITION_ENABLE的bit1:0启动分区选择bit[4]BOOT_ACK启动确认使能bit[3]BOOT_PARTITION_ENABLE的bit3扩展bit[2:0]PARTITION_ACCESS当前访问分区选择实际标准里BOOT_PARTITION_ENABLE是3位位于bit[5:3]PARTITION_ACCESS是3位位于bit[2:0]。PARTITION_ACCESS的取值0表示User Data Area1表示Boot Partition 12表示Boot Partition 23表示RPMB其他值保留。3.2 在u-boot中切换分区并读写u-boot的mmc子系统对分区切换做了封装。以常见的mmc命令为例# 切换到Boot Partition 1 mmc dev 0 1 # 此时对mmc 0的读写操作都作用于Boot Partition 1 mmc read 0x80000000 0x0 0x100 # 切换回User Data Area mmc dev 0 0这里的mmc dev 0 1第二个参数就是分区号。0是User Data Area1是Boot Partition 12是Boot Partition 2。这个命令背后做的事情就是修改PARTITION_CONFIG的PARTITION_ACCESS位然后等待eMMC完成切换。注意切换分区后之前缓存在控制器里的数据可能失效建议切换后重新初始化读写指针。另外频繁切换分区会增加命令开销批量操作时尽量在一个分区内完成。3.3 分区切换失败的常见原因实际项目中mmc dev 0 1执行后读写报错的情况不少见。排查下来通常有这几个原因第一BOOT_SIZE_MULT为0也就是Boot分区根本没配置。这种情况下PARTITION_ACCESS设为1会返回错误因为目标分区不存在。第二eMMC处于写保护状态。有些芯片的Boot分区可以通过BOOT_WP字段设置写保护如果之前被保护了切换过去也只能读不能写。第三PARTITION_CONFIG的写入没有生效。CMD6写EXT_CSD后需要检查响应中的SWITCH_ERROR位如果置位说明写入被拒绝。常见原因是目标字段是只读的或者当前电压/时序条件不满足写入要求。4. eMMC的主要引脚与硬件设计要点eMMC的引脚数量不多但每一根都有明确的功能划分。理解引脚定义对硬件设计和信号调试都很关键。标准eMMC封装通常是153-ball BGA也有169-ball等变体其中真正用于信号传输的引脚大概十几根其余是电源和地。4.1 信号引脚的功能分类eMMC的信号引脚可以分成几组时钟与控制组CLK是时钟输入频率范围从0到200MHzHS400模式下。CMD是双向命令线用于传输命令和响应。这两根线是所有操作的基础。数据组DAT0到DAT7共8根数据线。在1位模式下只用DAT04位模式用DAT0-DAT38位模式全用。HS400模式必须用8位。复位与电源RST_n是硬件复位输入低有效。VCC是NAND阵列的电源通常3.3VVCCQ是IO接口电源1.8V或3.3VVSS和VSSQ分别对应两者的地。其他DS是数据选通信号只在HS400模式下使用用于源同步传输。4.2 硬件设计中的走线与电源注意事项eMMC的PCB设计有几个硬性要求。CLK线要尽量短并且做阻抗控制通常50欧姆单端。CMD和DAT线要等长误差控制在几个mil以内尤其是HS200和HS400模式下时序余量很小。VCCQ的电源质量直接影响信号完整性。1.8V模式下纹波要控制在几十毫伏以内。实际项目中遇到过因为VCCQ纹波过大导致HS200模式训练失败、自动降速到DDR52的情况换了低ESR的电容后问题解决。RST_n引脚建议加上拉电阻避免悬空导致意外复位。如果SoC的复位输出和eMMC的RST_n之间有电平转换要注意转换芯片的延迟不能太大否则复位释放时序可能不满足eMMC的要求。4.3 引脚间距与封装选型的实际考量eMMC常见的封装是153-ball BGA球间距0.5mm。这个间距对PCB工艺有要求通常需要激光钻孔和盘中孔工艺。如果PCB层数有限扇出会比较困难。选型时如果空间允许可以考虑球间距更大的封装或者用eMMC和LPDDR叠封的PoP方案来节省面积。走线时DAT和CMD线要参考完整的参考平面避免跨分割。如果必须换层要在过孔附近加回流地过孔。这些细节在低速模式下可能看不出问题但到了HS400模式任何阻抗不连续都可能导致眼图闭合。5. eMMC命令格式与类型从CMD0到CMD6的完整脉络eMMC的命令体系是MMC协议的延续命令通过CMD线串行传输每个命令48位。命令格式分两种通用命令和特定命令。理解命令格式是看懂u-boot源码和调试底层问题的前提。5.1 命令的48位结构一个标准命令由48位组成起始位1位固定为0、传输方向位1位1表示主机到设备、命令索引6位、参数32位、CRC77位、结束位1位固定为1。响应也是48位或136位取决于命令类型。命令索引就是常说的CMD0、CMD1这些编号。参数的含义因命令而异比如CMD8的参数在读取EXT_CSD时全为0而CMD6的参数则编码了访问模式、索引、偏移和值。5.2 命令类型与响应类型eMMC命令按响应类型分几类响应类型长度典型命令用途R148位CMD6, CMD7普通操作响应含状态位R1b48位CMD12, CMD28带忙信号的R1R2136位CMD8, CMD9读取CSD/EXT_CSDR348位CMD1OCR寄存器响应R448位CMD39快速I/OR548位CMD40中断模式CMD8的响应是R2类型136位其中包含整个EXT_CSD的512字节数据分多次传输。CMD6的响应是R1其中的SWITCH_ERROR位指示操作是否成功。5.3 初始化流程中的关键命令序列eMMC上电后的初始化流程命令顺序不能乱CMD0复位让eMMC进入idle状态。CMD1发送OCR协商电压范围eMMC返回忙状态直到初始化完成。CMD2获取CID读取设备唯一标识。CMD3分配RCA相对卡地址后续命令用RCA寻址。CMD9读取CSD获取设备容量等基本信息。CMD7选中设备进入transfer状态。CMD8读取EXT_CSD获取扩展配置。CMD6根据需要配置总线宽度、速度模式等。这个序列在u-boot的mmc_init函数里完整实现。如果初始化卡在某一步可以用逻辑分析仪抓CMD线看是哪个命令没有正确响应。5.4 CMD6的双重角色SWITCH与EXT_CSD写入CMD6在eMMC里有两个用途。一个是切换总线宽度和速度模式比如从1位切换到8位、从默认速度切换到HS200。另一个是写EXT_CSD寄存器。两者的区别在参数字段当bit[25:24]为0x03时表示写EXT_CSD其他值表示切换功能。写EXT_CSD时参数格式为bit[31:26]访问模式0x03表示写字节bit[25:24]索引0x03bit[23:16]字节偏移bit[15:8]写入值bit[7:0]保留。这个格式在JEDEC标准里有详细定义写代码时容易搞错位域建议对照标准逐位核对。6. u-boot对eMMC的管理机制与调试手段u-boot是大多数嵌入式系统启动链的第一环它对eMMC的管理直接决定了系统能不能从eMMC启动、能不能在启动阶段读写eMMC。u-boot的mmc子系统代码量不小但核心逻辑集中在drivers/mmc/目录下。6.1 u-boot mmc子系统的分层结构u-boot的mmc驱动分三层主机控制器驱动如sdhci.c、dw_mmc.c、mmc核心层mmc.c、块设备接口层mmc_blk.c。主机控制器驱动负责和SoC的SD/MMC控制器打交道核心层实现协议状态机和命令发送块设备层把eMMC抽象成可读写的块设备。这种分层的好处是换SoC时只需要改主机控制器驱动核心层和块设备层基本不动。但调试时要注意问题可能出在任何一层。比如命令超时可能是控制器驱动的问题也可能是eMMC本身没响应。6.2 常用调试命令与输出解读u-boot命令行下mmc命令是最常用的调试入口# 查看所有mmc设备 mmc list # 选择设备0 mmc dev 0 # 查看设备信息 mmc info # 读取一个块到内存 mmc read 0x80000000 0x0 0x1 # 写入一个块 mmc write 0x80000000 0x0 0x1 # 读取EXT_CSD mmc extcsd read /dev/mmcblk0mmc info的输出包含容量、总线宽度、速度模式、RCA等信息。如果容量显示为0或者明显不对说明CID/CSD读取有问题。如果速度模式显示为默认而不是HS200说明速度切换没成功。6.3 从eMMC启动的配置链路要让系统从eMMC启动需要几个条件同时满足SoC的Boot ROM支持eMMC启动、eMMC的Boot分区里有有效的启动镜像、PARTITION_CONFIG的启动分区选择正确、硬件上eMMC的启动引脚如果有配置正确。以常见的ARM SoC为例Boot ROM上电后会先初始化eMMC然后从Boot Partition 1的偏移0处读取启动头。如果启动头有效就加载后续镜像。这个过程不依赖u-boot是SoC固化的行为。所以如果Boot分区里的数据被破坏系统会直接变砖连u-boot都进不去。6.4 u-boot SPL对eMMC的早期初始化在SPLSecondary Program Loader阶段u-boot的代码量受限mmc驱动通常是精简版。SPL需要完成eMMC的基本初始化把主u-boot从eMMC加载到内存。这个阶段的调试信息很少出问题往往只能靠点灯或者抓SPL的串口输出。SPL里eMMC初始化的关键点是时钟配置和电压切换。很多SoC的eMMC控制器默认工作在3.3V要切到1.8V才能跑高速模式。这个切换时序如果不对SPL可能读不到数据。实际调试时可以先让SPL跑在低速模式确认能读到数据后再逐步提速。7. 实际项目中eMMC问题的排查思路eMMC的问题往往表现为能识别但读写失败、启动时好时坏、容量不对这几类。排查时要有条理从硬件到软件逐层排除。7.1 识别正常但读写报错的排查路径第一步确认mmc info里的容量和实际芯片标称是否一致。如果容量偏小可能是BOOT_SIZE_MULT或RPMB_SIZE_MULT占用了过多空间也可能是分区配置把部分空间划给了Boot分区。第二步检查总线宽度和速度模式。如果实际跑在1位模式而硬件设计是8位可能是CMD6切换总线宽度失败。用逻辑分析仪抓CMD6的响应看SWITCH_ERROR位是否置位。第三步做单块读写测试。从偏移0读一个块再写回同样的数据然后读出来比对。如果单块正常但多块失败可能是DMA配置或者时序问题。7.2 启动失败的常见原因与验证方法系统从eMMC启动失败先确认Boot分区里有没有数据。用u-boot的mmc dev 0 1切到Boot Partition 1然后mmc read读偏移0的数据看是不是有效的启动头。如果Boot分区有数据但启动不了检查PARTITION_CONFIG的启动分区选择位。有些SoC默认从Boot Partition 1启动但eMMC配置成了从User Data Area启动两边对不上。还有一种情况是eMMC的RST_n引脚被意外拉低导致eMMC一直处于复位状态。用示波器量一下RST_n的电压确认上电后能正常释放。7.3 寿命与坏块相关的异常表现eMMC用久了可能出现写入变慢、偶发读写错误。这时候要查EXT_CSD里的DEVICE_LIFE_TIME_EST字段看磨损等级。如果已经到了C级或D级说明寿命接近耗尽。坏块方面eMMC的控制器会自动管理但EXT_CSD里的PRE_EOL_INFO字段可以反映即将到期的块信息。如果这个字段显示有块即将失效建议尽早备份数据。提示工业场景下如果对寿命要求高选型时要关注eMMC的TBW总写入字节数指标并且在实际使用中做写入量统计不要等到出问题才查。8. 几个容易被忽略但很关键的细节最后分享几个我在实际项目中踩过的坑都是文档里不太会强调、但一旦遇到就很费时间的点。第一个是eMMC的写保护状态。有些eMMC出厂时Boot分区是写保护的或者通过BOOT_WP字段设置了保护。如果你发现Boot分区写不进去先查BOOT_WP和BOOT_WP_STATUS字段确认是不是被保护了。解除保护需要发送CMD6写BOOT_WP字段但要注意这个操作可能不可逆。第二个是电压切换的时序。eMMC从3.3V切到1.8V需要遵循特定时序VCCQ先降到1.8V然后发送CMD6切换信号电压。如果顺序反了eMMC可能进入不确定状态。这个时序在JEDEC标准里有图调试时建议对照标准用示波器逐段确认。第三个是CMD线的上拉电阻。CMD线在空闲时应该被上拉到VCCQ如果上拉电阻缺失或阻值不对命令传输可能出错。尤其是在高速模式下上拉电阻的取值会影响信号上升沿进而影响时序余量。第四个是u-boot里mmc设备的编号。mmc dev 0里的0是u-boot枚举的编号不一定对应硬件上的第一个eMMC。如果板子上有多个mmc设备比如eMMC加SD卡编号可能和预期不同。用mmc list确认实际枚举顺序再操作。这些细节单独看都不复杂但组合在一起就是eMMC调试中最耗时的部分。把EXT_CSD的关键字段、分区切换机制、命令格式这几块吃透再结合u-boot的调试命令大部分问题都能定位到具体环节。
阅读完成 · 觉得有帮助?
咨询建站