LJgibbs · 2022年02月14日

PCI Express Technology 3.0:PCIe配置概述 3.1-3.7 节

关于前一章

前一章节对PCIe体系结构进行了全面介绍,我们将其看作是一种“执行层(executive level)”概述。它对协议中描述PCIe端口分层设计方法进行了介绍。在介绍事务协议时也一并介绍了各种数据包的种类。

关于本章

本章节将对如何对PCIe环境进行配置来展开介绍。内容包括了实现Function配置寄存器的空间、如何发现一个Function、如何产生并路由转发配置事务、 PCI兼容配置空间(PCI-compatible configuration space)与PCIe扩展配置空间的不同点(PCIe extended configuration space),以及软件是如何区分开EP和Bridge的。

关于下一章

下一章的内容将描述Function如何通过基址寄存器(Base Address Register,BARs)来请求访问memory或者IO地址空间,以及这种请求的目的与作用,并且将介绍软件是如何初始化这两种地址空间的。另外在下一章还会描述Bridge的Base/Limit寄存器(基/边界寄存器)是如何被初始化的,因为只有当Base/Limit寄存器初始化后Switch才能在PCIe网络中路由转发TLP。

3.1 总线/设备/功能/的定义(Definition of Bus,Device and Function)

正如PCI一样,每个PCIe功能(Function)的标识在其所在的设备内,以及这个设备所连接的总线内,都是唯一的。其标识符一般被称为“BDF”。对于任意一个 PCIe 拓扑结构,配置软件负责检测出拓扑中的每个Bus、Device和Function,缩写为BDF。接下来的几节将会结合一个PCIe拓扑的示例,来讨论BDF的主要特征。图 3‑1展示了一个PCIe拓扑结构,图中着重标识了示例系统中的Buses、Devices和Functions。本章后续内容将解释总线编号和设备编号分配的过程。
image.png
图 3‑1示例系统

3.2 PCIe总线(PCIe Buses)

软件总共可以分配256个总线编号。第一个总线号,Bus 0,通常由硬件分配给RC(Root Complex)。Bus 0由一个集成有EP的虚拟PCI总线,一到多个虚拟PCI-to-PCI Bridges(P2P)组成。其中的P2P Bridges拥有不可更改、硬件编码(hard-coded)的设备号和功能号。每个P2P Bridge都会产生一个新的总线,其他PCIe设备可以连接在到这些新产生的总线上去。每个总线都必须被分配一个唯一的总线号。配置软件分配总线号的过程中,首先从Bus 0/Device 0/Function 0开始搜索其他的Bridges。当找到一个Bridge之后,软件就给这个Bridge产生的新总线分配一个与上一级总线的总线号不同的、数字更大的编号。一旦新总线被分配了一个总线号之后,软件就会从新总线继续搜索更新的Bridges,而不是在上一级总线上继续搜索。这被称为“深度优先搜索(depth first search)”,关于这种搜索的细节内容,请参阅“Enumeration – Discovering the Topology”一节。

3.3 PCIe设备(PCIe Devices)

PCIe允许在单个PCI总线上最多挂载32个设备,然而PCIe点对点(point-to-point)的性质意味着只有一个设备可以直接连接在PCIe链路上,也就是Device 0。但是,通过RC和Switches包含的虚拟PCI总线。更多的设备可以“连接”到总线上。

每个设备都必须实现Function 0,其最多可以有8个功能(Function)。当一个设备拥有2个或以上的Function时,称之为多功能设备(Multi-Function Device)。

3.4 PCIe功能(PCIe Functions)

正如此前所讨论的一样,Function被设计为每个设备中之内的一个逻辑层次。这些Function可能包含硬盘驱动接口、显示控制器、以太网控制器、USB控制器等等。多Function的设备不需要依次按照编号逐个实现 Function。例如,一个设备可以只实现Function 0、2、7。因此,当配置软件检测到了一个多Function设备时,必须检查所有可能的Function,以了解当前Device存在哪些Function。每个Function都有它们自己的配置地址空间,这个配置地址空间用于设置与Function相关的资源。

3.5 配置地址空间(Configuration Address Space)

