ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

UFS 3.1 协议深度解析:从 eMMC 到 WriteBooster 与 HPB 的完整学习笔记

UFS 3.1 协议深度解析:从 eMMC 到 WriteBooster 与 HPB 的完整学习笔记 前阵子我拿到一台新手机顺手跑了一遍存储基准测试。顺序读稳定在1900MB/s左右数字很漂亮但切换到随机写场景成绩一下掉回几百MB/s。如果你也折腾过这类测试会明白背后不只是闪存颗粒单挑能决定的更多是协议栈在挑大梁——这个协议栈的主角就是UFS3.1协议。这个系列本来想拆成四篇慢慢讲背景、新特性、协议分层、学习路线。但拆开发出来总觉得自己在凑篇幅索性合并成一篇完整的学习笔记。内容面向两类人一类是嵌入式工程师、存储开发需要真正读协议文档、跟驱动代码另一类是手机爱好者或刚入门的学生想搞清楚厂商发布会上那串UFS3.1到底意味着什么。接下来我从为什么值得学开始一路讲到怎么学、有哪些坑尽量把我知道的都说透。1. 为什么UFS3.1协议值得专门花精力学1.1 从eMMC到UFS串行接口带来的性能革命在UFS普及之前手机上常见的闪存标准是eMMC。eMMC走的是并行总线硬件上需要拉很多根数据线当时能做到的读写带宽很快撞到天花板。UFS改用串行差分总线支持全双工通信读写可以同时跑还加入了多命令队列天然比eMMC更适合高性能场景。UFS从1.0到2.0再到2.1属于能打但偏小众的阶段。真正让它成为主流是UFS3.0时代接口速率大幅提升连续读写速度翻倍旗舰机从此告别了eMMC时代卡顿的尴尬。再往后的UFS3.1则是把性能、功耗、随机读这几个痛点做了一次集中的协议级优化。我在学习的时候有一个体会很多文章把UFS3.1当成一个性能标签来介绍列一下最大速率就完了。但如果你去读JEDEC规范会发现它真正定义的是主机Application Processor和UFS设备之间如何交换信息。设备能干什么主机怎么发命令电源状态怎么切错误怎么恢复这些都是协议管的范畴。不搞懂协议你连一个写入速度怎么突然下降的问题都定位不了。1.2 UFS3.1相比UFS3.0具体多了什么既然要专门学就先搞清楚UFS3.1增加了什么。我整理了一张表对照UFS2.1、UFS3.0和UFS3.1来看会更直观特性项UFS 2.1UFS 3.0UFS 3.1每通道最大速率5.8 GbpsHS-G311.6 GbpsHS-G411.6 GbpsHS-G4通道数双通道双通道双通道命令队列深度323232WriteBooster某些厂商私有实现不支持协议标准化Host Performance BoosterHPB不支持不支持协议标准化DeepSleep电源状态不支持仅Sleep等新增/强化DeepSleep注意UFS3.1的接口速率本身没有比UFS3.0更高它更像是把性能潜力挖掘出来、把功耗降下去的完善版本。协议层面的WriteBooster通过SLC缓存提升顺序写HPB通过主机内存缓存映射表来提升随机读DeepSleep则解决待机功耗问题。这三个特性才是UFS3.1的灵魂。1.3 协议的价值不只是性能参数为什么非要站在协议层面去理解因为只有协议才能告诉你支持某个功能不等于用得上某个功能。举个例子WriteBooster要生效主机驱动需要发送特定查询请求去读取设备支持状态并且在合适的时机把数据引导到WriteBooster缓冲区。如果设备固件或者主机驱动没有正确配合这个特性等于不存在。再比如HPB它需要设备把FTP映射表同步给主机主机缓存之后在后续读命令中回传映射信息。映射信息怎么管理、什么时候失效全部由协议约定。这些细节不读规范、不看代码是学不到的。所以做存储开发的人常开玩笑跑分只能证明手机厂商调得怎么样协议文档才证明这事到底是怎样运作的。本篇文章的核心就是把UFS3.1协议这套机制拆开给你看。2. UFS3.1的技术特性拆解性能与功耗背后的协议手段2.1 高速链路MIPI M-PHY与UniPro要理解UFS3.1绕不开MIPI联盟的两个规范物理层的M-PHY链路层的UniPro。UFS协议引用它们作为底层传输基础。UFS3.0和UFS3.1用的M-PHY HS-G4速率单条通道数据率最高11.6 Gbps双通道并行就是23.2 Gbps的理论带宽。看到这个数字不用惊讶实际可用负载要打折扣因为协议有封装开销、有链路管理开销。在M-PHY上有一堆速率为功率状态的配置比如低功耗模式PWM、高速模式HS。设备上电之后一般先跑PWM低速模式然后通过协商切换到HS高速模式。链路层UniPro在这里负责把上层协议数据包分割、重传、流量控制。很多人以为UFS直连闪存颗粒其实不是——真正的NAND接口在设备内部外部暴露给主机的只有M-PHY差分信号UFS控制器才是整个设备的大脑。做硬件的人还要注意信号完整性HS-G4速率下PCB走线长度、阻抗、端接电阻都变得敏感。协议学习虽然偏软件但掌握物理层大致能力能帮你理解为什么量产机高速模式偶尔会启动失败往往是链路协商不通过主机驱动会降级到低速模式性能自然受影响。2.2 WriteBooster顺序写加速和它的B面WriteBooster是UFS3.1协议标准化的一个功能本质上是设备内部增加一块SLC缓冲区。SLC一个Cell存1bit写入快、寿命长但容量小TLC/QLC一个Cell存多个bit容量大、写入慢。设备把峰值写流量先转到SLC缓冲区然后利用后台时间慢慢把数据搬回TLC区域。在协议层WriteBooster不是透明的。主机驱动需要查询设备是否支持WriteBooster还要了解缓冲区大小、当前可用空间。写入命令可以把它引导进缓冲区但这个缓冲区不像普通内存它在主机掉电后依然需要保证数据不丢所以设备必须有一套掉电保护机制。在实际使用中WriteBooster最大的副作用是后台回收。当SLC缓冲区写满或空闲时间足够设备就要把数据从SLC搬回TLC。这个过程会占用设备内部资源表现为写着写着突然掉速。我用手机实测大文件连续写入时经常能看到速度先冲到峰值一会儿之后明显下降这就是WriteBooster缓冲耗尽后的正常曲线。所以厂商跑分时往往只测前几GB的写入完整全盘写入才见真章。2.3 HPB把FTL映射表搬进主机内存HPB的全称是Host Performance Booster它解决的是随机读取的痛点。UFS设备内部有Flash Translation Layer负责逻辑地址到物理地址的映射。随机读小文件时设备要查映射表而映射表本身也存在于NAND里需要先加载到RAM读一次小文件可能要反复翻表延迟就上去了。UFS3.1协议允许主机和设备协商HPB特性设备把特定区域的FTP映射表同步给主机主机把这些映射信息缓存在自己的内存里。之后主机发出读命令时可以携带对应的物理映射信息设备拿到之后就不用自己查表了直接从映射指定的物理地址读取数据。这种把映射工作搬到主机侧的思路类似于PCIe固态硬盘里的主机内存缓冲效果就是随机读延迟显著降低。但代价也很明显主机侧缓存一定会占用系统内存还涉及映射表的有效性管理。如果设备发生垃圾回收物理映射变化了主机侧缓存必须同步更新否则读命令会指向旧地址引发错误。协议规定了一整套区域管理、地址更新、主动失效机制这部分正是UFS3.1协议里比较烧脑的内容。如果去看Linux内核的UFS驱动代码会看到专门处理HPB的模块逻辑相当复杂。2.4 DeepSleep和系统功耗管理UFS3.1还加强了设备在空闲时的功耗控制。协议层面定义了多个电源状态从Active、Idle到Sleep、DeepSleep等。设备在没有命令时可以经过主机请求或者自身超时进入到更省电的睡眠状态。DeepSleep状态下链路唤醒时间较长但功耗可以降到很低这对手机待机续航意义明显。主机需要有休眠唤醒策略。如果每次进DeepSleep再唤醒要花好几毫秒那频繁的网络推送消息反而会带来性能损耗。所以UFS驱动通常会用一套自适应策略短时空闲进Idle长时空闲再进Pre-Sleep或Sleep只有足够长时间没有访问才进DeepSleep。协议给的是命令和状态机制怎么用好是驱动的事。很多工程师在排查手持设备功耗异常时最后会发现UFS没有正确进睡眠状态——这往往不是协议bug而是主机驱动没能妥善处理挂起命令。3. UFS协议分层一次读写请求怎么从AP走到Flash3.1 UFS协议栈的分层模型UFS协议采用分层设计从上往下大致是应用层、UFS传输协议层UTP、UFS互联层UIC。应用层处理SCSI命令集例如READ/WRITE/UNMAP等UTP层负责把命令和数据封装为UPIU并提供传输服务UIC由UniPro和M-PHY组成负责把UPIU放到物理链路上。这个层次很像TCP/IP应用层到传输层增加头部传输层到底层再加链路封装。分层的好处是清晰。主机驱动关心的是我要发一个SCSI读命令UTP层关心这个命令怎么组成UPIU并保证送达UniPro关心数据包在链路上怎么保证可靠顺序,M-PHY关心电平信号怎么在物理差分线上传输。任何一个环节出问题现象、定位手段都完全不同。我排过一次UFS数据传输出错的案子现象是设备偶发返回读错误最终定位到UniPro层的重传计数达到上限链路进入恢复流程。如果只盯着应用层看永远找不到根因。3.2 UPIU协议层中的“信息单元”UTP层传输的基本单位是UPIUUFS Protocol Information Unit可以说是命令和数据的统一集装箱。每类UPIU都有严格位域定义。最常见的几类UPIU类型用途Command UPIU主机向设备下发命令通常包含SCSI命令描述符块Data In UPIU设备向主机传输数据读Data Out UPIU主机向设备传输数据写Response UPIU设备向主机返回命令执行结果Query Request UPIU主机用来查询或配置设备的属性/描述符Task Management Request UPIU主机管理命令队列如中止某个任务UPIU头部里有传输类型、插槽号、期望数据长度、LBA和传输属性等字段。主机驱动发出的每个命令都要组装成这样一个结构然后交给UniPro。学习协议阶段我会建议你把UPIU头部各个字段对着spec过一遍不用背但一定要知道哪些字节影响传输标记、哪些字段控制缓冲区偏移。3.3 一次顺序读的时序推演把一次顺序读拆开看有助于把分层串起来。假设主机要读取LBA 1000开始的4KB数据主机驱动先向设备发送一个Command UPIUSCSI命令是READ(10)命令里包含LBA、传输长度还有数据传输方向等信息。UTP层把它封装成UPIUUniPro层加链路控制信息后通过M-PHY差分线发出去。设备收到Command UPIU解析SCSI命令UFS控制器执行FTL翻译、NAND读取。注意UFS协议允许设备在接受命令后分多段把数据通过Data In UPIU传回主机而不是必须一个包搞定。数据传完设备发送Response UPIU包含命令执行状态。如果数据量太大可能需要多次Data In UPIU每个包都带有数据偏移和剩余数据长度。主机驱动收到Response后这次读才算完成接着可以回收命令插槽发布下一条命令。我在看内核驱动时这个流程在ufshcd.c里被拆成几个状态机回调比如send_command、read_data等。配合一个逻辑分析仪抓M-PHY波形你能清楚地看到UPIU的帧头、负载、CRC校验字段。没有设备时用软件模拟一个UFS控制器也行只是相对冷门。3.4 分层视角对实操的意义遇到性能问题先分层判断是很有用的。判断步骤可以这样如果主机驱动已经提交读命令但设备响应迟迟不来大概率是设备内部处理、FTL或NAND调度问题如果设备已经回了Data In但主机侧应用层数据校验不通过那是链路可靠性的问题。如果连链路协商都过不去那是物理层或UniPro配置问题。我之前接过一个案例频繁掉盘最后追到发现设备在一段数据突发传输中数据重传过多导致命令超时。这就不是NAND的问题而是M-PHY信号质量差或者地弹、隔离没做好。只有在协议分层清楚的前提下你才能不靠猜来排查问题。4. 学习UFS3.1协议的正确路线从JEDEC文档到Linux内核代码4.1 文档体系JEDEC规范、MIPI规范和安卓驱动代码要学习UFS3.1协议核心文档是JEDEC发布的UFS标准文档一般在JEDEC官网能查到叫JESD220系列对应的UFS3.1就是其中最新版本之一。MIPI联盟的M-PHY、UniPro规范也必不可少。不过这些英文文档动辄几百页直接通读不现实我建议按需读先读目录和概述再读你要深入的特性章节。另外一个极好的中文资料反向来源是Linux内核源码和安卓文档。比如drivers/ufs/目录下的ufshcd.c、ufs.h、ufshci.h内核把协议栈实现得很完整注释也相对多。读这些代码能帮你把英文规范翻译成实际可运行逻辑。很多国产手机厂商还会开放部分UFS调试节点配合cat /sys/kernel/debug/ufs等路径能直接看设备和主机之间的协议状态。4.2 学习路线先SCSI命令再UPIU最后链路层如果让我给一条路径先学SCSI命令因为UFS应用的命令集本质上是精简版SCSI。你不需要把所有SCSI命令都记住但要熟悉INQUIRY、READ CAPACITY、READ/WRITE(10)、UNMAP、SYNCHRONIZE CACHE等。这些命令会在UFS设备启动和读写中反复出现。下一步学UPIU。搞懂命令如何封装、数据如何组织、响应如何判断。这一步能让你理解协议的数据通道和管理通道的区别。前两步通了再回头学MIPI UniPro和M-PHY的链路层细节。物理层知识可以粗一些重点是理解协商、重传和功耗状态切换不用深入研究每一个电气参数。我踩过的弯路是先啃物理层结果被各种时序和均衡器搞得头大后来发现链路层知识储备不足根本无法联系实际。协议栈是立体的自上而下读才符合数据实际流动的方向。4.3 跟着Linux内核学实现ufshcd.cLinux内核的UFS驱动是一个非常完整的学习样本。路径一般在drivers/ufs/ufshcd.c。里面的核心函数是ufshcd_send_command()它会把一个请求转成UPIU通过ufshcd_tag_init分配命令槽然后提交给host控制器最后在完成中断里回调。这个过程能把协议层的Command UPIU、Response UPIU串起来。我学习时的做法是先读ufs.h里的野的宏定义和UPIU结构体再顺着ufshcd_queuecommand函数往下走。遇到不清楚的字段就回到JEDEC规范查它的位域意义。一遍不行就多读几遍特别是命令队列管理、插槽状态机这部分会和具体硬件控制器相关。只要读懂了这一套大多数UFS控制器的驱动逻辑都大同小异。4.4 验证环境的搭建思路学协议最好有硬件环境。最理想的方案是搞一块手机主板或带UFS的开发板利用Linux内核的调试节点和逻辑分析仪抓取M-PHY信号。逻辑分析仪不需要太高带宽因为普通PWM模式也就几百MbpsHS模式抓波形可能得用高速示波器但我们可以先在PWM低速模式下看协议帧结构把UPIU的类型和字段逐个验证。如果你只有手机也可以读/sys/class/scsi/host下的设备信息或者用dd命令测读写速度配合厂商提供的UFS工具箱观察设备状态。我甚至用过坏掉的sd卡转UFS转接卡配合简单主控就为了能看到裸协议帧效果虽然简陋但足够帮我建立直觉。4.5 学习过程中如何避免“中文讲解”变成“翻译噩梦”中文学习资料少这是现实。我在整理笔记时会做一个中英对照表把术语固定下来UPIU不要翻译成“统一协议信息单元”就完事背后还得记住英文缩写。UFS3.1协议很多概念在中文社区里根本没有成熟译法比如HPB、WriteBooster我通常直接保留英文缩写解释时配上中文含义。同时不要依赖单一翻译多看驱动代码和开源社区的讨论能避免把spec里某些专业词汇翻译得走样。如果只是死记硬背官方文档的翻译学到后面容易失去准确含义。做笔记时我还会回看自己的学习路径确保每个新名词都能指出它在协议栈哪一层、解决什么问题。5. 主机与设备交互中的关键细节链路建立、任务管理与错误恢复5.1 链路建立的完整过程UFS设备上电后首先要完成链路建立这个过程叫链路启动。主机和设备通过M-PHY低速模式开始交互UniPro层做链路同步和功能协商。协商内容很多是单通道还是双通道、每通道速率能否到HS-G4、重传机制怎么配置。完成这些UniPro链路才进入Ready状态。链路建立完成后主机还不能直接发读写命令需要先通过Query Request向设备读取描述符和属性。比如读取设备描述符获取厂商ID、读取配置描述符了解设备支持哪些功能还要初始化写保护、清空状态等。这一套先握手再加配置的流程是后续一切命令的基础。很多UFS低概率故障恰恰发生在这个阶段比如设备建立链路超时、协商结果不对主机只会反复重置设备。5.2 命令队列与任务管理UFS3.1支持多命令并行主机通过命令插槽来管理。每个命令被分配一个标签设备执行时可以乱序处理、乱序返回。这提升了吞吐量但也带来了管理复杂度如果一个命令超时主机需要决定是继续等待还是中止它。任务管理通过Task Management Request UPIU实现常见操作有Abort Task、Query Task等。主机想取消一个悬而未决的命令时就发Abort Task设备处理完成后返回Task Management Response。这个机制在正常运行时用得少但是在错误恢复时不可或缺。我之前在联调时遇到过一个问题某个命令一直不完成主机队列被占满其他命令全被卡死。后面才发现是需要正确发送Abort Task再配合设备侧的中断处理才能真正释放命令槽。5.3 UFS错误恢复的层次UFS错误恢复分为好几种粒度。最轻的是单命令错误设备返回Response UPIU的Check Condition状态附带SCSI sense key主机根据错误码决定重试或者中止。严重一些的UniPro链路出现不可恢复错误主机先尝试发UFS Reset让设备的UniPro状态复位再重新做链路建立。如果这样还不能恢复就要做Host Controller Reset也就是把整个主机端口复位。我刚开始调UFS的时候觉得错误恢复都是设备固件“自己搞定”的后来才发现主机驱动要参与很多决策。什么时候重试、什么时候复位链路既影响数据完整性也影响性能。乱发retry会掩盖物理层问题乱发复位会让上层应用短暂卡顿。协议文档给出的是一条条规则但这些规则在实践中如何取舍才真正考验工程师的功力。这部分建议对照内核中的错误处理函数来学不要只看spec。6. 学习UFS3.1时最容易被误导的五个概念6.1 UFS3.1只是UFS3.0加了个写加速这是最常见的第一印象。实际上UFS3.1还包含HPB、DeepSleep、部分针对可靠性和功耗的优化。只盯着WriteBooster会忽略随机读性能的成因。我在系统上测试发现同一块UFS3.1芯片如果主控没有开放HPB随机读表现和UFS3.0几乎没有本质差异所以3.1不等于必然强。6.2 UFS和MIPI M-PHY是同一个联盟制定的它们是两套规范UFS核心协议由JEDEC定义M-PHY和UniPro由MIPI联盟定义。UFS只是它们的“客户”。我见过不少文章混着写一会儿说MIPI规范定义了UFS一会儿又说JEDEC定义了M-PHY实际上去官网下载时要分清。UFS3.1设备在物理层必然包含M-PHY但M-PHY本身也能用于其他规范比如移动显示接口。6.3 WriteBooster一定能提升所有写入性能WriteBooster只对顺序写有明显帮助对部分随机写也有一定缓冲作用但不是万能的。如果测试时的写入量超过缓冲区大小或者设备做后台回收速度就会波动。想要验证它是否生效需要对比顺序写曲线前后段或者观察设备的WriteBooster生命周期统计而不是简单跑一个平均值。6.4 主机侧优化不如设备侧重要UFS性能是设备、主控、主机驱动三方合力的结果。同一款UFS3.1设备配上优秀的UFS驱动和调度策略吞吐量可能比简陋驱动高出20%以上。协议学习如果只盯着设备侧忽略主机侧的UFSC/ufshcd实现遇到性能问题会抓瞎。主机驱动决定命令排布、队列深度、电源策略这些同样写在协议允许的范围内但结果差异巨大。6.5 中文资料少所以学不会中文资料确实少但内核代码和JEDEC规范都是可直接访问的配合翻译工具加社区讨论学习路径并不比英文母语的人曲折。我更建议把时间花在动手分析上不要指望有一本书能让你躺平学会。给自己定个目标解析一次设备枚举成功的完整流程或者跑出一次读命令的完整缓存状态。这种具体目标比通读规范有效得多。7. 从UFS3.1往后看UFS4.0与今天的移动存储变化UFS3.1在2020年附近成为旗舰标配现在已经被更大更新的标准接棒。UFS4.0把接口速率再翻倍单通道最高23.2 Gbps双通道理论上能达到46.4 Gbps。这对8K视频录制、车载系统、AI端侧推理都很有意义。更重要的是新一代规范进一步优化了功耗和高负载稳定性Host Performance Booster这类思路也被继承和拓展。学习UFS3.1并不会随着时间过时因为它建立起来的分层模型、术语体系、调试方法在UFS4.0甚至未来的UFS5.0里依然是地基。我去年看UFS4.0的功能描述时很多名字都能在3.1里找到影子——只是数值更高、机制更复杂。换句话说把UFS3.1啃下来等于拿到了一本移动闪存的通用字典。最后再分享一点个人体会学协议最忌讳的就是只看文档不验证。JEDEC规范里一个命令格式、一个字位定义只有在实际报文中看到才有获得感。现在我遇到性能或功耗问题时仍然会翻回UFS3.1的规范章节配合驱动代码和调试节点做交叉确认。这个“文档—代码—实测”三环循环比任何捷径都靠谱。如果你正打算深入移动存储就从UFS3.1开始啃下来之后你会发现后续所有的闪存协议不过是同一个故事的不同版本。
返回列表