七、PCIE 总线
7.1 PCIE 概述
高速串行计算机扩展总线标准,是用来互连诸如计算和通信平台应用中外围设备的第三代高性能 I/O 总线,是由英特尔在 2001 年提出的,当时被称为“3GIO”,旨在替代前两代总线标准。 第一代总线包括 ISA、 EISA、 VESA 和微通道( MicroChannel)总线,而第二代总线则包括了 PCI、AGP 和 PCI-X。 PCIe 总线的提出是为了解决当时人民日益增长的美好生活需要和传输慢、带宽低、提升难的总线标准之间的矛盾 。PCIe 总线提出后,经 PCI-SIG 审核,于 2002 年 7 月正式公布了第一版规范,并更名为 PCI Express。从 2004 年开始,PCI Express 总线逐渐全面取代 PCI 和 AGP 总线,成为新的局部总线工业标准。 PCI Express 总线继承了第二代总线体系结构最有用的特点,并且采用了计算机体系结构中新的开发成果。例如, PCI Express 采用了与 PCI 和 PCI-X 相同的使用模型和读一写 ( load-store)通信模型。不过与 PCI 总线的共享并行架构不同, PCI Express 总线使用高速串行传送方式,能够支持更高的频率,连接的设备不再像 PCI 总线那样共享总线带宽。除此之外 PCI Express 总线还引入了一些新特性,如流量控制机制、服务质量管理(QoS)、热插拔支持、数据完整性和新型错误处理机制等。而且 PCI Express 总线在系统软件级与 PCI 总线保持兼容,总线系统可以通过 Switch 连接多个 PCIe 设备,也可以通过 PCIe 桥连接传统的 PCI 和 PCI-X 设备,最大程度上降低了系统软件从原有的 PCI 总线体系结构移植到 PCI Express 总线体系结构的难度。
简单的 PCIe 总线系统的拓扑结构图

端到端的连接方式,在一条 PCIe 链路的两端只能各连接一个设备,这两个设备互为是数据发送端和数据接收端。PCIe 链路可以由多条 Lane 组成,目前 PCIe 链路× 1 、× 2 、× 4 、× 8 、× 16 和× 32 宽度的 PCIe 链路,还有几乎不使用的× 12 链路。
PCIE 版本速度

7.2 PCIE 接口
PCIE 接口分为公口与母口两种,公口与显卡接口类似,是金手指类型的,母口则是电 脑主板上的接口,FPGA 板卡采用的是公口,插到电脑主板上的母口上实现相连接。PCIE 根据不同的通道划分为 X1,X2,X4,X8,X16,X32,一般常见的接口是 X1 与 X4, X8,X16 这 4 种
PCIE 母座插槽

PCIE X4 公口

对于公口而言,主要由 5 对差分线与复位线构成,2 对发送数据,2 对接收数据,1 对时钟,一个复位,构成 PCIE X2 通道。
7.3 PCIE 总线层次结构
PCIe 总线采用了串行连接方式,数据报文在接收和发送过程中,需要通过多个层次, 包括事务层、数据链路层、和物理层。PCIe 总线层次结构如下图所示

PCIE 总线结构与以太网的 OSI 模型类似,几乎所有大标准协议都会采用分层协议模型,对于核心层我们是不关心的,这是由应用程序与驱动程序组成。物理层与数据链路层可以利用 FPGA 的 IP 核实现,我们主要关心事务层的实现逻辑即可,关于事务层的实现逻辑,有许多参考,比如说 XILINX 的 XDMA,XAPP1052,这都很好的包含了事务层结构,还有一些第三方开源库 riffa 框架。许多框架都可以很快速的实现 PCIE 的搭建,后续 也会针对 riffa 框架与 XDMA 框架进行讲解,riffa 框架可以很好的分析事务层结构,xilinx 与 altera 都有相关例子,驱动程序也提供 c,java,python,matlab,等多种研发语言,而 XDMA 是简单易开发,快速上手,各自有各自好处,所以后续两种都会讲解。
7.3.1 事务层
服务质量管理QoS(Quality of Service)和流量控制( Flow Control)以及事务排序(Transaction Ordering) 等功能。

