LJgibbs · 2022年01月27日

PCI Express Technology 3.0:PCIe体系结构概述 2.1 节

关于前一章

前一章为我们提供了 PCI 技术发展的历史,以此来建立更好地理解 PCIe 的基础。主要回顾了 PCI 与 PCI-X 1.0/2.0 的基础内容,目的是为了给接下来对 PCIe 的概述内容提供一些前因后果,以方便理解 PCIe。

关于本章

本章对 PCI Express 体系结构进行了全面的介绍,旨在将本章作为一个“执行层”概述,涵盖该体系结构在高层的所有基础知识。它介绍了 PCIe 协议规范中给出的分层的方法,并描述了每个层级的职责作用。介绍了各种数据包类型的同时也一起介绍了用于数据包通信和增强数据包传输可靠性的协议。

关于下一章

下一章节介绍了 PCI Express 环境中的配置部分。它包括用于实现功能的配置寄存器的空间,一个功能如何在总线上被发现,配置事务是如何被生成和路由到正确的位置,PCI 兼容空间与 PCIe 扩展空间之间的差异,以及软件是如何区分端点和桥。

2.1 PCI Express 简介 (Introduction to PCI express)

PCI Express 的出现代表了其前身并行总线的重大转变。作为一种串行总线,它与早期的串行设计(例如 InfiniBand 或者 Fibre Channel)有许多的共同点,但是它完全保持了在软件层面对 PCI 的后向兼容。

正如许多高速串行传输方法一样,PCIe 使用双向连接的方式,可以在同一时间进行信息的收发操作。这种模型被称为双单工连接,因为每个接口都有一个单工发送路径和一个单工接收路径,图2‑1 展示了这种模型。因为数据流可以同时进行双向传输,因此在技术层面上来说两个设备间的通信其实是全双工的,但是 PCIe 协议规范依然使用双单工这个术语,这是因为这种称呼对实际通信信道也进行了一点描述。
image.png
图2‑1 双单工链路

用于描述设备之间信号传输路径的术语为“链路(Link)”,它由一个或以上的接收发送对组成。这样的一对接收和发送被称为一个“通道(Lane)”,协议规范允许一条链路内有 1、2、4、8、12、16 或 32 个通道。链路内通道的数量称为链路宽度,通常用 x1、x2、x4、x8、x16 以及 x32 来进行表示。用于权衡在实际设计中使用多少通道的思路其实很简单:使用更多的通道可以增加带宽,但是也会增加成本、增加空间占用以及增加功耗。更多关于这方面的信息,可以阅读“链路与通道”这一节。
image.png
图2‑2 一个通道

2.1.1 软件的后向兼容性 (Software Backward Compatibility)

PCIe 设计目标中极为重要的一点就是要保持对 PCI 软件的后向兼容性。如果一个设计使用的是现有的系统,而且这个设计已经能够在这个现有系统中正常工作,那么想要促使它转向使用另一种系统则需要满足两件事:第一,新技术需要一些改变但是有足够吸引人的性能提升,第二,尽可能减小技术迁移的成本、风险以及工作量。对于第二点来说,在计算机中通常的做法是让那些为旧模型所编写的软件在新模型中依然可用。为了在 PCIe 中做到这一点,PCI 中用到的所有的地址空间要么不做更改直接照搬,要么仅仅进行了了简单的扩展。内存、IO 和配置空间对于软件来说依然是可见的,而且连写入方式都和以前一样。因此就算是数年前为 PCI 而写的软件(BIOS 代码、设备驱动等等)将依然可以在现在的 PCIe 设备上使用。配置空间已经被大大的扩展,添加了许多新的寄存器来支持新的功能,但是老的寄存器依然存在且可以按照常规方式来访问他们(这部分的详细内容见“软件兼容性特性”)。

2.1.2 串行传输 (Serial Transport)

2.1.2.1 对传输速率的需求 (The need for Speed)

