第3章:Polling状态——速度协商与训练序列

好了,这一章我们来聊聊 Polling 状态。如果说 Detect 状态是“握手”,那 Polling 就是“互相认识”的关键阶段。说白了,链路两端在 Detect 里确认了对方存在之后,就得在 Polling 里商量好“我们用什么速度跑”、“咱们怎么同步时钟”。

我个人觉得,Polling 是整个 LTSSM 里最容易出幺蛾子的地方之一。很多 PCIe 链路训练失败的问题,追根溯源都是 Polling 没走通。为什么会这样?因为它既要协商速率,又要交换训练序列(TS1/TS2),任何一个环节卡壳,训练就停了。我接触过一个项目,PC 死活认不到 NVMe SSD,最后定位就是 Polling 状态内 TS1 序列的位锁(Bit Lock)没锁住,加了一级时钟调理就 OK 了。这种坑,回头我细说。

3.1 Polling 状态在干嘛

Polling 状态的目标很明确:

  • 速度协商:从 Gen1 开始,看能不能往更高速度冲
  • 位锁定与符号锁定:通过 TS1 序列把时钟和数据对齐
  • 链路宽度确认:检查到底有几条 Lane 是活的
  • 交换 TS2 序列:确认双方都准备好了,准备跳转到 Configuration

你看,这其实是一个逐步递进的过程。我习惯把 Polling 拆成四个子阶段来看:

子状态 主要动作 关键产出
Polling.Active 发送 TS1,做位锁定和符号锁定 Bit Lock + Symbol Lock
Polling.Configuration 发送 TS2,交换链路配置信息 确认宽度与速度
Polling.Speed 如果支持更高速度,在此协商 速度变更完成
Polling.Compliance 进入合规性测试模式 发送测试码型
小提示:很多工程师只盯着 Polling.Active 看,忽略了 Polling.Speed。实际上,速度协商失败导致的链路降级,比想象中更常见。我建议在调试时把逻辑分析仪深度开大一点,把 Polling.Speed 里的训练序列也抓全。

3.2 速度协商:从 Gen1 开始

PCIe 的速度协商,并不是一上来就跑最高速。它规定了一套“循序渐进”的机制:

  1. 默认从 Gen1 速率启动(2.5 GT/s)
  2. 在 Polling.Active 阶段交换 TS1 序列
  3. 双方通过 TS1 序列里的 Speed Change 位,表达“我支持高速”
  4. 如果检测到双方都支持,就跳到 Polling.Speed 去执行速度变更
  5. 速度变更完成后,继续交换 TS2 序列,才算整个 Polling 结束

你想想看,为什么要设计这条规矩?我理解的是,避免刚上电就跑高速,万一时钟还不稳呢?从 Gen1 起步,给了两边一个“热身的窗口”。

注意:如果链路协商到 Gen2 或 Gen3 时失败,LTSSM 不会退回 Polling 重新协商。它会进入 Recovery 状态尝试恢复。这个机制我后面讲 Recovery 时会重点说。现在你只要记住,Polling 阶段的速率一旦确定,后续的速度变更都走 Recovery。

3.3 训练序列:TS1 和 TS2

训练序列是 Polling 的灵魂。没有 TS1/TS2,这状态就是个空壳。

TS1 训练序列

TS1 主要用于三个事:位锁定、符号锁定、以及 训练控制(Training Control)。TS1 序列里有一堆字段,我觉得你必须关注这几个:

  • TS1 Identifier (0x7E):告诉接收方“我来了,我是个 TS1 序列”
  • Link Number & Lane Number:这回交换双方各自的 Lane 编号,用来做宽度协商
  • Training Control (TC):里面包含 Speed Change 位、Disable Scrambling 位等
  • TS1 OS (Ordered Set):同步用的特殊码

实际项目中,我最怕碰到的场景是:TS1 序列发了,但接收方始终锁不定。为什么会这样?

常见原因:
  • 时钟抖动太大,导致 Bit Lock 失败
  • 发送端信号幅度不够,接收端眼图不闭合
  • 链路中间有 AC 耦合电容,导致 DC 不平衡
  • 通道内有明显的串扰,尤其是多 Lane 链路