初期的PC需要用户设置开关和跳线来给每个安装上去的板卡分配资源,这样的方法经常会导致memory、IO和中断的设置出现冲突。这之后出现的两种IO架构——扩展ISA(EISA,Extended ISA)和IBM PS2系统,是初次实现了即插即用(plug and play)的架构。在这些架构中,配置文件随每个插件卡一起被提供,允许系统软件分配基本资源。PCI扩展了这种配置特性,它通过实现标准化的配置寄存器,允许通用的Shrink-Wrapped(压缩包装)操作系统管理几乎所有的系统资源。PCI 在拥有了一个标准化的方式来进行错误报告的开启与关闭、传送中断、进行地址映射以及更多其他的操作之后,就可以通过配置软件这一单一模块,来进行系统资源的分配和配置,这消除了几乎所有的资源冲突。

PCI为每个Function都定义了一个专用的配置地址空间块。映射在这个配置空间中的寄存器们使得软件可以发现这个Function的存在,并对这个Function进行一般操作和状态检查。大多数需要标准化的基本功能都存在于配置寄存器块的Header中,但是PCI架构师意识到若将可选功能(option feature,区别于前面的基本功能)也标准化可以带来很多好处,这些可选功能标准化后的结构被称为“能力结构”,(capability structures,例如电源管理、热插拔等等)。

对于每个Function,都含有256byte的PCI兼容配置空间(PCI-Compatible configuration space)。

3.5.1 PCI兼容空间(PCI-Compatible Space)

在阅读下面的讨论内容时,请同时参阅图 3‑2。之所以将这256Byte命名为PCI-compatible configuration space(PCI兼容配置空间),是因为这些配置空间原本就是为PCI所设计的。这个配置空间的前16DW(64bytes)是配置头部(header),有两种类型的Header,分别为Type 0和Type 1。Type 0 Header对于每个Function都是必须含有的,除了Bridge,对于Bridge Function来说它使用的是Type 1 Header。剩余的48DW是一些可选寄存器,包括PCI能力结构(capability structure)。对于PCIe Functions而言,一部分 capability structure也是必须的。例如PCIe Function就必须实现如下的能力结构:

  • PCI Express能力(PCI Express Capability)
  • 电源管理
  • MSI、MSI-X
    image.png
    图 3‑2 PCI兼容配置寄存器空间

3.5.2 扩展配置空间(Extended Configuration Space)

在阅读下面的讨论内容时,请同时参阅图 3‑3。当引入PCIe之后,最初始的256byte配置空间已经不足以放下所有新需要的Capability Structure了。因此配置空间的大小从原先的每个Function 256Byte扩展至了每个Function 4KByte。新增加出来的960DW扩展配置空间只能通过增强配置机制(Enhanced configuration mechanism)来进行访问,因为传统的PCI软件无法发现这个区域并进行访问,所以这部分区域对于 PCI 是不可见的。在扩展配置空间内包含了新增加的PCIe可选扩展能力寄存器(Extended Capability register),图 3‑3罗列出了一部分扩展能力寄存器。
image.png
图 3‑3每个PCIe Function所拥有的4KB配置空间

3.6 Host-to-PCI Bridge配置寄存器

3.6.1 整体说明(General)

对Host-to-Bridge配置寄存器的访问不必使用前面的章节所提到的配置机制,在内存地址空间中,它通常映射为设备特定寄存器,这对平台固件是已知的。然而,它的配置寄存器格式排布和用法都必须遵从PCI 2.3协议规范中所定义的Type 0模板。

3.6.2 只有RC发送配置请求(Only the Root Sends Configuration Request)

在协议规范中声明了,只有RC可以发起配置请求。RC作为 CPU 与 PCIe 拓扑的联络员,其传入 CPU 的 PCIe 请求包并在 PCIe 事务完成后向处理器报告。之所以限制 CPU 只能通过RC发起配置事务,是因为要是其他设备也有这种能力,那么他们可以任意改变配置内容,这样就会带来混乱。

