LTSSM 详细状态分析

链路训练状态机,也就是 LTSSM,说白了就是 PCIe 设备之间的“握手协议”。从物理层往上走,每个状态都有一套严格的规则。我当年刚接触 PCIe 时,总觉得这些状态跳来跳去很复杂,后来发现只要记住一条主线:从 Detect 开始,一步步走到 L0,然后根据电源策略下掉。下面我们逐个拆解。

Detect 状态——链路存在的证据

这是 LTSSM 的起点。设备上电或复位后,自动进入 Detect。干什么呢?检测对端有没有设备插着。

  • Detect.Quiet:发送器保持 Electrical Idle,等待接收器检测阻抗变化。
  • Detect.Active:发送器发出一个低摆幅脉冲,看看有没有反射回来。

我遇到过一件特有意思的事:有次板卡接触不良,Detect 状态反复超时,但示波器上看到脉冲幅度只有正常值的一半。后来发现是连接器虚焊,导致差分阻抗变了。所以啊,Detect 失败不一定是芯片问题,先查物理链路。

关键指标:Detect 超时时间 12 ms(Gen1/Gen2)或 24 ms(Gen3+)。如果超时,链路回退到 Detect.Quiet 重试。

Polling 状态——在位确认与速率协商

Detect 成功后就进入 Polling。这阶段分两步走:

  1. Polling.Active:两边互相发 TS1 和 TS2 训练序列。TS1 里带着发送端的速率能力(比如 Gen1 还是 Gen3)。
  2. Polling.Configuration:确认对端收到 TS2 后,锁定位锁定,并决定初始数据速率(默认 Gen1)。

这里有个常见的坑:Polling 状态在 Gen3 和 Gen4 中会做两次握手——先以 Gen1 速率完成 Polling,然后进入 Configuration 再协商高速率。我第一次调试 Gen3 卡时,发现 Polling 老是过不去,查 spec 才知道我忘了把 De-emphasis 级别切换到高速档位。

注意:Polling 阶段的 TS1/TS2 是 16-bit 符号,每个符号包含 Lane 号、速率 ID 和功能位。如果 Lane 反转或极性不一致,TS 训练序列会 CRC 校验失败,导致 Polling 超时。

Configuration 状态——链路宽度与 Lane 映射

这是所有状态里最灵活的。Configuration 要做三件事:

  • 确定 Lane 序号:两边通过 TS1 中的 Lane 号协商,把 Lane 0 对正。
  • 确定链路宽度:支持 x1、x2、x4、x8、x16、x32。如果一边是 x8,另一边是 x4,那就只协商成 x4。
  • 分配 Lane 极性:如果 PCB 走线反了,可以通过反转极性来补偿。
子状态主要动作超时时间
Configuration.Linkwidth等待所有 Lane 上的 TS1 匹配24 ms
Configuration.Lanenum分配 Lane 序号并发送 TS22 ms
Configuration.Idle进入 L0 前的最终同步1 ms

我个人习惯在仿真阶段把 Configuration 的 Lane 映射打印出来。你想想看,如果 Lane 0 和 Lane 1 互换,数据就全乱了。曾经有个项目,FPGA 端 x8 口接 ASIC x4 口,结果 Configuration 协商出 x4 但 Lane 0 对应的是 ASIC 的 Lane 1,导致后续 link 跑起来全错。最后发现是 ASIC 的 LTSSM 实现了没有正确解析 Lane 号码。

L0 状态——正常数据传输

终于到了干活的状态。L0 下,TLP 和 DLLP 可以正常收发。数据速率就是协商好的那个速度,同时支持链路控制功能比如 Flow Control、ACK/NAK 等。

要注意的是,L0 并不是一直稳定。它会因为各种原因跳回 Recovery 或直接进入电气空闲。常见故障原因包括:

  • RX 端发现帧定界符错误(STP/END 不对)。
  • CRC 连续出错超出阈值。
  • TS1 定时训练序列丢失。

经验之谈:如果 L0 下出现频繁的 Recovery 跳转,先抓一下链路层的 replay 次数。如果 replay 次数猛增,说明物理层误码率上来了,不一定是 LTSSM 的问题,可能是信号完整性问题。

L0s 和 L1——低功耗状态

PCIe 提供了两种低功耗模式,区别在于退出延迟和功耗节省量。

  • L0s:快速进入/退出(几微秒),只关发送器。接收器保持待机。功耗节省约 30-50%。
  • L1:更深睡眠,关掉 PLL 和一部分时钟。退出需要几十微秒到几百微秒。功耗节省能到 90%。

很多人搞混 L0s 和 L1 的触发条件。简单说:

  • L0s 由物理层自动控制——如果发送器空闲超过一个阈值就降。
  • L1 需要链路两端协商,通过 PM_Enter_L1 DLLP 进入。

避坑:我曾经遇到一个 case,系统挂起后无法唤醒。最后发现是 L1 进入后,PCIe 参考时钟被切断了,但根端口没有正确恢复 CLKREQ# 信号。所以啊,设计 L1 必须同步考虑时钟架构。

L2 状态——切断主电源

L2 是在主电源 VCC 断电后的状态,只保留 VAux(辅助电源)给 PME 唤醒电路。设备基本是“睡死”状态,连复位都不需要维持。

进入 L2 的流程:先从 L1 发起 PME_Turn_Off,然后切电源。有些设计中,L2 下 LTSSM 会直接死掉,直到重新上电才进入 Detect。但也有的实现支持 L2/L3 Ready 状态做短时恢复。

我一般在功耗验证时才关注 L2,因为大部分故障注入实验会刻意避免进入 L2——一旦进去了,链路就彻底断了,恢复起来很麻烦。

其他辅助状态:Recovery、Hot Reset 和 Loopback

除了主状态,还有几个短暂但重要的过渡状态:

  • Recovery:从 L0/L0s/L1 回到 Configuration 或 Polling。用于速率切换、位锁定再同步或错误恢复。
  • Hot Reset:由根端口发出 TS1 中的 Hot Reset bit,强制所有下游设备复位 LTSSM 到 Detect。
  • Loopback:测试模式,发送器发出的 PHY 信号从接收器返回。用于板级眼图测试。

LTSSM 状态流程图

下面我用一张 SVG 图把主要状态和典型转换串起来,方便你对照着理解。

LTSSM 主要状态转换图 Detect.Quiet Detect.Active Polling Configuration L0 L0s L1 L2 Recovery 说明 ● 实线箭头:正常链路训练流程(Detect → L0) ● 虚线箭头:故障或电源管理状态跳转(Recovery / L1 → L2) ● 红色虚线:错误引起的 Recovery 入口(CRC、帧错误等)

这张图里,核心路径是从 Detect 一路走到 L0,然后通过 L0s/L1 节能,或者因为故障跳回 Recovery。L2 是最终断电状态,一般不会主动进入。

特别提醒:在故障注入测试中,我们经常故意触发 Recovery 看看设备能否正确回退并重训练。但注意不要在 L0s 状态下注入故障,因为 L0s 的 TX 已经关了,你根本注不进去——得先退到 L0 再注入。

好了,LTSSM 的各个状态咱们都过了一遍。如果你在调试中碰到链路不稳定,不妨先看看当前 LTSSM 的状态寄存器——那个二进制位组合能告诉你很多硬件没说出口的秘密。嗯,到这儿就行,不啰嗦了。