PCIe 阶段①:物理上电 → 链路训练 → L0 就绪
按下电源键后,PCIe 总线上的第一件事是什么?不是 BIOS 扫描设备,不是内核枚举——而是硬件自动完成的链路训练。没有任何软件参与,PCIe 两端(Root Complex 的 PHY 和 Endpoint 的 PHY)在几十毫秒内完成从 Detect 到 L0 的全过程。本篇拆解这个"看不见的阶段"——链路训练的每个状态机转换、Link Width 和 Speed 的协商机制、训练失败会怎样、以及
lspci里LnkSta字段为什么能告诉你物理层问题。
零、总览:链路训练在时间线上的位置
按下电源键
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ① 硬件链路训练 (0~100ms) │
│ 纯硬件 LTSSM 自动完成 │
│ → 输出: L0 状态 + 协商好的 Width/Speed │
│ ← 本篇 │
├──────────────────────────────────────────────────────────────┤
│ ② 固件扫描 Bus 0 + 分配临时 BAR (UEFI/BIOS) │
├──────────────────────────────────────────────────────────────┤
│ ③ 内核深度优先枚举 + 建树 (pci_scan_root_bus) │
├──────────────────────────────────────────────────────────────┤
│ ④ BAR 激活 + 地址解码器生效 + 驱动 probe() │
└──────────────────────────────────────────────────────────────┘| 链路训练完成意味着 | 链路训练没做或失败意味着 |
|---|---|
| 物理层可以传 TLP | 软件连 Config TLP 都发不出去 |
| LTSSM 停在 L0 状态 | 设备在 lspci 里根本看不到 |
| 两端协商好了 Link Width & Speed | 金手指没插紧、信号质量差 |
核心结论:链路训练是所有 PCIe 通信的物理前提。没有它,就没有后面的枚举、BAR 分配、DMA——什么都不会发生。而它完全由硬件自动完成。
一、核心要点
- 链路训练是纯硬件行为:PCIe PHY 内部的 LTSSM(Link Training and Status State Machine)自动运行,软件零参与
- LTSSM 有 11 个状态,最关键的路径是:Detect → Polling → Configuration → L0
- 协商两个参数:Link Width(x1/x4/x8/x16)和 Link Speed(2.5/5/8/16/32 GT/s)
- L0 是正常工作的状态:可以传输 TLP/DLLP/Ordered Sets
- 训练失败 → Recovery 状态重试 → 降速/降宽 → 最终可能进入 Disabled(设备不可见)
二、LTSSM 状态机全景
2.1 状态机拓扑