7.3.2 数据链路层
数据链路层接收来自事务层的数据报文,添加 Sequence Number 前缀和 CRC 后缀。数据链路层还使用 ACK/NAK 协议保证报文的可靠传递。另外它还定义了多种 DLLP(Data Link Layer Pakcet),DLLP 产生于数据链路层结束于数据链路层。DLLP 于 TLP 并不相同,DLLP 不是 TLP 加上 Sequence Number 和 CRC 后缀组成的。
数据链路层数据包

7.3.3 物理层
物理层是 PCIe 总线的最底层,将 PCIe 设备相互连接在一起。它负责接收和转发各种数据包(TLP,DLLP)。实现 8b/10b 编码和解码(Gen1 & Gen2),128b/130b 的编码和解码(Gen3)机制。物理层的另一个重要的点是它还创建和解码一些专门的序列 OrderedSet Packet 或者叫做 PLP(Physical Layer Packet),这些序列只能称为命令集而不是包,因为这些命令集始于发送端的物理层,结束语接收端的物理层,用于同步和管理链路。物理层还实现了链路训练和初始化的功能,它通过 LTSSM 来完成(Link Training and Status State Machine)

整个过程实际上和以太网的过程很相似,都是在不同层级上进行数据的扩展。在 FPGA 的开发过程中,实际上从事务层到物理层都是封装好了的,组成标准 IP 核。用户通 过 IP 核要求的总线协议(如 AXI4-Stream)与之进行数据交换。
总线协议结构

7.4 PCIE 配置空间
每个 PCIE 设备都有自己的独立的一段配置空间,该部分空间是这个设备的(可能是一段 eeprom),系统会给这个设备分配一段内存空间,CPU 访问这段内存空间即访问对此设备的配置空间。设备在出厂时,配置空间是有默认初始值的。比如显卡的 BIOS 就是类似,不过对于 FPGA 这类来说,这个空间的数据是固化到 FPGA 的 BRAM 中,外部无法进行修改。
7.4.1 配置空间作用
配置空间主要是完成 PCIE 的枚举,对 PCIE 的一些配置与功能描述,最重要的是完成电脑地址空间与 PCIE 地址空间的映射关系。PCIE 是兼容 PCI 的,两者都有配置空间, PCIE 设备分为桥(Bridge)与端点(Agent),空间类型分别是 Type 01H 与 Type 00H,这个类型是 TLP 事务层协议的一部分。桥是 PCIE 桥,其中电脑的南桥就有 PCIE 桥,将 CPU 的 PCIE 扩展成多个 PCIE。端点(Agent)是类似显卡,NVME 固态,FPGA 板卡也是此类。
配置空间

PCIE 驱动就根据这两个进行加载驱动,完成 PCIE 的枚举过程,类 ID 主要描述了 PCIE 设备的功能,比如显卡是显示类,NVME 固态是大容量存储设备。基地址起电脑地址与 PCIE 地址进行映射的功能。
不可操的低比特决定了当前 BAR 支持的操作类型和可申请地址空间大小。 而软件对 BAR 的检测与操作(Evaluating)必须是顺序执行的,即先 BAR0, 然后BAR1,……,直到 BAR5。当软件检测到那些被硬件设置为全 0 的 BAR,则认为这个 BAR 没有被使用。值得一提的是 PCIe 没有规定第一个使用的 BAR 必须 是 BAR0,也就是我们完全可以把 BAR3 作为第一个 BAR,并将 BAR0~2 设置为不使用。
下面以申请一个 64MB P-MMIO(Memory Mapped IO)地址空间为例子,讲解申请地址空间的过程。这里我们采用 64bit 地址线,因此需要用到两个 BAR。

1. 未初始化的 BAR1 的低比特(25~4)都是 0,BAR1 高比特 (31~26)、BAR2(31~0)都是不确定的值。所谓初始化,就是系统(软件)向整个 BAR 都写 1,来确定 BAR 的可操作的最低位是哪一位。当前可操作的最低位为 26, 因此当前 BAR 可申请的(最小)地址空间大小为 64MB(2^26)。
2. 完成初始化(写1操作)之后,软件便开始读取BAR的值,来确定每一个BAR 对应的地址空间大小和类型。其中操作的类型一般由最低四位所决定,具体如 图 51-18右侧部分所示。
3. 最后一步是,软件向 BAR 的高比特写入地址空间的起始地址(Start Address)。如图红色线部分所示,为 0x0000_0002_4000_0000。注意起始地址由可操作部分的值左移 n 位得到,n 位不可操作部分位数(这里为 26),这也就是为什么说不可操作部分的位数决定了地址空间大小,而可操作部分的位数其实是决定了可以申请多少个相同的地址空间大小(这里的例子是 64MB)的数额。
7.5 事务层 TLP 包协议详解
处理层(transaction Layer specification)是请求和响应信息形成的基础。包括四种地址空间,三种处理类型,从下图可以看出在 transaction Layer 中形成的包的基本概括。

