第四节 传输层基础: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 也有固定长度。
4.2 FIS 类型一览
SATA 协议里定义了十几种 FIS 类型,但日常调试中你遇到最多的就下面这几种。我做过的项目里,差不多 80% 的问题都跟 Register FIS 和 Data FIS 有关。
| FIS 类型 | 方向 | 长度 (Dword) | 典型用途 |
|---|---|---|---|
| Register FIS – Host to Device | H→D | 5 | 发送命令、设置特征、写寄存器 |
| Register FIS – Device to Host | D→H | 5 | 返回状态、错误信息 |
| Data FIS | 双向 | 可变 (2~2048 字节) | 传输数据块 |
| DMA Activate FIS | D→H | 2 | 通知 Host 可以启动 DMA 读 |
| SET DEVICE BITS FIS | D→H | 4 | 异步状态更新 (非命令响应) |
你可能会问:为什么有这么多类型?简单说,就是协议想把控制流和数据流分开。控制流用小而快速的 Register FIS,数据流用大块连续传输的 Data FIS,互不干扰。
4.3 传输机制:FIS 如何封装和发送
传输层的具体工作流程是这样的:
- 发起端(Host 或 Device)拿到要发送的 FIS 内容后,先计算 CRC-32,追加到内容的末尾。
- 在内容前面插入 SOF 原语(
0x37),后面插入 EOF 原语(0x39)。 - 整个 FIS 被下发到链路层,排队等待发送。
- 接收端看到 SOF 就打开接收窗口,持续收集数据,直到 EOF 为止,然后验证 CRC。
这里面有个坑:SOF 和 EOF 虽然是 B10B 编码的原语,但传输层会让你在 FIS 内容里避开这些特殊码。我遇到过有人直接把 0x37 当成普通数据塞进去,结果链路层解析错乱。嗯,切记——除 CRC 和 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 值自然就上去了。
4.5 FIS 传输的整体流程
下面这个 SVG 图把 FIS 从生成到发送再到流控的脉络画出来了。你多看几遍,心里就有框架了。
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 与实际抓到的值对比,如果不对,大概率是链路层串扰或误码,需要补做链路训练。