只读分享解决方案文档原理方案设计📅 2026-10-14🔖 Rev 1.0⏳ 还有3日到期
🔧解决方案&应用市场分析 -> 原理方案设计 -> 电子设计&技术探讨 -> UFS协议栈与UniPro_M-PHY_技术探讨

UFS协议栈与UniPro_M-PHY_技术探讨

同系列 · 原理方案设计
扫码打开本页
微信扫码

UFS 协议栈与 UniPro/M-PHY

分层传输、Lane 与 Gear 的性能机制,以及相对 eMMC 的架构差异 —— 一份面向系统与硬件协同的协议栈参考 | 编制日期 2026-10-14 | 版本号 Rev 1.0

4 层
UFS 主机侧到 NAND 的逻辑分层
传输层 UTP / UniPro / M-PHY / 设备控制器
2 个变量
决定链路带宽的两个物理量
Lane 数与 Gear 档位
3 件套
UFS 身份的三份规范来源
JESD220 / JESD230 / SCSI 命令集
5 级
故障定位时的分层粒度
物理 / 链路 / 传输 / 设备 / 应用
报告总结

UFS 的资料通常散在三处:器件手册讲的是电压与时序,驱动代码讲的是队列与中断,标准文本讲的是状态机与 OMA。三者各自成立,但拼不成一张完整的地图,导致出现性能问题时无法判断问题落在哪一层。本文把这些内容按协议栈的分层顺序重新组织一次,让每一层「解决什么问题、依赖什么、坏了怎么查」都能独立成立。

因此本文不写电气设计、不写布线、不写器件选型(这些属于同系列的 UFS 电路设计指南),也不写主机控制器的寄存器级驱动流程与移植步骤(这些属于 UFS 软件设计规范)。本文只写一件事:UFS 这套协议栈是怎么分层的,每一层为什么必须存在,以及带宽与并发这两个工程变量如何由架构决定。

结论: UFS 相对 eMMC 的本质升级不是「更快」,而是把存储协议、通用互联与物理层拆成三层可独立演进的规范—— 带宽由 Lane 数 × Gear 档位 两个物理量直接决定,而并发能力由 任务 Tag 与多队列模型 决定; 这两组变量都在协议栈里有明确归属,因此性能与并发问题可以逐层定位,而不是像 eMMC 那样只能整体调时序。 所有数值均为量级描述,实际设计一律以正式 JEDEC / UniPro 规范与器件 datasheet 为准。

读者路径

  • 只想弄清 UFS 到底是什么:第 1 章(三件套身份与生态全景)+ 附录 A(术语表),十分钟可读完。
  • 要做 Host Controller 侧的软件设计:第 3 章(UTP 与队列)+ 第 4 章(命令语义)+ 第 7 章(并发与 QoS),这是本文的核心。
  • 要排查链路起不来 / 速率上不去:第 5 章(UniPro 分层与 AER)+ 第 6 章(M-PHY 与 Gear)+ 第 10 章(初始化全流程)。
  • 从 eMMC 迁到 UFS:第 2 章(架构对照)+ 第 4.5 节(迁移陷阱)+ 第 11 章(架构性差异)+ 第 11.6 节(检查项清单)。
  • 做容量规划与分区设计:第 8 章(LUN 与 LU 组织)+ 第 2.4 节的两级结构。
  • 现场出问题需要一个排查顺序:直接跳第 12.3 节 + 第 14 张图(分层定位排查树)+ 附录 C(分层排查手册)。

与同系列文档的分工

  • UFS 电路设计指南:负责电源域划分、电压与电流、时钟与复位、差分布线与阻抗、器件选型与替代、PCB 与信号完整性。本文的第 6 章只讨论物理层的协议机制,不讨论布线和阻抗。
  • UFS 软件设计规范:负责主机控制器的寄存器组配置顺序、中断处理、DMA 与描述符管理、驱动移植与调试。本文的第 3、4、10 章讨论的是这些流程背后的协议理由,不给出寄存器级步骤。
  • 本文:负责分层模型、每层的职责边界、Lane 与 Gear 的带宽机制、架构差异,以及分层定位方法。

1. UFS 是什么,以及为什么值得单独拆开讲

这一章不深入任何一层,只先把 UFS 的身份讲清楚:它不是一颗器件,也不是一条总线,而是三份规范共同定义的一整套系统。身份讲不清,后面所有的性能与并发讨论都会失去落点。

1.1 从 eMMC 到 UFS:变化的不是速率而是架构

eMMC 与 UFS 都能挂在同一块 SoC 上、都能跑同一个块设备接口,从上层看几乎一样。真正的差别埋在下面:eMMC 把协议、时序与数据通路压在一条并行总线上,UFS 把它们拆成了三份可以各自升级的规范。

这个差别带来的直接结果是:eMMC 时代的性能调优手段是「加宽总线、调时序」,UFS 时代的性能调优手段是「加 Lane、升 Gear、并发排队」。前者受制于引脚数量与布线,后者的上限由架构直接给出。