很明显,串行传输模型必须要比并行的设计跑的快很多才能达到相同的带宽,这是因为串行传输一次只发送 1 比特数据。然而,事实证明这并不困难,过去的 PCIe 在 2.5GT/s和 5.0GT/s 都能稳定的工作。之所以 PCIe 可以达到这样甚至更高的 8GT/s 的速率,是因为串行传输模型克服了并行模型的不足。

克服问题. 通过上一章对 PCI 历史的回顾,我们知道,并行总线的性能被一些问题所限制,图2‑3 展示了其中的三个问题。首先,回想一下,并行总线使用公共时钟;信号在一个时钟沿被输出,然后在下一个时钟沿被接收方接收。这个模型的第一个问题来自于信号从发送端传输到接收端所花费的时间,称为渡越时间。渡越时间必须小于一个时钟周期,否则将会出问题,这使得难以通过继续减小时钟周期来提升速度。因为若需要继续减小时钟周期,为了让信号渡越时间依然小于时钟周期,需要更短的布线并减少负载的设备数量,但是最终这样的做法都会到达极限并且越来越不现实。第二个造成并行模型性能受限的因素是使用公共时钟时,时钟到达发送方和接收方的时刻不一致,这称为时钟偏斜。电路板设计人员尽力去减小时钟偏斜的值,因为时钟偏斜将会降低信号传输时序预算,但是这种偏斜永远无法彻底消除。第三个因素是信号偏斜,它指的是多比特位宽数据的各个位到达接收端的时刻存在差异。显然,这样的多位宽数据在所有的比特都到达且稳定之前都不能被接收方采样,这使得我们必须去等待最慢的那一比特。
image.png
图2‑3 并行总线的局限

PCIe 这样的串行传输方法是如何处理这些问题的呢?首先,信号渡越时间将不再是一个问题,因为用于指示接收端锁存数据的时钟现在已经被内置入数据流中,不在需要额外传输线来传输参考时钟。因此,无论再小的时钟周期,或是再长的信号传输时间都不会产生以前的问题了,这是因为内置在数据流中的时钟必然能与数据一起到达接收方。同样地,时钟偏斜的问题将不再存在,这还是因为时钟被内置入数据流中,接收方通过恢复出数据流中的时钟来进行数据采样,自然不存在时钟偏斜的问题。最后,信号偏斜的问题在一个通道内被消除了,因为一个通道一次只传输 1 比特数据。在多通道设计中,虽然信号偏斜的问题再次出现了,但是接收端将自动对其进行纠正,它可以很大程度上补偿偏斜。虽然串行设计克服了并行模型的许多问题,但是串行设计自身也有一系列的复杂问题。不过,我们稍后将看到,对这些问题的解决方法是易于控制的,并可以让我们做到高速的、可靠的通信。

带宽. PCIe 支持的高速,多通道的链路带来了傲人的带宽数值,如表 2‑1 所示。这些数字的计算都来源于比特率与总线特性。其中一种特性与其他许多串行传输方法类似,那就是 PCIe 的前两代版本中使用了被称为 8b/10b 的编码过程,这种编码过程会根据 8 比特的输入而生成 10 比特 的输出。尽管这样做会引起一些开销,但是我们将会在后面讲到有几个很好的理由支持我们这样做。对于现在来说,只需要知道对于 8b/10b 编码来说,要发送 1 字节的数据实际需要传送 10 比特。

第一代 PCIe(称为 Gen1 或者 PCIe 协议规范版本 1.x)中,比特率为 2.5GT/s,将它除以 10 即可得知一个通道的速率将可以达到 0.25GB/s。因为链路可以在同一时刻进行发送和接收,因此聚合带宽可以达到这个数值的两倍,即每个通道达到 0.5GB/s。第二代 PCIe(称为 Gen2 或者 PCIe 2.x)中将总线频率翻倍,这也使得它的带宽相较于 Gen1 翻倍。

