4. 卡死常见原因(上):硬件信号问题——时钟、复位、电源异常
LTSSM 卡死,很多时候不是协议栈的 bug,而是最底层的硬件信号出了问题。
时钟、复位、电源,这三个东西只要有一个不干净,LTSSM 就会像断了线的木偶,怎么拉都拉不回来。我这些年排查的卡死案例里,至少有一半的根因都在这里。
咱们这一节专门掰开揉碎讲清楚,信号层面的坑到底长什么样。
4.1 时钟信号异常
PCIe 链路的基石是 100 MHz 参考时钟(Refclk)。它是差分对,用交流耦合进入器件。
LTSSM 的各个状态机,从 Detect 到 L0,再到 L1/L2,全都依赖这个时钟做同步和采样。一旦时钟有问题,状态机必然乱掉。
我个人习惯是,遇到卡死先看时钟波形,这比翻寄存器快多了。
- LTSSM 反复卡在 Detect.Quiet 或 Polling.Active,无法进入 L0。
- 链路已经 L0 了,但突然跳回 Recovery,并且重训练失败。
- 误码率(BER)飙升,严重时直接进入 Hot Reset 或 Disabled。
4.1.1 常见时钟错误类型
我总结了三类最常见的“时钟杀手”,你在日志里或者示波器上重点看这几个:
| 错误类型 | 典型现象 | 检查点 |
|---|---|---|
| 频率偏差过大 | REFCLK 偏离 ±300 ppm 之外的标称值 | 系统上电稳定后的时钟频率 |
| 抖动(Jitter)超标 | RMS 周期抖动 > 30 ps(PCIe Gen3) | SRIS 模式下 SSC 调制的有效性 |
| 展频(SSC)异常 | 调制度偏差或调制速率超标 | 发送器件输出波形完整性 |
| 差分电压不足 | Vpp 低于 500 mV | 交流耦合电容两端电压 |
我自己的一个排查技巧:
如果示波器不方便挂,先读 PCIe Capability Structure 里的 Link Status Register 的 Link Speed 和 Negotiated Width。如果速度和宽度频繁变化,时钟出问题的概率就很大。
4.1.2 时钟共享带来的“蝴蝶效应”
有一次我在一个多路服务器平台上排查 LTSSM 卡死,问题很奇怪:只有某几路槽位会卡在 Detection。
测了很久才发现,是时钟缓冲器(Clock Buffer)的 一路输出走线过长,导致到达远端 EP 时相位噪声太大。换了一个通道就正常了。
这种场景下,你的 LTSSM 日志里会看到 Polling.Speed 反复失败,因为无法完成速度协商。所以排查时钟不要只盯着本端,也要看看对端和缓冲器。
注意:
有些平台使用 SRIS(Separate Refclk Independent SSC) 架构,RC 和 EP 各自有独立的时钟生成。这在节省成本的同时,也给链路带来了额外的时钟差异。如果两边的 PLL 噪声特性差异很大,或者本端时钟的 SSC 调制方向配置错了,LTSSM 也会出现间歇性卡死。
4.2 复位信号异常
复位信号咱们一般看 PERST#。它是全局复位,控制着 整个 PCIe 子系统的初始化。
卡死问题里,复位相关的坑往往最隐蔽。因为很多工程师觉得“复位不就是拉低再拉高嘛”,但偏偏就是这一拉一放之间的时序,最容易出事。
4.2.1 复位时序边界的陷阱
规范里对 PERST# 的时序有明确要求,但在实际项目中我踩过不少次。
| 时序参数 | 规范要求 | 实际常见偏差 |
|---|---|---|
| Tperst-clk(复位相对时钟稳定时间) | 复位信号必须在供电稳定及 Refclk 稳定后的 100 µs 内释放 | 系统供电慢,导致 PERST# 释放过早,LTSSM 启动时时钟还没锁住 |
| Tperst(复位保持低有效时间) | 最少保持 100 µs | 设计上只给了 50 µs,导致尚未完全放电即启动,LTSSM 停留在 Detect 无法退出 |
我印象很深的一个案例:某 EP 在冷启动时总卡在 Detect.Quiet,热重启却正常。
定位后发现是系统的电源 PGOOD 信号比 Refclk 先到,导致 PERST# 生效时 PLL 还在锁相,回路根本没法建立起来。
应对策略:
1)上电时示波器同时抓 Vcore、Refclk、PERST# 三路,确认彼此的上升沿关系。
2)如果发现 PERST# 早于 Refclk 稳定,可以在硬件上增加一个 RC 延时电路 或通过 CPLD 加定时,确保复位信号释放总是晚于供电和时钟。
4.2.2 除 PERST# 之外的复位源
PE 端不止一个复位源。我建议你对所有复位域都过一遍:
- PERST#(全局复位):影响所有 PE 逻辑和 LTSSM。
- WAKE#(唤醒复位):从 L2/L3 恢复时,如果 WAKE# 抖动,LTSSM 可能卡在 L2.
- In-band Reset(带内复位):比如 TS1/TS2 中携带的复位序列,如果 TL 层持续发送错误复位序列,LTSSM 会被迫进入 Hot Reset,然后卡住。
我曾经排查过一个 EP 在中断睡眠唤醒后卡在 L2.Idle 的案例,原因是 WAKE# 信号线上串扰了高频噪声(被 CPU 的时钟耦合到了)。EP 上拉能力不够,WAKE# 信号反复跳变,LTSSM 一直进不到 Recovery 状态态。解决手段很简单——在 WAKE# 和地之间加一个 10 nF 电容滤掉毛刺,问题就解决了。
4.3 电源异常
电源出问题,LTSSM 的表现往往是 随机卡死 且 不可复现。这是排查中最让人头疼的事情。我记得有一个项目卡了两个月,最后发现是电源噪声影响到了 PLL 内部,LTSSM 训练阶段频繁失败。
4.3.1 电源上电时序不满足
PCIe 基础规范中,每个设备对不同电压轨的上电有 相对先后要求。咱们用一张表说明:
| 电压轨 | 典型目标电压 | 相对上电顺序 |
|---|---|---|
| Vcore | 0.8~1.2 V | 通常最先上电 |
| Vaux | 3.3 V | 早于 Vcore 稳定后才加载 Vcore,否则 LTSSM 内部寄存器无法初始化 |
| Vtt | 1.0~1.2 V(VDDA) | 通常是最后就绪 |
如果 Vaux 还未稳定时 Vcore 就已经上来了,逻辑单元内部的 复位状态机 可能锁在非法状态,LTSSM 自然卡在 Detect 或 Polling。这种情况我用示波器看电源轨启动时序就能一眼锁定。
4.3.2 电源纹波和瞬态跌落
进入 L0 状态后,LTSSM 对电源的要求更高了。PCIe Gen3 以上的链路训练对 电源噪声非常敏感:
- 卡在 Recovery.RcvrLock 或 Recovery.Speed:训练期间 PLL 失锁,频繁重试。
- 卡在 L0s/L1 进入失败:因为电源波动导致 Phy 层的进入条件始终不满足。
- 系统长时间低功耗时突然唤醒后卡死:VR 负载响应不及时。
有一次我碰到一个非常难缠的问题:LTSSM 只有在跑 Gen3 时才会卡在 Recovery.RcvrLock,Gen1/Gen2 完全正常。
后来用示波器盯了 Vtt 轨发现:当负载从 L1 切换到 L0 时,Vtt 跌落到标称值的 85% 以下,持续了约 20 µs。——对 PLL 来说,这 20 µs 的欠压足以让锁相环失锁,因此 Recovery 阶段始终无法完成。
排查建议:
用示波器的 AC 耦合模式,抓 Vcore 和 Vtt 轨 的噪声波形。重点关注:
- 是否存在 > 50 mVpp 的纹波(对 L0 状态来说是危险的)。
- 是否存在 > 100 µs 的瞬态跌落?
如果发现有,就请硬件同事在 VR 的输出电容上做个优化。
4.4 三大异常的诊断总览
讲到这里,我给自己画了个诊断路径图,你可以照着它去排查:
4.5 最终的排查方法
上面讲了原理和案例,最后分享一下我自己的 排查流程 —— 你可以当作一个 checklist:
- 先看时钟波形:示波器挂上差分头,检查 REFCLK 的 频率、Vpp、抖动,确认在规范范围内。
- 再抓复位波形:测 PERST# 相对于 Vcore 和 Refclk 稳定时间的先后,计算延迟是否满足 Tperst-clk 要求。
- 然后看电源轨:用示波器看 Vcore / Vaux / Vtt 的上电时序,并加测正常工作时 L0/L1 跳变时的动态响应。
- 打寄存器快照:在卡死的瞬间,读取 Link Status 和 Link Control 寄存器,看看当前的 LTSSM 状态是哪一步。如果能挂上 PCIe Analyzer,那更直观。
- 最后复查原理图:确认时钟缓冲器到 EP 的走线长度、电容值、电源去耦电容的安装位置。
做完这几步,大约 80% 的硬件信号类卡死问题都能锁定根因。剩下 20% 往往就涉及 PHY 层的逻辑 bug 或者复杂的 EMI 问题,那就进入下一层面的排查范围了——比如我们后面要讲到的 链路层训练失败 和 TLP 层错误,那也是导致 LTSSM 卡死的常见原因。