主线路径:Detect.Quiet → Detect.Active(检测到终端电阻)→ Polling.Active(bit lock / symbol lock)→ Polling.Configuration → Configuration.Linkwidth.Start(协商 Width/Speed)→ Configuration.Lanenum → Configuration.Complete → Configuration.Idle → L0(正常工作)。这条线是每次上电/热插拔必须走通的,中间任何一步失败都会回到起始状态重试。
省电旁路:L0 是"全速工作",但空闲时不需要一直开着全部模拟电路。L0s 是单端省电(一端主动,退出 ~几十 ns),L1 是双端省电(两端协商,退出 ~几 μs)。唤醒时必须经过 Recovery 重新锁定 bit/symbol 后再回 L0。
容错回退:L0 运行中信号质量下降(误码率过高、链路不稳定)→ 进入 Recovery → 重新训练 → 若成功回到 Configuration 重新协商 → 若反复失败且超阈值 → Disabled(链路关闭,lspci 不可见)。
2.2 11 个状态逐一解释
| 状态 | 子状态 | 做什么 | 时长 |
|---|---|---|---|
| Detect | Quiet / Active | 发送端检测对端是否有终端电阻(Receiver Detection)。PCIe 规范规定每个 Lane 的接收端必须在差分对上挂一个 50Ω 终端到地。发送端给差分对充电、看放电曲线——有终端电阻说明对面插了设备 | ~12ms |
| Polling | Active / Configuration / Compliance | 两端交换 TS1/TS2 Ordered Sets(训练序列),确认链路可用、极性不反转、Lane 不交叉。Compliance 子状态用于测试设备是否符合规范 | ~几 ms |
| Configuration | Linkwidth.Start / Accept / Lanenum.Wait / Accept / Complete / Idle | 协商 Link Width 和 Lane 编号。TS1/TS2 里带了"我支持 x16"和"我希望用 x8"等信息。两端取最小值。Lane 编号也在此阶段确定(Lane 0 必须是第一个可用的 Lane) | ~几 ms |
| L0 | — | 正常工作状态。TLP/DLLP 可以正常收发。周期性发 SKP Ordered Set 做时钟补偿(两端晶振不可能完全同步) | 一直 |
| L0s | — | 低功耗状态,一端主动进入。退出延迟 ~几十 ns(比 L1 快得多) | 空闲时 |
| L1 | — | 更深省电,两端协商后进入。退出延迟 ~几 μs | 深度空闲 |
| Recovery | RcvrLock / RcvrCfg / Idle | 从 L0s/L1 退出,或链路出错后重新训练。重新做 bit lock 和 symbol lock | ~几 ms |
| Hot Reset | — | 带内复位(通过 TS1 发复位命令),不改变 Link Width/Speed | — |
| Disabled | — | 链路不可用,发送端进入低阻抗状态。设备在 lspci 里看不到 | 直到重新上电 |
| Loopback | Entry / Active / Exit | 测试模式,发送端的数据环回到接收端(不经过远端 PHY) | 测试时 |
| Compliance | — | 一致性测试模式,设备按规范发送预定义码型,用于电气合规测试 | 测试时 |
三、关键协商过程详解
3.1 Link Width 协商:为什么 x16 插槽可能跑在 x8
链路训练过程中,两端在 Configuration 阶段交换 TS1/TS2,其中携带了支持的 Lane 数量:

实际降宽的常见原因:
| 原因 | 说明 |
|---|---|
| Endpoint 只做了 x8 PHY | 很多 NVMe SSD 物理金手指是 x4,即便插在 x16 槽里也跑 x4 |
| CPU PCIe 通道不足 | CPU 总 Lane 数固定,多个设备分摊后每个拿不到完整宽度 |
| Bifurcation 配置错误 | BIOS 里把 x16 槽拆成 x8x8 或 x4x4x4x4,每段 Lane 数减半 |
| 信号质量问题 | 差分对阻抗不匹配、PCB 走线过长,训练时某些 Lane 反复失败被排除 |
| PCB 布线偷工减料 | 物理插槽是 x16,但主板上只走了 x8 的差分对(杂牌主板常见) |
# 查看实际协商结果
lspci -vvv -s 01:00.0 | grep -E "LnkSta|LnkCap"
# LnkCap: Port #0, Speed 16GT/s, Width x16 ← 能力
# LnkSta: Speed 8GT/s, Width x8 ← 实际 ← 降速降宽了!3.2 Link Speed 协商:为什么 Gen4 设备可能跑在 Gen3
Speed 协商在 Polling 和 Configuration 阶段通过 TS1/TS2 的 Data Rate Identifier 字段完成:

但如果信号质量不够:链路训练在某个 Speed 上反复失败 → Recovery → 降一档 Speed 重试 → 最终稳定在较低 Speed。
# 查看协商速度
lspci -vvv -s 01:00.0 | grep "LnkSta:"
# LnkSta: Speed 8GT/s (downgraded), Width x8 (downgraded)
# ↑ 出现了 downgraded!3.3 Lane Reversal 与 Polarity Inversion
PCIe 物理层有两个"纠错"机制,在 Polling 阶段自动处理:
| 机制 | 问题 | 硬件怎么处理 |
|---|---|---|
| Lane Reversal | PCB 走线把 Lane 0 接到了对端 Lane 3 | TS1 中带 Lane 编号,接收端识别到编号倒序后自动映射 |
| Polarity Inversion | 差分对的 P/N 接反了 | 接收端检测到 D 码字极性不对,自动翻转 |
这两个机制让 PCB 设计更自由——走线时 Lane 顺序和差分极性都可以任意,PHY 在训练阶段自动纠正。
四、链路训练的硬件参与者
物理层分两个子层:
| 子层 | 全称 | 职责 |
|---|---|---|
| PCS | Physical Coding Sublayer | 8b/10b(Gen1/2) 或 128b/130b(Gen3+) 编解码、Elastic Buffer(跨时钟域)、LTSSM 状态机 |
| PMA | Physical Medium Attachment | SerDes(并串/串并转换)、差分驱动/接收、时钟恢复(CDR) |
LTSSM 位于 PCS 层。两端各有独立 LTSSM,通过 TS1/TS2 Ordered Sets 中嵌入的控制位来同步状态跳转。
五、训练失败的排查
5.1 从 OS 侧看到的症状
| 症状 | 含义 |
|---|---|
lspci 看不到设备 | 链路训练失败,LTSSM 停在 Detect 或 Polling,连 Config TLP 都发不了 |
lspci -vvv 显示 LnkSta: Speed ... (downgraded) | 高速协商失败,降速跑了 |
dmesg 里 link training error 或 PCIe link down | 训练中断或反复 Recovery |
| 设备偶尔出现、偶尔消失 | 信号边缘化(margin 不够),温度变化导致偶尔训练失败 |
5.2 常见原因速查
| 原因 | 怎么确认 | 怎么修 |
|---|---|---|
| 金手指氧化/接触不良 | 换槽位后恢复正常 | 用橡皮擦擦金手指、重新插紧 |
| 电源不足 | GPU 等高功率设备训练失败,换大电源后正常 | 换电源或加辅助供电 |
| 信号完整性问题 | 特定主板 + 特定设备组合失败,其他组合正常 | 检查 PCB 差分阻抗、走线长度匹配 |
| BIOS PCIe 配置 | 进 BIOS 看 PCIe Speed 是否被手动设低 | 恢复 Auto/Gen4 |
| Bifurcation 错误 | x16 槽配置成了 x4x4x4x4,但插的是 x8 设备 | BIOS 里改回 x16 或 x8x8 |
| Retimer/Redriver 故障 | 长距离线缆或背板连接,中间芯片挂了 | 换线缆或中间芯片 |
| 时钟源问题 | 共用时钟(Common Clock)配置错误,两端 REFCLK 不同源 | 检查 SRIS(Separate Refclk Independent SSC)配置 |
5.3 排查命令
# 1. 看设备是否存在
lspci -t -vv
# 2. 看具体设备的链路状态
lspci -vvv -s 01:00.0 | grep -E "LnkSta|LnkCap|LnkCtl"
# 3. 看内核报的链路错误
dmesg | grep -iE "pcie.*link|pci.*train|pcie.*error|pcie.*down"
# 4. 看 ASPM(省电)状态——可能因 ASPM 切换导致 Recovery
lspci -vvv -s 01:00.0 | grep ASPM
# 5. 看所有 PCIe AER(高级错误报告)
dmesg | grep -i "AER"六、与后续阶段的衔接
链路训练完成后(L0 状态),下一个问题是:CPU 怎么知道总线上插了设备?
→ 进入 pcie-firmware-enum(阶段②:固件枚举)
七、与现有文档的关系
- pcie §二.3 — 物理层概述(本篇是该节的深度展开)
- pcie-firmware-enum — 链路训练完成后,固件怎么用 Config TLP 扫描 Bus 0
- pcie-pre-enumeration — RC/BDF/BAR 在树构建前的硬件工作原理
八、一句话总结
链路训练是 PCIe 通信的物理前提——纯硬件 LTSSM 自动运行,从 Detect(检测对端电阻)→ Polling(交换 TS1/TS2)→ Configuration(协商 Width/Speed)→ L0(就绪),几十毫秒内完成,软件零参与。训练失败或降速降宽是
lspci看不到设备或性能打折的最底层原因。