由于只有RC能发起这些配置请求,所以这些配置请求只能在拓扑中向下转发,意味着Peer-to-Peer的配置请求是被无法实现的。请求包使用目的设备ID作为路由信息进行路由转发,这个目的设备ID就是它的BDF(拓扑中的Bus编号,Bus上的Device编号,Device中的Function编号)。

3.7 生成配置事务(Generating Configuration Transactions)

处理器一般无法直接进行配置读写请求,因为他们只能产生memory请求和IO请求。这意味着RC需要将 CPU 的其他类型访问转换成配置请求,这样才能进行配置的操作过程。配置空间可以通过以下两种机制进行访问:

  • 传统的PCI配置机制,使用IO间接访问(IO-indirect access)
  • 增强型配置机制,使用内存映射访问(memory-mapped access)

3.7.1 传统PCI机制(Legacy PCI Mechanism)

在PCI协议规范中定义了IO-indirect方法,用来指示系统(RC或其等效的组件)进行PCI配置访问。在当时的历史背景下,占主导地位的PC处理器(Intel x86)被设计为仅能寻址64KB的IO地址空间。在 PCI 协议产生的时候,这个有限的IO空间已经变得非常混乱,只有少数几个可用的地址范围:0800h-08FFh,和0C00h-0CFFh。因此,将所有可能的Function的配置寄存器都映射到IO空间是不可行的。与此同时,内存地址空间也十分有限,将这些配置空间都映射进内存地址空间也不是个好方法。于是,协议规范的作者使用了一种常用的方法来解决这个问题,使用间接地址映射(indirect address mapping)。为此,需要使用一个寄存器保存目标地址,同时用第二个寄存器保存来自或是发往目标的数据。一次对目标Function的一次读写事务,需要先将待访问的地址写入地址寄存器,随后再读写数据寄存器。这很好的解决了地址空间有限的问题,但是这意味着产生一次配置访问需要两次IO访问。

PCI兼容机制使用RC的Host Bridge中的两个32bit的IO端口。它们分别是配置地址端口(Configuration Address Port),位于IO地址区域0CF8h-0CFBh,和配置数据端口(Configuration Data Port),位于IO地址区域0CFCh-0CFFh。

要访问一个Function的PCI兼容配置寄存器,首先要将目标的Bus、Device、Function和DW号写入配置地址端口,并将其使能bit置为有效。然后第二步,一个1或2或4Byte的IO读或写将会发送到配置数据端口。RC的Host Bridge将对给定的目标总线和在Bridge下现存的总线范围进行比较。若目标总线在这个范围内,这个Bridge则会发起配置读或写请求(这取决于对配置数据端口的IO访问是读操作还是写操作)。

3.7.1.1 配置地址端口(Configuration Address Port)

配置地址端口仅在处理器对其完成一个完整的32bit写操作时,锁存住写入的信息,如图 3‑4,若对这个端口进行读操作则会返回它的这些内容。写入配置地址空间的信息必须遵照下面所描述的格式(图 3‑4)。

图 3‑4地址位于0CF8h的配置地址端口

  • Bits[1:0]固定为0不变,且只读的,在读取时只能返回0。它的位置是DW对齐的,不允许指定字节(byte-specific)偏移量。
  • Bits[7:2]用于标识目标Function的PCI兼容配置空间内的目标DW(target dword,其也被称作寄存器号),也就是用来标识Function内的寄存器号,要求这个寄存器必须位于兼容配置空间中。这种机制仅限于兼容 PCI 的配置空间中使用。(例如一个Function配置空间的前64DW)。
  • Bits[10:8]用于标识目标Device内的目标Function号(0-7)。
  • Bits[15:11]用于标识目标Device号(0-31)。
  • Bits[23:16]用于标识目标Bus号(0-255)。
  • Bits[30:24]为保留字段,必须为0。
  • Bit[31]为使能位,若要将随后的对配置数据端口的IO访问转换为配置访问则必须要将该bit置为1。当该bit为0时,若有IO读或者IO写被发送到配置数据端口,那么这些事务都会被当成普通的IO请求来处理。
3.7.1.2 总线比较和数据端口的使用(Bus Compare and Data Port Usage)