对比维度eMMCUFS差异的性质
物理拓扑并行数据总线加时钟线,主机与设备之间是共享总线点对点串行差分对,每 Lane 独立 TX/RX,全双工,可有多个 Lane架构级:总线变链路
速率提升方式提高总线宽度与时钟频率,受引脚与布线约束明显提高单 Lane 线路速率(Gear)并叠加 Lane 数架构级:从共享到独占
协议归属协议逻辑在设备内部,主机侧只负责时序与寄存器主机侧有完整的传输层(UTP)与通用互联层(UniPro)架构级:协议上移到主机侧
命令体系CMD 家族,命令号与参数一一对应,语义由设备定义SCSI 血统命令集,命令与描述块、数据段解耦语义级:换成通用命令模型
并发模型单命令队列,多块操作靠多块命令拼接任务 Tag 加多队列,响应允许乱序返回架构级:从串行到并行
空间组织硬件分区(Boot/RPMB/GP/User/Backup)加用户区内软件分区多个 LUN 并行,每个 LUN 下再分 LU架构级:两级逻辑单元
错误隔离出错通常只能整卡复位错误可按层归因,LUN 级失败可局部隔离架构级:可分层定位
「UFS 比 eMMC 快」这句话本身没有工程含义

因为快的幅度取决于Lane 数、Gear 档位、设备内部的通道数与并发度,而不是接口名字。把这句话拆成可判定的形式才有意义:带宽 = Lane 数 × 单 Lane 在当前 Gear 下的有效速率 × 有效编码效率,而并发能力取决于队列模型与队列深度。这两行公式就是第 6 章与第 7 章的全部内容。

1.2 UFS 的身份:存储标准、互联标准、命令集三件套

「UFS」这个词在实际语境里同时指三样东西,它们由不同的规范组织维护、版本独立演进。工程上出问题时,第一件事就是判断它属于哪一件,因为三者的责任方与排查路径完全不同。

身份规范来源解决的问题谁在实现出问题时找谁
存储标准JESD220 系列(UFS 设备类与主机控制器类规范)设备与主机控制器之间的命令语义、LUN 模型、寄存器行为、能力字段存储控制器 IP 与存储器件厂商接口协议与器件规格
互联标准JESD230 系列(UniPro);物理层为 MIPI M-PHY 规范链路如何建立、帧如何分、时钟如何恢复、错误如何重试、链路如何管理主控侧互联 IP 与物理层设计者互联 IP 与 PHY 设计者
命令集SCSI 命令集(UFS 采用其血统的子集)读、写、缓存、同步、查询等操作的通用语义与状态码主机侧驱动与设备控制器固件两边都要查,看命令与完成状态
为什么必须把三件套分开讨论

因为它们的演进节奏不同。命令集基本稳定,存储标准随功能集增加而修订,互联标准与 M-PHY 的档位演进最快。一个主机控制器宣称支持的最高 Gear 属于互联标准,它支持的命令族属于命令集,两者互相独立。把它们混在一起谈「UFS 支持什么」,是评审时最常见的失焦方式。

1.3 谁在用、用在哪

UFS 已经离开「手机专属」这个标签。它今天落在四类场景里,而这四类场景对协议栈的诉求并不相同——这正是理解 UFS 值得单独拆开讲的原因。

场景典型设备对协议栈最关心的事最容易被忽略的约束
移动 SoC手机、平板的主板级存储多队列并发、功耗门控下的唤醒延迟、带宽随档位切换的稳定性低功耗唤醒路径比峰值带宽更容易成为瓶颈
车规与工业仪表域控制器、车载网关、工业网关宽温下的链路稳定性、掉电一致性、长时间运行的错误恢复认证特性在车规场景常常是准入条件而不是加分项
工业与医疗设备监护设备、诊断设备、测试设备错误可追溯、命令语义清晰、长时间运行不漂移SCSI 血统的命令语义往往比自定义命令更好维护
嵌入式高可靠网关与数据采集、数据记录设备顺序写入稳定性、写合并的确定性行为、分区隔离写合并策略不当会让 SSD 类设备寿命急剧下降

1.4 UFS 生态全景图

把上面三件套与四类场景叠在一起,就是 UFS 的完整生态:中间是互联层,上面接主控侧的软件栈,下面接设备的存储管理,左右两侧是命令集与标准的版本。

