LTSSM 状态详解:从 Detect 到 Recovery 的修炼

各位做 Gen4 的小伙伴,链路训练说白了就是 LTSSM 在几个状态间来回跳。我刚开始接触 PCIe 时,看着那十几个状态头都大了。后来才发现,真正和调试密切相关的,也就 Detect、Polling、Configuration、L0 和 Recovery 这几个。其他状态要么是省电用的,要么是特殊场景才会碰到的。

这一章咱们就把这几个核心状态掰开揉碎。我会结合自己踩过的坑来聊,保证你听完就能上手排查。

状态总览:LTSSM 的核心状态机

先看一张我画的逻辑关系图。别嫌简单,链路训练就靠它撑场面。

LTSSM 核心状态迁移图(Gen4) Detect Polling Configuration L0 Recovery Hot Reset / Disable 发现接收器 位锁 & 符号锁 链路训练成功 训练失败 / 错误 错误、电气空闲、Retrain 成功恢复 恢复超时 / 无法恢复 调试图标说明 实线箭头 = 正常流程 虚线箭头 = 异常/恢复路径 红色箭头 = 错误导致的状态回退

你可能会问,为什么没把 L0s、L1、L2 画进来?说实话,那些省电状态在 Gen4 链路训练异常排查中碰到的概率很低。我们更关心的是“怎么跳进 L0”和“怎么从 L0 掉出来”。

Detect:发现就是胜利

Detect 是整个链路训练的起点。说白了,就是发送端往差分线路上灌一个脉冲,看对面有没有东西。

  • 做的事情:交替发送 Electrical Idle(电气空闲)和检测脉冲,探测接收端是否存在。
  • 成功条件:检测到接收器终端电阻,或者收到 TS1 有序集。
  • 卡住的原因:端点没上电、差分线断、共模电压不对。
我的一个坑:曾有一块 Gen4 转接卡,上电后死活过不了 Detect。拿示波器看眼图,差分信号幅度正常。当时折腾了两天,最后发现是金手指没插到底,有一个 Pin 的接触电阻偏大到 50Ω。换了个卡槽就过去了。所以排查 Detect 异常时,先检查物理连接。相信我,九成问题是接触不良。

Polling:对暗号阶段

过了 Detect 就进入 Polling。这个状态有四个子状态:Polling.Active、Polling.Compliance、Polling.Configuration、Polling.Speed。

我记得第一次调试 Gen4 时,就被 Polling.Compliance 坑了很久。当时我是想跳过这个子状态直接配置,结果链路不断回滚。

子状态作用常见异常
Polling.Active收发双方交换 TS1/TS2 有序集,做位锁和符号锁TS1 没对齐,时钟偏移过大
Polling.Compliance让发端进入合规性测试模式(抖动测试用)误入该模式导致无法退出
Polling.Configuration协商链路宽度、数据速率(Gen1/2/3/4)两边协商能力不匹配
Polling.Speed完成速率切换(比如从 Gen2 切到 Gen4)PLL 锁定不上,或者 Tx 再均衡失败
注意:Polling.Compliance 是个双刃剑。有些板卡如果没收到正确的 TS1,就会自动跳进去出不来了。你怎么看?
我的建议是,在逻辑分析仪上看链路是否长时间维持在 Polling.Compliance。如果超过 2ms,基本可以判断是主机或设备的 TS1 格式有问题。

Configuration:分车道,定速度

Configuration 是链路训练里信息量最大的状态。它要干三件事——分车道编号、协商宽度、协商速度。我个人习惯把这个状态拆成三个子阶段来看:

  1. 配置 Lane 编号:通过 TS1 里的 Lane Number 字段交换编号信息。
  2. 宽度协商:确认实际工作的 Lane 数量(x1 / x2 / x4 / x8 / x16)。
  3. 速度协商:从 Gen1 开始,逐步提升到 Gen4(或降级到 Gen3/2)。