如图 3‑5,RC内的Host Bridge实现了一个次级总线号(Secondary Bus Number)寄存器和一个从属总线号(Subordinate Bus Number)寄存器。次级总线号(图 3-5 中的 Sec)指的是当前Bridge下直接连接的总线的编号,例如图中RC的Host/PCI Bridge产生了Bus 0,因此Host/PCI Bridge的次级总线号就是0。从属总线号(图 3-5 中的 Sub)指的是Bridge之下的可作为目标总线的总线号,例如图中Device 1的Sub=9,那么就说明在Device 1下游最大的总线号是 Bus 9,而它的Sec=5则说明其下游的总线号编号从 Bus 5 开始,简单来说就是Sec和Sub共同指定出了这个设备下可访问总线的范围(对于Device 1来说就是5-9)。

在一个单RC的系统中,Host-Bridge的次级总线号应该被固定为0,也就是它的可读可写的次级总线号寄存器从一复位就被强制置为0,或者说,Host-Bridge知道它访问到的第一个总线一定是Bus 0。若配置地址端口(Configuration Address Port,见图 3‑4)的bit 31被置为1,那么Bridge就会将目标总线号与Bridge之下的从属总线范围进行比较,以此来检查这个目标总线是否从属于当前Bridge之下。

当Bridge收到一个请求,它将会评估目标总线号是否在其下的从属总线号范围内,这个范围大于等于从次级总线号(Sec),小于等于从属总线号(Sub)。若目标总线号与当前的次级总线号相匹配,那么说明当前的次级总线就是目标总线,这个请求就会被作为一个Type 0配置请求来传输。当设备们收到一个Type 0请求,它们就知道当前一级本地总线上的某个设备就是这个请求的目标设备(而不是在更下一级的总线所属的设备)。

若目标总线号要大于Bridge的次级总线号(Sec),但是小于或者等于Bridge的从属总线号(Sub),那么这个请求将会作为Type 1配置请求被转发到Bridge的次级总线上。对于一个Type 1配置请求,可以这样理解它:尽管这个请求需要经过这条总线,但是它的目标设备并不在这一级总线上,相反地,这个请求将会由当前总线上的Bridge们向下转发到各自的下一级总线上,当然转发该Type 1配置请求的Bridge必须是从属总线范围包含了目标总线的。所有,Type 1配置请求只对Bridge设备有作用。更多关于Type 0和Type 1配置请求的信息,请参阅“Configuration Request”一节。

3.7.1.3 单Host系统(Single Host System)

RC中的Host/PCI Bridge会将写入配置地址端口(Configuration Address Port)的信息锁存起来,如图 3‑1。若bit 31被置为1且目标总线在当前Bridge下方从属总线范围内,那么Bridge将把接下来的处理器对配置数据端口(Configuration Data Port)的访问转换成针对Bus 0的配置请求。处理器将会向配置数据端口(0CFCh)发起一个IO读请求或者一个IO写请求。这促使Bridge生成一个配置请求,这个配置请求是读请求还是写请求取决于IO访问是读还是写。若目标总线为Bus 0,那么它将是一个Type 0配置请求。若目标总线是从属总线范围内的其他总线,那么它将是一个Type 1配置请求。若目标总线不在从属总线范围内,那么这个Bridge将不会对这个请求进行转发操作。
image.png
图 3‑5 单Root系统

3.7.1.4 多Host系统(Multi-Host System)

