pcie


第一部分:概述与物理层

引言

这是四篇系列文章中的第一篇,提供 PCI Express (PCIe) 协议的概览。PCIe 本身内容庞大,因此我把它拆分为几份更容易消化的文档。即使如此,本系列也只是简要介绍。目标读者是希望进一步理解 PCIe 的人,例如在系统中使用该协议的工程师,或者在深入研究规范之前需要一个入门的人。

在本篇中,将介绍 PCIe 架构,并重点讨论协议三层中的第一层——物理层。事务层和数据链路层的细节会留在后续部分。

为了配合本文,我编写了一个用于 Verilog 的 PCIe 行为级仿真模型(pcievhost)。它运行一个 C 语言实现的 PCIe 协议模型,通过虚拟处理器 (VProc) 接口与 Verilog 环境交互。模型文档说明了如何搭建环境,以及如何使用 API 生成 PCIe 流量。本文中我会引用该模型的输出作为示例。模型还附带了一个测试环境,可以生成大部分常见流量,读者可自行修改驱动代码来探索 PCIe 协议。

起源

当 PCIe 协议最初发布(1.0a 版)时,我们所设计的高性能计算系统决定从 PCI 升级到 PCIe,以获得更高的带宽潜力。虽然当时有一些第三方 IP,但经评估后发现它们无法满足我们低延迟等设计需求。于是,我接到任务,设计一个符合 PCIe 规范的 16 通道端点接口,并满足其他要求。

拿到 500 多页的规范时,最初感觉压力巨大。后来在伦敦的一次 PCIe 会议上,我问一位已经实现过 PCIe 接口的团队负责人,她说她的团队用了 4 名工程师一年时间,外加 20 名验证方面的外包人员。而我只有 18 个月时间,并且是一个人完成。最终,通过分解规范、舍弃一些可选功能,我逐步把复杂问题拆解成可管理的部分,最后成功实现了符合 1.1 规范的 16 通道端点,以及符合 2.0 规范的 8 通道端点。

在这份文档中,我会以类似的可控步骤,介绍当时实现过程中学到的概念。

PCIe 概览

与前身 PCI 不同,PCIe 不是总线架构,而是点对点协议(类似 AXI)。PCIe 系统的结构由多个点对点接口组成,这些接口通过交换结构(fabric)连接 CPU 和外设。

拓扑层次具有明确的方向:CPU 连接到根复合体(Root Complex,RC),它是顶层互连组件。RC 可通过开关 (Switch) 扩展更多 PCIe 接口。开关之间还能互联,从而进一步扩展。最终,外设(端点,Endpoint, EP)通过接口连接,比如显卡、网卡等。

在每条链路上,有“下行链路”(从上游组件如 RC 到 EP/交换机)和“上行链路”(从 EP/交换机到 RC)。PCIe 协议在链路上定义了三层:


物理层

通道(Lanes)

PCIe 通过串行通道传输数据,每个通道是一对 AC 耦合的差分线。支持的通道宽度有 ×1、×2、×4、×8、×12、×16 和 ×32。通道越多,带宽越大。

例如显卡通常使用 ×16,而简单串行外设可能只需 ×1。两端接口不必宽度相同,链路初始化时会协商出双方都支持的最大宽度。

扰码(Scrambling)

PCIe 的基本数据单位是字节。在序列化之前,字节会先经过扰码处理(基于 LFSR)。这样可以避免长时间的直流分量,提升信号质量。不同代际使用的多项式不同(如 2.0 使用 16 阶,3.0 使用 23 阶)。

串行编码(Serial Encoding)

PCIe 使用编码方式将字节映射为 DC 平衡的符号:

这些编码保证:

  1. 消除直流分量

  2. 提供时钟恢复能力

  3. 区分控制符号和数据符号

有序集(Ordered Sets)

在链路训练和初始化阶段,会发送有序集(Ordered Set),例如:

链路初始化与训练(LTSSM)

链路的状态由 LTSSM(链路训练与状态机)管理。主要阶段包括:

此外还有低功耗状态(L0s、L1、L2)、Loopback、Disabled 等状态。

PIPE 接口

PCIe 的 SERDES(串并转换器)高速实现依赖不同工艺。Intel 提出的 PIPE 规范 把物理层划分为 MAC、PCS、PMA 三部分,并标准化了 MAC 与 PCS 的接口。这大大简化了逻辑设计与验证,使其更容易移植到不同的 SERDES 实现。


小结

在本部分中,我们介绍了:

物理层的核心在于:建立稳定链路,使其能承载上层(数据链路层和事务层)的数据包。


第二部分:数据链路层

引言

在第一部分中,我们介绍了 PCIe 架构,包括根复合体 (RC)、交换机 (Switch) 和端点 (EP),并深入讲解了最底层——物理层。物理层定义了数据传输的串行通道及其电气要求,高速数据传输需要编码与扰码,链路初始化通过有序集和状态机来实现,并且有 PIPE 规范虚拟化 SERDES 接口。

现在链路已经准备好传输字节,接下来我们进入 数据链路层 (DLL, Data Link Layer)。数据链路层位于物理层之上,主要负责:


数据链路层基本概念

三类事务数据包

在讨论数据链路层之前,先复习一下事务层的数据包分类,DLL 必须知道这些:

数据链路层不仅要承载事务层数据包 (TLP),还有自己的一类小包,称为 数据链路层包 (DLLP)


DLLP

结构

DLLP 固定长度为 6 字节

