大家看这个系列看到第五篇,说明前四篇的架构、命令集、UTP传输层和UniPro链路层已经啃得差不多了。UFS 3.1协议学习最尴尬的地方在于,越往下越没人讲,网上清一色都在聊Command、UPIU、WriteBooster,真正把M-PHY物理层讲明白的中文资料少得可怜。今天这篇就补上这个缺口,专门把UFS 3.1协议栈里最底层、也最容易被当成“硬件工程师才需要关心”的M-PHY物理层拆开,讲清楚链路训练、速率协商、Gear档位和眼图测试,也聊聊实际调试中踩过的坑。
这篇内容适合三类人看:正在做UFS驱动或者固件开发的软件工程师、负责UFS主控和PCB layout的硬件工程师,以及单纯想系统性搞懂UFS 3.1协议、不想只停留在“快,很快,非常快”这种层面的学习者。物理层在协议栈里的位置很尴尬,它不产生任何业务逻辑,但所有业务都跑在它上面。链路建立不起来,上层协议再好也是空中楼阁。
1. 协议栈最底层的那一层,为什么反而最难啃
1.1 UFS 3.1的三层协议栈到底长什么样
先把基础框架拉齐。UFS 3.1的通信架构跟网络协议栈一个思路,分了三层,每一层各管一段。最上面是应用层,跑的是SCSI命令集,什么READ、WRITE、UNMAP,都是在这里发出的。再往下是UTP层,也就是UFS Transport Protocol,它把上层命令、数据和状态封装成统一格式的数据包,叫作UPIU。UPIU往下一层交给UniPro,MIPI UniPro协议负责把UPIU进一步封装成UniPro帧,同时干链路管理和流控的活,比如丢包重传、缓冲管理,还负责链路启动时的参数协商。最底层就是今天的主角,MIPI M-PHY物理层,它把UniPro帧变成一对差分线上的高速电信号,真正把比特从Host搬到Device。
打个比方。你要寄一个包裹,应用层决定寄什么,UTP是填写快递单,UniPro是快递分拨中心,它决定包裹走哪条路、怎么避免丢件,而M-PHY就是那辆卡车,负责在物理道路上把货拉过去。前面几篇文章把快递单和分拨中心讲完了,今天看卡车。
M-PHY这个标准的出身要注意,它不是JEDEC自己搞的,而是MIPI联盟定义的物理层标准。MIPI联盟大家更熟的是D-PHY和C-PHY,这两兄弟主要用于显示和摄像头,手机屏幕和CIS传感器的数据传输就是它们在管。UFS没有走D-PHY和C-PHY,而是走了M-PHY,原因是M-PHY专门为存储类场景设计,更强调低功耗状态管理和高速传输并存,是存储领域最合适的物理层方案。
1.2 为什么UFS 3.1比上一代更吃物理层的性能
很多人以为UFS 3.1的核心更新都在软件上,WriteBooster、HPB、DeepSleep,看着全是上层策略。这话只对了一半。UFS 3.1能够把顺序读速度推到1.5GB/s、2GB/s这个量级,靠的绝不仅仅是算法调整,底层物理链路的带宽天花板必须率先到位。链路跑多少Gear、用几条lane、信号质量能不能撑住高速传输,决定了上层策略能榨出多少实际性能。
功耗方面也一样。DeepSleep是UFS 3.1的重点特性,手机待机时把UFS拉到极低功耗状态,而进入和退出这个状态,都要通过M-PHY的Hibernation线路状态来配合。M-PHY本身就专门设计了低速PWM模式和高速HS模式两套运行状态,这套机制是UFS省电的物理基础。所以无论是追求性能上限,还是压低功耗基线,最终都要看物理层的眼色。
2. M-PHY的两种工作模式和速率档位,搞懂Gear怎么翻倍
2.1 为什么同一对引脚要做两套工作模式
M-PHY的设计者在一对差分线上塞了两套完全不同性格的电路。第一套是PWM模式,字面意思就是低速脉冲宽度调制传输,速率从几Mbps到几百Mbps,功耗极低,适合设备刚上电、还没建立稳定高速链路的时候先用。第二套是HS模式,全速跑起来单lane能干到Gbps级别,但相应的功耗也高,信号质量要求也苛刻。
为什么需要两套模式?这个道理跟开车一样。车辆刚启动时不可能直接上高速,总得先在小区里低速挪出车位,等确认路况没问题了再提速。UFS设备上电瞬间,Host和Device之间还没有任何信任关系,信号质量、时钟同步全都没验证过,贸然拉高速大概率直接翻车。所以链路总是先在低速PWM模式下完成握手和参数交换,确认双方能力之后再逐步切到HS模式。
两套模式共用同一对差分引脚,通过M-PHY定义的Line State来切换。差分信号可以通过电平组合表达不同的线路状态,高速数据、低速数据、高阻空闲、休眠状态,本质都是这对差分线上的电压状态组合。这种设计把引脚数压到最低,同时把功耗和性能的调度权交给协议栈,非常巧妙。
2.2 一张表看懂Gear档位和速率对应关系
M-PHY的速率是用Gear来命名的,每一档Gear都是上一档速度的两倍,规律非常整齐。
| 模式 | Gear档位 | 单lane速率(典型值) | 典型用途 |
|---|---|---|---|
| PWM | PWM-G1 | 约3.9Mbps | 上电初始握手 |
| PWM | PWM-G2 | 约7.8Mbps | 链路低速通信 |
| PWM | PWM-G3 | 约15.6Mbps | 低吞吐场景 |
| PWM | PWM-G4 | 约31.2Mbps | 调试/诊断 |
| PWM | PWM-G5 | 约62.5Mbps | 低功耗数据传输 |
| PWM | PWM-G6 | 约125Mbps | 低速数据交换 |
| PWM | PWM-G7 | 约250Mbps | PWM模式最高档 |
| HS | HS-G1 | 约1.2Gbps | 高速入门档 |
| HS | HS-G2 | 约2.5Gbps | 中速传输 |
| HS | HS-G3 | 约5.0Gbps | 高速传输主力档 |
| HS | HS-G4 | 约6.0Gbps | 高速最高档(视版本) |
需要说明的是,上表中的数值是MIPI M-PHY规范里的常见参考值,不同版本、不同芯片实现会有差异,具体以你手里的datasheet为准。但翻倍规律是确定的:每升一个Gear,速率翻一倍。
UFS 3.1的物理通道最多支持两条lane,也就是一个Host可以跟Device之间拉两条差分对并行传数据。理论总带宽等于单lane速率乘以2,再乘以lane数。以常见的双lane HS-G3为例,理论链路带宽在10Gbps量级,换算下来超过1.2GB/s,再算上协议开销,实际跑出1GB/s的有效吞吐是合理的。
2.3 算一笔账:一次顺序读到底能跑多快
很多人被宣传页面上“2100MB/s”这种数字搞混,以为链路带宽就等于实际吞吐。完全不是这回事。链路速率只是毛带宽,UPIU有头部开销,UniPro层有帧头、流控和CRC校验,数据还要按块对齐,几层开销叠下来,有效吞吐通常只有链路带宽的七八成甚至更低。
举个具体例子。假设某颗UFS 3.1设备跑在双lane HS-G3档位,每lane约5Gbps,链路总带宽约10Gbps。扣掉协议开销和调度损耗,净吞吐按7.5Gbps算,也就是不到1GB/s。若设备支持HS-G4档位,把链路带宽拉到12Gbps量级,净吞吐才能摸到1.2GB/s以上。所以如果你想通过调试手段压榨性能,第一件事就是确认链路到底协商在哪个Gear、几条lane,别被上层缓存刷分的数据迷惑了。
3. 链路初始化与速率协商:从慢速到满速的完整过程
3.1 上电之后,Host和Device到底聊了什么
链路启动的全过程,就是一次完整的速率协商。UFS设备上电之后,VCC和VCCQ电压稳定,Device完成内部复位,Host侧的M-PHY开始尝试建立通信。双方默认在最低档PWM-G1上碰头,这个档位又慢又稳,即使信号质量很差也能保证通信建立。
接下来是能力交换。这部分的实际实现是在UniPro层完成的,UniPro的PA子层(Physical Adapter)和M-PHY之间有对应的控制接口,PA层会发送参数协商请求,告诉对方自己支持哪些Gear、最多几条lane、支持哪些电源模式。然后双方取一个交集,选一个彼此都支持的最高速率作为目标,再通过Line Startup流程一步步Gear Up,直到达到目标档位。
这个流程设计得很稳妥。快速拉升Gear存在风险,信号质量不够时链路会直接报错,所以协议里允许一步步往上升,每升一档都要确认链路还能正常工作。实际协商出来的结果可以在链路的参数集合里看到,比如PA_ActiveTxDataRate、PA_ActiveRxDataRate这类配置,软件可以读取当前协商状态。
3.2 怎么确认设备当前实际跑的速率
开发中最常见的问题不是链路起不来,而是链路起来了、但你不知道它现在跑在什么档位上。我的习惯是先查芯片的链路状态寄存器,UFS Host Controller在不同厂商的datasheet里命名有差异,但通常会有链路状态、当前速率、当前lane数这类字段。很多主控还支持在调试工具里输出链路协商日志,直接能看到“Gear3, 2 lanes, HS mode”这类信息。
更高阶的手段是上协议分析仪。UFS协议分析仪能够抓取链路上的物理信号和解码后的UniPro帧,你可以直接看到设备上电后的完整握手过程:从PWM-G1开始,每个GearUp命令、每个参数确认帧,全过程一目了然。我们自己调试的时候,如果遇到“路径跑起来了但速度异常”的诡异问题,协议分析仪基本上一抓一个准,比瞎猜效率高太多。
调试早期我还踩过一个坑:代码里把Host支持的MaxGear配得过高,但Device端其实不支持那么高的档位,协商结果反复跳变,性能反而上不去。后来改成先固定一个保守的Gear跑通功能,再逐步放开MaxGear,问题立刻就清晰了。这个经验后面细说。
3.3 协商过程中最容易踩的坑
速率协商看似简单,真正调起来坑不少。最常见的是PCB阻抗不达标导致高速档位上不去。链路在PWM-G1这种低速档完全正常,一旦尝试切到HS-G2、HS-G3,立刻误码率飙升、重传风暴,最后协商失败回退到低速。这种问题从协议日志上看就是“反复GearUp失败”,但根因却在物理层。
另一个坑跟软件配置有关。部分主控要求一次性把两个lane的端接、预加重参数都配好,配置漏了或者顺序写错,就会出现单lane正常、双lane协商失败的情况。所以我建议做链路调试时,先把lane数固定为1,功能通了再开2 lane,手把手把变量降到最少。
4. 物理信号与测试:眼图为什么是物理层的第一道关口
4.1 差分信号和眼图,不懂这些就没法排查高速问题
M-PHY的HS模式跑的是高速串行差分信号。差分信号就是一对线,一条走正电平、一条走负电平,接收端看的是两根线之间的电压差。差分传输的好处是抗干扰能力强,共模噪声在两根线上是同向的,相减之后就会被抵消,这也是高速接口普遍采用差分对的原因。
眼图是衡量高速信号质量最直观的手段。用示波器把大量比特的波形叠加在一起,由于触发位置对齐,屏幕上会出一个像眼睛一样的图案。眼睛睁得越大,说明信号质量越好;眼睛闭合甚至模糊,说明信号抖动大、噪声高,接收端误码概率就会升高。MIPI M-PHY规范对不同的Gear档位定义了对应的眼图模板,测试时信号必须落在模板之外才算合格。
我举个例子帮大家理解。眼图就像人的心电图,医生看一眼形状大概就知道心脏状态。眼图测试也是在高速总线这个“心脏”上做体检,眼睛的边缘、宽度、高度和抖动特性都对应着信号质量的特定维度。
4.2 物理层测试的标准动作与实战建议
做UFS物理层测试,几样设备是少不了的:高带宽示波器、差分探头、协议分析仪,有条件的话再配上矢量网络分析仪测通道的S参数。常规测试项目包括:差分阻抗、眼图模板、信号摆幅、共模电压、上升下降时间、抖动。
测试过程中的几个实操要点,都是用真金白银换来的经验。第一,差分探头一定要做校准,探头本身的skew和衰减会直接影响测试结果,不校准测出来的眼图根本不能信。第二,测试点尽量靠近芯片引脚,走线越长信号退化越严重,测出来的结果不是你实际链路的真实水平,而是整条通道的累积损耗。第三,一条通道的多个lane都要测,不要只测一个lane就当完事了,不同lane之间的串扰和损耗差异经常超出预期。
还要提醒一点,温度对高速信号的影响很大。高温下信号摆幅下降、抖动增大,有些设备在常温下眼图漂亮得很,一到高温环境就开始疯狂报错。如果条件允许,最好在常温、高温、低温三个温度点各做一轮物理层测试,提前暴露问题。
5. 常见问题排查与避坑技巧实录
5.1 速率协商不上去,性能卡在低速
现象非常典型:设备能识别,读写有数据,但速度就是上不去,用协议分析仪一看,链路协商结果停在PWM模式或者HS-G1,怎么都升不上HS-G3、HS-G4。
排查这类问题我习惯按三步走。第一步先排除软件配置,检查Host侧的最大Gear参数、lane数参数、电源模式配置,确认没有限制链路发挥的死配置。第二步查PCB和器件,重点看差分对的阻抗是否匹配、串阻是否贴错、端接电阻是否焊好。第三步上示波器实测信号,确认高速档位下的眼图是否达标。
这个思路的优先级是按成本排的:改配置最便宜,查硬件次之,测信号最贵但最准。每次遇到这类问题,流程走完基本能定位到根因。
5.2 CRC错误和重传风暴,链路不稳定
链路虽然协商到了高速档,但运行时不停报CRC错误、反复重传,吞吐掉得厉害。这种情况多半是信号质量临界,链路能跑高速、但跑不稳。常见诱因是PCB layout的回流路径不完整、参考地层被割裂,或者差分对走了过孔导致阻抗突变。
应对手段有几招:一是尝试降低一个Gear档位,如果降到HS-G2后CRC错误明显减少,基本坐实是信号裕量不足;二是检查走线,看有没有办法缩短长度、减少过孔、优化参考地;三是尝试调整端接电阻或驱动强度,有些主控允许软件配置驱动能力和预加重,这相当于在系统层面帮信号一把。
5.3 低功耗唤醒异常,链路重建失败
UFS 3.1对DeepSleep依赖很大,但深睡之后唤醒出问题的情况在开发中非常常见。典型的症状是设备进入深睡后无法正常唤醒,或唤醒后链路重建失败,主机端直接报错。
排查时先看M-PHY的Hibernation线路状态是否干净地退出,再看唤醒时序是否符合规范。很多问题的根因是主机端唤醒信号发出得太早,设备还没准备就绪;或者恰好相反,主机等得太久导致设备端超时。我们实际调试中通过协议分析仪抓唤醒阶段的波形,几次就能定位是时序余量不足还是状态机错乱。唤醒问题要多做压力测试,连续唤醒几百次,确保每次都能稳定重建链路,再考虑放量产。
5.4 问题排查速查表
| 现象 | 可能原因 | 优先排查手段 |
|---|---|---|
| 速率只能协商到低速档 | MaxGear配置错误、PCB阻抗不达标 | 查寄存器配置,实测眼图 |
| 高速下CRC错误/重传多 | 信号裕量不足、端接不良、回流路径差 | 降档验证,查layout,调驱动强度 |
| 双lane协商失败 | lane使能配置遗漏、单lane信号差 | 固定单lane验证,逐lane测试 |
| 深睡后无法唤醒 | Hibernation时序问题、电源域未就绪 | 协议分析仪抓波形,压力测试 |
| 偶发链路中断 | 供电不稳、共模干扰 | 查电源纹波,查信号完整性问题 |
最后分享一点实际体会
我最初做UFS调试时也犯过同样的错误,总觉得链路起不来、速度上不去,肯定是上层想得太复杂,命令发错、参数配错。后来被一个诡异问题折磨了一周,最后发现是PCB上差分对的共地过孔打少了,高速回流路径不完整,导致信号质量临界,时不时就重传。那次之后我才真正明白,协议栈里的每一层都值得尊重,物理层虽然最枯燥、最不产生业务逻辑,但它就是整个通信系统最大的变量。软件工程师也别觉得看眼图是硬件的事,能够同时看懂协议日志和示波器波形的人,在团队里是不可替代的。
还有一个百试百灵的土办法,遇到通信不畅,先把Gear强制降下来,功能跑通之后再一档一档往上升,锁定是哪个档位开始出问题的。这个手段不需要昂贵的分析仪,不需要复杂的排查流程,但每次都能快速缩小范围,是我这几年用下来性价比最高的调试技巧。UFS 3.1协议学习到这篇,通信链路这条主线基本串完整了,后面如果再遇到跟物理层相关的问题,记得回来来翻这篇。