UFS 生态全景:主机侧、互联层、设备侧与三份标准主机侧Host操作系统与块层块设备节点、文件系统挂载与 I/O 路径主机控制器驱动UFS 传输层 UTP 的任务提交与回收Host Controller队列、QoS、掉电保护与主机侧缓存UFS 互联层 UniPro + M-PHYUniPro 控制器L1 数据链路 / L1.5 抽象 / L2 链路 / L3 传输把上层请求变成可分帧的包M-PHY差分对、Lane 与 Gear、训练与均衡只做物理层,不理解命令设备侧 UFS DeviceUFS 设备控制器协议处理、LUN 路由命令队列与执行排序LU 与虚拟块管理逻辑块到物理块的映射写单元与缓存管理NAND 控制器与阵列FTL、ECC、磨损均衡坏块管理与温度管理三份规范,版本独立演进JESD220 系列UFS 存储标准:设备类、命令集与 LUN 模型JESD230 系列UniPro 与 M-PHY:互联层与物理层规范SCSI 命令集UFS 采用 SCSI 血统的命令语义与状态码读图提示:UFS 不是一条总线,而是「存储标准 + 互联标准 + 命令集」三份规范共同定义的一整套系统。三份规范由不同组织维护、版本独立演进,这也是工程上最容易出现「版本对不上」的地方。本文按这三层组织:第 2 章讲系统全貌,第 3~5 章逐层拆协议栈,第 6 章单独讲 M-PHY 的 Lane 与 Gear。
图 1 · UFS 生态全景:主机侧、互联层、设备侧与三份规范的关系
这张图最该记住的一句话

互联层是 UFS 相对 eMMC 最大的架构增量。它既不关心命令语义,也不关心 NAND,只做一件事:把上层的请求变成一条可靠的高速链路。正因为它的职责足够窄,它才可以独立于存储标准单独演进——也正因为它足够窄,链路层的问题往往可以被独立定位。

1.5 本文的主线:一个请求从主机到 LUN 再回来的全程

全文都围绕同一条主线展开:一个读请求从主机应用出发,经过协议栈的每一层,到达某个 LUN 的 NAND,再把响应原路送回。这条主线在第 2.5 节展开为完整路径,在第 12.3 节反向使用——从故障症状倒推它卡在哪一层。

把这条主线先记住,后面每一章都是在放大其中某一环。第 3、4 章放大第 ②③ 环,第 5、6 章放大第 ④⑤ 环,第 7、8 章放大第 ⑥⑦ 环,第 9、10 章则讲每一环失败时怎么归因。

2. 系统架构:主机控制器、设备与 LUN

这一章把 UFS 系统拆成两个主体、一条链路和两级逻辑单元。重点不是罗列模块,而是说清每个模块为什么必须存在——因为每个存在理由都对应一类线上问题。

2.1 UFS 系统的两大主体:Host Controller 与 UFS Device

一个 UFS 系统只有两个主体:主机侧的 Host Controller 与设备侧的 UFS Device。两者之间是一条点对点的串行链路,没有总线仲裁,也没有第三方设备挂在中间。

  • Host Controller 才是「主控」:它持有队列、调度、QoS 与主机侧缓存,是性能的主要决定方。SoC 里集成的是它,而不是某个存储控制器外挂器件。
  • UFS Device 是「被访问方」:它内部有自己的命令执行器、LUN 管理、FTL 与 NAND 阵列,主机无法直接看到物理布局。
  • 链路是独占的:这是 UFS 与 eMMC 最大的拓扑差异。独占意味着没有仲裁开销,但也意味着主控与设备必须成对设计:主控的 Gear 档位必须落在设备支持的档位之内。

2.2 Host Controller 的组成

Host Controller 从上到下分四块,每一块的职责边界都很清晰。划清这四块的意义在于:性能问题必须先定位到是哪一块的职责——排队问题看队列管理,链路问题看物理层,两者混淆是排查效率最低的原因。

层组成职责出问题时该看什么
应用层队列若干条命令队列,各自绑定不同的 LUN 或不同的处理策略接收上层请求、给请求打Tag、决定先执行谁队列是否失衡、是否有队列长期饥饿
UFS 传输层UTP 引擎与 UPIU 处理逻辑把请求封装成命令 UPIU,把数据组织成数据段,把响应解析成状态UPIU 类型是否匹配、响应是否与 Tag 对应
UniPro 控制器L1 至 L3 的组帧、链路管理、流量控制分帧、重传、流控、链路状态维护链路是否已就绪、重传率、流控是否卡住
M-PHY物理层收发器、训练与均衡电路把并行数据变成差分比特流,并从比特流里恢复时钟与数据速率档位、误码、训练是否收敛
为什么必须把链路层从协议层里剥出来看

因为它们的故障表现完全不一样,而恢复手段也完全不同。协议层的问题(Tag 对不上、命令语义不匹配)必须靠软件修;链路层的问题(训练不收敛、误码率高)必须靠改互连、查布线、换 Gear 甚至换封装。把两者混在一起讨论「UFS 不通」,基本等于无法排查。

2.3 Device 内部的组成

设备侧同样可以拆成四块。它的存在理由比主机侧更「物理」:NAND 天生不能随机写、不能原地覆盖、单元会慢慢失效,所以设备侧必须有人负责把这些物理特性屏蔽掉。

层组成职责去掉它会怎样
UFS 设备控制器协议处理、命令队列、模式切换解析 UPIU、维护任务状态、执行命令主机无法可靠发出任何命令
LU 与虚拟块管理LUN / LU 结构、写单元、缓存把主机的逻辑块映射到物理块NAND 的块结构直接暴露给主机,性能不可用
Flash 控制器FTL 映射、ECC、磨损均衡、坏块管理动态映射、纠错、磨损均衡、坏块替换容量迅速衰减,坏块导致写入失败
NAND 阵列多颗 NAND 颗粒,可能含 die 级堆叠真正存放数据的介质——

