4. SATA 传输层与命令协议
好,咱们进入传输层。这一层说白了,就是 FIS(Frame Information Structure)的组装与拆解。嗯,我个人觉得,传输层是排查很多“莫名其妙”故障的第一现场。你信号测试全 pass,链路层训练也过了,但死活读不回数据。这时候,十有八九是传输层或者命令协议出了问题。
4.1 FIS 结构:一切通信的基石
SATA 里没有“裸数据”在线上跑。所有信息——数据、命令、状态——都要装进 FIS 这个“信封”里。FIS 结构是固定的,Host 和 Device 都得按这个格式来。
| FIS 类型 | 方向 | 主要用途 |
|---|---|---|
| Register – Host to Device | Host → Device | 发送命令、写寄存器 |
| Register – Device to Host | Device → Host | 回传状态、PIO 设置 |
| DMA Activate | Device → Host | 通知 Host 可以 DMA 了 |
| Data | 双向 | 传输用户数据 |
| BIST Activate | 双向 | 进入 BIST 测试模式 |
| PIO Setup | Device → Host | PIO 模式下的数据段描述 |
| Set Device Bits | Device → Host | 异步状态更新 |
每个 FIS 的开头都是 DW0(4 字节),它决定了这个帧的类型和属性。举个例子,一个 Host 发出去的 Register – Host to Device FIS,结构是这样的:
DW0: FIS Type (27h) | C (1 bit) | PM Port (4 bits) | Rsvd
DW1: Feature (7:0) | Command (7:0)
DW2: LBA (7:0) | LBA (15:8) | LBA (23:16) | Device
DW3: LBA (31:24) | LBA (39:32) | LBA (47:40) | Feature (15:8)
DW4: Count (7:0) | Count (15:8) | ICC | Control
你注意看那个 C bit。这玩意儿是“C”还是“S”,直接决定了 FIS 的角色。C=1 表示这是从 Shadow Register Block(也就是 Host 视角)发出的命令。我见过有 FPGA 原型把这位搞反了,结果设备解析出来的命令码全是乱码。
4.2 命令协议:非 NCQ vs NCQ
SATA 命令执行方式分两大类:非 NCQ 和 NCQ。我建议你把它们理解成单线程和多线程的关系。
4.2.1 非 NCQ 协议
这是最老、也是最“死板”的方式。Host 发一个命令,等着 Device 回完状态,才能发下一个。常见的就是 Read DMA (25h) 和 Write DMA (35h)。流程差不多就是:
- Host 发送 Register FIS(命令+参数)
- Device 准备数据 -> 发送 DMA Activate FIS
- Host 收到 Activate 后,发送 Data FIS(如果是写)或者接收 Data FIS(如果是读)
- Device 发送 Register FIS(D2H)带状态
4.2.2 NCQ 协议
NCQ(Native Command Queuing)是 SATA 协议里一个很漂亮的改进。Host 可以一次性扔 32 个命令下去,设备自己排序、优化寻道,再逐个完成。你想想看,这对于机械硬盘来说,能省多少来回寻道的时间。
我遇到过一例:机械盘做视频监控,写入小文件多,不开 NCQ 的时候,I/O 深度稍微上去一点,盘就开始“哗啦哗啦”响,而且掉速。开了 NCQ 之后,盘内部重新排序,磁头臂来回次数少了一半多,性能直接翻倍。
NCQ 的关键在于 Tag。每个命令都挂一个 Tag 编号(0~31)。设备完成某个命令后,回送的 Register D2H FIS 里必须带那个 Tag。Host 靠这个才知道“哦,是 Tag 3 的命令做完了”。
咱们可以把非 NCQ 和 NCQ 的差异做个对比:
| 对比项 | 非 NCQ | NCQ |
|---|---|---|
| 并发命令数 | 1 | 最多 32 |
| 命令 Tag | 无 | 必须 |
| 适用场景 | 顺序大块传输 | 随机小块 & 多线程 |
| 排查侧重点 | 超时 & 状态机 | Tag 冲突 & 队列溢出 |
4.3 传输层故障根因清单
嗯,这里我结合自己几十次排查经验,列个清单。你在现场遇到问题时,对着这个清单先过一遍,能省很多时间。
- FIS 类型不对齐: Host 发出的是 Register H2D,设备却回了一个 Data FIS。我见过有人把 Flash 控制器内部的 FIS 类型映射表写错了。
- CRC 校验错误: 传输层的 CRC 是跟在每个 FIS 后面的。CRC 错,设备或 Host 会发 ERR,然后重传。如果重传次数超过协议限制(通常是 3 次),链路就要挂了。排查时先看 ERR 计数器。
- Tag 冲突: 两个不同的命令用了一样的 Tag。这种情况 NCQ 下尤其要命。你查 trace 时,如果发现同一个 Tag 在完成前又被下发,那就是 bug。
- 状态机死锁: Host 发了命令,等着 Device 回 DMA Activate。但 Device 因为内部缓存满了,一直没发。Host 也不超时重试——就卡死了。我曾经在一个老平台上看过这种,最后加了 Watchdog 才解决。
- 命令参数错位: LBA 地址、扇区数写错了。比如要写 LBA 1000,但写成了 LBA 1。这种表现就是“写盘成功,但读出来是错的”。数据一致性就被破坏了。
总结一下第 4 章:传输层是 SATA 的灵魂。FIS 是它说话的语言,命令协议是它办事的规矩。非 NCQ 简单可靠,NCQ 高效复杂。你先搞懂 FIS 结构,再记住那套完整的命令交互流程,后面排查任何挂死、超时、数据错误,脑子里就有一张清晰的地图了。