一类是对 i/o 口和 memory 的读写包(TLPS:transaction Layers packages),另一类是对配置寄存器的读写设置包,还有一类是信息包,描述通信状态,作为事件的信号告知用户。
对 memory 的读写包分为读请求包和响应包、写请求包(不需要存储器的响应包)。 而 i/o 类型的读写请求都需要返回 I/O 口的响应包,configuration 包对配置寄存器的读写请求也有响应包。这些请求包还可以按属性来分就是:NP-nonposted ,即请求需要返回 completion 的响应包;还有一种就是;poste,即不需要 completion 返回响应包。 每种类型的包都有一定格式的包头(Tlp Header),根据不同的包的特性,还包括有效数据负荷(Data Payload)和 TLP 开销块(Tlp Digest)。包头中的数据用于对包的管理和控制。有效数据负荷域存放有效数据信息。具有数据的 TLP 传递是有一定规则的:以 DW 为长度单位,发送端数据承载量不得超过“ Device Control Register ”中的 “Max_Payload_Size”数值,接收端中,所接收到的数据量也不能超过接收端“Device Control Register”中的“Max_Payload_Size”数值。

TLP 包前缀(TLP Prefixes):
在 PCIe V2.1 总规范引入,主要起扩展帧头的作用。如果用不到,可以省去该字段。 对于 7 系列的 FPGA 来说,采用的是 PCIE 2.0 总线规范,因此此段落将没有。TLP 数据包头部就是从头部(TLP Header)开始。
TLP 包数据有效负载(Data Payload):
主设备要传输的数据。数据的长度最小为 0,最大为 1024DW,视具体情况而定。 该字段也是一个可选项,因为有些 TLP 并不需要传递数据,如存储器读请求、配置和 I/O 写完成 TLP 也不需要。
TLP 包摘要(TLP Dignest):
摘要是一个可选项,长度为 1DW。一个 TLP 是否需要 Dignes 是由 Header 中 TD 字段决定。如果接受设备支持 ECRC 校验的功能的话,则该字段用来防止 TLP 中的数据校验码 ECRC。
TLP 包帧头(TLP Header):
TLP Header 是 TLP 中最重要的标志,不同的 TLP 包头的定义并不相同。TLP 头标长 3 或者 4 个 DW(DW = double word——双字,32 位),格式和内容随事物类型变化,但是对于所有 TLP Header 来讲,都拥有相同的第一个 DW 定义,就是 TLP 包帧头(TLP Header)

R 为保留信息位,应设为 0,路由器 switch 对此位不做修改,接收器应该忽略此位。
Fmt[1:0]:Format of TLP – bits 6:5 of byte0。TLP 包的格式。
Type[4:0]:Type of TLP – bits 4:0 of byte 0。TLP 包的类型。
TC[2:0]: Traffic Class – bits [6:4] of byte1。TLP 包的类。
Length[9:0]:Length of data payload in DW。TLP 包数据负载长度。
1. Fmt([31:29])
Fmt 即是 Format of TLP,Fmt 是关于头标长度和该 TLP 是否有数据(字段)的信息, 如下图所示。实际上是 3 DW 还是 4 DW 是根据要访问目标的地址位宽有关。

Fmt 的最高位保持为 0,如果为 1,数据是无效的。后两位说明是 3DW,还是 4DW 包。DW 是 32 位的数据宽度,即双字(Double Word)。当 Fmt 为 3’b000 时,代表 TLP 头 部是 3 个 DW 的数据段,并且不带数据;当 Fmt 为 3’b001 时,代表 TLP 头部是 4 个 DW 的数据段,并且不带数据;当 Fmt 为 3’b010 时,代表 TLP 头部是 3 个 DW 的数据段,并且带数据;当 Fmt 为 3’b011 时,代表 TLP 头部是 4 个 DW 的数据段,并且带数据。
2. Type([28:24])
Type 的 5 位编码与 Fmt 字段一起用于规定事务类型、头标长度和是否有数据负载,如下图所示,只列举了一部分常用的类型,主要是关于存储器读写,IO 读写,配置读写事务类型,完整版可以查阅官方协议规范。

