PCIe基础回顾:从拓扑到物理层的完整链路

各位同学,今天我们聊聊PCIe的基础。说实话,很多做SSD固件的工程师,对PCIe的理解往往停留在「能跑就行」的层面。但如果你真想深入NVMe协议,PCIe的这几个层次——拓扑结构、事务层、数据链路层、物理层——必须吃透。

我个人习惯把PCIe比作一个快递系统。你想想看,CPU发一个读命令给SSD,就像寄一个包裹。这个包裹怎么封装、怎么运输、怎么确认送达,就是PCIe各层干的事。

一、PCIe拓扑结构:点对点的连接方式

PCIe和传统的PCI总线最大的区别是什么?

传统PCI是共享总线架构。所有设备挂在一根总线上,谁想通信得先抢总线使用权。这就像一条单车道,所有车挤在一起,效率可想而知。

PCIe不一样。它是点对点的拓扑结构。每个设备都有自己的专用通道,直接和Root Complex(根复合体)或Switch相连。

核心概念:

  • Root Complex (RC):系统的老大,连接CPU、内存和PCIe总线。我习惯叫它「总调度中心」。
  • Endpoint (EP):终端设备,比如你的NVMe SSD、显卡、网卡。
  • Switch:相当于一个PCIe交换机,把一条上游链路扩展成多条下游链路。注意,Switch不是路由器,它只是透明转发。
  • Bridge:用于连接PCIe和传统PCI总线,现在用得少了。

我在项目中遇到过一个问题:一个NVMe SSD插在Switch下游,性能始终上不去。后来发现是Switch的端口配置不对,导致链路宽度协商成了x2而不是x4。嗯,这种坑踩过一次就记住了。

下面这张图是我画的PCIe拓扑结构示意,帮你快速建立整体认知:

CPU 内存 DMI/直连 Root Complex (RC) 上游端口 Switch 下游端口 Endpoint (SSD) 下游端口 Endpoint (GPU) 下游端口 Endpoint (网卡) 图1:PCIe拓扑结构示意图(点对点连接,每个Endpoint有独立通道)

二、事务层(Transaction Layer):TLP的封装与解析

事务层是PCIe协议栈里最「上层」的一层。说白了,它负责把CPU或设备要传输的数据,打包成事务层包(TLP)

TLP长什么样?我直接给你看格式:

TLP格式(简化版):
+--------+--------+--------+--------+--------+--------+
| 前缀   | 头部   | 数据   | ECRC   |        |        |
| (可选) | (12B/16B) | (0-4KB) | (可选) |        |        |
+--------+--------+--------+--------+--------+--------+

头部里最关键的是什么呢?

  • Fmt/Type:告诉接收方这是个什么类型的包。是读请求?写请求?还是完成包?
  • Requester ID:谁发的这个包。Bus:Device:Function,简称BDF。
  • Tag:事务标签。我发一个读请求,SSD回一个完成包,Tag必须对上。否则乱套了。
  • Address/Length:要读写的地址和数据长度。

避坑指南:我曾经在调试NVMe驱动时,发现读命令总是超时。抓了PCIe分析仪一看,原来是Tag管理出了问题——驱动把同一个Tag发了两遍,SSD那边直接懵了。记住:Tag是事务的唯一标识,绝对不能重复使用,直到收到对应的完成包。

事务层还负责流量控制(Flow Control)。每个接收端会告诉发送端:「我的缓冲区还能收多少个TLP」。发送端收到这个信息后,才会继续发。这就像你给朋友寄快递,先问一句「你家里还有地方放吗?」

三、数据链路层(Data Link Layer):DLLP与可靠传输

数据链路层夹在事务层和物理层之间。它的核心任务就一个:保证TLP能可靠地从A点传到B点

怎么保证?两个手段:

  1. ACK/NAK协议:发送端发一个TLP,接收端必须回一个DLLP(数据链路层包)说「收到了」或者「没收到,重发」。这就像你寄了快递,对方签收了给你回个短信。
  2. CRC校验:每个TLP在数据链路层会被加上一个LCRC(链路层CRC)。接收端收到后重新算一遍,对不上就NAK。

数据链路层还维护一个序列号。每个TLP都有一个递增的序列号,接收端靠这个检测有没有丢包。我见过一个案例:某款SSD在高温下偶尔丢包,就是数据链路层的序列号处理逻辑有bug,导致重传机制失效。

注意:数据链路层的重传是硬件自动完成的,软件完全感知不到。这也是PCIe能做到「可靠传输」的原因。但如果你在固件里发现某个DMA传输偶尔数据错乱,别急着怀疑数据链路层——先查查你的DMA描述符是不是写错了。

四、物理层(Physical Layer):电气特性与链路训练

物理层是最底层,负责真正的比特传输。它又分成两个子层:

  • 电气子层:处理电压、阻抗、时钟恢复这些硬件细节。
  • 逻辑子层:处理8b/10b编码(Gen1/Gen2)或128b/130b编码(Gen3+)、加扰、去扰。

物理层有一个非常重要的过程叫链路训练(Link Training)。每次上电或复位后,PCIe设备之间会通过物理层的LTSSM(链路训练状态机)协商链路参数:

LTSSM状态 说明 我踩过的坑
Detect 检测对端是否存在 ——
Polling 交换训练序列,协商位宽和速率 ——
Configuration 分配链路编号,完成链路初始化 曾经遇到Switch配置错误,导致链路卡在Configuration状态
L0 正常工作状态,可以收发TLP ——
L0s/L1 低功耗状态,省电用的 SSD在L1状态下唤醒延迟过大,导致IO超时

物理层的速率演进也很重要。从Gen1的2.5GT/s到Gen5的32GT/s,每一代速率翻倍。但注意,速率越高,信号完整性越难做。我调试Gen4 SSD时,PCB走线稍微长一点,链路就训练不到Gen4速率,只能降级到Gen3。

核心总结:

  • 事务层:打包TLP,定义事务类型(读、写、完成)。
  • 数据链路层:加序列号、加CRC、ACK/NAK重传,保证可靠。
  • 物理层:比特传输、链路训练、速率协商。

这三层各司其职,共同构成了PCIe的完整通信链路。你写NVMe驱动时,大部分时间打交道的是事务层(构造TLP),但出了问题往往要往下层查。

好了,PCIe基础就回顾到这里。这些内容虽然基础,但非常重要。后面讲NVMe队列和命令提交时,你会反复用到今天讲的概念。


公众号:蓝海资料掘金营,微信deep3321