第三代 PCIe(称为 Gen3 或者 PCIe 3.0)再次让带宽翻倍,但是这次协议的制定者们并没有选择将频率翻倍。相反,出于一些原因(我们稍后将会进行讨论),他们仅将频率提升到 8GT/s(Gen2中是 5GT/s,未翻倍),并不再采用 8b/10b 的编码方式,而是采用了另一种编码机制,即 128b/130b 编码(关于这个内容的更多信息,可以阅读“物理层-逻辑(Gen 3)”章节)。表 2‑1 列出了当前几代 PCIe 的各种通道数量情况下的带宽,展示出了链路对应情况下的峰值吞吐量。
image.png
表 2‑1 PCIe Gen1,Gen2,Gen3 的各种链路宽度下的带宽汇总

2.1.2.2 PCIe 带宽计算方法 (PCIe Bandwidth Calculation)

要计算上述表格中的 PCIe 带宽大小,可以参照如下的计算方法。

  • Gen1 PCIe 带宽 =(2.5Gb/s x 2 directions)/ 10bits per symbol = 0.5GB/s
  • Gen2 PCIe 带宽 =(5.0Gb/s x 2 directions)/ 10bits per symbol = 1.0GB/s

需要注意,上述计算中,我们是除以 10bits 而不是 8bits,这是因为 Gen1 和 Gen2 的协议中要求将字节进行 8b/10b 编码后进行数据包的传输,因此原数据中的 1 字节在实际传输时其实是需要传输 10 比特。

  • Gen3 PCIe 带宽 =(8.0Gb/s x 2 directions)/ 8bits per byte = 2.0GB/s

注意到在 Gen3 速率的计算中,我们除以的是 8bits 而不再是 10bits 了,这是因为 Gen3 中不再使用 8b/10b 编码方式,而是 128b/130b 编码方式。这种编码方式每 128位 引入 2 比特开销,这个开销非常小以至于我们暂且可以将它在我们的计算中忽略。

上述三种方法计算出来的带宽只需要再乘以链路宽度即可得到整个多通道链路的链路带宽。

2.1.2.3 PCIe 的差分信号 (Differential Signals)

每个通道都使用差分信号进行传输,差分信号是指每次传输一个信号时同时发送它的正信号和负信号(D+ 和 D-,这两种信号振幅相同相位相反),如图2‑4 所示。当然,这样会将引脚增加一倍,但是相对于单端信号而言,差分信号在高速传输上的两个明显的优点足以抵消其引脚数方面的不足:它提高了噪声容限,并降低了信号电压。

差分信号的接收端将会接收这一对相位相反的信号,用正信号的电压减去其反相信号的电压,得到它们的差值,以此来判定这个比特的逻辑电平值。差分传输设计内置了抗噪声干扰的设计,因为它要求成对的差分信号必须位于每个设备的相邻的引脚上,它们的走线也必须彼此非常靠近,以保持合适的传输线阻抗。因此,任何因素在影响差分对中的一个信号的时候,都会同等程度且同样方向地影响到另一个信号。但是接收端所在意的是它们的差值,而这些噪声干扰并不会改变这个差值,所以带来的结果就是大多数情况下噪声对信号的影响并不会引起接收端对比特值的错误判别。
image.png
图2‑4 差分信号示意图

2.1.2.4 不再使用公共时钟 (No Common Clock)

在先前的内容中有提到,PCIe 链路不再像 PCI 一样使用公共时钟,它使用了一个源同步模型,这意味着需要由发送端给接收端提供一个时钟来用于对输入数据进行锁存采样。对于 PCIe 链路来说,并不包括输出时钟信号。相反地,发送端会将时钟通过 8b/10b 编码来嵌入数据流中,然后接收端将会从数据流中恢复出这个时钟,并用于对输入数据进行锁存。这一过程听起来可能非常神秘,但是其实很简单。在接收端中,PLL 电路(Phase-Locked Loop 锁相环,如图2‑5)将输入的比特流作为参考时钟,并将其时序或者相位与一个输出时钟相比较,这个输出时钟是 PLL 按照指定频率产生的时钟。也就是说 PLL 自身会产生一个指定频率的输出时钟,然后用比特流作为的参考时钟与自身产生的输出时钟相比较。基于比较的结果,PLL 将会升高或者降低输出时钟的频率,直到所比较的双方达到匹配。此时则可以称 PLL 已锁定,且输出时钟(恢复时钟)的频率已经精确地与发送数据的时钟相匹配。PLL 将会不断地调整恢复时钟,快速补偿修正由温度、电压因素对发送端时钟频率造成的影响。