这里容易出问题的地方在于速度协商。Gen4 要求在 24ns 内完成从 Gen1 到 Gen4 的切换,同时对接收端做均衡训练。我见过不少 PCIe 4.0 设备就是因为 TxFFE 系数没调好,在 Configuration 阶段反复回退到 Polling。

实战经验:拿 Gen4 SSD 举例,它宣称支持 Gen4 x4,但实际只焊了两个 Lane。
在 Configuration 阶段,Root Complex 会尝试 x4 宽度,但设备只回复 Lane 0 和 Lane 1 的编号。这时主机如果发现 Lane 2 和 Lane 3 无应答,会自动降级为 x2。你如果在示波器上看到宽度降级,别慌,这不是错误,是正常协商。

L0:稳定的数据传输态

L0 是大家都想待的状态。说白了,就是所有 Lane 都训练完成,数据包可以正常收发。

但 L0 也不是铁板一块。我遇到过一个案子,设备在 L0 下跑得好好的,突然掉进 Recovery。后来发现是电源纹波太大,导致接收端的 CDR 失锁。这种现象多发于用低端 DC-DC 的 PCIe 扩展卡上。

  • 正常退出:进入 L0s(省电)或 L1/L2 睡眠。
  • 异常退出:进入 Recovery(最常见),或者直接 Hot Reset。
避坑指南:我曾经为了省成本,在一款 Gen4 加速卡上用了廉价晶振。结果 L0 下每 10 秒就掉一次 Recovery,重启都没用。换回高精度晶振后,问题消失。所以 L0 不稳定时,优先检查时钟抖动的 RMS 值是否超过 PCIe 规范的限值。

Recovery:救火队长

Recovery 的地位很特殊。它不是异常状态,而是“尝试修复”的状态。链路在 L0 或 Configuration 中遇到不可纠正的符号错误、电气空闲违规、或者收到 Hot Reset,就会跳进 Recovery。

Recovery 内部也有子状态:Recovery.RcvrLock、Recovery.Equalization、Recovery.Speed、Recovery.Idle。整体思路是——重新锁住接收器、重新做均衡、重新协商速度,如果能成功就返回 L0;如果失败次数超限,就进入 Hot Reset 或 Down 状态。

子状态作用我在项目中观察到的典型场景
Recovery.RcvrLock重新锁相环,找位锁突然的高频噪声导致 CDR 失锁
Recovery.Equalization重跑 Gen3/Gen4 均衡训练Tx 系数不够优,接收端 CTLE/DFE 无法收敛
Recovery.Speed尝试回到原来的速率或降级Gen4 链路回退到 Gen3
Recovery.Idle等待链路空闲,准备返回 L0正常完成恢复后跳转
重点注意:在 Recovery 中多停留并不可怕。可怕的是 Recovery 次数太多,比如每秒钟进 10 次 Recovery。这种情况多半是硬件有底噪导致周期性误码。
我建议你在调试时,用逻辑分析仪统计 Recovery 的触发次数和持续时间。如果平均恢复时间超过 1ms,说明均衡参数可能需要手动优化。

其他状态:Hot Reset、Disable、Loopback

这些状态在实战中出现的频率低一些,但我还是忍不住提两个点:

  • Hot Reset:当 Recovery 重试次数超限(典型值是 4 次),链路进入 Hot Reset,相当于硬件重启。此时整个链路会回到 Detect 重新来过。
  • Loopback:用于测试模式,数据从 Tx 环回到 Rx,不参与正常通信。如果你发现链路卡在 Loopback 出不来,大概率是主机端配置错了 Loopback 触发条件。

你看,LTSSM 的状态并不复杂。核心就五个——Detect、Polling、Configuration、L0、Recovery。你只要掌握了它们之间的跳转条件和常见卡住的原因,就能像个老手一样,对着逻辑分析仪三分钟定位问题。嗯,也不说大话……至少能少走一半弯路。