2. Polling 状态机详解:进入条件与时序归纳
链路训练刚开始,第一个真正有戏份的状态就是 Polling。别小看它,很多莫名其妙的链路卡顿,根因就在这儿。我个人习惯是先看状态机到底怎么跳进来的,再看它在里面怎么转,最后才是超时和退出。顺序对了,定位问题才能快。
2.1 进入 Polling 的前置条件
从 Detect 状态出来后,链路已经完成了阻抗检测和去抖。这时候两个方向上的 Receiver 都检测到电气空闲退出(Electrical Idle Exit),并且各自至少收到一个有效的 FTS 序列。说白了,物理层已经握手确认——对面确实有颗活着的芯片。
为什么会强调“至少一个 FTS”?我在项目中遇到过一种情况:链路明明能跑,但 Gen4 死活训练不过,抓 Link EQ 日志发现 Receiver 检测逻辑认为没退出电气空闲,导致反复回退到 Detect。后来定位是 RC 端的去抖阈值设置偏大,FTS 被当噪声滤掉了。这个坑我后面会再提。
进入 Polling 的完整条件可以归纳为下面几条:
- Detect.Quiet 状态中检测到接收器存在
- Detect.Active 状态完成了电气空闲退出检测
- 至少收到一个有效的 FTS 序列
- 无电气空闲重新进入(否则回退到 Detect)
嗯,这里要注意:很多平台实际实现中,FTS 检测窗口只有几个微秒。如果对端发送器刚上电时序抖动偏大,很可能第一个 FTS 就丢了。所以调试时我会先看寄存器里 Polling 进入次数,如果反复进入,十有八九是 Detect 阶段留下的尾巴没扫干净。
2.2 Polling 的子状态流转
Polling 不是一站到底。它内部拆了四个子状态,每个干不同的事。
2.3 子状态逐个拆解
2.3.1 Polling.Active
这是第一个落脚点。两端设备都开始发送和接收 TS1 有序集。每个 TS1 里包含了 Link Number、Lane Number、Speed 等信息。你想想看,如果这一阶段收不到 8 个连续的 TS1,状态机就不会往 Configuration 跳。
我踩过的坑是:某款 FPGA 开发板的 TS1 序列里 Link Number 设成了 0xFF,而对接的 Root Complex 要求范围 0~31,结果一直停在 Active 不前进。最后用分析仪抓波形才发现 TS1 里 Link Number 字段全是 F。所以调试时我建议先检查 TS1 内容,不要只看有没有收到。
2.3.2 Polling.Configuration
收到 8 个 TS1且 其中有 2 个是回环匹配的,就进入 Configuration。这个子状态的作用是进一步确认 Lane 的编号和排列。之后会发送 TS2,并期望收到对方的 TS2 回环。如果这一步 TS2 收不对,链路会一直在这里兜圈。
2.3.3 Polling.Speed
这是处理速率协商的地方。如果两端都支持 Gen3/Gen4,就会在这个状态里完成升速。注意,升速过程中 TS1/TS2 的发送速率会切换。我记得在 Gen4 早期芯片里,Speed 状态切换时的 PLL 锁定时间差异导致大量链路训练失败。
2.3.4 Polling.Compliance
这个分支只有调试或测试时才会走进来。正常链路训练不会走这里。如果状态机进了 Compliance,要么是软件强制设了 Compliance 模式,要么是硬件在 Active 阶段收不到有效 TS1 后超时进入作为兜底。实际项目中很少见,但一旦见到,基本可以确认对端发送器有问题。
2.4 关键时序参数归纳
Polling 阶段有一组时间参数决定了“等多长时间才算完”。这些参数在 Spec 里有,但不同芯片的实际实现可能会有偏差。我把最关键的列出来。
| 时序参数 | 典型值 | 说明 |
|---|---|---|
| Polling Active 超时 | 12 μs | 从进入 Active 开始计算,必须在此时间内收到有效 TS1 |
| TS1 序列间隔 | ~ 如单 Lane 每次约 20 UI | 每个 TS1 长度固定,连续发送需满足最小间隔 |
| FTS 序列要求 | Gen1/Gen2:至少 2 个 FTS | Detect 阶段遗留要求,Polling 入口需保证已经完成 |
| Speed 切换稳定时间 | 按具体速率,通常 < 1 μs | PLL 重锁和 Rx 均衡训练的硬件开销 |
| Polling 总超时 | 最大 2 ms(由各实现决定) | 超过此时间未完成所有子状态则回退 Detect |
2.5 Polling 退出条件与常见行为模式
正常退出 Polling 只有一个方向——进入 Configuration.Lanenum。异常退出则有几种:
- 超时回退 Detect:任一子状态超时都会触发整个 Polling 失败,重新走 Detect。这是最常见的卡顿场景。
- 进入 Compliance:调试模式,不走正常链路训练。
- 电气空闲退出:如果链路在 Polling 过程中重新检测到电气空闲,立刻回退。
我整理了一个常见退出模式的速查表,方便你对照波形或寄存器日志快速定位。
| Exit 类型 | 触发条件 | 后续状态 | 常见根因 |
|---|---|---|---|
| 正常退出 | Speed 协商完成 | → Configuration.Lanenum | 时序正常,各阶段都收到预期序列 |
| Active 超时退出 | 12 μs 内未收到 8 个连续 TS1 | → Detect.Quiet | 对端未发送 TS1 或 TS1 内容异常 |
| Configuration 超时退出 | 未收到 TS2 回环 | → Detect.Quiet | Lane 编号协商不一致,或 TS2 信号质量差 |
| Speed 超时退出 | 升速过程中 PLL 锁定失败 | → Detect.Quiet | 时钟源不稳定,或 Rx 均衡配置不匹配 |
| Compliance 模式 | 软件强制或超时兜底 | → 维持在 Compliance/回退 | 测试模式或对端不可用 |
2.6 调试中值得注意的细节
总结几条自己的经验,给各位参考:
- Polling 阶段卡住的案子,80% 以上跟 TS1/TS2 内容异常有关。先抓波形确认 Link Number 和 Lane Number 字段是否在合法范围内。
- 不要只依赖超时计数。有些芯片会在子状态间循环而不出发总超时,你需要 Check 每个子状态的停留时间。
- 如果测量到 Polling 持续时间总是接近 2 ms 的上限,说明训练过程非常勉强,链路质量大概率有问题。
- 速查 TS1 的速率标识位(Speed ID),确认两端是否真的在尝试匹配目标速率。
Polling 是整个训练流程里信息密度最高的阶段之一。把它的状态流转和时序参数吃透,后续排查 Configuration 和 L0 的卡顿会轻松很多。