关于时钟恢复,有一件需要注意的事情,PLL 需要输入端的信号跳变来完成相位比较。如果很长一段时间数据都没有任何跳变,那么 PLL 的恢复时钟可能会偏离正确的时钟频率。为了避免这种问题,8b/10b 编码中的设计目标之一就是要确保比特流中连续的 1 或者 0 的数量不能超过 5 个(想获得更多关于这部分的内容,请参考“8b/10b 编码”一节)
image.png
图2‑5 简单的锁相环图示

一旦时钟被恢复出来,就可以使用它来锁存输入数据流的比特,并将锁存到的结果给到解串器。有时候学生们可能想知道这个恢复时钟能否作为接收端的所有逻辑所使用的工作时钟,但是这个问题的答案是“不行”。一个原因是,接收端不能指望用于恢复出时钟的比特流参考时钟一直存在且活跃,因为当链路的低功耗状态就包括了停止数据传输,此时必然也就无法继续恢复时钟。因此,接收端必须要有自己本地生成的内部时钟。

2.1.2.5 基于数据包的协议体系 (Packet-based Protocol)

将并行传输转变为串行传输可以极大的减少数据传输需要的引脚数量。如其他大多数串行传输协议一样,PCIe 通过消除了绝大部分原来并行总线中常用的边带控制信号来减少了引脚数量。然而,如果没有控制信号来指示被接收的信息的类型,接收端如何知道输入的比特是什么信息呢?因此,在 PCIe 中,所有的事务在发送时都使用已经定义好的结构,称为数据包(packets)。接收端需要找到数据包的边界,并且知道这个数据包的被定义的结构是什么样子的(即数据包的模板),然后解析数据包结构来获知它需要执行什么操作。

关于数据包协议体系的详细信息将在“TLP 元素”这一章进行全面讲解,但在本章也可以找到对各种包格式的介绍以及他们各自用途的概述,请见“数据链路层”一节。

2.1.3 链路和通道 (Links and Lanes)

如先前的内容所提到,两个 PCIe 设备之间的物理连接被称为一条链路,这个链路由许多通道所构成。如图2‑2,每个通道都含有一对发送差分信号对和一对接收差分信号对。这样的一个通道已经足够用于设备之间的通信,不需要其他额外的信号。

2.1.3.1 可扩展的性能 (Scalable Performance)

尽管一个通道就可以满足两个设备之间通信的需求,但是使用更多通道可以提升链路的传输性能,这个性能取决于链路的传输速率以及链路宽度。例如,使用多通道链路可以增加每个时钟传输的比特数量,因此这样就可以提升带宽。如表 2‑1 所提到,PCIe 协议规范支持一条链路中含有的通道数目为 2 的幂,最多 32 通道。除了 2 的幂以外,x12 链路也是支持的,这可能是为了支持 InfiniBand 的 x12 链路,它是一种较早的串行设计。PCIe 这种允许多种链路宽度的特性,使得平台设计者可以在成本和性能之间做出适当的权衡,根据链路中通道的数量轻松地进行增减。

2.1.3.2 灵活的拓扑结构选择 (Flexible Topology Options)

一条 PCIe 链路必须是一个点对点的连接,而不是像 PCI 一样的共享总线,这是因为 PCIe 使用的链路速率非常高。由于一条链路只能连接两个接口,因此需要一种扩展连接的方法来构建一个不琐碎的系统,这里不琐碎的意思是指不过于细碎和冗杂,例如若直接对所有设备都采用直接的两两相连,那么会使得整个系统十分冗杂琐碎。这种需求在 PCIe 中通过交换机和桥接来实现,这两者可以灵活的构建系统拓扑——系统中元素之间的连接集。对系统中一个元素的定义以及一些拓扑结构的例子将在下面的小节中给出。

