3. 卡顿现象定界:如何判断是Polling状态卡顿而非其他状态

拿到一个链路训练失败的日志,第一件事不是去翻代码。我个人的习惯是,先回答一个问题:它到底卡在哪个状态?

很多人一看到训练没完成就喊“Polling卡住了”,结果折腾半天,发现是Configuration状态压根没进去。这就像看病,连病灶在哪都没找到就开药,浪费时间。

3.1 先看状态机的“指纹”

每种状态在寄存器、调试日志和电气特性上都有独特的“指纹”。我们不用猜,用数据说话。

下面这张表,是我调了无数链路训练问题后总结的。重点关注Polling与其他状态的区别。

特征维度 Polling状态(卡顿) Detect状态(卡顿) Configuration状态(卡顿) L0状态(正常)
主要寄存器位 LTSSMState[3:0] = 0x1
Training_Error置位较少
LTSSMState[3:0] = 0x0
Detect_Quiet循环
LTSSMState[3:0] = 0x2~0x5
Training_Error频繁置位
LTSSMState[3:0] = 0x6
LinkUp=1
电气信号特征 Electrical Idle无
信号翻转频率高
Electrical Idle后
突然爆发式翻转
有数据块传输
但经常重传
稳定数据流
反复进入耗时 每次进入Polling时间
基本固定(几十μs)
时间不固定
随链路参数变化
时间越来越长
直到超时
不适用
退出条件 收到8个连续无错的
TS1序列后退出
检测到下行端口
的存在
完成链路宽度/频率
协商后退出
稳定运行
我的小技巧:先读LTSSMState寄存器。如果看到0x1持续不变,且没有超时复位,那99%是Polling卡住了。其他状态卡顿通常伴有超时和复位循环。

3.2 从日志里“听”出来的不一样

日志不会骗人,但解读方式很重要。你想想看,Polling状态卡顿的日志,读起来有一种“单调重复感”。

举个例子,这是我以前在一款PCie Gen4 Retimer项目里调过的日志片段:

[1234.567] LTSSM_CHANGE: state=0x1 (Polling.Active)
[1234.600] LTSSM_CHANGE: state=0x1 (Polling.Active)
[1234.633] LTSSM_CHANGE: state=0x1 (Polling.Active)
[1234.666] LTSSM_CHANGE: state=0x1 (Polling.Active)
...(无限重复)

看到了吧?时间戳间隔非常规律,大约33μs一次。这说明链路一直在Polling.Active和Polling.Inactive之间循环,但就是进不去Configuration。

但你再看Configuration状态卡顿的日志——

[1234.567] LTSSM_CHANGE: state=0x2 (Config.Lanenum.Wait)
[1234.700] LTSSM_CHANGE: state=0x1 (Polling.Active) // 回退了!
[1234.900] LTSSM_CHANGE: state=0x3 (Config.Lanenum.Accept)
[1235.100] LTSSM_CHANGE: state=0x1 (Polling.Active) // 又回退了!

这里的时间戳完全乱了,状态来回跳。Polling卡顿的日志很干净,就是机械重复。Configuration卡顿的日志很凌乱,充斥着回退和异常跳转。

3.3 利用调试器作“定点打击”

光靠读日志还不够。我通常会在调试器里布几个硬件断点,精准定位。

第一步:跟踪Polling的退出条件

在硬件代码里找到退出Polling状态的判断逻辑,打一个while循环断点或简单打印。

// 伪代码
if (polling_exit_condition()) {
    // 进入Configuration
    // 在这里打断点,或者加一个printf
    // 如果从来没进入这里,说明退出条件一直不满足
    printf("Polling exit! TS1_valid_count=%d\n", ts1_valid_count);
}

退出Polling的条件是什么?嗯,这里要注意:需要连续收到8个有效的TS1序列。8个,不是7个,不是9个。我见过一个案子,因为一个反相器的阈值问题,导致PCie控制器每次都只收到7个有效TS1就复位重来了。

第二步:看Error Count是不是在涨

Polling状态卡顿,一个很关键的指标是Training_Error计数。

  • Training_Error缓慢增长(每秒几十次):大概率是电气通道有轻微退化,比如轻微阻抗不匹配、码间串扰。链路每次都差一点点成功。
  • Training_Error不增长,但状态就是卡住:这种情况最诡异。我遇到过是解码模块被前一次遗留的无效数据锁住了,导致TS1训练序列根本进不了状态机判定窗口。
  • Training_Error爆发式增长:注意,这种情况往往不是Polling本身的问题。它很快就会被超时机制踢出去,然后系统复位。日志里会看到大量Polling -> Detect -> Polling的来回跳。
关键区别:如果Training_Error峰值很高但状态始终不切,那Polling状态机内部的“健康检查”逻辑可能有bug。我曾在一款定制控制器中遇到过,状态机判定退出的条件是“连续8个有效TS1”,但硬件实现时把“连续”理解错了,导致每收到一个有效序列都要重置计数器。

3.4 电气层面的“肉眼可见”

如果你能抓示波器,那更直观。

Polling卡住的示波器特征:差分信号幅值正常,但始终在重复一个固定的训练序列模式。你会看到一个“心跳式”的波形包络——正转、反转、反转……宽度恒定,频率稳定。这是因为链路处于Polling.Active,在不断地发送标准TS1序列,但因为对端设备没有正确回复,所以它只能一遍遍重试。

Configuration卡住的示波器特征:信号幅值可能逐渐变小(因为链宽协商失败导致回退),或者出现明显的时序抖动(因为参考时钟被重新配置)。波形包络没有固定模式,一会儿长一会儿短。

避坑指南:我曾经在示波器上看到一个很规则的心跳波形,以为就是Polling卡住了。后来深入排查发现,那是Device驱动把PCIe接口配成了Loopback模式,根本不是Polling!所以,示波器看到的规则波形,一定要和驱动配置对照确认。

3.5 决策流程:三问定界法

别慌。拿到问题时,问自己三个问题:

  1. 状态码是不是固定为0x1?
    • 是 → 继续往下看
    • 不是0x1但发生状态跳转 → 不可能是Polling卡顿
  2. Training_Error计数是否缓慢且稳定增长?
    • 是 → 电气层面或解码逻辑有问题
    • 不增长 → 判定窗口或状态机控制逻辑有问题
  3. 退出条件是否永远不满足?
    • 是 → 锁定TS1序列接收或校验段
    • 偶尔满足但又被踢回来 → 检查Configuration的准入条件

下面这张图,是我在项目里画的排查流程图。它能帮你把思路理清。

Polling卡顿判定三问流程图 第1问:LTSSMState是否固定0x1? 是 → 继续往下 否 → 非Polling卡顿 第2问:Training_Error缓慢增长? 是 → 电气/解码问题 否 → 判定窗口/逻辑问题 第3问:退出条件永远不满足? 是 → 排查TS1接收/校验 否 → 检查Configuration准入 明确卡顿类型,定向调试 说明: 三个问题都回答“是”,才能确认是Polling状态卡顿。任何一步走不通,都说明问题不在此处。
最后提醒一句:定界不是猜。不要看到Polling状态就一股脑钻进TS1序列细节里。我见过太多人因为没做定界,在错误的方向上浪费了三天。记住:问自己那三个问题,用寄存器、日志、示波器去回答,而不是靠感觉。拿到证据再下结论。