这里有一个必须建立的判断:第 9.2 节讲的设备端 ECC 校验与磨损均衡,实际都在设备侧完成。主机只看得到「逻辑块读出来了没有」,看不到 NAND 上的物理错误与磨损分布。这也是为什么某些看起来像主机驱动问题的现象,根因在设备侧。

2.4 LUN 与 LU:逻辑单元的两级结构

UFS 把「逻辑单元」分成两级:LUN 是主机可见的并行单元,LU 是 LUN 内部的执行单元。两级结构的意义在于,并行性由 LUN 提供,主机不必关心;执行粒度由 LU 提供,设备可以自由优化。

一个请求如何落到 NAND:LUN 与 LU 的两级结构主机 I/O 请求逻辑块地址 + 方向 + 长度LUN 0主机可见的并行单元内部再分为多个 LULUN 1可被不同队列绑定实现负载隔离LUN 2可独立格式化承载不同用途Boot LU逻辑单元LU 0逻辑单元LU 1逻辑单元Boot LU逻辑单元LU 0逻辑单元LU 1逻辑单元写单元(Write Unit):主机侧可见的最小写入单位,与逻辑块大小解耦NAND Flash 物理块:映射、磨损均衡与坏块替换都在设备侧完成,主机不可见读图提示:并行性来自 LUN 这一级,主机侧可绑定不同队列;执行粒度来自 LU 与写单元,设备侧可自由优化。写单元与逻辑块解耦是 UFS 相对 eMMC 的一个重要自由度,它让设备可以按自己的 NAND 组织选择最优组合。
图 2 · LUN 与 LU 的两级结构:并行单元与执行单元的分工

2.5 一个请求的完整路径

下面把第 1.5 节的八个环节展开成一张完整的路径表。每一环都标明它消耗的时间属于哪一类:协议开销、排队等待、链路传输还是介质访问。这四类时间的比例关系,决定了优化应该往哪个方向使劲。

序号环节在哪一侧这一环的主要开销优化的着力点
1应用发起读请求主机侧软件系统调用与拷贝开销减少不必要的拷贝与系统调用
2分配 Tag 并封装成 UPIU主机侧 UTP内存准备与描述符组织预分配与复用,减少每请求的内存开销
3进入队列等待调度主机控制器排队等待,尾延迟的主要来源队列划分、QoS 权重、优先级策略
4分帧、加密校验、流控UniPro L3 / L2组帧开销与流控等待包长、发送时机、信用配置
5串行化并送上差分对M-PHY链路传输时间Lane 数与 Gear 档位
6设备接收、解析、路由到 LUN设备控制器协议解析开销设备侧固件效率,主机不可优化
7查映射、可能读缓存或读NAND设备侧 FTL 与阵列介质访问时间,量级最大缓存命中、通道并行、写合并
8数据上行,逐层封装返回全链路上行传输与响应解析响应与 Tag 的对应关系是否可靠
性能优化的第一步永远是分类,而不是试参数

把一次读操作的耗时拆成排队等待 / 链路传输 / 介质访问 / 协议开销四类之后,优化方向立刻唯一:排队占比高就调队列与并发,链路占比高就调Lane 与 Gear,介质占比高就查缓存命中与设备内部并行,协议占比高才去查驱动实现。不分类就试参数,是性能工作里最常见的无效劳动。

2.6 eMMC 与 UFS 的架构对照

下图把两者按四个维度并排对照。阅读时建议逐行看「差异的性质」一列:如果某一项写的是「语义级」,那么迁移时只需要改代码逻辑;如果写的是「架构级」,那么迁移意味着要重新设计并发与调度策略。

eMMC 与 UFS 的架构对照:四个维度逐行比较eMMC:共享并行总线模型UFS:点对点串行互联模型① 物理层单端口并行数据总线与时钟线,速率靠加宽总线① 物理层点对点串行差分对,速率靠 Lane 数与 Gear 档位② 协议与传输层协议逻辑在设备内,主机侧负责时序与寄存器② 协议与传输层UTP 在主机侧,UniPro 与 M-PHY 分层承担链路③ 命令与语义CMD 家族,命令号与参数一一对应③ 命令与语义SCSI 血统命令集,命令与描述块、数据段解耦④ 并发模型单命令队列,多块操作靠多块命令拼接④ 并发模型任务 Tag 加多队列,响应允许乱序返回本质差异:eMMC 把协议、时序与数据通路压在并行总线上,UFS 把它们拆成可独立演进的层读图提示:四行里只有第③ 行属于语义级差异,代码改写即可;第 ①② ④ 行都是架构级差异,迁移意味着重做并发与调度设计。
图 3 · eMMC 与 UFS 在物理层、协议层、命令层与并发模型上的对照

2.7 架构差异带来的工程后果

架构差异不是抽象概念,它直接决定四类工程后果。这四类后果分别对应四个团队:硬件团队、驱动团队、系统团队与测试团队,职责边界清楚,协作时才不会互相等待。