2.1.4 关于 PCIe 拓扑的一些定义 (Some Definitions)

如图2‑6 展示了一个简单的 PCIe 拓扑示例,它将有助于我们对本节的一些定义内容进行讲解。
image.png
图2‑6 PCIe 拓扑示例

2.1.4.1 拓扑特征 (Topology Characteristics)

在图的最上方是一个 CPU 。这里需要指出,CPU 被认为是 PCIe 层次结构的顶端。就像 PCI 一样,PCIe 只允许简单的树结构,这意味着不允许出现循环或者其他复杂的拓扑结构。这样做的原因是为了保持与 PCI 软件的向后兼容性,因为 PCI 软件使用简单的配置方法来记录拓扑结构,它并不支持复杂的环境。

为了保持这种向后兼容性,软件必须能够以与从前一样方式产生配置周期,并且总线拓扑结构也必须与以前一样。因此,软件希望找到的所有配置寄存器依然存在,而且它们的行为方式等也依然与从前相同。我们将稍晚些在回过头来讨论这一块的内容,因为我们要先定义一些术语概念。

2.1.4.2 根组件 (Root Complex)

CPU 与 PCIe 总线之间的接口可能包含一系列的组件(处理器接口,DRAM 接口等等),甚至是包含多个芯片。将这些组件合起来,称这一组组件为根组件(Root Complex, RC, Root)。RC 存在于 PCI 树状拓扑的“根部”,并代表 CPU 与系统的其余部分通信。但是,PCIe 协议规范并没有很仔细的对 RC 进行定义,而是给出了一个 RC 必需功能与可选功能的列表。从广义上说,根组件可以被理解为系统 CPU 与 PCIe 拓扑之间的接口,这个 PCIe 端口,即 RC,在配置空间中被标记为“根端口”。

2.1.4.3 交换机与桥 (Switches and Bridges)

交换机提供了扇出以及聚合能力,使得单个 PCIe 端口上可以连接更多的设备。它扮演数据包路由器的角色,可以根据所给数据包的地址或者其他路由信息来识别这个数据包要走哪条路径。

桥提供了一个通往其他总线的接口,例如 PCI 或者 PCI-X,甚至也可以是其他的 PCIe 总线。图2‑6 中展示的桥有时被称为“前向桥”,它使得一个旧的 PCI 或者 PCI-X 板卡可以插入一个新系统中(PCIe 系统)。与前向桥相反类型的桥称为“反向桥”,它使得新的PCIe 板卡可以插入到旧的 PCI 系统中。

2.1.4.4 原生与传统端点 (Native PCIe Endpoints and Legacy PCIe Endpoints)

端点是 PCIe 拓扑中的既不是交换机也不是桥的设备,它们可以作为总线上事务的发起者也可以作为事务的完成者。端点存在于树状拓扑的分支的底部,仅实现一个上行端口(Upstream Port,与 RC 的连接方向),这里的上行指的是拓扑结构的向上,表示端点已经是树的分支的端点,不再产生下级分支。相比之下,一个交换机可以有几个下行端口(Downstream Port)。

用于老式总线(例如 PCI-X)的设备,如今拥有了可供它们使用的 PCIe 接口,这种 PCIe 接口将在配置寄存器中将自身标识为“传统 PCIe 端点”。它们使用了在 PCIe 设计中被禁止的东西,例如 IO 空间、支持 IO 事务以及支持锁定请求。与之相反,“原生 PCIe 端点”是从一开始就被设计用来在 PCIe 系统中使用的,这区别于在旧的 PCI 设备上添加 PCIe 接口。原生 PCIe 端点设备是内存映射设备。

2.1.4.5 软件兼容特性 (Software Compatibility Characteristics)

一种保持软件兼容性的方法是保持端点和桥的配置首部(Header)与 PCI 一致,如图2‑7 所示。
image.png
图2‑7 配置首部

