TS1 协议详解:传输协议头、有效载荷、自适应字段、同步字节
TS1 是做什么用的?说白了,它就是 PCIe 链路训练阶段握手的第一步。我刚开始接触时,以为它只是一串随机的比特流,后来踩过坑才明白——TS1 里的每一个字节都有明确含义,读懂了它,你就掌握了链路训练的核心。
TS1 的构成:一个有序集的骨架
TS1 属于 Ordered Set,跑在物理层的逻辑子块里。标准的 TS1 由 16 个符号组成,我习惯把它切开来看:
- 同步字节(COM):1 个符号,标记帧起始
- 传输协议头:3 个符号,包含 TS1 标识和类型
- 有效载荷:8 个符号,携带链路和通道信息
- 自适应字段:4 个符号,用于扩展或厂商自定义
你想想看,总共 16 个符号,编码后就是 20 位宽的并行数据。为什么是 16?嗯,这和 8b/10b 编码后的对齐需求有关,不多不少。
关键点: 任何链路训练状态机(LTSSM)的切换,都必须先收到对端的有效 TS1 或 TS2。我曾遇到一块 FPGA 板,因同步字节 COM 被噪声干扰,导致链路一直卡在 Detect 状态。检查了三天才发现是 COM 的 10b 编码有误。
同步字节(COM)—— 帧的锚点
COM 符号的 10b 编码是固定的:0011111010 或 1100000101。它在 8b/10b 码表中是唯一的 K28.5 码型,不会出现在数据流中。所以接收器只要检测到这个模式,就知道“哦,一个新的有序集开始了”。
我记得有一次在示波器上抓链路训练波形,看到反复出现的 0xBC 字节(COM 的 8b 值),心里就踏实了——至少物理层通着。反过来,如果 COM 对不上,后面的所有符号都会错位。
传输协议头 —— 我是谁
COM 后面的三个符号构成协议头:
| 符号序号 | 字段名 | 宽度(bits) | 含义 |
|---|---|---|---|
| 1 | TS1 Identifier | 8 | 固定 0x1E,标识这是一个 TS1 |
| 2 | Link Number | 4 | 请求的链路编号,训练中动态协商 |
| 2(高4位) | Reserved | 4 | 保留,必须为 0 |
| 3 | Lane Number | 4 | 当前通道编号,从 0 开始 |
| 3(高4位) | Reserved | 4 | 保留 |
这里有个容易掉坑的地方:Link Number 在训练初期总是 0xFF(表示未知),等到链路宽度协商完成后才填入真实值。我曾经在产品联调时发现两边 Link Number 不匹配,结果链路宽度始终被锁定在 x1,浪费了不少带宽。
有效载荷 —— 身体里的秘密
传输协议头之后的 8 个符号是有效载荷区。每个符号 8 位,但实际有用的只有低 4 位或低 6 位,高位多数保留。具体分布如下:
- 符号 4~7:FTS(Fast Training Sequence)计数,用于恢复相位信息
- 符号 8~9:比特锁定状态和符号锁定状态标志
- 符号 10~11:速率 ID 和扩频时钟(SSC)信息
这些字段在 LTSSM 的不同子状态下的含义略有不同。比如在 Polling 状态下,FTS 计数被用来做比特对齐;到了 Recovery 状态,它的优先级降低。
实践技巧: 调试时,把 TS1 的有效载荷逐字节打印出来,对照 PCIe 规范 Table 4-44 核对每一位。我习惯用 160 行的逻辑分析仪抓 16 个符号,解析出每个字段后画成时序图,问题往往一眼就能看出来。
自适应字段 —— 未来的预留地
最后 4 个符号是自适应字段。标准规定这些符号的值为 0x00,但允许厂商用于扩展。比如某些处理器会在自适应字段里携带电源管理请求或错误指示。
说实话,大多数商用芯片并没有用这个字段。但我碰到过一家做定制交换机芯片的客户,他们在 TS1 的自适应字段里塞了温度告警信息,结果跟标准解码器冲突。最后只能通过软件把这个字段的值过滤掉。
注意: 自适应字段虽然叫“adaptive”,但接收端默认必须忽略其内容。任何非零的自适应字段都应被视为可选特征,如果对方不支持,会产生未定义行为。
TS1 完整的帧结构
把上面所有部分拼在一起,就是一帧标准的 TS1:
符号序号: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
内容: COM ID LN Ln FTS0 FTS1 FTS2 FTS3 BI SI Rate SSC 0 0 0 0
其中 ID=0x1E,LN=Link Number,Ln=Lane Number,FTS0~FTS3 是 4 个 FTS 计数字节,BI=Bit Lock indicator,SI=Symbol Lock indicator,Rate=Rapid ID,SSC=SSC Status。最后四个 0 是自适应字段。
SVG:TS1 协议帧结构图
解码 TS1 的实用步骤
如果你需要自己实现解码器,我建议按这个顺序来:
- 等待 COM 符号出现,标记帧起始
- 读取第 1~3 符号,检查 ID 是否为 0x1E,不是则丢弃
- 提取 Link Number 和 Lane Number,注意初始值为 0xFF
- 读取 8 个有效载荷字节,解析 FTS、Bit Lock、Symbol Lock 和速率信息
- 忽略最后 4 个自适应字段(除非你知道对方有扩展)
我曾经偷懒直接用一个状态机连续接收 TS1,结果没对 COM 做余数检查,导致某个时钟周期偏移后整个解析错位。后来我加了一个滑动窗口校验,每次收到 16 个符号后验证最后一个自适应字段是否为 0,才算过关。
避坑指南: PCIe 规范允许接收端在任意时刻丢弃 TS1(比如遇到了 Link Number 不匹配)。但千万别在训练没完成时就把有效载荷里的 Bit Lock 当作最终锁定信号——那个只是中间状态。我见过有人直接读它去触发 DFE 系数更新,结果滤波器从来没收敛过。
TS1 协议本身其实不复杂,难的是在高速串行环境下的边界处理和状态机同步。你只要把帧结构背下来,再配合示波器或逻辑分析仪看过几次波形,自然就能一眼识别出每个字段的位置。嗯,就是这样。