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

UFS 3.1 协议 JESD220E 深度解析:从 UPIU 到 WriteBooster 实战指南

UFS 3.1 协议 JESD220E 深度解析:从 UPIU 到 WriteBooster 实战指南 ★ FEATURED ARTICLE
简介JESD220E是JEDEC固态技术协会于2020年1月发布的UFS 3.1官方协议标准文档面向移动存储固件开发、驱动调试及存储系统设计人员用于解决高速闪存接口的规范理解与工程落地问题。资源包内含1个PDF文件大小约4.36MB即完整的JESD220E标准正文涵盖术语定义、命令集、描述符、传输层与互操作规范等核心章节。协议在eMMC基础上实现全双工运行支持多通道架构、命令队列、HS-G4接口与动态电源管理理论最大传输速率可达11.6Gbps并集成CRC、ECC校验及设备身份验证与加密机制。目前已有5381人学习下载适合需要对照官方原文查阅UFS 3.1协议细节、开展固件开发或存储性能调优的工程师与研究人员参考。1. 拿到 JESD220E 之后为什么 UFS 3.1 的协议文档值得逐页啃如果你正在做手机、平板或者车载存储模块的底层开发大概率绕不开 UFS 3.1 协议。JESD220E 就是 JEDEC 在 2020 年 1 月发布的 UFS 3.1 官方标准文档全称 Universal Flash Storage (UFS) Version 3.1。它把 UFS 从物理层到应用层的所有行为都定义死了——M-PHY 的 HS-G4 速率怎么协商、UniPro 的传输层怎么组包、UPIU 的每一个字节代表什么、WriteBooster 怎么开、RPMB 怎么做安全读写全在里面。很多人第一次翻这份文档会觉得像天书但只要你做过 eMMC 的驱动或者裸 NAND 的管理就会发现它其实是一份“升级版说明书”eMMC 只能半双工读写要排队UFS 3.1 用两对差分线同时收发还带命令队列性能翻倍不是玄学是协议层面就设计好的。这份文档适合存储固件工程师、驱动开发者、测试认证人员以及需要做 UFS 设备选型和兼容性验证的硬件工程师。下面我按自己啃文档的顺序把关键章节拆开讲。2. 从 JESD220E 目录拆出三条主线架构、传输、命令2.1 先看第 5 章和第 6 章UIC 层和电气特性决定了你能不能跑起来第 5 章 UFS Architecture Overview 给了一张顶层架构图把 UFS 分成 UICInterconnect Layer、UTPTransport Protocol Layer和 UAPApplication Layer。UIC 又拆成 MIPI M-PHY 和 MIPI UniPro 两部分。很多新手一上来就去翻 UPIU 格式结果连 HS-G4 的参考时钟要求都没搞清板子做出来直接跑在 PWM 模式速度连 eMMC 都不如。我一般会先看第 6 章 Electrical重点盯三个参数参考时钟频率、HS Gear 速率、电源上电时序。第 6.4 节给出了参考时钟的典型值常见做法是给 26 MHz 或 19.2 MHz 的差分时钟。HS-G4 的线速率是 11.6 Gbps per lane但这是理论值实际能跑多少取决于你的 PCB 走线、阻抗控制和电源纹波。第 6.6 节 Absolute Maximum DC Ratings 里写清楚了各电源域的电压范围VCC 通常是 2.5V 或 3.3VVCCQ 是 1.2V 或 1.8VVCCQ2 是 1.8V。如果你把 VCCQ 和 VCCQ2 接反上电瞬间就可能烧掉主控的 PHY。注意第 7 章 Reset, Power-Up And Power-Down 里定义了 Power-on Reset 和 Hardware Reset 的时序要求RST_n 拉低到电源稳定的时间必须满足 tRST 的最小值否则设备可能枚举失败。2.2 第 10 章 UTP 层是核心UPIU 结构决定了命令怎么发第 10 章 UFS Transport Protocol 是整份文档最厚的一章也是实际调试中翻得最多的。UPIUUFS Protocol Information Unit是主机和设备之间通信的基本单元所有命令、数据、状态都封装在 UPIU 里。第 10.6 节给出了 General UFS Protocol Information Unit Format基础头是 12 字节后面跟具体类型的载荷。以 COMMAND UPIU 为例第 10.7.1 节定义了它的格式字段偏移长度说明Transaction Type0x001 byte0x01 表示 COMMAND UPIUFlags0x011 bytebit0 表示读/写方向LUN0x021 byte逻辑单元号Task Tag0x031 byte命令标签用于匹配响应Command Set Type0x041 byte0x00 表示 SCSITotal EHS Length0x051 byte扩展头长度Data Segment Length0x062 bytes数据段长度Expected Data Transfer Length0x084 bytes期望传输的数据量CDB0x1016 bytesSCSI 命令描述块这个表在调试时非常有用。比如你发现设备返回 REJECT UPIU先检查 Transaction Type 和 LUN 是否匹配。如果 Task Tag 对不上主机会一直等不到 RESPONSE UPIU最后超时。2.3 第 11 章 SCSI 命令和第 13 章功能描述写驱动时查得最多第 11 章列出了 UFS 支持的 SCSI 命令包括 READ (6/10/16)、WRITE (6/10/16)、INQUIRY、TEST UNIT READY、REPORT LUNS 等。第 13 章 Functional Descriptions 则讲了 Boot、Logical Unit Management、WriteBooster、RPMB 这些高级功能。以 WriteBooster 为例第 13.4.17 节定义了它的作用把写数据先缓存到 SLC 区域提升写入速度。开启方式是通过 WRITE BUFFER 命令或者配置 Descriptor。具体步骤是读取 Device Descriptor 的 bUFSFeaturesSupport 字段确认 bit3 是否为 1表示支持 WriteBooster。通过 QUERY REQUEST UPIU 写 Attribute 0x0FWriteBooster Buffer Type和 0x10WriteBooster Buffer Size。发送 WRITE BUFFER 命令Mode 设为 0x1F激活 WriteBooster。如果你跳过第 1 步直接写 Attribute设备会返回 QUERY RESPONSE UPIU 带错误码常见的是 0x02Invalid Attribute。3. 用 JESD220E 做一次 UFS 3.1 设备初始化从上电到 READ 命令3.1 上电和复位时序别让设备死在枚举阶段UFS 设备上电不是简单地把电供上就行。第 7.2 节 Power up ramp 要求 VCC、VCCQ、VCCQ2 按特定顺序上电通常是 VCC 先上然后 VCCQ2最后 VCCQ。如果顺序反了设备内部的电荷泵可能无法正常启动。上电后主机需要拉低 RST_n 至少 1ms然后拉高。接着主机通过 M-PHY 发送 DMEDevice Management Entity命令读取设备的能力信息。这一步在 UniPro 层完成不涉及 UPIU。如果 DME 读不到设备先查参考时钟有没有起振再查 RST_n 的时序。# 用示波器抓上电时序的检查点 # 通道1VCC (2.5V) # 通道2VCCQ (1.2V) # 通道3RST_n # 触发条件VCC 上升沿 # 检查项 # 1. VCC 稳定后VCCQ 是否在 10ms 内建立 # 2. RST_n 拉低时间是否 1ms # 3. 参考时钟在 RST_n 拉高前是否已经稳定这段不是代码是示波器抓波形的检查清单。我一般会把触发点设在 VCC 上升沿然后看三个信号的时间关系。如果 RST_n 拉高时参考时钟还没稳定设备会进入一个未定义状态后续所有命令都无响应。3.2 发送第一个 COMMAND UPIU用 Python 模拟主机行为如果你没有硬件可以用 Python 写一个 UPIU 构造器把字节流打出来对照文档检查。下面这段代码构造一个 TEST UNIT READY 命令的 COMMAND UPIUimport struct def build_command_upiu(lun, task_tag, cdb): 构造 COMMAND UPIU :param lun: 逻辑单元号0-7 :param task_tag: 命令标签1-255 :param cdb: SCSI 命令描述块bytes :return: 完整的 UPIU 字节流 # Transaction Type 0x01 (COMMAND UPIU) # Flags 0x00 (主机到设备) # LUN lun # Task Tag task_tag # Command Set Type 0x00 (SCSI) # Total EHS Length 0x00 # Data Segment Length 0x0000 # Expected Data Transfer Length 0x00000000 header struct.pack(BBBBBBH I, 0x01, # Transaction Type 0x00, # Flags lun, # LUN task_tag, # Task Tag 0x00, # Command Set Type 0x00, # Total EHS Length 0x0000, # Data Segment Length 0x00000000 # Expected Data Transfer Length ) # CDB 固定 16 字节不足补 0 cdb_padded cdb b\x00 * (16 - len(cdb)) return header cdb_padded # TEST UNIT READY 的 CDB操作码 0x00其余为 0 cdb bytes([0x00]) upiu build_command_upiu(lun0, task_tag1, cdbcdb) print(upiu.hex()) # 输出0100000100000000000000000000000000000000000000000000000000000000这段代码的关键是 struct.pack 的格式字符串BBBBBBH I表示大端序B是 1 字节H是 2 字节I是 4 字节。注意中间有个空格实际使用时要去掉。构造出来的 32 字节就是 COMMAND UPIU 的完整内容。你可以把它和文档第 10.7.1 节的表格逐字节对照确认每个字段的位置。3.3 解析 RESPONSE UPIU判断命令是否成功设备收到 COMMAND UPIU 后会返回 RESPONSE UPIU。第 10.7.2 节定义了它的格式关键字段是 Response 和 Status。def parse_response_upiu(data): 解析 RESPONSE UPIU :param data: 设备返回的字节流至少 32 字节 :return: 字典包含关键字段 if len(data) 32: raise ValueError(RESPONSE UPIU 长度不足) fields struct.unpack(BBBBBBH I B B B B I, data[0], # Transaction Type data[1], # Flags data[2], # LUN data[3], # Task Tag data[4], # Command Set Type data[5], # Total EHS Length data[6:8], # Data Segment Length data[8:12],# Residual Transfer Count data[12], # Response data[13], # Status data[14], # Number of Residuals data[15], # Reserved data[16:20]# Reserved ) return { transaction_type: fields[0], lun: fields[2], task_tag: fields[3], response: fields[8], status: fields[9] } # 假设设备返回的 RESPONSE UPIU resp bytes.fromhex(0200000100000000000000000000000000000000000000000000000000000000) result parse_response_upiu(resp) print(result) # 输出{transaction_type: 2, lun: 0, task_tag: 1, response: 0, status: 0}Response 为 0x00 表示目标成功执行了命令Status 为 0x00 表示 GOOD。如果 Response 是 0x01说明目标检测到命令有问题需要看 Status 的具体值。常见错误Status 0x02 表示 CHECK CONDITION需要发 REQUEST SENSE 拿详细信息。4. 避坑调试 UFS 3.1 时最容易翻车的五个地方4.1 现象设备枚举失败DME 读不到任何信息原因参考时钟没起振或者 RST_n 时序不对。第 6.4 节要求参考时钟的抖动小于 100ppm如果你用的晶振精度不够UniPro 的链路训练会失败。解决用示波器测参考时钟的峰峰值和频率确认在设备要求的范围内。RST_n 的拉低时间要大于 1ms拉高后至少等 1ms 再发 DME 命令。4.2 现象READ 命令返回 DATA IN UPIU但数据长度不对原因Expected Data Transfer Length 字段填错了。第 10.7.1 节规定这个字段是 4 字节单位是字节。如果你按块数填设备会返回多余或不足的数据。解决在构造 COMMAND UPIU 时把 Expected Data Transfer Length 设为 块大小 × 块数。比如读 4 个 512 字节的块就填 2048。4.3 现象WriteBooster 开启后写入速度反而下降原因WriteBooster Buffer Size 设得太小或者设备温度过高触发了 Performance Throttling。第 13.4.16 节定义了温度事件通知当结温超过阈值时设备会主动降速。解决先读 Device Descriptor 的 bWriteBoosterBufferSize 字段确认设备支持的最大值。然后通过 QUERY REQUEST 写 Attribute 0x10设置一个合理的值。如果温度过高先改善散热。4.4 现象RPMB 写入返回 SECURITY PROTOCOL OUT 错误原因RPMB 的认证密钥没写对或者写入计数器溢出。第 12.4 节规定 RPMB 区域只能通过 SECURITY PROTOCOL IN/OUT 命令访问每次写入都需要 MAC 校验。解决先用 SECURITY PROTOCOL IN 读 RPMB 的 Write Counter确认没有溢出。然后检查 MAC 的计算是否正确密钥是否烧录到了设备的 OTP 区域。4.5 现象设备在 HS-G4 速率下不稳定偶尔丢包原因PCB 走线的差分阻抗不匹配或者电源纹波太大。第 8.8 节给出了发送端和接收端的电气特性HS-G4 对信号完整性的要求比 HS-G3 高得多。解决用 TDR 测差分线的阻抗确保在 90Ω±10% 以内。电源纹波要控制在 50mV 以内必要时加去耦电容。5. 用 Annex B 做版本对比快速定位 UFS 3.0 到 3.1 的增量5.1 Annex B 的用法只看变化点不重读全文JESD220E 的 Annex B 列出了从 JESD220DUFS 3.0到 JESD220EUFS 3.1的所有变化。如果你已经熟悉 UFS 3.0直接看这个附录就能知道 3.1 新增了什么。根据 Annex B.1主要变化集中在三个方面WriteBooster、Performance Throttling 和 DeepSleep 的电源管理优化。具体来说第 13.4.17 节 WriteBooster 是全新的第 13.4.16 节 Performance Throttling Event Notification 也是新增的。另外第 7.4.1 节 Device Power Modes 里增加了 DeepSleep 模式的进入和退出条件。5.2 用表格对比 UFS 3.0 和 3.1 的关键参数特性UFS 3.0 (JESD220D)UFS 3.1 (JESD220E)HS-G4 线速率11.6 Gbps11.6 GbpsWriteBooster不支持支持通过 Attribute 0x0F/0x10 配置Performance Throttling无通知机制支持事件通知通过 Attribute 0x0E 读取DeepSleep 模式支持优化了进入/退出时序RPMB 区域大小最大 16MB最大 16MB但增加了密钥管理要求这个表是我自己整理的方便选型时快速对比。如果你要开发 UFS 3.1 设备WriteBooster 是必须实现的否则在随机写入场景下性能会明显落后。5.3 验证方法用 QUERY REQUEST 读 Attribute 确认功能支持不管文档怎么写最终都要落到设备实际支持哪些功能。我习惯在驱动初始化时读一遍关键 Attribute确认设备的能力。# 构造 QUERY REQUEST UPIU 读 Attribute 0x0F (WriteBooster Buffer Type) def build_query_request_upiu(lun, task_tag, opcode, idn, index, selector): 构造 QUERY REQUEST UPIU :param opcode: 0x01 读 Attribute0x02 写 Attribute :param idn: 0x0F 表示 WriteBooster Buffer Type :param index: 通常为 0 :param selector: 0x00 表示读当前值 header struct.pack(BBBBBBH I, 0x16, # Transaction Type QUERY REQUEST UPIU 0x00, # Flags lun, # LUN task_tag, # Task Tag 0x00, # Command Set Type 0x00, # Total EHS Length 0x0000, # Data Segment Length 0x00000000 # Expected Data Transfer Length ) # Query 参数Opcode, IDN, Index, Selector query_params struct.pack(BBBB, opcode, idn, index, selector) return header query_params b\x00 * 8 # 补齐到 32 字节 upiu build_query_request_upiu(lun0, task_tag2, opcode0x01, idn0x0F, index0, selector0x00) print(upiu.hex())这段代码构造的 QUERY REQUEST UPIU 会返回 QUERY RESPONSE UPIU里面的 Query Response 字段就是 Attribute 的值。如果返回 0x00说明 WriteBooster Buffer Type 是 0即不支持。如果返回 0x01说明支持 SLC 缓存。从那以后我每次拿到新的 UFS 设备都会先跑一遍这个查询流程把关键 Attribute 和 Descriptor 读出来存档。这样在后续调试时一旦出现性能不达标或者功能异常就能快速判断是设备本身不支持还是配置写错了。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站