后果具体表现受影响的角色如果沿用 eMMC 的做法会怎样
并发能力同一时刻可以有大量未完成任务,响应乱序返回驱动团队用轮询等完成的方式会浪费一半以上并发能力
队列深度需要显式决定队列数量、每队列深度与 Tag 数量驱动团队、系统团队沿用单队列假设会让多流场景互相饿死
错误隔离错误可按层归因,某LUN 失败不一定影响其它 LUN驱动团队、测试团队一律整卡复位会掩盖真实故障点
性能上限由 Lane 数与 Gear 档位共同决定,且与设备成对约束硬件团队主控支持的最高档位可能超出设备支持范围
本章最该带走的一句话

UFS 的性能与并发问题都是可分层归因的。这是它相对 eMMC 最大的工程价值——不只是快,而是出了问题能定位到层。第 14 张图给出的排查树,就是把本章的分层模型反过来用一遍。

3. UFS 传输层 UTP:请求的封装与排队

UFS 传输层简称 UTP,它是主机侧真正处理 I/O 请求的地方。这一章要讲清一件事:一个上层的读或写请求,是怎么变成链路上的字节、又是怎么把响应准确送回原请求的。

3.1 UTP 的任务单元模型:一条 UPIU 对应一个请求

UTP 的基本抽象是任务(Task):一个任务就是一个请求,它由若干个 UPIU 组成——命令 UPIU 必有一份,数据段按读写方向可选,响应 UPIU 必有一份。这三条合起来构成一次完整的交互。

「一条 UPIU 对应一个请求」这个说法需要精确化:一个请求对应一组 UPIU,而不是恰好一条。之所以仍然按「一条对应一个」来理解,是因为 UPIU 内部自带 Tag 字段,Tag 把这组 UPIU 绑定成一个不可分割的整体。

UPIU 类型方向是否必发承载什么与 Tag 的关系
命令 UPIU主机到设备必发命令码、描述块指针、传输长度、属性携带本次交互的 Tag
数据 UPIU读时设备到主机,写时主机到设备读写请求必发,纯控制命令可不发真正的数据内容,按写单元组织必须携带同一个 Tag
响应 UPIU设备到主机必发状态与计数信息,含完成标志与错误信息原样带回请求的 Tag
把 UPIU 理解成「信封」而不是「数据」

UPIU 本身不是数据,它是描述这次传输的信封:里面写清楚了「我要做什么」「数据在哪里」「多少长度」以及「你是谁」。数据本身在 UPIU 之外单独传送。这个分离是 UFS 相对 eMMC 的一大优势——命令处理与数据搬运解耦,设备可以先认命令、后搬数据,甚至乱序执行。

3.2 UPIU 的种类与流向

UPIU 的流向规则可以用一句话概括:命令向下、数据按方向、响应向上。所有复杂度都来自「数据方向」这一条——同一种数据 UPIU,在读请求里由设备发出,在写请求里由主机发出。

3.3 请求的排队与调度:Tag 的作用

Tag 是一个由主机分配、由设备原样回填的编号。它的生命周期只有四步:分配、绑定、随响应返回、归还池中。这四步任何一步出错,表现都是「数据串了」或者「请求丢失」。

队列模型:SQ 负责提交,CQ 负责回收,Tag 负责配对主机侧:命令队列与完成队列SQ(提交队列)按队列头顺序取出任务,填入 UPIU 发出UniPro 链路按当前 Gear 与 Lane 数串行化送出CQ(完成队列)按到达顺序写入,主机按 Tag 找回对应请求队列不是先进先出的完成顺序,而是到达顺序Tag:把乱序返回重新对齐的编号提交时请求拿到 Tag = 7返回时响应带回 Tag = 7Tag 在提交时分配由主机控制器从空闲 Tag 池中取一个,绑定到该请求的全部 UPIU响应必须原样带回设备把 Tag 原样回填,主机据此索引到原始请求的上下文释放后才能复用主机处理完响应才把 Tag 归还池中,否则会造成上下文错配Tag 数量是并发上限Tag 耗尽时新请求只能等待,这是并发的硬上限之一Tag 位宽与数量由主控实现决定,不在本文展开SQ 是提交视角描述提交顺序不代表完成顺序CQ 是到达视角描述返回顺序可能与提交不同Tag 是配对视角唯一标识一个请求并发度的直接来源两者解耦的意义允许乱序完成尾延迟大幅改善读图提示:把 SQ 理解成「我按什么顺序交作业」,把 CQ 理解成「作业按什么顺序发回来」,Tag 就是作业编号。正因为发回来的顺序可以与交上去的顺序不同,慢请求不会挡住快请求,这是并发能力的来源。
图 4 · SQ 与 CQ 的分工以及 Tag 在乱序返回中的作用
Tag 泄漏是最难查的一类 UFS故障

因为它不会在启动时暴露,只会在运行一段时间后突然发作,而且一旦耗尽就再也无法恢复。定位这类问题的唯一可靠办法是同时监控 Tag 池水位与 CQ 的推进速度:Tag 水位只涨不落,就说明有响应没被消费或没被归还。这条判据比任何日志都管用。

