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序列后退出 |
检测到下行端口 的存在 |
完成链路宽度/频率 协商后退出 |
稳定运行 |
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的来回跳。
3.4 电气层面的“肉眼可见”
如果你能抓示波器,那更直观。
Polling卡住的示波器特征:差分信号幅值正常,但始终在重复一个固定的训练序列模式。你会看到一个“心跳式”的波形包络——正转、反转、反转……宽度恒定,频率稳定。这是因为链路处于Polling.Active,在不断地发送标准TS1序列,但因为对端设备没有正确回复,所以它只能一遍遍重试。
Configuration卡住的示波器特征:信号幅值可能逐渐变小(因为链宽协商失败导致回退),或者出现明显的时序抖动(因为参考时钟被重新配置)。波形包络没有固定模式,一会儿长一会儿短。
3.5 决策流程:三问定界法
别慌。拿到问题时,问自己三个问题:
- 状态码是不是固定为0x1?
- 是 → 继续往下看
- 不是0x1但发生状态跳转 → 不可能是Polling卡顿
- Training_Error计数是否缓慢且稳定增长?
- 是 → 电气层面或解码逻辑有问题
- 不增长 → 判定窗口或状态机控制逻辑有问题
- 退出条件是否永远不满足?
- 是 → 锁定TS1序列接收或校验段
- 偶尔满足但又被踢回来 → 检查Configuration的准入条件
下面这张图,是我在项目里画的排查流程图。它能帮你把思路理清。