3. TC([22:20])
Traffic Class,传输类型也代表优先级,优先级高的先得到服务。这里是 3 比特,说明 可以分为 8 个等级,0-7,TC 默认是 0,数字越大,优先级越高。
4. Attr([18]、[13:12])
该字段表述 TLP 的属性,由 3 位组成,注意不是连续的。具体含义见规范。
5. TH([16])
此位为 1 表示当前 TLP 中含有 TPH(TLP Processing Hint)信息,TPH 是 PCIe V2.1 总线规范引入的一个重要功能。TLP 的发送端可以使用 TPH 信息,通知接收端即将访问数 据的特性,以便接收端合理地预读和管理数据。
6. TD([15])
表示 TLP 中的 TLP Digest(之前说 ECRC 可选)是否有效,如果这个这个 bit 置起 来,说明该 TLP 包含 ECRC,接收端应该做 CRC 校验;
7. EP([14])
表示当前 TLP 中的数据是否有效,为 1 表示无效,为 0 表示有效。
8. AT([11:10])
Address Type,地址种类,与 PCIe 总线的地址转换相关,可暂时不考虑。
9. Length([9:0])
用来描述 TLP 的有效负载(Data Payload)大小。PCIe 总线设置 Length 字段的目的是 提高总线的传送效率。Length 字段以 DW 为单位,其最小单位为 1 个 DW。
7.6 TLP 路由寻址
TLP 的路由是指 TLP 通过 Switch 或 PCIe 桥片时采用哪条路径,最终到达 EP 或者 RC 的方法(RC 可以理解成 PCIe host bridge, 有时也叫 PCIe 控制器,完成 CPU 域地址到 PCI 域地址的转换,RC 在 Soc 的内部。EP 是具有 PCIe 接口的网卡,SATA 控制器等)
,基于地址的路由,基于 ID 的路由和 Implicit 路由方式。
存储器和 IO 读写请求 TLP 使用基于地址的路由方式,这种方式使用 TLP 头中的 Address 字段进行路由选择,最终到达目标 EP。
凡是基于地址路由的 TLP,其 TLP 头中必含有 Address 字段;凡是基于 ID 路由的 TLP 必含有 RequestID+TAG 字段用于路由选择;凡是基于 implicit 路由的 TLP,必有 message-code,由此 code 决定如何对其进行路由。
对于 ID 寻址与路由寻址我们只需要知道就行,下面来看一下 address 寻址。 address 寻址主要用于 memory 和 i/o request 请求包,memory 读写请求包支持 64 位地址和 32 位地址,i/o 读写请求只支持 32 位地址。64 位地址寻址的 TLP Header 有 4DW(16 字节),32 位地址寻址的 TLP Header 有 3DW 长。

(32 位地址寻址 Header)

(64 位寻址 Header)
7.7存储器请求包(memory request package)
存储器请求包采用直接地址寻址,有 64bit 地址和 32bit 地址两种,其中读请求包的 Length 域不应大于 Max_Read_Request_Size 寄存器设置的值,此值是在 FPGA 的 IP 核设置。请求器件不会示例一个所访问的 memory 空间超过 4KB 的请求包。以下是两种不同地址长度的 memory request 包。

(64 位 memory 请求包 Header)

(32 位 memory 请求包 Header)
7.8 配置请求包(configuration request)
配置请求包采用 ID 寻址方式,包头(Tlp Header 长度是 3DW)。有如下规定:
1. TC[2:0] must be 000b
2. Attr[1:0] must be 00b
3. AT[1:0] must be 00b
4. Length[9:0] must be 00 0000 0001b
5. Last DW BE[3:0] must be 0000b

7.9 消息请求包(Message request)
Message 包分为:
1. INTx Interrupt Signaling INTx 中断信息包
2. Power Management 电源管理机能。
3. Error Signaling 错误信息包
4. Locked Transaction Support 锁住事务支持
5. Slot Power Limit Support 插槽电源限制的支持
6. Vendor-Defined Messages 制造商自行定义信息
所有的 Message 包都用 Msg 编码 ,即不包括数据负荷的 Message 包 ,除了 Vendor_Defined Messages 和 Set_Slot_Power_Limit Message 包。
















