链路训练状态机(LTSSM)入门

我在做 PCIe 调试那几年,最常打交道的就是 LTSSM——链路训练状态机。说白了,咱们的 PCIe 链路能不能正常工作,完全看它怎么跳舞。我不止一次跟新人讲,学懂 LTSSM 的三个初始状态,你就掌握了链路训练的一半。

先把大框架放这儿:LTSSM 有 11 个状态,但咱们搞 TS1/TS2 解码,最关键的其实是前三个 Detect、Polling、Configuration。你想想看,链路从物理连接到正常工作,得先确认对面有人(Detect),然后对下口令(Polling),最后分配好手拉手的姿势(Configuration)。

核心观点:LTSSM 的前三个状态是链路训练的“三板斧”。TS1 和 TS2 训练序列只在 Polling 和 Configuration 阶段出现——这也是你抓日志时最该盯住的地方。

1. Detect 状态——物理层握手

Detect 状态是所有状态中最低调的。它不发送任何 TS 序列,只做一件事:检查接收器是否在位。我遇到过好几次板卡“假死”,Debug 到最后发现是 Detect 阶段就没过去,连个接收器阻抗都没检测到。

这个阶段靠的是电气信号。发射器会先拉高信号线,然后拉低,在这个过程中测一下线上有没有反射或者阻抗突变。电容耦合、接收端终端电阻异常,都会导致 Detect 失败。我个人习惯是看 Gen 寄存器里的 LinkStatus 是否为 01b,Detect 失败会停在 Detect.Quiet 一直循环。

避坑指南:我曾经遇到过一块测试板,每次上电链路起不来。抓眼一看,Detect 状态反复回退。最后发现是 PCIe 连接器一个引脚虚焊,电容耦合信号根本没给到接收器。Detect 失败时别急着查协议,先拿示波器看眼 RX 差分信号有没有。

2. Polling 状态——比特对齐与速度协商

Detect 确认对面有人后,立马跳进 Polling。这个阶段的目的是:让两端把比特对齐,确定一起跑多快。

Polling 又分成几个子状态:

  • Polling.Active:发送端开始发 TS1 训练序列。TS1 在这里的作用是“我来喊话,你听着”。
  • Polling.Config:收到足够多的 TS1 并且对齐后,切换成 TS2,表示“我准备好了”。
  • Polling.Compliance:如果上游没收到应答,会强制发一个固定模式的 TS1 队列给测试用(咱们教学很少碰)。

TS1 和 TS2 在 Polling 阶段的分工很明确:

子状态 发送内容 作用
Polling.Active TS1 序 (TS1 OS) 建立比特同步、符号锁定
Polling.Config TS2 序 (TS2 OS) 确认双方符号边界一致
Polling.Compliance TS1 循环 接收器信号完整性测试,不参与正常训练

记得有一次,客户报修某款网卡在服务器上无法识别链路速度。我抓了 LTSSM 日志一看,Polling.Active 阶段反复超时,就是不进入 Polling.Config。后来发现是那个 PCIe 插槽的参考时钟抖动过大,导致符号锁定总对不齐。换个时钟源就好了。

注意:Polling 阶段如果连续 1024 个 TS1 都没有收到对端的 TS2 响应,LTSSM 会超时退回 Detect 状态。这个超时值在 PCIe spec 里是写在 Gen 寄存器里的,但硬件实现在不同芯片上可能有细微差异。

3. Configuration 状态——链路宽度确定

过了 Polling 的符号对齐,就进入了 Configuration。这个阶段是 TS1/TS2 最密集的地方——因为它要解决一个核心问题:到底用几个 Lane 来跑数据。

Configuration 分两个子状态:

  • Configuration.Linkwidth:发送端在每条 Lane 上发 TS1。接收端看哪些 Lane 能收到,然后通过 TS2 回告自己能支持的宽度。
  • Configuration.Lanenum:大家确认好 Lane 编号,把数据分到对应 Lane 上。此时发送 TS2 做最终确认。

我个人觉得 Configuration 阶段最容易被误读的地方是:Lane 编号的分配。不是简单地按物理位置,而是根据 TS2 里的 Lane Number 字段协商出来的。我手边一个项目里,就因为两边板卡对 Lane 反转的处理不一致,导致 Configuration 阶段反复失败——抓日志发现两边报的 Lane Number 对不上。

下面这张图把三个状态的转换和 TS 发送时机串了起来,你看一遍就清楚:

LTSSM 前三个状态与TS1/TS2发送时机 Detect 不发送TS Polling 发TS1→TS2 Configuration 发TS1→TS2 L0(正常工) 不发送训练序列 text 超时回退Detect Lane协商失败回Detect

看了这张图你应该能明白:Detect 不用 TS,Polling 和 Configuration 都要先发 TS1,得到确认后再发 TS2。TS1 是探路石,TS2 是确认函。

Configuration 阶段还有个隐藏知识点:如果两端支持的 Lane 宽度不一致(比如一端 x8 一端 x4),训练会降级到双方都支持的最大值。我遇到过坑是,某款控制器在 Configuration.Linkwidth 阶段如果检测到 Lane 数不匹配,默认退回 x1,而不是报个错误——当时 debug 花了两天才发现它自动降级了。后来我在代码里加了寄存器校验,发现协商失败及时打印告警。

实战要点:培训时我总强调,一定要先分清 TS1 和 TS2 在哪个状态出现的。如果你在 Configuration 阶段只看到 TS1 没看到 TS2,多半是链路宽度协商出了问题。如果 Polling 阶段连 TS1 都很少,先回 Detect 查电气信号吧。

一张表把三个状态看清楚

状态 TS1 时机 TS2 时机 常见失败原因
Detect 无(只做阻抗检测) 无 接收器未连接、电容失效
Polling Polling.Active 阶段连续发 在 Polling.Config 阶段发 符号锁定失败、时钟抖动
Configuration Linkwidth 协商阶段发 Lanenum 确认阶段发 Lane 数匹配、Lane 编号冲突

你拿这张表去对照抓到的 LTSSM 日志,几秒就能定位问题出在哪个状态。我个人习惯在检测 TS 序列时先看 Trainer 模式字段——Polling 阶段它的值是 01b,Configuration 阶段是 10b,一目了然。