3. UFS 协议栈简介:四层结构

UFS 协议栈看着复杂,拆开看就四层:应用层、传输层、链路层、物理层。跟 OSI 七层模型比,它砍掉了会话层和表示层,因为存储场景不需要那么多花活。我自己刚开始啃 UFS 规范时,就盯着这四层看,一层一层扒开,心里才踏实。

为什么分四层?说白了,每层只干一件事,解耦。你改物理层的信号幅度,不用动上面三层。我团队以前有个项目,M-PHY 换了一个版本,链路层配置调了几个寄存器就搞定,应用层代码一行没改——这就是分层的好处。

UFS 协议栈四层架构 应用层 (UFS Command Set) SCSI 命令、UFS 原生命令 传输层 (UTP) UPIU 封装/解封装 链路层 (UniPro) 帧定界、流控、错误恢复 物理层 (M-PHY) 差分信号、HS/LS 模式 应用层 (Device端) 命令处理、状态返回 传输层 (UTP) UPIU 封装/解封装 链路层 (UniPro) 帧定界、流控、错误恢复 物理层 (M-PHY) 差分信号、HS/LS 模式 注意:Host 与 Device 各层一一对应,数据通过物理层双向传输。 传输层的 UPIU 横跨整个协议栈,是分析问题的关键抓手。

3.1 应用层:命令的入口

应用层是离用户最近的一层。它处理 SCSI 命令集和 UFS 原生命令。比如读写、格式化、安全擦除。我早期有个项目,主机一直发 READ CAPACITY 命令,设备就是不回数据。查到最后,是应用层命令队列的 tag 冲突——同一个 tag 被用了两次,设备直接忽略。嗯,从那以后我检查命令标签特别仔细。

命令类别典型命令功能
基本 I/OREAD/WRITE逻辑块读写
管理命令FORMAT UNIT低格设备
安全命令SECURITY PROTOCOL IN/OUT加密密钥管理
UFS 特有QUERY REQUEST读写设备属性、标志
小提示: 应用层的命令超时次数不能忽略。我建议在初始化阶段先发一个 TEST UNIT READY,确保设备 alive,再发真正的 I/O。不然链路层还没建好就直接报错,浪费时间。

3.2 传输层:UFS 的“快递员”

传输层干的事——把应用层命令打包成 UPIU(UFS Protocol Information Unit),再通过下层传出去。UPIU 有固定格式,头部包含命令类型、LUN、任务标签等。你看下面这张表,常见 UPIU 就几种:

UPIU 类型方向主要用途
COMMAND UPIUHost → Device携带 SCSI 命令描述块 (CDB)
DATA IN UPIUDevice → Host读取数据回传
DATA OUT UPIUHost → Device写入数据发送
RESPONSE UPIUDevice → Host命令完成状态
TASK UPIUHost → Device任务管理(中止、复位)

为什么要有这么多类型?因为 UFS 是双工通信,数据可以在数据段里混着控制信息。有一次我在分析 trace 时,发现 RESPONSE UPIU 居然带了一段 payload——原来是设备把额外的 sense data 塞进去了。幸亏规范允许这么做,否则我可能冤枉它是个 bug。

实操要点: 抓 UPIU 时重点关注“数据长度”和“期望数据长度”是否一致。不一致就意味着传输层或链路层有重传,性能会掉下来。

3.3 链路层:UniPro 的可靠性保障

链路层基于 MIPI UniPro 协议。它负责帧定界、流控、检错重传。说白了,就是保证前面包装的 UPIU 能完整地传到对面,不丢包、不乱序。

UniPro 有多个版本(1.6、1.8、2.0 等)。我踩过一个坑:两个芯片用的 UniPro 版本不一样,链路层协商时一直失败,链路始终上不了。后来发现是 PA(Physical Adapter)层的配置参数不兼容。解决办法?强制降级到低版本协议。

警告: 链路层初始化有严格的时序——需要先建立 PA 层同步,再建立 DME(Device Management Entity)层握手。如果中间插入了未经授权的数据帧,链路会直接进入 error recovery。我曾经在测试时故意破坏一帧,结果设备卡在 recovery 循环里 500ms,主机那边已经超时复位了。

3.4 物理层:M-PHY 的高速公路

物理层用的是 MIPI M-PHY,差分信号传输。有高速模式(HS)和低速模式(LS)。HS 能跑几 Gbps,LS 用于电源管理。链路启动时,先低速握手机制(PWM 模式),再切换到高速。

说到物理层,我不得不提眼图。那时候项目量产前,有一批芯片温度高了就丢包。换上示波器看眼图,发现 HS 通道的摆幅偏低,信号幅度只有 200mV,而规范要求至少 360mV。排查下来是 PCB 走线阻抗失配,还窜了一根时钟线的谐波。改版后眼图张开漂亮,再没出过问题。

模式符号率每通道带宽典型场景
HS-G11.248 Gbps~1.16 GB/s常见高速模式
HS-G22.496 Gbps~2.32 GB/sUFS 2.1 及以上
HS-G35.824 Gbps~5.36 GB/sUFS 3.0 起步
LS-PWM几十 MHz ~ 几百 MHz低功耗空闲、休眠
我的经验: 物理层调试时先复位 M-PHY,再逐步上电。经常有人只改 UniPro 配置忘了重新 calibrate 物理层,结果链路始终起不来。你想想看,跨层问题往往藏在层间交互里。

3.5 四层如何协作?

举个例子:你发一条 READ (LBA=100, len=8)。应用层生成 SCSI READ 命令,传给传输层。传输层封装成 COMMAND UPIU,附上 LUN、tag。链路层把 UPIU 切成数据帧,加上 CRC,发送。物理层把比特流变成差分信号,穿过去。设备端反向解析,最后通过 RESPONSE UPIU 返回数据。

如果任何一层出问题,链路要么起不来,要么数据错。所以排查问题时,我习惯先确定物理层眼图 OK,再确认链路层帧计数正常,最后看传输层 UPIU 类型对不对。一层一层剥开,不盲目猜。

一个实用方法: 把协议栈各层的计数器和状态寄存器同时 dump 出来,对照时序图看异常发生在哪一层。有个项目我这样干,十分钟就定位到链路层重传次数暴涨——原来是对端设备 buffer 太小,流控参数配得不对。