ARTICLE DETAIL

资讯详情

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

UFS3.1协议栈精讲:UTP封装UPIU与UIC链路M-PHY/UniPro全解析

UFS3.1协议栈精讲:UTP封装UPIU与UIC链路M-PHY/UniPro全解析 这篇是UFS3.1协议学习系列的第六、七篇。前面我们聊过UFS的基本架构、设备枚举和命令集初始化今天把协议栈里最劝退的两块内容拆开揉碎讲清楚第6章UTPUFS Transport Protocol是命令和数据怎么封装成UPIU并在主机与设备之间传递第7章UICUFS Interconnect是这些UPIU到底怎么通过MIPI M-PHY和UniPro在物理链路上跑起来。如果你和我一样读JEDEC JESD220E规范时在第6章和第7章反复卡住这篇就是为正被位域、Gear、CPort折磨的人写的。读完之后你再去看厂商的trace、抓协议分析仪波形脑子里会立刻浮现该看哪个字段、该查哪一层。1. 先打通整个UFS协议栈别一上来就啃UPIU1.1 UFS三层协议架构UFS的软件架构和网络协议栈很像从上到下可以分为三层应用层UFS Command SetUCS、传输层UTP、互连层UIC。应用层负责解释和执行命令比如READ/WRITE、INQUIRY、START STOP UNIT等这些本质上都是SCSI命令的裁剪版传输层负责把命令、数据、状态打包成标准格式的UPIUUFS Protocol Information Unit互连层负责把UPIU变成位流通过物理信号发送出去并保证对端能正确接收。把这三层的关系类比成快递发货应用层决定“要寄什么东西”命令内容传输层把东西装进标准纸箱并贴好面单UPIU互连层是货车和道路负责把箱子运到目的地。你在驱动代码里看到的ufshcd_send_command实际上是站在传输层的视角而寄存器、PHY配置则属于互连层。很多调试问题之所以难定位就是因为问题发生在某一层症状却出现在另一层。1.2 UTP和UIC各自管什么UTP的核心产物是UPIU它类似网络协议里的TCP段。所有主机发给设备的请求以及设备返回主机的响应都以UPIU为单位。UPIU有固定32字节的头部后面可以跟可选头EHS和数据段。UTP不关心底层是M-PHY还是别的物理层它只负责“内容对不对、格式对不对”。UIC则完全不同它关心的是“怎么传”。UIC由MIPI联盟定义的M-PHY物理层和UniPro协议栈组成。M-PHY定义电气特性、时钟、差分信号、Gear速率和PWM/HS模式UniPro负责链路启动、数据传输、流控、重传相当于把“TCP/IP”能力搬到了芯片互联场景。UIC之上还有个DMEDevice Management Entity它用来配置M-PHY和UniPro的参数比如当前链路速率、激活的Lane数、是否开启CRC校验。1.3 学习顺序建议初学者最常犯的错是直接啃UPIU结构或者直接看M-PHY速率表结果越看越懵。我的建议是先建立“命令—UPIU—物理链路”的流水线认知再逐个细节突破。具体顺序先搞懂UCS层的几个核心命令READ、WRITE、QUERY其中QUERY用于访问描述符和属性再理解UTP层描述的完整命令交互流程最后才去看UIC层的链路启动和Gear协商。这样当你看到协议分析仪抓到的UPIU时才能明白它属于哪一步、下一步该出现什么。2. UTP传输层命令是怎么变成UPIU的2.1 UPIU头部字段逐位看一个标准UPIU头部固定32字节。头部的核心字段包括字段作用调试时怎么看Transaction Type指明UPIU类型比如命令、数据、响应、任务管理抓包第一眼先看它Flags方向、EHS标志、是否带数据段等确认数据方向LUN逻辑单元号0~31确认命令发给哪个LUNTask Tag命令标识用于并发和多命令匹配超高并发时检查是否会重复Initiator ID发起端标识主机侧通常为0多主机场景少一般忽略Command Type区分是SCSI命令还是UFS原生命令判断走的是哪套命令集Expected Data Transfer Length主机期望的总数据长度和DATA段长度对比可查超时Data Segment Length当前UPIU携带的数据字节数配合EDTL定位丢包Data Unit Number数据起始编号用于校验连续性和重传重排序时特别有用很多新手看到这些字段觉得复杂其实只需要抓住三个Transaction Type决定“这条UPIU是什么角色”Task Tag决定“它属于哪个命令”EDTLExpected Data Transfer Length决定“这次传输总共该有多少数据”。只要这三个对得上大概率命令流程不会乱。2.2 常见的UPIU类型不止COMMAND和RESPONSEUFS协议里常用UPIU类型有下面这些建议记成一张速查表UPIU类型方向用途NOP_OUT / NOP_IN主机→设备 / 设备→主机链路保活类似pingCOMMAND主机→设备携带SCSI或UFS命令的CDBRESPONSE设备→主机返回SCSI状态、Sense信息、任务管理结果DATA_OUT主机→设备写数据DATA_IN设备→主机读数据TASK MANAGEMENT REQUEST主机→设备中止任务、逻辑单元复位等TASK MANAGEMENT RESPONSE设备→主机返回任务管理结果QUERY REQUEST / RESPONSE主机→设备 / 设备→主机访问描述符、属性、标志以及DME命令REJECT设备→主机通知主机UPIU非法或不被支持这里最容易忽略的是QUERY UPIU。UFS没有专门的“寄存器”命令设备描述符、设备属性、标志位全都靠QUERY请求来读写。驱动初始化时你会看到一串QUERY REQUEST读设备描述符、几何描述符、能力描述符这些都是在UTP层完成的。2.3 用一次READ命令串一遍UTP流程咱们拿一次最简单的16KB读操作来看完整UTP交互假设LUN0LBA0x1000。主机侧在软件里构造一个READ(10)命令CDB里填好LBA和传输长度然后封装成COMMAND UPIU发送。这个UPIU的Transaction Type是0x01COMMANDTask Tag被主机分配为比如0x0AEDTL填16384。设备收到后因为这是读操作会拆成4个DATA_IN UPIU每个带4KB数据返回给主机每个DATA_IN UPIU的Task Tag都是0x0AData Segment Length都是4096。数据全部发完后设备再回一个RESPONSE UPIU里面携带SCSI状态GOOD。实际上设备不一定会严格按“数据完后响应”的顺序也可能先回RESPONSE再回DATA IN这取决于设备的实现。所以主机驱动不能傻等响应而应该根据Task Tag分别匹配数据和状态。这也是为什么Task Tag必须严格唯一如果两个命令共用一个Tag重排序场景下数据归属就会错乱。2.4 UTP字段的几个坑踩过UTP的坑后我列几个最常见的问题。第一EDTL填错很多驱动用blk_rq_bytes直接填但没考虑分片导致设备发现EDTL和实际数据长度不一致直接报错或挂起。建议用块层的传输字节数而不是请求总长度。第二Data Segment Length的字节序UPIU头部里的长度字段都是大端序如果你用cpu_to_be16习惯性转成小端设备解析出来的长度会非常离谱。第三LUN范围UFS规范中LUN只有4位有效值0到7但部分厂商设备支持扩展LUN在额外字段里用EHS表示如果你只在标准字段填LUN高位会丢。第四NOP_OUT是调通UTP最快的办法驱动初始化互相扯皮时先发一个NOP_OUT如果能收到NOP_IN说明UTP基本通路是通的再往下查命令层。3. UIC互连层M-PHY和UniPro如何把比特送到对方3.1 DME设备管理实体UIC并不是单纯的一根线它内部有一套完整的管理体系。DME就是这套体系里管配置的模块。你可以把DME理解成设备内部的一个“控制面板”里面挂满了属性比如PA_ActiveTxDataLanes、PA_ActiveRxDataLanes、PA_TxGear、PA_RxGear等。主机要改链路配置不能直接写寄存器而是通过UTP层发一个QUERY REQUEST在UPIU的数据段里携带DME_GET或DME_SET的命令让设备内部的DME去执行。DME的属性和UniPro/M-PHY强相关。比如你想知道当前链路跑在HS-Gear3还是HS-Gear4就发一个DME_GET去读PA_RxGear。想切换链路的Gear就先DME_SET写新值再把PHY重新适配。调试时看链路协商失败第一件事就是确认DME操作有没有正常返回。DME属性读出来的值往往很直接0表示PWM-G11表示PWM-G23表示HS-G15表示HS-G36表示HS-G4。知道了这个映射你就不用靠猜去看链路速率了。3.2 M-PHY物理层PWM和HS模式的选择M-PHY是MIPI联盟定义的物理层标准它有两种工作模式。PWM模式用低速、低功耗的方波信号适合设备空闲或待机HS模式用高速差分信号适合大数据吞吐。每个模式下面又有多个Gear级别相当于档位。PWM-G1到PWM-G7速度逐步提升HS-G1到HS-G4才是真正跑满UFS3.1性能的档位。UFS3.0引入了HS-Gear3UFS3.1在消费电子场景常见的是HS-Gear3和HS-Gear4。理论上一条lane在HS-Gear4大约11.6GbpsUFS最多支持两条发送lane和两条接收lane所以双lane合计最高可以到23.2Gbps左右。为什么搞这么多档位因为链路并不是永远全速跑。手机待机时需要省电主机会通过DME把M-PHY降到PWM模式甚至进入休眠一旦有读写请求再通过唤醒流程升到HS模式。这个切换过程如果时序没处理好常见现象就是设备第一次读特别慢或者链路起来后频繁CRC Error。我调试过的项目里至少有一半的“性能差”问题出在Gear切换策略上而不是主控或Flash本身。3.3 UniPro协议栈链路启动和流量控制UniPro位于M-PHY之上可以看成一个小型网络协议栈包含物理适配层、数据链路层、网络层和传输层。物理适配层把UPIU映射成UniPro的帧数据链路层负责成帧、CRC校验、重传和流控网络层处理不同CPort之间的路由传输层保证端到端可靠传递。链路启动是UniPro里最壮观也最容易出错的过程。当UFS设备上电或从休眠唤醒时主机会发起Link Startup。这期间双方先以最低速PWM模式交换一轮训练序列然后逐步协商到双方都支持的更高Gear和Lane数。这个过程就像两个人打电话先互相“喂喂”确认通话质量再切到高清语音。协议里涉及START、END、TRAFFIC等多种序列以及时序参数PA_TActivateTime等。如果链路启动失败大概率不是硬件完全坏而是其中一方没有在超时时间内回复。这时优先去查DME属性里的PA_Error和链路状态机比直接换主板更有用。UniPro的流控机制非常关键。它采用基于Credit的流控接收方会告诉发送方自己还有多少缓冲区可用。如果发送方缓冲区被写满它就不会再发数据直到对端释放Credit。很多吞吐量异常的案例不是因为物理层不稳定而是Credit配置太小导致链路永远跑不满。修改UniPro缓冲区大小是通过DME属性配置的具体属性名通常是DataLane0 Credit或Local Rx Buffer这一类。遇到速率忽高忽低别急着换Gear先把Credit加大试试。3.4 为什么UFS选M-PHY而不是C-PHY同为MIPI家族的C-PHY和D-PHY大家可能更眼熟因为手机摄像头接口用的就是D-PHY和C-PHY。但UFS没有选它们主要原因有三个。第一M-PHY天然是为存储类双向高带宽设计的它提供独立的发送lane和接收lane读和写可以同时进行而D-PHY/C-PHY更多是单向视频流场景。第二M-PHY支持从极低功耗PWM到高速HS的宽范围适配存储设备长时间待机和突发高吞吐的两种极端需求。第三M-PHY的差分信令在抗干扰和功耗控制上更适合板级连接。C-PHY在三线制编码、无独立时钟上有优势但代价是复杂度和功耗在存储领域并不合算。你如果去看手机主板UFS芯片和SoC之间的走线通常就是一对用于发送、一对用于接收的差分线外加时钟、复位和电源。走线短、阻抗匹配好才能让M-PHY的HS-Gear4稳定跑起来。PCB设计时差分对间距、参考地完整性、过孔数量都会直接影响眼图质量。这也是为什么明明主控和设备都支持UFS3.1但跑到高速时忽然读写速率掉一半——大概率是信号质量问题不是代码问题。4. UFS3.1新增特性与UTP/UIC的关联4.1 HS-Gear4速率支持UFS3.1规范一个明显变化是正式支持HS-Gear4把单lane理论速率拉到11.6Gbps量级。这个速率带来的影响不只是数字好看它对M-PHY的物理层设计和UniPro的重传机制都提出了更高要求。速率越高信号的建立和保持时间窗口越窄对时钟抖动和PCB损耗更敏感。对开发者而言UFS3.1设备在初始化时要通过QUERY/UPIU读取设备能力描述符看设备支持的最高Gear是多少再去DME里设置主机和目标一致的Gear。强行让仅支持HS-G3的设备跑HS-G4通常会导致Link Startup失败或大量CRC Error。这种不匹配在兼容性测试里很常见。4.2 WriteBooster对UTP的影响UFS3.1引入了WriteBooster本质上是在设备内部划出一块SLC缓存先以高速写进SLC后台再把数据搬到TLC。这给UTP层带来的变化是主机可以发出WriteBooster Buffer相关的QUERY命令比如查询当前Buffer状态、手动触发Flush。如果你在调UFS3.1写性能看到写速先快后慢是正常的SLC缓存满了就会回到TLC原生速度。但由于WriteBooster的数据段往往很大一旦UTP层的EDTL和数据段长度字段配合不好很容易出现命令超时或数据段不完整。解决思路是在驱动里把大块写请求分片而不是让设备处理超大UPIU。设备端通常对单次UPIU数据段长度有限制查一下量产spec的MaxDataSegmentSize字段就明白了。4.3 其他扩展性能降级通知与休眠优化UFS3.1还新增了一些和UTP/UIC相关的辅助机制比如温度过高时的性能降级通知。设备可以通过异步事件或QUERY响应告诉主机“我现在很热我会自动降速”主机的调频调度器需要识别这种情况不要误判是主控问题。另外UFS3.1对深睡状态做了更细的功耗优化要求链路在唤醒时能更快完成Link Startup。这里有个实操细节深睡唤醒慢往往是因为UniPro的唤醒序列时序参数没有按UFS3.1规范配成最短值。设备厂商会在量产固件里把这些参数调好但如果你在自研主控上调试记得在DME属性里确认PA_TActivateTime和PA_Hibern8Time不是默认的保守值。5. 实操过程与排查技巧从初始化到压力测试5.1 读取UFS设备的能力信息不管做什么优化第一步永远是确认设备真实能力。在Linux内核里你可以通过cat /sys/block/sda/device/ufs_device_descriptor这类节点看到设备描述符的内容包括厂商ID、型号、最大Lane数、最大Gear等。如果想看更底层的能力用ufs-utils发QUERY命令去读几何描述符和属性。重点关注这几个值字段含义建议值bDeviceMaxGear设备支持的最高Gear3或4取决于UFS3.1bDeviceMaxLanes最大Lane数多数为2bDeviceMaxWriteBoosterCap写加速缓冲大小几GB到十几GBqTotalRawDeviceCapacity设备总容量与你买的容量一致bNumberOfLUNsLUN数量通常1或2我自己遇到过设备明明标称UFS3.1但bDeviceMaxGear只支持到3的情况。这说明芯片用的是UFS3.1协议但物理层只做到Gear3性能上限就锁在那了。驱动初始化时不要默认按Gear4去协商先从描述符读出来再配参数能省掉后面一连串链路问题。5.2 常见问题与排查方法把多年调试经验浓缩成一张速查表遇到问题先对号入座。现象大概率原因排查动作Link Startup失败Gear/Lane协商不一致读DME属性确认双方Gear降级到HS-G1试链路频繁CRC Error信号质量差、唤醒时序不对用示波器看眼图调整PHY参数命令超时Task Tag一直pendingUPIU的EDTL或数据段长度错误抓UTP trace比对EDTL和DataSegLen吞吐量上不去Credit配置太小或Gear被降增加UniPro Credit读取当前PA_RxGear写入先快后慢WriteBooster SLC耗尽查WriteBooster状态不是异常休眠唤醒后命令超时深睡唤醒时序没配短调整PA_Hibern8Time等属性特别提醒一句遇到“莫名其妙的性能差”先排除链路降级。很多平台会把UFS自动降级到PWM模式然后再也不升回HS模式。你可以持续读PA_RxGear如果发现一直是PWM-G1说明升级逻辑坏了。这种问题跟协议本身关系不大但表现上很像协议错误。5.3 抓取和分析UTP流量的经验调UFS协议最有力的工具是协议分析仪比如Keysight的UFS Protocol Analyzer或者部分逻辑分析仪配合UFS解码插件。没有硬件工具时软件trace也能看到UTP层交互内核里开启/sys/kernel/debug/ufs下的tracepoint可以记录UPIU发送和接收事件。抓下来的分析要点有三个第一看Transaction Type是否序列正常比如COMMAND后面有没有RESPONSERESPONSE有没有超时第二看Task Tag是否在同一时间点出现重复重复意味着驱动或设备挂了第三比较EDTL和所有DATA段总和如果总和小于EDTL一定有数据丢了。我第一次抓UFS trace时困在一个问题上RESPONSE UPIU明明已经收到了但应用层还在等。最后发现RESPONSE里的Sense Key和Additional Sense Code在QUERY Request的扩展字段里而驱动只解析了标准SCSI状态把扩展Sense数据当噪声丢掉了。从那以后我就养成了习惯凡是抓包分析一定要先确认UPIU的可选EHS部分有没有内容。EHS通常用于承载超过标准头容量的信息比如扩展LUN、厂商特定数据忽略它等于丢掉一部分关键情报。结尾写了这么多总归一句话UFS3.1协议里UTP和UIC是相辅相成的命令能不能跑通看UTP跑得稳不稳快不快看UIC。我最初读协议时觉得第6章和第7章是两座孤岛后来在调试一个深睡唤醒失败问题时才意识到唤醒的命令传输依赖UTP格式正确而能否及时唤醒依赖UIC的Link Startup时序。那个问题最后就是靠同时看UTP的NOP_OUT/IN和DME属性才定位到的。所以别嫌协议文档枯燥每一章都有它存在的意义。这套协议栈你越往后调越会发现那些当初咬牙啃下来的位域和Gear参数最终都会变成你手里最顺手的调试工具。
返回列表