UFS系统架构:控制器、设备、层次模型

要理解UFS,你得先看清它的骨架。说白了,UFS不是一颗孤零零的芯片,而是一套完整的主-从通信系统。主机端有控制器,设备端有闪存芯片,中间靠一套严谨的层次模型连接。

我刚开始学UFS时,总觉得分层太繁琐。但后来调过几次驱动、定位过几次链路问题后,才真正体会到——这些层,缺一层都不行。

UFS系统整体架构

先看一张宏观图。下面这个结构,你最好刻在脑子里:

UFS系统层次架构图 Host Software Layer UFS Driver + 块层 + 文件系统 UFS Host Controller (HCI) UFSHCI寄存器 + Doorbell + 中断 UTP Layer (传输层) UPIU封装 / 解封装 UIC Layer (互联层) MIPI UniPro + M-PHY 物理层 UFS 协议通道通信 Device Manager 描述符、标志位、任务管理 SCSI / UFS 指令层 READ/WRITE/UNMAP 命令处理 UFS Transport Layer UPIU 接收/发送 UIC Layer (Device端) UniPro + M-PHY UFS 闪存介质层 (NAND Array + Controller) 每一层职责清晰:Host负责发指令,Device负责执行,中间靠UPIU报文在UTP和UIC层穿梭

这张图你看懂了吗?主机侧有3层,设备侧也有3层,中间靠UIC互联层握手。我个人习惯把UFS的层次模型想象成一个"三层的快递系统"——上层打包,中间转运,下层派送。

UFS控制器 (Host Controller Interface)

UFS控制器,业界叫它UFSHCI。本质上是一组寄存器,挂在AHB或AXI总线上。

核心要点:UFSHCI通过Doorbell寄存器机制与驱动交互。驱动写Doorbell,控制器收到后就去取任务。

控制器内部主要模块:

  • UFS Protocol Engine:负责解析UPIU,处理流控和CRC校验
  • DMA Engine:把系统内存中的UPIU搬运到控制器本地缓冲
  • Interrupt Controller:传输完成、异常时触发中断
  • Task Manager:处理中止、查询等管理命令

我记得第一次调UFS驱动时,死磕的就是UFSHCI的CAP寄存器——它告诉我控制器支持多少条传输通道、缓冲区有多深。不看这个直接用,很容易踩坑。

注意:各厂商的UFSHCI实现有差异。Qcom和高通的寄存器偏移稍有不同,移植代码时一定要对照datasheet。

UFS设备 (UFS Flash Device)

设备这边,说白了就是一颗带控制器的NAND Flash,但它比eMMC复杂得多。

设备内部通常包含:

  1. 前端接口层:实现MIPI M-PHY接收发送,解析链路帧
  2. 协议处理单元:解析UPIU,区分命令、数据、任务管理三类报文
  3. 闪存控制器:FTL层,做坏块管理、GC、磨损均衡
  4. NAND Array:多层Cell,2D/3D堆叠
特性 eMMC UFS
接口 并行8位 串行差分 (M-PHY)
传输方向 半双工 全双工
命令队列 单命令 多队列 (CQ)
分层层次 简单 多层协议栈

你会发现UFS设备的优势很明显——全双工意味着收发可以同时进行。我曾经在项目中实测,UFS读带宽超过1200MB/s时,主控还能同时处理写请求,这在eMMC上根本不可能。

UFS层次模型

UFS分了四层,但说到底,真正站在协议栈顶端的其实是两层半:

UTP层 (UFS Transport Protocol Layer)

负责把上层命令封装成UPIU。一个UPIU报文包含传输头(Request ID、Opcode、LUN) + 数据Payload。

实战建议:抓取UPIU时,重点关注Header的CID (Connection ID) 和BUFF (Buffer Offset)。这两处经常出错导致数据错位。

UIC层 (UFS Interconnect Layer)

实现MIPI UniPro和M-PHY。UniPro负责逻辑链路管理,M-PHY负责物理差分信号传输。UFS 2.0用HS-G2,UFS 3.0升到HS-G4,速度翻倍。

关键点:UIC层包含Link Startup、PA协商、CRC校验三大阶段。任何一个阶段失败,链路就起不来。

SCSI / UFS指令层

应用层主要用SCSI命令的子集,比如READ(10)/WRITE(10) + UFS专属的UNMAP、RPMB。

这里有个冷知识——UFS的UNMAP命令跟SSD的TRIM是一个道理。很多工程师只关心读写性能,却忽略了空间回收指令对寿命的影响。

UFS设备管理器 (Device Manager)

负责访问描述符(Descriptor)、设置设备属性(Attribute)、执行标志位(Flag)。比如我们常调的bDeviceState,就是它管理的。

我曾经踩过的坑:在初始化阶段,设备处于Power-On状态,必须先发QUERY命令切换到Active状态,否则读写命令会被直接忽略。有些Spec说得很隐晦,实际调试时才发现。

层次间的交互流程

我用一个典型的写操作流程让你快速理解各层级怎么配合:

  1. Host驱动构造SCSI WRITE(10) UPIU,写Doorbell通知控制器
  2. UTP层封装报文,UIC层编码,通过M-PHY差分线发送
  3. Device端M-PHY接收,UniPro校验帧完整性
  4. 设备UTP层解封装,提取命令和数据LBA
  5. 设备Flash控制器执行实际写入,完成后返回Response UPIU
  6. Host端收到响应中断,驱动唤醒等待线程

你看,一个写请求在Host和设备间来回了若干趟UPIU。如果中间任何一层出问题——比如CRC校验失败、流控暂停,整个数据通路就会卡住。

调优经验:我建议你把各个层次的关键寄存器做成一张排查表。比如UIC层报错就看UIC_ERROR_CODE,UTP层不通就看UTP_TRANSFER_REQ有没有挂起。这样定位问题比瞎猜快三倍。

嗯,UFS系统架构就先讲到这里。记住一句话:分层让协议清晰,但调试时需要你打通所有层。