物理层逻辑子层:有序集与训练序列归纳总结

好,咱们直接切入正题。上一节我们在链路训练状态机里转了一圈,现在该把底层那些「有序集」和「训练序列」拿出来仔细捋一捋了。说实话,刚接触 PCIe 协议时,我觉得这些东西就像天书——各种 TS1、TS2、SKP 和 EIOS,完全记不住谁是谁。后来在实验室里抓了几次逻辑分析仪的波形,才真正搞明白它们是怎么工作的。

物理层的逻辑子层,说白了就是处理这些有序集的工厂。它从数据链路层拿到数据后,先把它们塞进有序集或者数据包的壳里,再交给电气子层去发送。反过来也一样,从电气子层收到一串比特流,逻辑子层要能认出来这是 TS1 还是数据包,然后做不同的处理。

有序集家族一览

PCIe 的有序集,可以分为四大类。多数的培训材料会列一张大表,但我更喜欢从功能上把它们分成两组:链路管理用的 和 数据流控制用的。

分类 有序集名称 主要作用
链路管理 TS1 / TS2 链路训练与协商(速度、宽度、极性等)
SKP 时钟补偿与弹性缓冲
FTS 快速退出 L0s 省电状态
数据流控制 EIOS / EIEOS 进入电气空闲状态的信号
电气空闲退出序列 从电气空闲恢复到正常工作

我个人习惯:把 TS1/TS2 看作链路协商的「握手信号」,SKP 是时钟的「呼吸间隙」,EIOS 则是进入省电模式的「熄灯号」。理解了这个比喻,做调试时思路就清晰多了。

TS1 与 TS2:训练序列的核心

这一节的主角是 TS1 和 TS2。它们是链路协商的基础,也是物理层握手的关键。

TS1 和 TS2 结构一模一样——都是 16 个符号(Symbol)组成的一个有序集。每个符号是 8/10b 编码前的 8 bit 数据。我见过有些工程师误以为 TS1 和 TS2 是两种完全不同的序列,其实它们 唯一的区别是第一个符号的值:TS1 的符号 0 固定为 K28.0,TS2 的符号 0 固定为 K28.1。就这么简单。

训练序列的内部结构

看一下它们的数据格式(只写符号 0~7,符号 8~15 是重复):

符号0: K28.0 (TS1) 或 K28.1 (TS2)  —— 起始标识
符号1: 链路号(Link Number)          —— 本地端期望的链路编号
符号2: 通道号(Lane Number)          —— 本地端的物理通道号
符号3: FTS数量(Sequence Number)    —— 快速退出 L0s 时需要的 FTS 个数
符号4: CRC 校验和                     —— 用于简单的错误检测
符号5: 速度标识(Data Rate)          —— 训练速度:2.5/5/8 GT/s
符号6: 保留位                         —— 厂商可自定义,或填 0
符号7: 保留位

这里要注意符号 1 和 符号 2。如果你做过链路宽度协商,一定对它们不陌生。发送端把这两个字段填好,接收端会把它和自己的期望值做对比。如果链路号不匹配,接收端就知道这个序列不是发给自己的,直接忽略。我在调试一套多路卡的时候,就遇到过因为固件把链路号写错了,导致下行端口一直收不到有效 TS1 的情况。

避坑指南:我曾经在调试某 SoC 平台时,发现 TS1 的符号 1 总是 0xFF,链路协商死活过不去。排查了半天才发现,是硬件初始化时链路号寄存器没有正确赋值。这个坑很隐蔽,检查符号 1 和 符号 2 的值 是定位链路协商问题的第一步。

训练序列在协商中的角色

链路训练状态机有 7 个主要状态:Detect、Polling、Configuration、Recovery、L0、L0s、L1、L2。其中,Polling、Configuration 和 Recovery 是发送 TS1/TS2 最频繁的阶段。

  • Polling:发送 6 个 TS1(或 TS2),然后开始等对方的 TS1。这里主要目的是让接收端的 CDR(时钟数据恢复)锁定到发送信号的频率。
  • Configuration:这个阶段最复杂。发送端连续发 16 个 TS1,然后发送 16 个 TS2。这一轮的 TS1/TS2 里,符号 1 和符号 2 会携带协商后的链路编号和通道编号。
  • Recovery:当现链路丢失同步,或需要改变速度/宽度时进入此状态。会先发 TS1,再发 TS2,重新协商。

核心要点:TS1 和 TS2 的切换顺序是固定的——先 TS1 后 TS2。如果接收端在期望收到 TS1 时收到 TS2,或者在期望收到 TS2 时收到了 TS1,都会立即判定链路出现错误,导致状态机回退到 Reset 状态。这种检查被称作「序列顺序一致性校验」。

一个经典的 TS1/TS2 转换流程

我画个图来帮助你理解链路协商过程中 TS1/TS2 的角色转换:

TS1/TS2 在链路协商中的转换流程 PCIe 设备 发送 TS1 连续 TS1 序列 检测收到 TS1? 切换为 TS2 发送 TS2 序列 链路进入 L0 TS1/TS2 转换顺序不可颠倒 否则状态机立即进入 Recovery 或复位

TS1 与 TS2 的异同总结

很多初学者问我:两个序列结构一样,为什么要分两个?嗯,这里要注意,它们虽然长得像,但角色完全不同:

对比项 TS1 TS2
起始符号 K28.0 K28.1
主要用途 链路初次训练 / 协商 / 恢复 链路宽度与速度确认 / 完成训练
发送时机 Polling、Configuration 前期、Recovery 前期 Configuration 后期、Recovery 后期
确认机制 接收端收到 TS1 后才响应 接收端收到 TS2 后认为训练完成
Speed/Width协商 在 TS1 中完成速度与宽度提案 在 TS2 中完成最终确认

实用技巧:如果你想在逻辑分析仪上快速判断当前训练到了哪个阶段,直接看 TS1/TS2 的比例就明白了。如果看到的 TS1 远多于 TS2,大概率还在 Polling 或 Configuration 的初期;如果 TS2 开始出现并且逐渐增多,说明快要完成训练了。

为什么顺序这么重要?

一个无法颠倒顺序的典型场景是:某些 IP 核在实现时,对 TS1 和 TS2 的转换有严格的时钟域条件。如果 TS1→TS2 的切换太快,接收端的弹性缓冲可能还没锁定,就会误判为 TS2,进而提前进入 L0,造成链路工作不稳定。这种现象在低速模式(2.5 GT/s)下不常见,但到了 5 GT/s 或 8 GT/s,时钟抖动稍微大一点就可能暴露问题。

反过来,如果 TS2 迟迟不出现——比如发送端因某个错误一直卡在 TS1 阶段——接收端会一直等待,最终超时后状态机回退到 Detect 状态。我之前调一个 16 通道的 Gen3 交换芯片时,就遇到过因为一个通道的 TS1 发送模块死锁,导致整条链路无法完成训练。

小结要点

  • TS1 和 TS2 的长度都是 16 个符号,区别只在第一个符号的值(K28.0 vs K28.1)。
  • 链路号与通道号 是 TS1/TS2 中最重要的数据字段,必须与硬件配置一致。
  • TS1→TS2 的顺序不可逆,这是状态机规范强制要求的。
  • 在调试时,对逻辑分析仪抓到的 TS1/TS2 做统计,能快速定位问题的阶段。