TS1 TS2序列解析与链路协商判定 - 第1节

一、PCIe 是什么?从并行到串行

先聊聊 PCIe 的出身。PCIe 全称是 Peripheral Component Interconnect Express,也就是“快速外设互连”。它取代了早期的 PCI 总线,解决了并行总线在高速下的瓶颈问题。

我个人习惯把 PCIe 理解为一条点对点的高速串行链路。你想想看,PCI 是并行总线,所有设备共享同一组数据线,频率一旦提高,信号同步就成问题。PCIe 改成串行,用差分对传数据,每条链路独立工作——这想法很聪明。

我在项目中遇到过一块老主板,PCI 插槽上插了张 1GbE 网卡,跑满时 CPU 占用飙升。换成 PCIe 网卡后,CPU 占用直接掉到 5% 以下。为什么?因为 PCIe 是点对点,不占用共享总线,背后有专门的交换架构来处理路由。

1.1 PCIe 架构速览

PCIe 系统由三部分组成:根复合体(Root Complex,RC)、交换器(Switch)和端点(Endpoint,EP)。

  • 根复合体(RC):连接 CPU 和内存,是链路的最顶层。CPU 发起的配置、内存读写都得经过它。
  • 交换器(Switch):相当于 PCIe 的中枢,内部有一个虚拟 PCI 桥,把上游端口的数据转发到下游端口。多个端点可以挂在同一 Switch 下。
  • 端点(EP):实际的功能设备,比如显卡、NVMe SSD、网卡。它不能发起配置周期,只能响应来自 RC 的配置请求。
PCIe 系统架构 根复合体 (RC) 交换器 (Switch) 端点 (EP) #1 端点 (EP) #2 说明:PCIe 是树形拓扑,RC 为根,Switch 做中间转发,EP 为叶子节点

另外,PCIe 的拓扑图里还有一个隐藏角色叫 PCIe 桥(Bridge),一般集成在芯片组的内部,用来向上挂接 RC,向下连接多个 Switch 或 EP。你不太会直接看到它,但它负责做事务层包的转发。

1.2 链路、通道与速率

一条 PCIe 链路(Link)由多条通道(Lane)组成。每条 Lane 包含两对差分信号:一对发送、一对接收。x1、x4、x8、x16 这些称呼就是指 Lane 的宽度。

核心点:链路建立前,两端必须通过训练序列(Training Sequences)协商出共同的工作参数,包括 Lane 宽度、速率、极性等。

版本编码方式单 Lane 数据速率x16 总带宽
PCIe 1.08b/10b2.5 GT/s~4 GB/s
PCIe 2.08b/10b5 GT/s~8 GB/s
PCIe 3.0128b/130b8 GT/s~15.75 GB/s
PCIe 4.0128b/130b16 GT/s~31.5 GB/s

注意!GT/s 是每秒传输的 Giga-Transfers,不等于真实带宽。8b/10b 编码会有 20% 开销,128b/130b 只有约 1.5% 开销。PCIe 3.0 之后效率提升很大。

二、链路初始化:从电气闲逛到正常数据流

链路初始化听着挺玄乎,说白了就是设备上下电、时钟稳定、然后两端相互确认的过程。

我记得我第一次看 PCIe Spec 里那个训练状态机(LTSSM)的状态图——密密麻麻,差点劝退。但后来发现,核心逻辑就那么几步:

  1. 检测(Detect):物理层发送电气空闲退出信号(Electrical Idle Exit),判断对端是否存在。
  2. 轮询(Polling):配置基础的速度和 Lane 宽度,发送训练序列 TS1/TS2。
  3. 配置(Configuration):确认 Lane 到 Lane 的映射,协商极性、去抖动等细节。
  4. 恢复(Recovery):如果速度和 Lane 宽度需要调整,这里再协商。
  5. L0(正常操作):数据传输阶段。还可以进入 L0s/L1/L2 省电状态。

嗯,这里要注意:Polling 阶段是 TS1/TS2 序列第一次登场的地方。在 Polling.Active 状态下,两端连续发送 TS1 序列,携带链路编号、Lane 编号、速率等信息。我以前调试一块 FPGA 板卡时,发现 TS1 始终不对,最终定位到是时钟抖动太大导致物理层无法锁定——修了电源再试就过了。

一个小技巧:如果链路起不来,先拿示波器看差分信号的摆幅和共模电压。PCIe 3.0 的规范要求摆幅在 800–1200 mV 之间,太弱或太强都可能让 TS1 无法被正确识别。

2.1 训练状态机(LTSSM)的骨架

LTSSM,全称 Link Training and Status State Machine。PCIe 设备内部有一个硬件状态机,独立运行在物理层。它负责控制链路的训练、重训练和电源管理。

状态子状态举例作用
DetectDetect.Quiet, Detect.Active检测对端是否在线
PollingPolling.Active, Polling.Compliance, Polling.Configuration发送 TS1,交换基本参数
ConfigurationConfiguration.LaneWidth, Configuration.Complete确定 Lane 到 Lane 的映射
RecoveryRecovery.RcvrLock, Recovery.Equalization, Recovery.Speed速度/宽度变更时的重新训练
L0—正常数据传输
L0s/L1/L2—省电模式

我们这门课重点关注 Polling 和 Configuration 这两个阶段里的 TS1/TS2 序列内容。因为链路能不能协商成功,全看这两个阶段有没有正确识别出对端发来的序列。

2.2 链路初始化的关键指标

我从实际项目中总结出三个最常见的链路问题:

  • Lane 宽度不匹配:两端支持的宽度不同,比如一端是 x8,另一端是 x4,最终会协商为较小的宽度。
  • 极性翻转:差分信号的 P/N 接反了。PCIe 允许通过训练序列中的极性翻转位来修正。
  • 去抖动未通过:去抖动(De-Skew)阶段需要每个 Lane 调整自身相位,如果某个 Lane 的延迟超出范围,会导致链路断开。

我曾经遇到一个坑:在 Recovery 阶段,由于一端的参考时钟频率偏差超过 ±300 ppm(百万分之一),链路无法进入 L0。查了半天才想起来 PCIe 3.0 要求时钟精度必须控制在 ±300 ppm 以内,如果用的是分离参考时钟,这个值还可能更严格。

三、小结