有一点不同,现在 PCIe 中的桥经常会被整合进交换机和根组件,但是遗留的 PCI 软件无需关注这种区别,它只把这些内含桥的设备简单的当做桥。在这里我们先对一些概念进行熟悉,所以我们不会在这里讨论这些寄存器的细节。配置是一个相当大的主题,关于它的具体介绍将在“配置综述”这一节进行。

为了举例说明 PCIe 系统在软件中的展现形式,我们可以参考图2‑8 中所展示的拓扑结构的例子。像之前所说的一样,RC 位于整个层次结构的顶端。RC 自身的内部可以非常复杂,但是它通常会实现一个内部总线结构以及一些桥接结构,以便将拓扑扇出到几个端口。RC 的内部总线将被配置软件视作 PCI 总线 0,且 RC 上这几个 PCIe 端口将被视作是 PCI-to-PCI 桥。这种内部结构其实并不是一个实际的 PCI 总线,但是它在软件中看起来就是这种结构。因为这个总线是在 RC 内部的,所以它实际的逻辑设计并不需要遵循任何的标准,这里是可以由供应商来进行自定义的。
image.png
图2‑8 拓扑结构示例

类似的,PCIe 交换机的内部结构对于软件来说,就是简单地几个桥共享一条公共总线,如图2‑9 所示。这样做的主要优点就是使得事务的路由方法能够与 PCI 一致。

枚举(Enumeration)是配置软件用来发现系统拓扑结构,并分配总线号和系统资源的过程,它的工作方式也与 PCI 中相同。在稍后的内容中我们将通过一些例子来讲述枚举是如何工作的。一旦枚举完成,系统中的总线号就将按照图2‑9 所示的方式进行分配。
image.png
图2‑9 系统枚举结果的示例

2.1.4.6 系统示例 (System Examples)

如图2‑10 中举例说明了一个基于 PCIe 的系统,它被设计用于一些低成本应用比如消费级台式计算机。有一些 PCIe 端口实现时附带了一些板卡插槽,但是基本的框架与老式的 PCI 系统并没有太大区别。

相比之下,如图2‑11 所示的高端的服务器系统展示了一些内置在系统的用于连接其他网络的接口。在早期 PCIe 中,有人曾考虑将其作为一个网络来运行,用来取代那些旧的模型。毕竟,如果 PCIe 在大体上是其他网络协议的简化版本,或许它也能满足所有需求。由于各种各样的原因,这一概念从来没有真正得到大力发展,基于 PCIe 的系统通常仍然需要使用其他的协议来连接到外部网络。

这也给了我们一个机会来重新审视一个问题,RC 是由什么组成的。在这个例子中,被标识为“英特尔处理器”的方块包含了许多组件,大多数现代的 CPU 架构都是如此。这个处理器包含了一个用于访问图形设备(例如显卡)的 x16 PCIe 端口,以及两条 DRAM 通道,这两条 DRAM 通道意味着内存控制器以及一些路由逻辑已经被集成到 CPU 封装中。总的来说,这些资源通常被称为“非核心”资源,这样的称呼用于将他们与 CPU 封装中的几个 CPU 核心区分开来。此前,我们描述过 RC 是 CPU 与 PCIe 拓扑相连接的接口,这意味着在 CPU 封装中必须含有 RC 的一部分。正如图2‑11 中虚线所框出的,RC 由多个组件的一部分共同组成。这种 RC 的组成方式可能将会是未来很长一段时间内的系统的设计方式。
image.png
图2‑10 低成本的 PCIe 系统
image.png
图2‑11 服务器 PCIe 系统

原文: Mindshare
译者: Michael ZZY
校对: XTang

欢迎参与 《Mindshare PCI Express Technology 3.0 一书的中文翻译计划》
https://gitee.com/ljgibbs/chinese-translation-of-pci-express-technology

转载自:知乎

推荐阅读

更多招聘及面经请关注FPGA的逻辑
推荐阅读
关注数
10488
内容数
502
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息