四类 DLLP

  1. 流控包 (Flow Control)

  2. 确认包 (ACK/NAK)

  3. 电源管理包 (PM)

  4. 厂商自定义包 (Vendor Specific)


物理层回顾

DLLP/TLP 的传输同样依赖物理层:

在 PCIe 3.0+ 的 128b/130b 编码中,两个控制位指示这是数据帧还是有序集,数据帧内部再用符号表示 SDP/STP/IDL/EDB 等标记。


虚拟通道 (Virtual Channels, VC)

一条 PCIe 链路在同一时刻只能传输一个完整包,不能中断。但在跨越多个交换机的路径中,如果只依赖单一通道,低优先级数据可能阻塞高优先级数据。

解决方案是 虚拟通道 (VC0–VC7)

这样,在交换机等节点上,可以实现不同优先级流量的复用。


信用机制 (Flow Control & Credits)

PCIe 使用基于 credit 的流控:

例如一次内存写需要:

这样可以避免小包被大包阻塞,也允许先处理包头。


流控初始化与更新

初始化 (InitFC)

当链路进入 L0 状态后,发送端并不知道接收端有多少可用 buffer。于是 DLL 需先初始化:

流程:

  1. 发送方周期性发 InitFC1 (P→NP→Cpl)

  2. 接收方收到后,记录数值并开始发 InitFC2

  3. 双方完成 InitFC 交换 → 链路进入 DL_LinkUp

更新 (UpdateFC)

后续流控通过 UpdateFC DLLP

定时要求:必须周期性发更新包(默认 30µs,一般不超过 120µs),防止 DLLP 丢包导致死锁。


数据传输与重传

TLP 在 DLL 封装时:

接收端:

发送端维护一个 重传缓存 (Retry Buffer)

序列号范围 0–4095,最多允许 2048 个未确认包,以确保回绕不混淆。


电源管理支持 (PM DLLP)

电源管理主要由更高层驱动,但 DLL 提供 DLLP 形式的支持:


厂商自定义 DLLP


小结

本部分介绍了 PCIe 数据链路层

数据链路层夹在物理层与事务层之间,不可避免要涉及上下层的内容,例如虚拟通道和配置空间。但它的核心任务就是:保证可靠传输 + 提供流控

下一部分将进入 事务层 (Transaction Layer),讲解如何在链路上传输内存/IO/配置访问,以及消息(电源管理、错误报告、中断等)。


第三部分:事务层

引言

在前两部分中,我们介绍了 PCIe 的物理层和数据链路层。到目前为止,我们已经有了一个可以传输数据的物理通道,并通过 CRC 与重传机制保证了数据的可靠性,同时利用基于 credit 的流控管理了数据流量。

现在,我们终于可以进入 事务层 (Transaction Layer, TL),在链路上传输实际的事务数据包 (TLP)。事务层定义了三类数据传输事务:读、写和完成 (Completion),以及一类用于通信和状态信号的 消息 (Message)。相比物理层和数据链路层,事务层包含了更多详细规则,但整体概念并不复杂。

本部分将逐一介绍不同的事务层数据包类型 (TLP),分析它们的结构与用途,帮助形成对 PCIe 工作机制的整体认识。


事务层数据包 (TLP, Transaction Layer Packets)

正如第二部分提到的,事务被分为三大类:

事务层定义了多种 TLP 类型,每个 TLP 属于上述三类之一:

TLP 类型分类
Memory Read (MRd)NP
Memory Write (MWr)P
I/O Read (IORd)NP
I/O Write (IOWr)NP
Config Read Type 0/1 (CfgRd0/1)NP
Config Write Type 0/1 (CfgWr0/1)NP
MessageP
Completion (Cpl, CplD, CplLk)Cpl

其中:

TLP 一般结构

一个 TLP 包含:

  1. Header(3DW 或 4DW)

  2. 可选数据负载(最多 4096 字节,端点可在配置空间中声明更小)

  3. 可选 CRC (TLP Digest, ECRC)


TLP 头部 (Header)

第一 DW 的通用格式

事务层所有 TLP 的头部第一字包含公共字段:

Type 字段值

不同 TLP 的 Fmt+Type 编码如下:

TLPFmt/Type说明
MRd / MRdLk00/01 00000 / 00001内存读(32/64位地址,可锁定)
MWr10/11 00000内存写(32/64位地址)
IORd00 00010IO 读
IOWr10 00010IO 写
CfgRd0/100 00100 / 00101配置读 Type 0/1
CfgWr0/110 00100 / 00101配置写 Type 0/1
Msg/MsgD01/11 10rrr消息请求(可带数据)
Cpl/CplD00/10 01010Completion (无数据/有数据)
CplLk/CplDLk00/10 01011对锁定读的 Completion

内存访问 (Memory Accesses)

特点


Completion (完成包)

Completion 是对 NP 请求(如 MRd、IORd、CfgRd)的响应:

Completion 头部包含:


I/O 访问 (I/O Accesses)

为兼容 PCI 传统 I/O 地址空间而保留:


配置空间访问 (Configuration Space)

配置空间是 PCIe 设备的寄存器空间,用于:

访问方式:


消息 (Messages)

消息是一类特殊的 TLP,用于系统内的管理与信号传递:

消息包可以带数据 (MsgD) 或不带数据 (Msg)。


小结

事务层定义了 PCIe 的核心通信模型

事务层与上层软件/配置空间紧密相关,而其传输则依赖下层数据链路层的可靠传输保障。