3. 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。这阶段分两步走:
- Polling.Active:两边互相发 TS1 和 TS2 训练序列。TS1 里带着发送端的速率能力(比如 Gen1 还是 Gen3)。
- 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 序号并发送 TS2 | 2 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 图把主要状态和典型转换串起来,方便你对照着理解。
这张图里,核心路径是从 Detect 一路走到 L0,然后通过 L0s/L1 节能,或者因为故障跳回 Recovery。L2 是最终断电状态,一般不会主动进入。
特别提醒:在故障注入测试中,我们经常故意触发 Recovery 看看设备能否正确回退并重训练。但注意不要在 L0s 状态下注入故障,因为 L0s 的 TX 已经关了,你根本注不进去——得先退到 L0 再注入。
好了,LTSSM 的各个状态咱们都过了一遍。如果你在调试中碰到链路不稳定,不妨先看看当前 LTSSM 的状态寄存器——那个二进制位组合能告诉你很多硬件没说出口的秘密。嗯,到这儿就行,不啰嗦了。