3.4 多个请求并发时的乱序返回

UFS 允许不同Tag 的请求以任意顺序完成。这一条是并发能力的直接来源,也是与 eMMC 最大的行为差异之一。它带来的收益有两面。

  • 收益一:消除队头阻塞。一个慢请求不会挡住排在它后面的快请求。在混合负载下(读多写少、随机小块与顺序大块混杂),尾延迟改善非常明显。
  • 收益二:设备侧可以乱序调度。设备可以把请求按自己的 NAND 通道与缓存状态重排,最大化内部并行度,主机无需知道这件事。
  • 代价一:主机必须正确配对。没有顺序约束意味着配对责任完全落在 Tag 上,任何一处Tag 处理错误都会造成数据串位。
  • 代价二:错误处理更复杂。设备上报错误时可能只说「某个 Tag 失败了」,主机必须自己判断这个 Tag 对应哪个请求、是否需要重试。
乱序返回要求主机具备「请求上下文」而不是「请求指针」

因为响应回来时主机并不知道是哪个请求,只能靠 Tag 反查。因此主控侧必须为每个在途请求保存一份上下文(缓冲区、长度、方向、回调标识),而不是只保存一个指针。这条差异是eMMC 时代的老代码迁到UFS 时最容易踩的坑,第 4.5 节会再展开。

3.5 队列模型:CQ 与 SQ 的角色

UTP 用两条队列把「提交」与「回收」分开管理。这种设计看似多了一层,实际上是并发能力的必要条件:提交方向由软件掌握节奏,完成方向由硬件异步推送。

维度SQ 提交队列CQ 完成队列
方向主机写入、设备读取设备写入、主机读取
顺序语义严格先进先出按到达顺序,可能乱序
作用限定提交节奏,决定在途任务上限承载状态与完成通知,驱动中断
满时的处理无法继续提交,必须等待无法写入完成信息,属于异常状态
数量与深度由主控实现决定,需与队列绑定 LUN需足够深以承接乱序峰值
与 Tag 的关系决定 Tag 消耗速度决定 Tag 归还速度

这里有一个必须建立的配对关系:Tag 消耗速度由 SQ 决定,Tag 归还速度由 CQ 决定。两者一旦失衡——提交快于完成,或完成快于消费——都会出问题。所以调优队列时不能只看单侧深度,必须成对考虑 SQ 深度、CQ 深度与 Tag 数量三者的关系。

3.6 任务状态机

一个任务从诞生到释放会经历五个状态。状态机本身不复杂,但状态之间的合法与非法转移是最容易出bug 的地方,因此值得明确列出。

状态含义进入条件允许的下一步非法动作的后果
创建已分配 Tag 与资源,尚未进入 SQ主机从Tag 池分配成功就绪或直接释放资源泄漏
就绪已进入 SQ,等待被取出发送写入 SQ 成功执行或被撤回队列状态错乱
执行命令已送出,设备正在处理被调度并组帧发出完成设备侧无响应,任务悬挂
完成响应已写入 CQ,等待主机消费设备发出响应 UPIU释放Tag 无法归还
释放上下文清理完毕,Tag 归还池中主机处理完响应回到创建态无

3.7 流控与信用机制

流控解决的是一个很朴素的问题:对端不一定收得过来。发送速率由本地驱动决定,而接收能力由对端的缓冲深度与当前负载决定,两者不一致时如果不管,缓冲就会溢出丢数据。

  • 分级信用:UniPro 的流控是分级而不是单一的,缓冲区满时可以先挂起某一级流量,让其它流量继续。这比「一卡就全停」的方式平滑得多。
  • 缓冲与流控发生在 L1.5 抽象层:这正是 L1.5 这一层存在的理由——它不搬运数据,只负责告知对方还能发多少。
  • 流控等待是最隐蔽的性能杀手:它不表现为错误,只表现为吞吐下降,且日志里什么都看不到。可靠的判据是流控等待时长占比。
流控等待不会被任何错误日志记录

因为它不是错误,而是协议正常协商的结果。所以性能分析必须把流控等待当成一个独立指标来监控。如果发现链路利用率长期上不去而错误计数为零,第一件事应该是看流控等待占比,而不是继续加 Lane 或升 Gear——后者在流控受限时完全没有帮助。

4. UFS 命令集:SCSI 血统与语义差异

UFS 采用的是 SCSI 血统的命令集,而不是 eMMC 那种命令号加参数寄存器的风格。这一章讲清三件事:为什么这样选、命令的结构长什么样、从 eMMC 迁过来会在哪些语义上出错。

4.1 为什么 UFS 用的是 SCSI 命令集而不是 CMD 家族

CMD 家族的设计前提是「设备就是一张卡」:命令号唯一决定行为,参数通过寄存器给出,操作符与参数一一对应。这套模型在 eMMC 时代很好用,因为它极其直白。