若在一个系统中存在多个RC,如图 3‑6所示,那么配置地址端口和配置数据端口将被这两个RC各自的Host/PCI Bridge复用,且两种配置端口各自的IO地址在两个Host/PCI Bridge中相同,也就是两个RC使用相同的一个IO地址来访问各自配置地址寄存器,同理,访问配置数据寄存器也是。为了防止争用,在两个Host/PCI Bridge中同时只能有一个响应处理器对配置端口的访问。

  1. 当处理器发起对配置地址端口的IO写操作时,通过配置两个Host Bridge( the host bridges are configured, //TODO),仅有一个Host Bridge会参与到此次事务中来。
  2. 在枚举过程中(Enumeration),软件将搜索所有活跃(active)Bridge之下的所有总线,并对这些总线进行编号。当这一总线搜索过程完成后(不是枚举过程完成),软件将使能那个不活跃的Host Bridge,并给它赋予一个总线编号,这个总线编号会在之前那些活跃Bridge的总线编号范围之外,然后软件继续进行枚举过程。这两个Host Bridge都会看见请求,但是由于它们二者拥有相互不重叠的总线编号范围,而它们各自又只会对自己的范围内的请求作出响应,因此并不会存在冲突的情况。
  3. 在上面的过程之后,两个Host Bridge都会收到对各自配置地址端口的访问,而随后进行的的对配置数据端口的读访问或写访问,则仅会被包含目标总线的Host/PCI Bridge所接收。接收访问的Bridge将会响应处理器的事务,而另一个Bridge则会忽略这个事务。
  • 若目标总线就是次级总线,那么Bridge将把这个访问转换成Type 0配置访问。
  • 否则,这个访问将被转换成Type 1配置访问。

编者注:上文中的两个 RC 是对应于图 3-6 的情况,更一般的情况中会存在 1-N 个 RC。
image.png
图 3‑6多Root系统

3.7.2 增强型配置访问机制(Enhanced Configuration Access Mechanism)

3.7.2.1 整体说明(General)

当协议制定者在选择PCI-X和之后的PCIe该如何访问配置空间时,有两个考虑。第一个,每个Function的256Byte空间限制了那些想要在这个空间内放一些专有信息的厂商,而且未来的协议制定者可能也需要更多的空间来放置更多的能力结构(capability structure)。为了解决这个问题,这个空间从每个Function 256Bytes扩展至4Kbytes。第二个要考虑的是,PCI协议应用的时代多处理器系统还很少。对于旧的模型来说,当系统中仅有一个CPU且其只有一个线程时,生成一次访问需要两步并不会有什么问题。但是对于使用多核多线程CPU的计算机来说,使用IO间接访问模型就会产生一些问题,因为没有机制能够阻止多线程同一时间访问配置空间。因此,在没有线程锁机制(lock semantics)时不再使用“2步”模型(IO间接访问)。在没有线程锁机制的情况下,当线程A往配置地址端口(CF8h)写入一个值后,在它继续对配置数据端口(CFCh)做相应的操作之前,并没有一个机制来防止线程B将这个值覆盖掉。

为了解决这个新问题,协议制定者决定采用一个不同于IO间接访问的方法。他们不再尝试去保留地址空间,而是通过将所有配置空间都映射到内存地址,以此来创造出一个单步(single-step)、不可中断(uninterruptable)的访问过程。由于一个针对特定地址范围的memory请求会在总线上产生一个配置请求,所以这种访问方式只需要一个命令序列即可。这种方式带来的开销考量(trade-off)在于地址大小。每个Function映射地址空间需要4KB,实现最大数量的 function 需要256MB的memory(内存)地址空间。由于现代体系结构一般支持36到48位的物理内存地址空间,所以相较于现在的内存地址空间的总大小,256MB的空间占用很少。

为了处理地址映射,每个Function的4KB配置空间都以一个4KB对齐的地址作为起始地址,且这些4KB配置空间所映射的地址都要位于那段为了配置访问而预留的256MB内存地址空间内。并且现在的地址中的bit还携带了识别信息(identifying information),用于表示哪一个Function是访问目标(见表 3‑1)。
image.png
表 3‑1增强型配置机制的内存映射地址范围

3.7.2.2 一些规则(Some Rules)

如果一个访问跨了dword地址边界(跨越了相邻的两个内存DW的边界,即操作长度大于了一个DW,或者是操作长度等于一个DW但是起始地址没有位于DW对齐的地址),那么RC可以不支持它访问增强型配置内存空间。RC们也不需要支持某些总线锁定协议(bus locking protocol),一些处理器使用这些总线锁定协议来实现原子操作或是不可中断的一系列命令。软件在访问配置空间时,需要避免上述这两种情况,除非软件知道RC可以支持它们。

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

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

转载自:知乎
作者:LogicJitterGibbs

推荐阅读

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