第四章:Configuration状态——链路宽度协商与 Lane 极性交换

进入 Configuration 状态之后,链路就开始干正事了。这个阶段要做的事情说白了就两件:确定到底用几根 lane 跑,以及把每根 lane 上的信号极性对齐。很多芯片第一次调通 PCIe 的时候,卡就卡在这两个环节上。

4.1 宽度协商:从 1x 到目标宽度的多轮握手

你可能以为宽度协商是一上来就谈好 ×4 还是 ×16,其实不是。PCIe 的物理层有一个很聪明的机制:从一根 lane 开始建链,然后在 Configuration 状态里逐步“扩容”。为什么这么设计?

我早年做一块 FPGA 的 PCIe 硬核时,遇到过链路始终只有 ×1 的情况。检查了半天,发现是参考时钟抖动太大导致某几根 lane 的 CDR 锁不住。硬件没坏,但宽度协商就是上不去。那个时候我才深刻理解:宽度协商本质上是一种“能跑几根就用几根”的优雅降级策略。

具体的协商流程,以 LTSSM 视角来看是这样的:

  1. Config.Linkwidth.Start:UP 端按照接收到的 TS1/TS2 中的 Lane Number 字段,决定自己当前被分配了哪些 lane。所有 lane 在此时都被认为已对齐。
  2. Config.Linkwidth.Accept:双方互相确认宽度。UP 端会持续发送 TS2 有序集,并在 TS2 的 Link Number 字段中填入协商好的链路编号。DN 端收到后回复相同内容。
  3. Config.Lanenum.Wait / Accept:再次确认每条 lane 的编号分配,防止出现 lane 错位。
  4. Config.Complete:双方都确认无误后,发送两个 TS2 并转出到 L0。

这里有一个细节:宽度协商必须满足“所有 lane 必须同时进入同一个子状态”。如果某根 lane 的电气条件不好,迟迟没有收到有效 TS,这条 lane 就会被标记为“Lane Not Present”,宽度协商就会自动降低到可用 lane 数。

我个人的经验:在调试硬件时,用逻辑分析仪抓 TS1/TS2 的 Lane Number 字段非常有用。如果看到某根 lane 一直发 0x00 而不是物理 lane 号,十有八九是这跟 lane 的极性反了或者电气阻抗匹配出了问题。

4.2 Lane 极性交换:为什么要手动翻转?

你可能会想,信号极性反转不是硬件 PCB 设计上的错误吗?为什么不直接要求 PCB 保证正确?

理想很丰满,现实很骨感。PCIe 的差分管脚定义是固定的:P 极和 N 极(正负),但在多层 PCB 上为了走线方便,经常会把一对差分线在接收端交叉。如果不做处理,接收端就会收到反相数据,无法解码。

PCIe 规范里留了一个后门:Lane Polarity Inversion。接收端可以检测到极性反了,然后在物理层内部把数据线的 D+/D- 逻辑上交换回来。这样一来,PCB 工程师就不用小心翼翼地保证每一根 lane 都不交叉了。

我见过一个项目,板子回来后链路死活无法进入 L0,复位后始终反复在 Config.Linkwidth.Start 和 Detect.Quiet 之间跳动。最后发现是因为 PCB 上有一组 lane 的差分对交叉了,而接收端芯片的极性交换逻辑默认没有开启。手动在配置寄存器里将 Polarity Inversion Enable 置位后,问题立刻解决。

4.3 极性交换的检测与协商流程

极性交换不是自动进行的,它依赖于两阶段检测:

  • 在 Recovery 和 Configuration 状态中:接收端会检查收到的 TS1/TS2 的 EIEOS(Electrical Idle Exit Ordered Set)之后的数据模式。如果发现 COM 码型(K28.5) 的极性反了(比如本该是 0xBC 却收到 0x43),就说明该 lane 极性反了。
  • Invert 动作:一旦判定极性反,接收端内部的并串转换器会自动反转输入数据的极性,然后重新做 8B/10B 解码。这个过程完全是物理层内部完成,对链路层透明。

关键点:极性交换 不是 通过握手报文来协商的。接收端检测到极性反了就自己悄悄地翻过来。发送端并不知道也不需要知道。这跟宽度协商完全是两回事儿。

下面这张图总结了 Configuration 状态下宽度协商与极性交换的并行流程:

Configuration 状态:宽度协商 & 极性交换并行流程 Config.Linkwidth.Start 宽度协商 TS2 中的 Link Number 逐 lane 确认编号 极性检测 检查 TS1/TS2 的 COM 极性 检测到反转则自动翻转 Config.Linkwidth.Accept Config.Lanenum.Wait/Accept Config.Complete → L0 极性交换在物理层内部完成

4.4 宽度协商失败时的退避策略

如果某些 lane 在 Config.Linkwidth.Start 阶段始终无法对齐,LTSSM 会怎么做?

规范规定:每个 lane 独立计时。如果一个 lane 在 Tx_TS2_Count 达到设定上限后仍然没有收到有效的 TS2 回复,该 lane 会被标记为 Lane Not Present。然后宽度协商会回退到剩余可用的 lane 数。举个例子,一块 ×16 的根端口可能因为某根 lane 的电容不良只能跑 ×8,甚至 ×4。

我曾经踩过的坑:有一款桥片在低温下出现 ×16 只能协商到 ×1 的怪现象。排查了所有电气特性都没问题,最后发现是桥片的固件里写死了一个宽度限制寄存器。硬件其实能跑 ×16,但软件在冷启动时把最大宽度设成了 ×1。所以各位排查宽度协商问题时,别忘了看看 Device Control Register 里的 Max_Read_Request_Size 以及隐式的 Max_Link_Width 字段。

4.5 极性交换的配置寄存器

虽然物理层自动检测并翻转,但软件可以通过配置寄存器强制关闭极性交换。相关寄存器在PCIe 扩展开端 Capability 的 Link Control 2 寄存器中,有一个 Polarity Inversion Enable 位。默认是 1(使能自动翻转)。我建议除非你有特殊测试需求,否则保持默认。

配置项 位置 默认值 说明
Polarity Inversion Enable Link Control 2 寄存器 bit 7 1b 1=允许物理层自动反转;0=禁止反转,必须硬件正确
Max Link Width (软件限幅) Link Capabilities / Device Control 实际硬件宽度 软件可以在初始化阶段将最大宽度设得更低,用于调试

4.6 一个小总结:宽度与极性是两个独立维度

最后,我这里想强调一个容易混淆的点:宽度协商和极性交换是并行的,互不依赖。一根 lane 可能极性反了但宽度协商正常(因为接收端自动反转了),也可能宽度协商失败但极性正常(电气环境太差)。

调试时,我习惯先抓 TS1/TS2 看 Lane Number 是否连续递增,再用示波器看每对差分信号的摆幅和极性。两者结合起来,基本五分钟就能定位到根因。

我的调试清单:

  • 检查 Link Control 2 寄存器的极性翻转开启。
  • 用协议分析仪抓 Config.Linkwidth.Start 阶段每个 lane 发送的 TS2 中的 Lane Number。
  • 测量所有 lane 的差分信号到接收焊盘,确认没有反接。
  • 查看链路状态寄存器中的 Negotiated Link Width 和 Link Speed字段,确认协商结果。

嗯,宽度协商和极性交换说到这里,你应该已经对 Configuration 状态的核心动作有了一个比较立体的认识。下一环节,我们会深入 Equalization 协商——那是速率从 Gen1 往 Gen3/Gen4 爬升时必须经历的一场“信号质量谈判”。