但 UFS 面对的场景变了:主机侧要维护队列、要支持乱序、要处理多条数据流。此时「一条命令对应一个参数寄存器组」的模型显得太窄——它没有地方放描述块指针,也没有地方放队列上下文。SCSI 血统的命令结构天然带描述块概念,因此更适合。

考量CMD 家族风格SCSI 血统风格UFS 的选择与理由
命令表达能力命令号直接决定行为,参数走寄存器命令码 + 描述块 + 数据段三段分离选 SCSI:能表达队列、描述块与数据段的关系
数据传输方式在命令寄存器中指定地址与长度数据段与命令分离,各自独立传输选SCSI:命令处理与数据搬运可解耦
语义通用性语义由设备自定义,跨厂商差异大语义由标准定义,跨设备一致选 SCSI:主控侧一套代码可对接多家器件
设备实现难度低,逻辑直接中高,需要描述块解析与队列管理接受:换来主控侧的可移植性
与上层的关系需要为每个设备写适配可直接对接块层与文件系统语义选 SCSI:与既有存储软件栈天然衔接
一句话抓住这一节的要点

选SCSI 的真正理由是「主控侧一套代码能对接多家器件」,而不是「SCSI 更好」。UFS 面向的是多个器件厂商与多个主控厂商互相组合的开放生态,任何需要为每款器件单独适配的模型都会被生态拖死。命令集的标准化换来的是可移植性,代价由设备厂商承担。

4.2 命令、描述块、数据段的统一结构

一条 UFS 命令在协议上由三段组成。三段的职责必须分清,因为混淆三段是写驱动时最常见的结构性错误。

  • 命令段回答「做什么」:命令码、传输方向、长度、属性标志(如是否绕过缓存、是否强制落盘)。
  • 描述块段回答「数据在哪」:一段连续内存的起始位置与总长度。主机在发起命令前必须先把数据缓冲区准备好并保持稳定,直到命令完成。
  • 数据段段是「数据本身」:按写单元大小切成多个 UPIU 依次传送。
描述块指向的缓冲区生命周期是最容易出错的地方

因为命令与数据是分离的,存在一个真实的时间窗:从命令发出到响应回来,描述块指向的那段内存必须保持有效、不能被改写、不能被释放。在 eMMC 时代这条约束往往隐含在同步接口里,而 UFS 因为乱序返回,这段等待时间可能远长于直觉。用可复用缓冲区时必须配好「已提交未完成」的引用计数。

4.3 常见命令族:读、写、缓存、同步、查询

UFS 的命令按用途可以分成五族。理解这五族的划分方式,比记住具体命令名更有用——因为每族的语义约束完全不同。

命令族典型代表语义要点对一致性模型的影响使用时的注意事项
读族READ、WRITE 之外的读类命令从某个 LBA 读取指定长度受设备缓存影响,属性可要求绕过缓存长度与写单元的关系决定效率
写族WRITE、WRITE 及其属性变体写入指定 LBA 区间可要求进缓存、也可要求直写属性选择直接决定掉电一致性
缓存族缓存相关的管理与查询命令查询与控制设备缓存行为直接决定读是否命中缓存缓存尺寸与命中策略由设备决定
同步族同步刷新与同步完成相关命令要求设备把缓存中的数据落盘是保证掉电一致性的唯一手段主机必须等到完成才算结束
查询族查询设备与单元属性读取容量、写单元尺寸、特性支持情况不影响数据,但决定其它命令怎么用初始化阶段必须先查清再配置

4.4 与 eMMC CMD 家族的语义映射表

下表给出两套命令体系的语义对应关系。它不是等价替换表,而是「意图相同、实现不同」的对照表:最右一列写的是迁移时真正需要注意的地方。

操作意图eMMC 侧的典型做法UFS 侧的典型做法迁移时最容易错的地方
读一个区间指定块地址与块数的读命令带描述块的读命令,数据长度按字节长度单位与对齐假设不同,直接照搬会读错偏移
写一个区间写命令或写多块命令带描述块的写命令,属性决定落盘行为是否需要强制落盘没显式指定,会丢数据
小块随机写写命令逐次调用写命令 + 写合并由设备完成以为每次写都落盘,实际可能被缓存合并
顺序大块写多块写命令同样命令,靠更大的长度获得效率误以为多块写命令存在,其实要靠循环
查询容量读取容量寄存器查询类命令返回属性结构属性的字段含义与单位需逐项核对
查询特性读特性寄存器的位查询类命令返回特性字段特性位含义更细,逐位照搬必错
确保落盘带缓存关闭标志的写,或专用同步写命令属性或同步刷新命令以为写完即落盘,掉电后丢数据
掉电后数据完整依赖缓存关闭与块对齐必须用同步命令显式确认完成缺少同步步骤,一致性无法保证
识别设备读识别字段与序列号查询类命令返回识别信息识别字段的名称与含义需按规范重新对应
多分区硬件分区或逻辑分区命令按 LUN 划分,运行时枚举枚举时序与容量上报方式完全不同
迁移时最容易出的三类错

