第四节 传输层基础:FIS 类型、传输机制与流控制

传输层在 SATA 协议栈里,算是承上启下的角色。链路层负责把数据变成串行比特流,而传输层呢,它管的是——怎么把命令、数据、状态这些东西打包成一个个标准的小包裹,发给对端。这个小包裹就是 FIS(Frame Information Structure)。

我刚接触 SATA 那会儿,总觉得 FIS 就是个固定格式的帧嘛,没什么好研究的。后来调了几个月链路,才发现 FIS 的类型、时序、credit 流控要是没搞透,连个简单的读盘命令都跑不通。嗯,这节课咱们就把 FIS 掰开揉碎了看。

4.1 什么是 FIS

FIS 的全称叫 Frame Information Structure。你可以把它理解成传输层的“消息体”。一个 FIS 包含一个起始原语 (SOF)、内容数据、一个 CRC 校验,最后再加一个结束原语 (EOF)。

结构很简单:

SOF + FIS 内容 (Dword 对齐) + CRC + EOF

每个 FIS 的长度是固定的,由内容部分的第一个 Dword 来标识类型和长度。我记得最早看协议时被各种长度搞晕过,其实你只要记住:常见的 Register FIS 是 5 个 Dword,Data FIS 按实际数据量变化,带 DMA 的 FIS 也有固定长度。

要点: 传输层不关心数据对不对,只负责生成和解析 FIS;纠错是链路层的事(CRC 在 FIS 尾部,但也算传输层封装的一部分)。

4.2 FIS 类型一览

SATA 协议里定义了十几种 FIS 类型,但日常调试中你遇到最多的就下面这几种。我做过的项目里,差不多 80% 的问题都跟 Register FIS 和 Data FIS 有关。

FIS 类型方向长度 (Dword)典型用途
Register FIS – Host to DeviceH→D5发送命令、设置特征、写寄存器
Register FIS – Device to HostD→H5返回状态、错误信息
Data FIS双向可变 (2~2048 字节)传输数据块
DMA Activate FISD→H2通知 Host 可以启动 DMA 读
SET DEVICE BITS FISD→H4异步状态更新 (非命令响应)

你可能会问:为什么有这么多类型?简单说,就是协议想把控制流和数据流分开。控制流用小而快速的 Register FIS,数据流用大块连续传输的 Data FIS,互不干扰。

4.3 传输机制:FIS 如何封装和发送

传输层的具体工作流程是这样的:

  1. 发起端(Host 或 Device)拿到要发送的 FIS 内容后,先计算 CRC-32,追加到内容的末尾。
  2. 在内容前面插入 SOF 原语(0x37),后面插入 EOF 原语(0x39)。
  3. 整个 FIS 被下发到链路层,排队等待发送。
  4. 接收端看到 SOF 就打开接收窗口,持续收集数据,直到 EOF 为止,然后验证 CRC。

这里面有个坑:SOF 和 EOF 虽然是 B10B 编码的原语,但传输层会让你在 FIS 内容里避开这些特殊码。我遇到过有人直接把 0x37 当成普通数据塞进去,结果链路层解析错乱。嗯,切记——除 CRC 和 FIS 原始数据外,别碰原语值。

调试经验: 在逻辑分析仪里抓 FIS 时,先定位 SOF (7C 8B ... 等 K28.5 之后的 D10.2),然后数 5 个 Dword,再看是不是 Register FIS。用这种方法,可以快速识别出异常帧。

4.4 流控制:Credit-Based 机制

SATA 的流控不是靠 XON/XOFF 那么粗暴,它用了一套基于 credit 的信用量机制。每个方向(H→D 和 D→H)都有独立的 credit 计数器。

  • Receiver(接收方) 在初始化阶段告诉对端自己的接收缓冲区大小,换算成多少个 FIS 的 credit。
  • Transmitter(发送方) 每发送一个 FIS,减少一个 credit;credit 归零就不能再发,必须等 receiver 发回 credit 返还信号。
  • credit 返还通过 R_RDY 原语或特定 FIS(如 Register FIS 中的 R_OK 状态)来实现。

我以前在一个 SSD 项目中碰到性能上不去,查来查去发现是 Device 端的 credit 给得太少,Host 每发两个 Data FIS 就要等几十微秒返还。优化方法很简单:在协商阶段把 Device 的接收 buffer 报大一点,credit 值自然就上去了。

注意: credit 流控只在传输层有效,链路层的同步原语(SYNC、ALIGN)独立于此。千万别把链路层空闲等待和传输层 credit 不足搞混。看逻辑分析仪时,先看是否有 R_RDY 原语不断出现,如果没有,那多半是链路层问题。

4.5 FIS 传输的整体流程

下面这个 SVG 图把 FIS 从生成到发送再到流控的脉络画出来了。你多看几遍,心里就有框架了。

传输层 FIS 处理流程 应用层下发命令 传输层选择 FIS 类型 计算 CRC 并封装 检查发送 credit 发送 FIS (SOF→Data→CRC→EOF) 接收端验证 CRC 流控循环:发送方消耗 credit → 接收方返还 R_RDY / 状态 FIS → credit 恢复 注意:H→D 与 D→H 使用独立 credit 池,不可混用

4.6 实际调试中的 FIS 识别技巧

在示波器或逻辑分析仪里看 SATA 流量,最头疼的就是 FIS 边界怎么找。分享几个我常用的方法:

  • 找 SOF 原语: 链路层会在 SOF 前加一个 ALIGN,跟着 K28.5+D10.2 (0x37)。看到这个序列,基本就是 FIS 开始。
  • 读第一个 Dword: 这个 Dword 的低 8 位是 FIS 类型 (比如 0x27 是 H2D Register,0x34 是 D2H Register),高 24 位是其他控制信息。
  • CRC 校验: 算出 CRC 与实际抓到的值对比,如果不对,大概率是链路层串扰或误码,需要补做链路训练。
老工程师的提醒: 早期芯片经常出现 FIS 类型识别错误,原因是发送方在计算第一个 Dword 时用了错误的字节序。如果你遇到“发送端明明发了 Register 0x27,接收端却收到 0x72”这种反转,别慌,检查一下字节序配置——SATA 的小端序经常被忽视。