我记得有个客户问题,Gen2 链路连不上。逻辑分析仪一看,TS1 发得好好的,但接收端报 Symbol Lock Fail。最后发现是 PCB 走线时,某条 Lane 的参考地被切断了,导致共模噪声过大。重新打板解决问题。

TS2 训练序列

一旦 TS1 阶段完成了位锁定和符号锁定,链路就进入 TS2 阶段。TS2 的主要目的是交换 链路配置参数,包括链路宽度、通道编号、是否启用去加重等。跟 TS1 相比,TS2 不带 Speed Change 位,也就是说,这时候速度已经定死了。

TS2 的帧结构跟 TS1 非常像,但含义不同。我用一个表格给你对比清楚了:

字段 TS1 含义 TS2 含义
Training Control 包含 Speed Change 等 不含 Speed Change
Link Number 用于宽度发现 用于确认宽度
Lane Number 用于 Lane 排序 用于 Lane 排序确认
TS1 OS 用于同步 用于同步保持

3.4 Polling 状态机内部流转

为了让你对整个 Polling 脉络有更直观的感觉,我画了一张简化版的状态机图。你注意看红色箭头,那是我曾踩过的“坑”路径:

Polling 状态机简图 Polling.Active 发送TS1,位锁定 Polling.Configuration 交换TS2,确认配置 Polling.Speed 速度协商变更 Polling.Compliance 合规性测试模式 退回 Detect 超时或无应答 进入 Configuration TS2交换完成,速度确认 锁定成功 Speed Change 速度变更完成 超时无应答 配置失败 速度协商失败 进入 Compliance TS2 交换完成 注意:并非所有设备都支持 Compliance 模式

3.5 调试 Polling 的实战技巧

下面这些技巧是我做项目时积累的,你收好。我不敢说百分百管用,但大概率能帮你节约半天时间。

抓序列的要点:
  • 用逻辑分析仪抓取 从 Detect.Quiet 开始到 Configuration.Linkwidth.Start 为止 的全过程
  • 重点看 TS1 的 Bit Lock 位置:如果在 2ms 内没锁住,设备就会超时退出 Polling
  • 检查 TS1 序列里的 Speed Change 位 是否正确设置(0b1 表示请求速度变更)

典型问题与排查思路

现象 可能原因 排查手段
Polling.Active 内超时退出 时钟不稳定 / 信号幅度过低 / 阻抗不连续 示波器看眼图,查时钟抖动,测 TX 摆幅
TS1 锁定,但 TS2 无法交换 去加重设置不匹配 / 链路宽度协商失败 强制 Gen1 跑,看 TS2 能否通过;检查去加重寄存器
Polling.Speed 协商失败 双方速度能力不一致 / 高速通道电气问题 读取双方 Device Capabilities 里的 Max Link Speed;差分对检查
链路进入 Polling.Compliance 接收端没检测到有效 TS1,触发 Compliance 模式 上电时序;检查 PERST# 与参考时钟的时序关系
避坑指南:我曾经遇到过一个看起来像“Link Training Fail”的 bug,其实是 TS1 序列里 Scrambling 没关。有些 Root Complex 默认开了 Scrambling,但 Endpoint 没处理好,导致符号锁定失败。解决办法是强制在 Polling.Active 阶段 Disable Scrambling(通过 Training Control 字段)。你不妨先试试强制不下扰,如果链路通了,那基本就是 Scrambling 的问题。
经验之谈:Debug Polling 阶段时,我习惯在软件侧先设置一个“强制 Gen1”的选项。如果 Gen1 没问题,但 Gen2 或更高速度有问题,那基本能断定是高速信号完整性问题,而不是 LTSSM 逻辑问题。这样可以快速缩小排查范围。

好了,Polling 就讲这么多。你记得 TS1 是锁定位,TS2 是锁配置,速度协商要走 Polling.Speed。下次你调试时,可以拿这张状态机图对照,一步一步看你的设备走到了哪里。碰到 TS1 锁不住的情况,先从时钟和幅度查起,大概率能解决。