① 长度与对齐:eMMC 的很多命令以块为单位,UFS 的数据段以字节为单位并受写单元约束,直接照搬会读错偏移。
② 落盘假设:eMMC 里「写完成」往往意味着已落盘,UFS 里默认可能只进了缓存,必须显式要求同步。
③ 分区概念:eMMC 的分区在设备内部已经划好,UFS 的分区由 LUN 表达,需要在主机侧枚举与挂载。

4.5 语义映射带来的迁移陷阱

把上表的陷阱按「写代码时会不会想错」重新组织一遍,得到六条具体的迁移检查项。它们都属于语义级差异,改代码逻辑即可,不需要重新设计架构。

陷阱eMMC 时代的直觉UFS 下的正确做法后果
读写单位以块为单位,块大小固定以字节为单位,且受写单元尺寸约束长度算错,读到错位数据
缓存模型写完通常已落盘默认进缓存,落盘时机由属性决定掉电丢数据
完成含义命令完成即数据稳定完成可能只代表数据已接收一致性承诺落空
错误码错误码含义按设备定义标准状态码,含义按规范定义错误处理分支走错
超时判断超时即视为失败超时可能只是排队久,任务仍在执行重复提交导致同一操作做两次
缓冲区假设同步接口下缓冲区立即可复用命令未完成前缓冲区必须保持有效数据被改写,磁盘内容错乱

4.6 关键命令的时序要求与完成顺序约束

UFS 的命令之间存在若干顺序约束。这些约束不写清楚,代码在上并发下会出现极难复现的错误,因此值得单独列出来。

时序图:读命令从提交到完成的泳道与约束主机应用UFS 传输层UniPro L3M-PHY设备控制器NAND提交读命令取 Tag,填命令组织数据段按写单元准备分帧送出包长与信用对齐解析并执行选缓存或阵列取数并上行数据段原路返回完成响应带回原 Tag顺序约束一:数据 UPIU 必须在对应命令 UPIU 之后发出,不能提前顺序约束二:完成响应只能在对应数据段之后返回,不能提前顺序约束三:多个 Tag 之间没有顺序要求,这才允许乱序完成读图提示:泳道自上而下是请求的流动方向,事件框挂在对应泳道上;事件框之间的横向间距不代表耗时比例。
图 5 · 读命令从提交到完成的泳道时序与三条顺序约束
约束具体要求违反后的表现如何保证
命令先于数据命令 UPIU 必须先于对应的数据 UPIU 发出设备拒绝执行或直接报错发送路径上先构造命令,再挂数据
数据先于响应响应 UPIU 必须在数据段之后返回主机拿到响应但数据不完整由设备保证,主机侧校验长度
同Tag 内有序同一 Tag 的多条 UPIU 必须保持顺序描述块与数据对不上同一请求的 UPIU 走同一发送路径
跨 Tag 无序不同 Tag 之间没有顺序要求——(这是可利用的自由度)并发设计时充分利用
同步命令必须等待同步类命令必须等到响应才算结束一致性承诺落空同步调用或在提交前确认队列空闲
第 4 章的核心提醒

UFS 时代的每一个写请求都要回答一个问题:「这条数据什么时候才算真的安全?」在 eMMC 里这个问题往往不需要问,在 UFS 里必须问,而且答案由命令属性、设备缓存配置、是否等待同步三者共同决定。缺少这个意识,一致性相关的现场问题几乎必然出现。

第 5.7 节把唤醒代价留在了「策略」层面。下面这张图把它落到具体的阶段顺序与超时上——它是全文第一张用于故障定位的图:链路起不来时,先判断卡在哪个阶段,再判断这个阶段的超时设置是否合理。

AER:从低功耗到可传输数据的五步唤醒流程① 触发有待发送数据或需访问寄存器② 供电时钟电源与参考时钟进入稳定范围③ 训练时钟恢复、均衡与失真补偿收敛④ 协商确认 Gear 档位与 Lane 数⑤ 可传输链路层与流控进入初始就绪每一步都有超时;任一步超时后的现象与前一步超时完全不同① 超时根本没发出去队列积压,主机侧看不到任何进展② 超时时钟或供电不稳链路偶尔能起、偶尔不能,与温度和负载相关③ 超时物理层训练不收敛训练反复重来,错误计数持续增长④ 超时档位或 Lane 不匹配链路是通的但速率停在较低档⑤ 超时流控未就绪能握手但一发数据就挂起省电与响应的取舍是策略问题链路常开则响应快但功耗高;门控省电则每次访问都要付唤醒代价。唤醒代价应计入架构级时延预算不计入就会表现为「偶发地第一次操作特别慢」这类难查问题。读图提示:五步严格顺序执行,跳步只会把问题推到更后面,且让现象更难解释。
图 7 · AER 唤醒流程的五个阶段与各阶段超时后的现象对照
相关报告 · 交叉引用 2 器件全部展开收起 ▾
点开类目即可展开:具体文档 / 章节(点章直接跳转到该章)
eMMC
电路设计指南
软件设计规范
Linux 驱动开发
样机问题排查
量产问题排查