注:本文为 “PCIe | 架构 / 配置空间” 相关文章合辑。
图片清晰度受引文原图所限。
略作重排,未整理去重。
如有内容异常,请看原文。
PCIe 学习笔记之 PCIe 结构和配置空间
Hober_yao 于 2020-06-19 21:16:41 发布
PCIe 概述
PCI Express(PCIe)是计算机总线 PCI 的一种,沿用了现有的 PCI 编程概念及通信标准,但基于更快的串行通信系统。PCIe 总线采用高速差分总线,并采用端到端的连接方式。现代高速总线大多为串行总线,能够支持更高的时钟频率。
当前 PCIe 协议支持到 5.0 版本,不同 PCIe 版本对应的传输速率如下:
PCIe 版本 | 编码 | 传输速率(GT/S) | x4 吞吐量(MB/s) |
---|---|---|---|
1.0 | 8b/10b | 2.5 | 1 |
2.0 | 8b/10b | 5 | 2 |
3.0 | 128b/130b | 8 | 4 |
4.0 | 128b/130b | 16 | 8 |
5.0 | 128b/130b | 32 | 16 |
PCIe 总线的拓扑结构
PCIe 采用树形拓扑结构,其体系架构一般由 Root Complex、Switch、Endpoint 等类型的 PCIe 设备组成。
Root Complex
Root Complex 是 PCIe 最重要的组成部件之一,主要负责 PCIe 报文的解析和生成。它接收来自 CPU 的 I/O 指令,生成对应的 PCIe 报文,或者接收来自设备的 PCIe TLP 报文,解析数据并传输给 CPU 或内存。
Switch
PCIe 的转接器设备,用于扩展 PCIe 总线。与 PCI 并行总线不同,PCIe 总线采用高速差分总线,并采用端到端的连接方式,因此每一条 PCIe 链路的两端只能各连接一个设备。如果需要挂载更多的 PCIe 设备,则需要使用 Switch 转接器。在 Linux 下,Switch 不可见,软件层面可以看到的是 Switch 的上行口(Upstream Port,靠近 RC 的一侧)和下行口(Downstream Port)。
通常,一个 Switch 只有一个 Upstream Port,但可以有多个 Downstream Port。
PCIe Endpoint
PCIe 终端设备是 PCIe 树形结构的叶子节点,例如网卡、NVMe 卡、显卡等都是 PCIe Endpoint 设备。
使用 lspci -tv
命令可以显示 PCIe 的总线拓扑。一个 PCIe 设备的 ID 由以下几部分组成:
- PCI 域:PCI 域 ID,用于突破 PCIe 256 条总线的限制。
- PCI 总线号:PCI 设备的总线 ID,占用 8 位,因此 PCIe 总线最多支持 256 个子总线。
- PCI 设备号:指定总线上的 PCI 设备 ID,占用 5 位,因此每个子总线最多支持 32 个设备。
- PCI 功能号:指定设备上的 PCI 功能 ID,一个 PCI 物理设备可以实现多个功能设备,且逻辑功能相互独立,占用 3 位,因此每个物理设备最多支持 8 个功能。
BDF(Bus,Device,Function)构成了每个 PCIe 设备节点的身份标识。
PCIe 配置空间
PCIe 设备有三个相互独立的物理地址空间:Memory 地址空间、I/O 地址空间和配置空间。这三个地址空间均采用唯一的地址进行寻址。例如,使用地址 0x100
时,需要明确指定该地址属于哪个地址空间。配置空间、I/O 地址空间和 Memory 地址空间的 0x100
偏移对应不同的存储位置。
通过读取配置空间,可以获取设备信息,也可以通过配置空间来配置设备。通过 PCI 设备的 ID 和配置空间的偏移地址,软件可以访问具体的寄存器。
每个 PCIe 设备的功能(Function)都对应一个独立的配置空间,其布局如下:
如上图所示,PCIe 的配置空间为 256 字节,其中前 64 字节是标准配置空间头,后面的 192 字节是 Capability 结构,用于展示 PCIe 设备的能力。为了兼容 PCI,PCIe 的配置空间前 256 字节与 PCI 保持一致,而 256~4096 字节是 PCIe 扩展配置空间,包含 PCIe 的扩展能力,例如 AER 等。
1. PCI 标准配置空间头(0~64 字节)
PCI 标准配置空间分为 Type 0 和 Type 1 两种。Type 0 主要用于 PCI Endpoint 设备,而 Type 1 主要用于 PCI Bridge 和 Switch。
Type 0 和 Type 1 有一些共同的寄存器描述:
- Device ID:设备 ID,表示该 PCI 设备的设备号,只读。
- Vendor ID:厂商 ID,表示生产该设备的厂商编号,只读。
- Status:PCI 设备状态寄存器,用于保存 PCI 设备的状态,例如中断状态或运行时产生的错误状态。
- Command:PCI 设备命令寄存器,在启用 PCI 设备时会配置该寄存器。主要负责启用或禁用 PCI 设备的 I/O 访问、Memory 访问和 INTx 中断等。
- Class Code:设备分类信息,表示 PCI 设备所属的类别,例如网卡、存储卡、显卡等。这是一个需要重点关注的寄存器。之前曾遇到一个问题,PCI 设备资源分配失败,原因是将 Class Code 设置为 0,而该设备是一张 PCIe 网卡,Class Code 应设置为 2。
- Revision ID:设备版本 ID,表示 PCI 设备的版本号。该寄存器可以视为 Device ID 寄存器的扩展,只读。
- BIST:可选,用于内部自检。
- Header Type:PCI 设备头类型寄存器,用于标识该设备是 PCI Endpoint 设备还是 PCI 桥设备。PCI 配置空间是 Type 0 还是 Type 1 由该寄存器决定。
- Latency Timer:在 PCI 总线中,多个设备共享同一条总线带宽,该寄存器用于控制 PCI 设备占用 PCI 总线的时间。PCIe 设备不需要使用该寄存器,其值必须为 0。因为 PCIe 总线的仲裁方法与 PCI 总线不同,连接方法也不同。
- Cache Line Size:缓存大小。对于 PCIe 设备,该寄存器的值无意义。
- Expansion ROM Base Address:扩展 ROM 映射基地址寄存器。分配给 ROM 使用,用于 PCI 设备在处理器尚未运行操作系统之前完成基本的初始化设置。
- Base Address Register:BAR 地址寄存器,负责 PCI 设备内部空间的映射。
Type 0 有 6 个 32 位的 BAR 寄存器,而 Type 1 有 2 个 32 位的 BAR 寄存器。每个 BAR 地址对应一个地址空间。
在 BAR 寄存器中,有些位是只读的,这些位在设备出厂前就已经固定。向其写入全 1 后,如果值保持不变,则说明这些位是厂家固化的。这些固化位提供了设备内部空间的一些信息。
例如:
上电时,系统软件首先读取 PCIe 设备的 BAR0,得到数据:
然后系统软件向该 BAR0 写入全 1,得到:
低 12 位未变,表明该设备空间大小为 4 KB(2^12),BAR 地址的低 4 位表明了该存储空间的一些属性([0]:I/O 映射还是 Memory 映射,[2:1]:32 位地址还是 64 位地址,[3]:是否可预取)。
然后系统软件根据这些信息,在系统内存空间中找到一块地方来映射这 4 KB 的空间,并将分配的基地址写入 BAR 寄存器。
BAR 地址寄存器负责 PCI 设备内部空间的映射。通过这种映射,CPU 可以访问 PCIe 设备空间。
例如,CPU 想要读取 PCIe 设备的数据,系统启动时通过 BAR 地址将 PCIe 设备空间映射到内存空间。CPU 要访问该 PCIe 设备空间,只需访问对应的内存空间。CPU 发出一个物理地址,Root Complex 检查该地址,如果发现该内存空间地址是某个 PCIe 设备空间的映射,则会触发其产生 TLP,去访问对应的 PCIe 设备,读取或写入数据。
Capability Pointer:PCI Capability 的地址偏移,用于表示 PCI 设备支持的能力。该寄存器存放 Capability 结构链表的头指针。在一个 PCIe 设备中,可能包含多个 Capability 结构,这些寄存器组成一个链表:
Interrupt Pin:PCI 设备中断引脚,支持 INTx A/B/C/D 四个中断引脚。
Interrupt Line:表示当前 PCI 设备使用的中断号。
Type 0 独有的寄存器:
- CardBus CIS Pointer:只读,可选,用于标识访问 CIS(Card Info Structure)的地址空间,通常不会涉及。
- Subsystem ID/Subsystem Vendor ID:子系统和子厂商 ID,可以结合 Device ID 和 Vendor ID 组成完整的 PCI 设备标识。
- Max_Lat:设备期望的最大延时,只读。
- Min_Gnt:设备期望的最小延时,只读。
Type 1 独有的寄存器:
- Primary Bus Number:表示 PCI 设备所在的 PCI 总线号。
- Subordinate Bus Number:PCI 桥可以管理其下的 PCI 总线子树。该寄存器存放当前 PCI 子树中编号最大的 PCI 总线号。
- Secondary Bus Number:存放当前 PCI 桥 Secondary Bus 使用的总线号,该 PCI 总线号也是该 PCI 桥管理的 PCI 子树中编号最小的 PCI 总线号。
- Secondary Latency Timer:PCI 桥下游的延时寄存器,与 Latency Timer 寄存器的含义相近。
- I/O Base:表示 I/O 寻址的基地址,低 4 位只读,因此 PCI 设备默认 I/O 寻址 4 KB 对齐,高 4 位可写。
- I/O Limit:I/O 寻址的上限,高 4 位可写,低 4 位为全 F,因此 I/O 寻址上限是 I/O Limit 地址 + 4 KB。
- Secondary Status:保存 PCI 下游总线和设备的状态。
- Memory Base:表示 Memory 寻址的基地址。
- Memory Limit:表示 Memory 寻址的上限,与 I/O Limit 类似。
- Prefetchable Memory Base:可预期内存的基地址。
- Prefetchable Memory Limit:可预期内存寻址的上限。
- Bridge Control Register:管理 PCI 桥的 Secondary Bus,可以控制 Secondary Bus 的 Reset。
2. PCI Capability 结构(64~256 字节)
该段空间主要存放与 MSI/MSI-X 中断机制和电源管理相关的 Capability。
在内核 include/uapi/linux/pci_regs.h
中归纳了 Capability ID 和各个 Capability 名称的对应关系:
/* Capability lists */
#define PCI_CAP_LIST_ID 0 /* Capability ID */
#define PCI_CAP_ID_PM 0x01 /* Power Management */
#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */
#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */
#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */
#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
#define PCI_CAP_ID_HT 0x08 /* HyperTransport */
#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */
#define PCI_CAP_ID_DBG 0x0A /* Debug port */
#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */
#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */
#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */
#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */
#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
#define PCI_CAP_SIZEOF 4
3. PCIe 扩展配置空间(256~4 KB 字节)
该段空间主要存放 PCIe 独有的 Capability 结构,例如 AER、SR-IOV 等。
PCIe 扩展能力定义如下:
/* Extended Capabilities (PCI-X 2.0 and Express) */
#define PCI_EXT_CAP_ID (header) (header & 0x0000ffff)
#define PCI_EXT_CAP_VER (header) ((header >> 16) & 0xf)
#define PCI_EXT_CAP_NEXT (header) ((header >> 20) & 0xffc)
#define PCI_EXT_CAP_ID_ERR 0x01 /* Advanced Error Reporting */
#define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel Capability */
#define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */
#define PCI_EXT_CAP_ID_PWR 0x04 /* Power Budgeting */
#define PCI_EXT_CAP_ID_RCLD 0x05 /* Root Complex Link Declaration */
#define PCI_EXT_CAP_ID_RCILC 0x06 /* Root Complex Internal Link Control */
#define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */
#define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function VC Capability */
#define PCI_EXT_CAP_ID_VC9 0x09 /* same as _VC */
#define PCI_EXT_CAP_ID_RCRB 0x0A /* Root Complex RB? */
#define PCI_EXT_CAP_ID_VNDR 0x0B /* Vendor-Specific */
#define PCI_EXT_CAP_ID_CAC 0x0C /* Config Access - obsolete */
#define PCI_EXT_CAP_ID_ACS 0x0D /* Access Control Services */
#define PCI_EXT_CAP_ID_ARI 0x0E /* Alternate Routing ID */
#define PCI_EXT_CAP_ID_ATS 0x0F /* Address Translation Services */
#define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */
#define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virtualization */
#define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */
#define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */
#define PCI_EXT_CAP_ID_AMD_XXX 0x14 /* Reserved for AMD */
#define PCI_EXT_CAP_ID_REBAR 0x15 /* Resizable BAR */
#define PCI_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */
#define PCI_EXT_CAP_ID_TPH 0x17 /* TPH Requester */
#define PCI_EXT_CAP_ID_LTR 0x18 /* Latency Tolerance Reporting */
#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
#define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */
#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
#define PCI_EXT_CAP_DSN_SIZEOF 12
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
PCIe 配置空间的访问
CPU 可以通过访问 BAR 地址读取 PCIe 设备空间,但要读取配置空间以获取 BAR,需要访问 PCIe 的配置空间。ARM 使用 ECAM(Enhanced Configuration Access Mechanism)方式访问 PCIe 配置空间。ECAM 是一种将配置空间映射到 Memory 空间的规则。硬件根据 ECAM 规则将某块 Memory 空间映射给 PCI 配置空间,CPU 访问对应的 Memory 空间即可操作 PCIe 配置空间。
映射的地址定义如下:
PCIe 总线最多支持 256 个子总线,每个子总线最多支持 32 个设备,每个设备最多支持 8 个功能,每个配置空间为 4 KB。因此,需要映射的空间大小为:
256 buses × 32 devices × 8 functions × 4 KiB = 256 MiB 256\ \text {buses} \times 32\ \text {devices} \times 8\ \text {functions} \times 4\ \text {KiB} = 256\ \text {MiB} 256 buses×32 devices×8 functions×4 KiB=256 MiB
在 ACPI 规范中,需要通过 MCFG 表上报 ECAM 的地址映射:
完成映射后,如果 CPU 发出的地址落在 ECAM 范围内,则根据对应的 BDF(Bus:Device.Function)即可访问到对应设备的配置空间。
Linux 内核中的 MCFG 实现读写操作分别对应以下函数:
EXPORT_SYMBOL (pci_bus_read_config_byte);
EXPORT_SYMBOL (pci_bus_read_config_word);
EXPORT_SYMBOL (pci_bus_read_config_dword);
EXPORT_SYMBOL (pci_bus_write_config_byte);
EXPORT_SYMBOL (pci_bus_write_config_word);
EXPORT_SYMBOL (pci_bus_write_config_dword);
PCIe 常用命令
lspci
-
** 查看 PCI 设备拓扑结构**:
lspci -tv
-
** 查看 PCI 设备详细信息**:
lspci -s [b:d:f]
-
16 进制查看 PCI 设备配置空间:
lspci -x
:PCI 标准配置头空间lspci -xxx
:PCI Capability 配置空间lspci -xxxx
:PCIe 扩展配置空间
例如,查看设备的详细信息:
红色框中的第一列对应具体的偏移,第二列对应的是设备的 Capability ID,第三列对应的是 Next Capability ID 的偏移。
因此,40: 0d 48
表示偏移 0x40
的位置的 Capability ID 是 0xd
(SSVID),它指向的下一个 Capability 位于 0x48
处。偏移 0x48
的位置的 Capability ID 是 0x1
(即 Power Management),它指向的下一个 Capability 位于 0x50
处。偏移 0x50
的位置的 Capability ID 是 0x10
(MSI),它指向的下一个 Capability 位于 0x8c
处。偏移 0x8c
的位置的 Capability ID 为 0x0
,查找结束。
得到的结果可以与 lspci
的结果对应:
setpci
可以使用 setpci
命令修改配置空间:
setpci -s 00:00.0 0x 地址.L = 0x 值 -- 修改设备地址的数值,一次修改 4 个字节
例如,之前提到向 PCI 设备的 BAR 寄存器写全 1 可以计算 BAR 空间的大小需求:
[root@localhost ~]# lspci -s 02:01.0 -x
02:01.0 Ethernet controller: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) (rev 01)
00: 86 80 0f 10 17 01 30 02 01 00 00 02 10 00 00 00
10: 04 00 5c fd 00 00 00 00 04 00 ff fd 00 00 00 00
20: 01 20 00 00 00 00 00 00 00 00 00 00 ad 15 50 07
30: 00 00 00 00 dc 00 00 00 00 00 00 00 07 01 ff 00
网卡设备的 PCI 配置头空间如上所示。通过 setpci --dumpregs
可以查看寄存器偏移:
[root@localhost ~]# setpci --dumpregs
cap pos w name
00 W VENDOR_ID
02 W DEVICE_ID
04 W COMMAND
06 W STATUS
08 B REVISION
09 B CLASS_PROG
0a W CLASS_DEVICE
0c B CACHE_LINE_SIZE
0d B LATENCY_TIMER
0e B HEADER_TYPE
0f B BIST
10 L BASE_ADDRESS_0
14 L BASE_ADDRESS_1
18 L BASE_ADDRESS_2
1c L BASE_ADDRESS_3
20 L BASE_ADDRESS_4
24 L BASE_ADDRESS_5
......
BAR0 的偏移是 0x10
,通过 setpci
命令写该地址全 F:
刚开始的 BAR0 的值为 fd5c0004
,写过全 F 后,BAR0 的值为 fffe0004
。
最低位可写入的 bit 是 18(低 4 位不算 bar 地址,是特殊标记)。因此该 BAR 的空间大小是 2^18 = 256K。
参考资料
- PCI 桥与 PCI 设备的配置空间(链接已沉)
- 老男孩读 PCIe 之六:配置和地址空间(链接已沉寂.)
- PCI.EXPRESS 系统体系结构标准教材
PCIe 总线 - 配置空间介绍
业余程序员 plus 已于 2024-09-07 22:35:24 修改
1. 概述
配置空间是 PCIe 设备 / 桥的标识符,其保存了设备 / 桥的信息。主机在枚举设备 / 桥的时候需要先访问配置空间,获取设备厂家、型号、类型、所需资源等信息,然后再分配资源,最后才能访问 PCIe 设备的存储或 I/O 地址空间。PCIe 总线规定了三种类型的配置空间,分别是 PCIe Agent 设备使用的配置空间、PCIe 桥使用的配置空间和 Cardbus 桥片使用的配置空间,本节只介绍前两种。
2. 配置空间布局
PCIe 配置空间的布局如下图所示,总共 4 KB。PCIe 设备的每个 Function 都对应一个配置空间。
- 0 - 3Fh(64 字节):PCI 兼容配置空间头,按类型可分为 Type 0 配置空间头和 Type 1 配置空间头。PCIe 设备使用 Type 0 配置空间头,PCIe 桥使用 Type 1 配置空间头。所有 PCI、PCI-X、PCIe 设备(桥)必须支持 64 字节的配置空间头,即 00h - 40h。
- 40h - FFh(192 字节):PCI/PCI-X 和 PCIe 扩展的配置空间,主要存放一些与 MSI 或 MSI-X 中断机制和电源管理相关的 Capability Structures。支持中断的 PCI、PCI-X、PCIe 设备(桥)必须支持 40h - FFh 的配置空间。
- 0 - FFh(256 字节):PCI 兼容配置空间,可通过传统的 PCI 方式(基于 ID 寻址)或 PCIe ECAM(Enhanced Configuration Access Mechanism)访问。
- 100h - FFFh(3840 字节):PCIe 协议扩展的可选配置空间,主要存放 AER、虚拟通道、设备序列号等 Capability Structures。100h - FFFh 只能通过 PCIe ECAM 访问。
在一个 PCIe 总线中,Type 0 配置空间头和 Type 1 配置空间头如下图所示。
3. Type 0 Header
Type 0 配置空间头如下图所示。
每个字段的具体意义如下:
字段 | 意义 | 偏移地址 | 宽度 |
---|---|---|---|
Vendor ID | 厂商 ID,由 PCI-SIG 统一分配, 例如 Intel 的厂商 ID 为 0x8086 | 0x00 | 2B |
Device ID | 设备 ID,由 PCI-SIG 统一分配, 例如 Intel 82571EB 网卡的设备 ID 为 0x105E | 0x02 | 2B |
Command | 命令寄存器,初始化时默认值为 0,具体见下文解释 | 0x04 | 2B |
Status | 保存 PCIe 状态 | 0x06 | 2B |
Revision ID | 保存 PCIe 版本号 | 0x08 | 1B |
Class Code | 保存 PCIe 设备的分类,由 Base Class Code、Sub Class Code 和 Interface 组成。 Base Class Code 是设备的基本分类,例如显卡、网卡、PCIe 桥等设备。 Sub Class Code 则会将设备进一步细分。Interface 定义编程接口。 | 0x09 | 3B |
Cache Line Size | 保存主机处理器的 Cache 行长度,该寄存器由主机的系统软件设置。 若 PCIe 设备不支持与 Cache 相关的总线事务,系统软件可不设置该寄存器。 | 0x0C | 1B |
Latency Timer | 控制 PCI 设备占用总线的时间,对 PCIe 总线无意义 | 0x0D | 1B |
Header Type | 记录设备的类型。 bit[7] = 0 表示单功能设备, bit[7] = 1 表示多功能设备, bit[6:0] = 0 表示 PCIe Agent 设备, bit[6:0] = 1 表示 PCIe 桥设备, bit[6:0] = 2 表示 PCIe CardBus 桥。 | 0x0E | 1B |
BIST | 可选,用于内部自检 | 0x0F | 1B |
Base Address 0 | BAR 寄存器,保存主机分配给该 PCIe 设备的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x10 | 4B |
Base Address 1 | BAR 寄存器,保存主机分配给该 PCIe 设备的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x14 | 4B |
Base Address 2 | BAR 寄存器,保存主机分配给该 PCIe 设备的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x18 | 4B |
Base Address 3 | BAR 寄存器,保存主机分配给该 PCIe 设备的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x1C | 4B |
Base Address 4 | BAR 寄存器,保存主机分配给该 PCIe 设备的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x20 | 4B |
Base Address 5 | BAR 寄存器,保存主机分配给该 PCIe 设备的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x24 | 4B |
Cardbus CIS Pointer | 不介绍 | 0x28 | 4B |
Subsystem Vendor ID | 记录 PCIe 设备生产厂商 | 0x2C | 2B |
Subsystem ID | 记录 PCIe 设备生产厂商 | 0x2E | 2B |
Expansion ROM Base Address | 固件程序地址,用于初始化 PCIe 设备 | 0x30 | 4B |
Capabilities Pointer | Capabilities 寄存器相对于配置空间的偏移地址,PCI-X 和 PCIe 必须支持该功能 | 0x34 | 1B |
Reserved | 保留 | 0x35 | 3B |
Interrupt Line | 保存当前 PCIe 设备使用的中断向量号,由主机系统软件分配,通常不使用 | 0x3C | 1B |
Interrupt Pin | 保存 PCIe 设备使用的中断引脚,由主机系统软件分配。 1 表示 INTA#, 2 表示 INTB#, 3 表示 INTC#, 4 表示 INTD#, PCIe 设备可使用 INTx 模拟 INTA~D# 中断 | 0x3D | 1B |
Min_Gnt | PCIe 设备能忍受的最大延时,只读寄存器 | 0x3E | 1B |
Max_Lat | PCIe 设备期望的最小延时,只读寄存器 | 0x3F | 1B |
3.1 Command
Command 寄存器为 PCIe 设备的命令寄存器,默认值为 0,此时 PCIe 设备除了能够接收配置请求总线事务之外,不能接收任何存储器或者 I/O 请求。
Command 寄存器位域如下图所示:
Command 寄存器位域定义如下表所示:
位域 | 意义 | 属性 |
---|---|---|
0 | I/O Space 位,默认值为 0。 表示是否响应 I/O 请求。 为 1 时响应,为 0 时不响应。 如果支持 I/O 空间,系统软件会将该位置 1。 | RW |
1 | Memory Space 位,默认值为 0。 表示是否响应 Memory 请求。 为 1 时响应,为 0 时不响应。 如果支持 Memory 地址空间,系统软件会将该位置 1。 | RW |
2 | Bus Master 位,默认值为 0。 对于 EP 表示是否可以发送 I/O 或者 Memory 请求,包括 MSI/MSI-X 中断。 对于 Root 和 Switch 端口表示是否会将 I/O 或者 Memory 请求转发到上行端口。 为 1 时可以发送或者可以转发,为 0 时不发送或者不转发。 | RW |
3 | Special Cycle 位,默认值为 0。 表示是否响应 Special 总线事务。 为 1 时响应,为 0 时不响应。 Special 总线事务可以将一些信息广播到多个目标设备。 | RO |
4 | Memory Write and Invalidate 位,默认值为 0。 对于 PCIe 总线没有意义 | RO |
5 | VGA Palette Snoop 位,默认值为 0。 对于 PCIe 总线没有意义。 | RO |
6 | Parity Error Response 位,默认值为 0。 此位控制 TLP 出现奇偶校验错误时是否在 Status 寄存器中记录。为 1 时记录,为 0 时不记录 | RW |
7 | IDSEL Stepping/Wait Cycle Control 位,默认值为 0。 对于 PCIe 总线没有意义。 | RO |
8 | SERR# Enable 位,默认值为 0。 对于 PCIe 总线没有意义。 | RO |
9 | Fast Back-to-Back Transactions Enable 位,默认值为 0。 对于 PCIe 总线没有意义。 | RO |
10 | Interrupt Disable 位,默认值为 0 表示 EP 是否可以发送 INTx 中断。 为 1 时不可以,为 0 时可以。 | RW |
3.2 Status
Status 寄存器保存 PCIe 设备的状态。
Status 寄存器的位域如下图所示:
Status 寄存器的位域定义如下表所示:
位域 | 意义 | 属性 |
---|---|---|
0 - 2 | 保留 | 只读 (RO) |
3 | 中断状态位。 该位为 1 且 Command bit[10] = 0,表示 PCIe 设备使用 INTx 信号提交中断。 大多数的 PCIe 设备有自己的中断状态寄存器,可通过 BAR 访问,很少使用该寄存器。 | 只读 (RO) |
4 | Capabilities List 有效位。 为 1 时 Capabilities Pointer 有效,否则无效 | 只读 (RO) |
5 | 66MHz 能力位,默认值为 0。 为 1 时表示 PCIe 设备支持 66MHz 的 PCI 总线。对 PCIe 总线无意义 | 只读 (RO) |
6 | 保留 | 只读 (RO) |
7 | 快速回背事务能力位,默认值为 0。 对于 PCIe 总线没有意义 | 只读 (RO) |
8 | 主设备数据奇偶校验错误位,默认值为 0。 当该位设置且 Command 寄存器 bit[6] = 1, 对于 EP 功能,表示其收到了错误的完成报文或者其发送了错误的请求, 对于根端口、交换机上行口或者交换机下行口,表示其接收到了下行口的错误完成报文或者向上行口发送了错误的请求。 Command 寄存器 bit[6] = 0,该位保持为 0 | 写 1 清零 (RW1C) |
9 - 10 | DEVSEL 定时,默认值为 0。 对于 PCIe 总线没有意义 | 只读 (RO) |
11 | 信号目标中止位,默认值为 0。 当一个功能作为完成者以完成中止错误完成一个已发布或未发布的请求时,此位被设置。 对于具有类型 1 配置头的功能,当其主端生成完成中止时,此位被设置。 | 写 1 清零 (RW1C) |
12 | 接收到目标中止位,默认值为 0。 当请求者接收到一个带有完成者中止完成状态的完成报文时,此位被设置。 对于具有类型 1 配置头的功能,当其主端接收到完成中止时,此位被设置。 | 写 1 清零 (RW1C) |
13 | 接收到主设备中止位,默认值为 0。 当请求者接收到一个带有不支持请求完成状态的完成报文时,此位被设置。 对于具有类型 1 配置头的功能,当其主端接收到不支持请求时,此位被设置。 | 写 1 清零 (RW1C) |
14 | 信号系统错误位,默认值为 0。 当一个功能发送一个 ERR_FATAL 或 ERR_NONFATAL 消息,且 Command 寄存器中的 SERR# 使能位为 1 时,此位被设置。 | 写 1 清零 (RW1C) |
15 | 检测到奇偶校验错误位,无论 Command 寄存器中的奇偶校验错误响应位的状态如何,每当一个功能接收到一个被毒化的 TLP 时,此位被设置。 对于具有类型 1 配置头的功能,当其主端接收到被毒化的 TLP 时,此位被设置。 | 写 1 清零 (RW1C) |
3.3 Base Address
Base Address 寄存器简称为 BAR,保存了 PCIe 设备 / 桥的 Function 使用的 PCIe 总线域地址的基地址。Type 0 设备每个 Function 最多可以有 6 个 BAR,Type 1 设备每个 Function 最多可以有 2 个 BAR。若使用 64 位 PCIe 总线域地址,则 2 个 BAR 表示一个 64 位地址,BARn 表示低 32 位,BARn+1 表示高 32 位。
BAR 保存的 PCIe 总线域基地址有两种类型,分别是 IO 地址空间和存储地址空间,存储地址空间有 4 种属性,分别是预取(prefetchable)、非预取(non-prefetchable)、32 位和 64 位。主机在枚举时先读取 BAR,然后判断所需的总线域基地址类型,若是存储地址空间,则进一步获取存储地址空间的属性,接着向 BAR 写入 0xFFFFFFFF,最后再读取 BAR,根据可以写入的最低有效位,获取 Function 请求 IO 地址空间和存储地址空间的长度。
当 BAR 表示的是存储地址空间时,位域如下图所示:
当 BAR 表示的是 IO 地址空间时,位域如下图所示:
1.32 位存储地址空间
32 位存储地址空间分为预取(prefetchable)和非预取(non-prefetchable)。以下以 32 位非预取存储地址空间的初始化流程为例进行分析:
- 当 PCIe Function 初始化完成后,BAR 的基地址类型和属性也初始化完毕。图中 bit[3:0] 都是 0,表示这是一个 32 位非预取存储地址空间。
- 主机先读取 BAR,获取 BAR 的类型和属性。
- 主机向 BAR 的所有 bit 写 1,此时 BAR 中可以写入的 bit 变为 1,不可写入 bit 的保持原来的值。图中 bit[31:12] = 1,表示该 BAR 请求的存储地址空间大小为 2^12 = 4 KB。
- 主机获取这些信息后,向 BAR 中配置存储地址空间的基地址。图中配置的是 0xF9000000,bit[11:0] 保持为 0,即该 BAR 表示的 32 位非预取存储地址空间范围为 0xF9000000 - 0xF9000FFF,大小为 4 KB。
实质上,当该 BAR 表示的 PCIe 总线域地址向存储域地址转换时,bit[31:12] 被转换为存储域基地址,bit[11:0] 被视为存储域基地址的偏移,直接映射到存储域空间,不做转换。
2.64 位存储地址空间
64 位存储地址空间分为预取(prefetchable)和非预取(non-prefetchable)。以下以 64 位预取存储地址空间的初始化流程为例进行分析:
- 当 PCIe Function 初始化完成后,BAR 的基地址类型和属性也初始化完毕。图中 bit[3:0] = 0xC,表示这是一个 64 位预取存储地址空间,需要两个 BAR 表示。
- 主机先读取 BAR,获取 BAR 的类型和属性。
- 主机向 BARn 和 BARn+1 的所有 bit 写 1,此时 BAR 中可以写入的 bit 变为 1,不可写入 bit 的保持原来的值。图中 bit[63:26] = 1,表示该 BAR 请求的存储地址空间大小为 2^26 = 64 MB。
- 主机获取这些信息后,向 BAR 中配置存储地址空间的基地址。图中配置的是 0x240000000,bit[25:0] 保持为 0,即该 BAR 表示的 64 位预取存储地址空间范围为 0x240000000 - 0x243FFFFFF,大小为 64 MB。
实质上,当该 BAR 表示的 PCIe 总线域地址向存储域地址转换时,bit[63:26] 被转换为存储域基地址,bit[25:0] 被视为存储域基地址的偏移,直接映射到存储域空间,不做转换。
3.IO 地址空间
IO 地址空间的初始化流程如下图所示:
- 当 PCIe Function 初始化完成后,BAR 的基地址类型也初始化完毕。图中 bit[0] = 0x0,表示这是一个 IO 地址空间。
- 主机先读取 BAR,获取 BAR 的类型。
- 主机向 BAR 的所有 bit 写 1,此时 BAR 中可以写入的 bit 变为 1,不可写入 bit 的保持原来的值。图中 bit[31:8] = 1,表示该 BAR 请求的存储地址空间大小为 2^8 = 256 B。
- 主机获取这些信息后,向 BAR 中配置 IO 地址空间的基地址。图中配置的是 0x4000,bit[7:0] 保持为 0,即该 BAR 表示的 IO 地址空间范围为 0x4000 - 0x40FF,大小为 256 B。
早期的 IO 设备内部寄存器只能通过 IO 地址空间进行访问,但这种方式局限性很大,效率低。后来为了提高灵活性和效率,将 IO 设备内部寄存器统一映射到存储地址空间,使用 MMIO 方式访问 IO 设备内部寄存器。PCIe 为了兼容 PCI,保留了 IO 地址空间,但现在很少使用。
4. Type 1 Header
Type 1 配置空间用来描述 PCIe 桥,PCIe 桥除了作为 PCIe 设备之外,还需要管理其下面连接的 PCIe 子总线使用的各类资源。Type 1 配置空间头如下图所示。
每个字段的具体意义如下:
字段 | 意义 | 偏移地址 | 宽度 |
---|---|---|---|
Vendor ID | 厂商 ID,由 PCI-SIG 统一分配,例如 Intel 的厂商 ID 为 0x8086 | 0x00 | 2B |
Device ID | 设备 ID,由 PCI-SIG 统一分配,例如 Intel 82571EB 网卡的设备 ID 为 0x105E | 0x02 | 2B |
Command | 命令寄存器,初始化时默认值为 0,具体见 Type 0 Header 解释 | 0x04 | 2B |
Status | 保存 PCIe 状态 | 0x06 | 2B |
Revision ID | 保存 PCIe 的版本号 | 0x08 | 1B |
Class Code | 保存 PCIe 设备的分类,由 Base Class Code、Sub Class Code 和 Interface 组成。 Base Class Code 是设备的基本分类,例如显卡、网卡、PCIe 桥等设备。 Sub Class Code 则会将设备进一步细分。Interface 定义编程接口。 | 0x09 | 3B |
Cache Line Size | 保存主机处理器的 Cache 行长度,该寄存器由主机的系统软件设置。 若 PCIe 设备不支持与 Cache 相关的总线事务,系统软件可不设置该寄存器。 | 0x0C | 1B |
Primary Latency Timer | 上游总线的延迟时间,对 PCIe 总线无意义 | 0x0D | 1B |
Header Type | 记录设备的类型。 bit[7] = 0 表示单功能设备, bit[7] = 1 表示多功能设备, bit[6:0] = 0 表示 PCIe Agent 设备, bit[6:0] = 1 表示 PCIe 桥设备, bit[6:0] = 2 表示 PCIe CardBus 桥。 | 0x0E | 1B |
BIST | 可选,用于内部自检 | 0x0F | 1B |
Base Address 0 | BAR 寄存器,保存主机分配给桥的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x10 | 4B |
Base Address 1 | BAR 寄存器,保存主机分配给桥的 PCIe 域地址,64 位地址使用 2 个 BAR | 0x14 | 4B |
Primary Bus Number | 保存该 PCIe 桥的上游 PCIe 总线号 | 0x18 | 1B |
Secondary Bus Number | 保存桥的下游总线中总线编号最小的 PCIe 总线号 | 0x19 | 1B |
Subordinate Bus Number | 保存桥的下游总线中总线编号最大的 PCIe 总线号 | 0x1A | 1B |
Secondary Latency Timer | Secondary 总线的延迟时间,对 PCIe 总线无意义 | 0x1B | 1B |
I/O Base | 保存桥的下游总线连接的所有 PCIe 设备 IO 地址空间的基地址。 若桥没有实现 I/O 地址空间,则应该只读并且返回 0,反之高 4 位可以修改 | 0x1C | 1B |
I/O Limit | 保存桥的下游总线连接的所有 PCIe 设备 IO 地址空间的大小。 若桥没有实现 I/O 地址空间,则应该只读并且返回 0,反之高 4 位可以修改 | 0x1D | 1B |
Secondary Status | 记录 Secondary 总线的状态 | 0x1E | 2B |
Memory Base | 保存桥的下游总线连接的所有 PCIe 设备非预取存储地址空间的基地址 | 0x20 | 2B |
Memory Limit | 保存桥的下游总线连接的所有 PCIe 设备非预取存储地址空间的大小 | 0x22 | 2B |
Prefetchable Memory Base | 保存桥的下游总线连接的所有 PCIe 设备预取存储地址空间的基地址 | 0x24 | 2B |
Prefetchable Memory Limit | 保存桥的下游总线连接的所有 PCIe 设备预取存储地址空间的大小 | 0x26 | 2B |
Prefetchable Base Upper 32 Bits | 保存桥的下游总线连接的所有 PCIe 设备预取存储地址空间基地址的高 32 位 | 0x28 | 4B |
Prefetchable Limit Upper 32 Bits | 保存桥的下游总线连接的所有 PCIe 设备预取存储地址空间大小的高 32 位 | 0x2C | 4B |
I/O Base Upper 16 Bits | 保存桥的下游总线连接的所有 PCIe 设备 IO 地址空间基地址的高 16 位。 若桥没有实现 I/O 地址空间,则应该只读并且返回 0 | 0x30 | 2B |
I/O Limit Upper 16 Bits | 保存桥的下游总线连接的所有 PCIe 设备 IO 地址空间大小的高 16 位。 若桥没有实现 I/O 地址空间,则应该只读并且返回 0 | 0x32 | 2B |
Capabilities Pointer | Capabilities 寄存器相对于配置空间的偏移地址,PCI-X 和 PCIe 必须支持该功能 | 0x34 | 1B |
Reserved | 保留 | 0x35 | 3B |
Expansion ROM Base Address | 固件程序地址,用于初始化 PCIe 桥 | 0x38 | 4B |
Interrupt Line | 保存当前 PCIe 设备使用的中断向量号,由主机系统软件分配,通常不使用 | 0x3C | 1B |
Interrupt Pin | 保存 PCIe 设备使用的中断引脚,由主机系统软件分配, 1 表示 INTA#, 2 表示 INTB#, 3 表示 INTC#, 4 表示 INTD#, PCIe 设备可使用 INTx 模拟 INTA~D# 中断 | 0x3D | 1B |
Bridge Control | 控制桥的寄存器,最重要的 bit[6](Secondary Bus Reset), 当 bit[6] = 1 时,PCIe 桥将对该 port 下面连接的所有 PCIe 设备 / 桥发起 hot reset。 | 0x3E | 2B |
4.1 Base Address
Type 1 设备中只含有两个 BAR,其意义和 Type 0 中的 BAR 寄存器相同。但在 Type 1 表示的 PCIe 桥中,这两个 BAR 寄存器是可选的,如果桥设备不存在私有寄存器,可以不使用这两个 BAR 寄存器。不存在私有寄存器,且没有专门驱动的 PCIe 桥,被称为透明桥。
4.2 I/O 地址空间
I/O Base、I/O Limit、I/O Base Upper 16 Bits 和 I/O Limit Upper 16 Bits 共同决定了桥的下游总线 IO 地址空间范围。计算方法如下图所示:
- I/O Base 和 I/O Limit 的低 2 位决定了 IO 地址是 16 位还是 32 位,若是 32 位则需要使用 I/O Base Upper 16 Bits 和 I/O Limit Upper 16 Bits。
- I/O Base 表示 I/O 地址空间基地址,按 4 KB 对齐,低 12 位都是 0,因此图中的 I/O 基地址为 0x4000。
- I/O Limit 表示 I/O 地址空间最大地址,低 12 位都是 1,即为 0xFFF。因此图中的 I/O 地址空间最大地址为 0x4FFF。
- 图中 I/O 地址空间为 16 位,因此 I/O 地址空间范围为 0x4000 - 0x4FFF。
4.3 非预取地址空间
Memory Base 和 Memory Limit 共同决定了非预取地址空间的范围。计算方法如下图所示:
- Memory Base 和 Memory Limit 低 4 位只读,默认为 0,非预取地址空间默认为 32 位。
- Memory Base 表示非预取地址空间基地址,按 1 MB 对齐,即低 20 位为 0。
- Memory Limit 表示非预取地址空间最大地址,低 20 位都为 1,即为 0xFFFFF。
- 因此非预取地址空间范围为 0xF9000000 - 0xF90FFFFF。
4.4 预取地址空间
Prefetchable Memory Base、Prefetchable Memory Limit、Prefetchable Base Upper 32 Bits 和 Prefetchable Limit Upper 32 Bits 共同决定了预取地址空间的范围。计算方法如下图所示:
- Prefetchable Memory Base 和 Prefetchable Memory Limit 低 4 位只读,而且决定了预取地址空间是 32 位还是 64 位,0x0 表示 32 位,0x1 表示 64 位。若是 64 位地址,则需要使用 Prefetchable Base Upper 32 Bits 和 Prefetchable Limit Upper 32 Bits。
- Prefetchable Memory Base 表示预取地址空间基地址,按 1 MB 对齐,即低 20 位为 0。
- Prefetchable Memory Limit 表示预取地址空间最大地址,低 20 位都为 1,即为 0xFFFFF。
- 图中预取地址空间为 64 位,因此 I/O 地址空间范围为 0x240000000 - 0x243FFFFFF。
5. Capabilities Structures
Capabilities Structures 主要有 MSI、MSI-X、PCI Express、电源、AER、PCI Express Extended 等 Capabilities Structures。第一个 Capabilities Structures 的地址由配置空间 Header 的 Capabilities Pointer 寄存器保存,系统可以根据此遍历所有的 Capabilities Structures。Capabilities Structures 的链表如下图所示。
每一个 Capabilities Structures 都有一个独一无二的 Capability ID,该 ID 保存在 Capabilities Structures 的开始地址,系统软件根据此判断 Capabilities Structures 的类型。
PCI 标准 Capabilities ID 如下图所示。
PCIe 扩展 Capabilities ID 参考 PCI Express® Base Specification Revision 5.0 的 9.3.7 节。
5.1 MSI Capability Structures
MSI 使用报文的形式向主机发送中断。具体是使用存储器写请求,向 MSI Capability Structures 中的 Message Address 写入 Message Data,写入后会在主机侧触发中断。
MSI 最多支持 32 个中断,且要求主机分配的中断号连续。MSI Capability Structures 共有 4 种形式,分别是 32 位和 64 位不带中断 Mask 和 Pending Structure,32 位和 64 位带中断 Mask 和 Pending Structures,具体如下图所示。
MSI Capability Structures 各个寄存器的定义如下表所示:
定义 | 描述 | 属性 |
---|---|---|
Capability ID | 记录 MSI Capability 的 ID 号,值为 0x05 | RO |
Next Capability Pointer | 存放下一个 Capability Structure 的偏移地址,0x00 表示该 Capability 是最后一个 | RO |
Message Control | 存放 Function 使用 MSI 机制进行中断请求的状态与控制信息 | - |
Message Address | 存放 MSI 存储器写事务目的地址的低 32 位,bit[1:0] 为 0,4 字节对齐,该地址由系统软件分配并设置 | RW |
Message Upper Address | 存放 MSI 存储器写事务目的地址的高 32 位,该地址由系统软件分配并设置 | RW |
Message Data | 存放 MSI 存储器写事务携带的数据。 总共 4 字节,Message 占低 2 字节,若扩展 Message 数据使能,则高 2 字节为扩展 Message 数据,否则为 0。 当只有一个 MSI 中断(Multiple Message Enable=0)时,Message 表示一个真实的数据,Function 不会改变 Message。 当有多个 MSI 中断(Multiple Message Enable>0)时,Message 表示 Function 可以改变 Message 低位的 bit 数,以产生不同的中断。 比如当 Multiple Message Enable=2 时,表示 Function 需要 4 个中断,则 Function 可以修改 Message 的 bit[1] 和 bit[0],以区分 4 个中断。 | RW |
Extended Message Data | 存放 MSI 存储器写事务携带的数据。总共 4 字节,Message 占低 2 字节,若扩展 Message 数据使能,则高 2 字节为扩展 Message 数据,否则为 0。 | RW/undefined/RsvdP |
Mask Bits | 存放 MSI 中断屏蔽位。系统软件可以向某个 bit 写 1,此时会将对应的中断禁止,清零时会使能对应的中断。 | RW |
Pending Bits | 存放 MSI 中断 Pending 位。当 Function 中断发生时,根据中断号会将对应的 bit 设置为 1。 | RO |
Message Control 寄存器位域如下图所示:
Message Control 寄存器位域的定义如下表所示:
位域 | 定义 | 描述 | 属性 |
---|---|---|---|
15:11 | RsvdP | 保留 | - |
10 | Extended Message Data Enable | 扩展 Message 数据使能位, 系统软件需要先读取 Extended Message Data Capable 判断 Function 是否支持扩展 Message 数据,若支持再判断是否使能该位 | RW/RO |
9 | Extended Message Data Capable | Function 是否支持扩展 Message 数据 | RO |
8 | Per - Vector Masking Capable | Function 是否支持 MSI 中断 Mask 和 Pending。 0 表示不支持, 1 表示支持 | RO |
7 | 64 - bit Address Capable | Function 是否支持 64 位地址 Structure。 0 表示只支持 32 位地址 Structure, 1 表示支持 64 位地址 Structure | RO |
6:4 | Multiple Message Enable | 系统软件给 Function 实际分配的 MSI 中断数量。 系统软件先读取 Multiple Message Capable 位确定 Function 请求的中断数量,然后再分配。 通常情况下两者相等,若系统中断数量不足,则 Multiple Message Enable 可能会小于 Multiple Message Capable。最多和 Multiple Message Capable 设置的中断数量一样 | RW |
3:1 | Multiple Message Capable | Function 需要的中断向量数量,按 2 的 n 次方对齐, 如 0 表示需要 1 个中断向量,1 表示需要 2 个中断向量,…,5 表示需要 32 个中断向量,6 和 7 保留。 若设备实际上需要 3 个中断,则需要设置为 2。系统软件根据此位域给 Function 分配 MSI 中断数量 | RO |
0 | MSI Enable | MSI 中断机制使能位, 当 MSI 或 MSI-X 使能时,INTx 中断将被自动禁止 | RW |
5.2 MSI-X
MSI-X 的中断机制和 MSI 类似,都是向主机的某个地址写 Message 数据以产生中断。但 MSI-X 每一个中断都有独立的 Message Address 和 Message Data,Message Address 和 Message Data 组成一个中断向量表,同时 MSI-X 使用了独立的中断 Pending 表。中断向量表和中断 Pending 表存放在 BAR 空间中。因此 MSI-X 支持的中断数量更多,且不需要中断号连续。
5.2.1 MSI-X Capability Structures
MSI-X Capability Structures 主要的作用是记录中断向量表和 Pending 表保存的位置。MSI-X Capability Structure 如下图所示。
MSI Capability Structures 各个寄存器的定义如下表所示:
定义 | 描述 | 属性 |
---|---|---|
Capability ID | 记录 MSI-X Capability 的 ID 号,值为 0x11 | RO |
Next Capability Pointer | 存放下一个 Capability Structure 的偏移地址, 0x00 表示该 Capability 是最后一个 | RO |
Message Control | 存放 Function 使用 MSI-X 机制进行中断请求的状态与控制信息 | - |
Table BIR | 表示 MSI-X 中断向量表存放在那个 BAR 空间中, 0 - 5 与 BAR0 - 5 对应,若是 64 位的 BAR,则表示低 32 位 BAR 的编号 | RO |
Table Offset | 表示该 MSI-X 中断向量表在对应 BAR 空间中的偏移地址, 偏移地址为 32 位,低 3 位为 0,按 8 字节对齐 | RO |
PBA BIR | 表示 MSI-X 中断 Pending 表存放在那个 BAR 空间中, 0 - 5 与 BAR0 - 5 对应,若是 64 位的 BAR,则表示低 32 位 BAR 的编号。 通常情况下中断向量表和中断 Pending 表存放在同一个 BAR 空间中 | RO |
PBA Offset | 表示该 MSI-X 中断 Pending 表在对应 BAR 空间中的偏移地址, 偏移地址为 32 位,低 3 位为 0,按 8 字节对齐 | RO |
Message Control 寄存器位域如下图所示:
Message Control 寄存器位域的定义如下表所示:
位域 | 定义 | 描述 | 属性 |
---|---|---|---|
15 | MSI-X Enable | MSI-X 中断机制使能位, 当 MSI、MSI-X 和 INTx 中断只能使用其中一个 | RW |
14 | Function Mask | MSI-X 中断全局 Mask 位, 当此位为 1 时,无论 Pending 表如何设置,所有中断都会被屏蔽 | RO |
13:11 | Reserved | 保留 | RsvdP |
10:0 | Table Size | MSI-X 中断向量表的大小,存放 Message Address 和 Message Data。 若系统软件读取的值为 0x3,则中断向量表的大小为 4 字节。 | RO |
5.2.2 MSI-X Table
MSI-X Table 如下图所示。每一个 Entry 表示一个 MSI-X 中断向量,占用 16 字节。一个 Entry 由 4 部分组成,分别是 Message Address、Message Upper Address、Message Data、Vector Control。
MSI-X Table 每个 Entry 的寄存器定义如下表所示:
定义 | 描述 | 属性 |
---|---|---|
Message Address | 存放 MSI 存储器写事务目的地址的低 32 位, bit[1:0] 为 0,4 字节对齐,该地址由系统软件分配并设置 | RW |
Message Upper Address | 存放 MSI 存储器写事务目的地址的高 32 位, 如果为 0,则使用 32 地址,否则使用 64 位地址, 该地址由系统软件分配并设置。 | RW |
Message Data | 存放 MSI 存储器写事务携带的数据,总共 4 字节 | RW |
Vector Control | 控制该中断 entry 的行为 | - |
Vector Control 寄存器位域如下图所示:
Vector Control 寄存器位域定义如下表所示:
位域 | 定义 | 描述 | 属性 |
---|---|---|---|
31:24 | ST Upper | 如果 Function 实现了 TPH Requester Extended Capability 而且 ST 表位域值为 10b,同时 Extended TPH Requester Supported 位为 1,则 ST Upper 保存了高 8 位的 Steering Tag,可以读写,否则保留 | RW or Rsvd |
23:16 | ST Lower | 如果 Function 实现了 TPH Requester Extended Capability 而且 ST 表位域值为 10b,则 ST Lower 保存了低 8 位的 Steering Tag,可以读写,否则保留 | RW or Rsvd |
15:1 | Reserved | 保留 | RW or RsvdP |
0 | Mask Bit | 中断屏蔽位, 为 1 屏蔽该 MSI-X 中断, 为 0 时可以正常发送 MSI-X 中断,默认值为 1 | RW |
5.2.3 MSI-X PBA
MSI-X 中断 Pending Table 如下图所示。每一个 Entry 由 64 位组成,其中每一位与 MSI-X 中断表中的一个 Entry 对应,则一个 Entry 和 MSI-X 中断表的 64 个 Entry 对应。当 Pending Table 中的某一个 bit 置 1 时,表明与之对应的 MSI-X 中断发生了。
5.3 PCI Express Capability Structure
PCI Express Capability Structure 存放和 PCIe 总线相关的信息,包括 PCIe 链路、插槽、设备状态、是否支持 PCIe 新特性等信息。PCI Express Capability Structure 整体的 Structure 如下图所示。
所有 PCIe 设备 Functions 必须实现 PCI Express Capabilities、Device Capabilities、Device Status 和 Device Control 寄存器。PCIe 设备 Functions 选择实现 Device Capabilities 2、Device Status 2 和 Device Control 2 寄存器,若没有实现,则保持为 0。
Root Ports、Switch Ports、Bridges 和 Endpoints(非 RCiEPs)必须实现 Link Capabilities、Link Status、Link Control、Link Capabilities 2, Link Status 2 和 Link Control 2 寄存器,Functions 无需实现上述寄存器,保持为 0 即可。
Switch Downstream 和 Root Ports 选择实现 Slot Capabilities、Slot Status 和 Slot Control 寄存器。如果 port 连接了插槽,则必须实现 Slot Capabilities 寄存器,如果 port 连接了插槽或者 Data Link Layer Link Active Reporting Capable 位设置,则必须实现 Slot Status 和 Slot Control 寄存器。Functions 无需实现上述寄存器,保持为 0 即可。Switch Downstream 和 Root Ports 选择实现 Slot Capabilities 2、Slot Status 2 和 Slot Control 2 寄存器,Functions 无需实现上述寄存器,保持为 0 即可。
Root Ports and Root Complex Event Collectors 必须实现 Root Capabilities, Root Status, and Root Control 寄存器,Functions 无需实现上述寄存器,保持为 0 即可。
下面只介绍比较重要的寄存器。
5.3.1 PCI Express Capabilities Register
PCI Express Capabilities 寄存器记录了 PCIe 设备 Function 类型及相关的 capabilities,位域如下图所示。
PCI Express Capabilities 位域的定义如下表所示:
位域 | 定义 | 描述 | 属性 |
---|---|---|---|
14 | Undefined | PCIe5.0 之前的版本表示是否支持 TCS Routing,PCIe5.0 未定义 | RO |
13:9 | Interrupt Message Number | 使用 MSI 或 MSI-X 中断的编号。 当 Slot Status 或 Root Status 寄存器的状态发生变化时,该 PCIe 设备可以通过 MSI 或 MSI-X 中断通知主机。 | RO |
8 | Slot Implemented | 该位为 1 时表示当前 port 连接了 PCIe 插槽。 对下行 port 有效,对上行 port 无效 | HwInit |
7:4 | Device/Port Type | 保存 PCIe Function 类型,对于多 Function 的设备,Function 类型可能不相同。 Type 0 配置空间头: 0x0 表示 PCI Express Endpoint, 0x1 表示 Legacy PCI Express Endpoint, 0x9 表示 RCiEP, 0xA 表示 Root Complex Event Collector。 Type 1 配置空间头: 0x4 表示 Root Port of PCI Express Root Complex, 0x5 表示 Upstream Port of PCI Express Switch, 0x6 表示 Downstream Port of PCI Express Switch, 0x7 表示 PCI Express to PCI/PCI-X Bridge, 0x8 表示 PCI/PCI-X to PCI Express Bridge | RO |
3:0 | Capability Version | 保存 PCI-SIG 定义的 PCI Express Capability structure 版本编号 | RO |
5.3.2 Device Capabilities Register
Device Capabilities 寄存器记录了 PCIe 设备 Function 特有的 capabilities,位域如下图所示。
Device Capabilities 寄存器位域的定义如下表所示:
位域 | 定义 | 描述 | 属性 |
---|---|---|---|
28 | Function Level Reset Capability | 是否支持 FLR | RO |
27:26 | Captured Slot Power Limit Scale | 上行 port 最大功耗系数, 0x0 表示 1.0, 0x1 表示 0.1, 0x2 表示 0.01, 0x3 表示 0.001 | RO |
25:18 | Captured Slot Power Limit Value | 上行 port 最大功耗,和 Captured Slot Power Limit Scale 一起使用 | RO |
16 | ERR_COR Subclass Capable | 是否支持 ERR_COR Subclass | RO |
15 | Role - Based Error Reporting | 是否支持错误上报 | RO |
14:12 | Undefined | 未定义 | RO |
11:9 | Endpoint L1 Acceptable Latency | 退出 L1 状态所能接收的延时。 0x0 表示最大 1us, 0x1 表示最大 2us, 0x2 表示最大 4us, 0x3 表示最大 8us, 0x4 表示最大 16us, 0x5 表示最大 32us, 0x6 表示最大 64us | RO |
8:6 | Endpoint L0s Acceptable Latency | 退出 L0s 状态所能接收的延时。 0x0 表示最大 64ns, 0x1 表示最大 128ns, 0x2 表示最大 256ns, 0x3 表示最大 512ns, 0x4 表示最大 1us, 0x5 表示最大 2us, 0x6 表示最大 4us | RO |
5 | Extended Tag Field Supported | 表示最大支持的 Tag 长度, 0x0 表示支持 5bit, 0x1 表示支持 8bit | RO |
4:3 | Phantom Functions Supported | 是否支持 Phantom Functions | RO |
2:0 | Max_Payload_Size Supported | 保存了 TLP 包中数据包的最大长度。 0x0 表示 128 字节, 0x1 表示 256 字节, 0x2 表示 512 字节, 0x3 表示 1024 字节, 0x4 表示 2028 字节, 0x5 表示 4096 字节。 | RO |
5.3.3 Device Control Register
Device Control 寄存器的位域如下图所示,主要用于设置 MPS(Max_Payload_Size)、MRRS(Max_Read_Request_Size)。
5.4 PCI Express Extended Capabilities
PCI Express Extended Capabilities 保存在配置空间的 100h - FFFh 中。PCI Express Extended Capabilities 只能通过 EACM 访问。PCI Express Extended Capabilities 布局如下图所示,详细信息可以参考 PCIe Space 文档。
Linux 中访问 PCI Express Extended Capabilities 的代码如下所示。
[include/uapi/linux/pci_regs.h]
#define PCI_CFG_SPACE_SIZE 256
#define PCI_CFG_SPACE_EXP_SIZE 4096
[drivers/pci/pci.c]
int pci_find_next_ext_capability (struct pci_dev *dev, int start, int cap)
{
u32 header;
int ttl;
int pos = PCI_CFG_SPACE_SIZE;
/* minimum 8 bytes per capability */
ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
if (dev->cfg_size <= PCI_CFG_SPACE_SIZE)
return 0;
if (start)
pos = start;
if (pci_read_config_dword (dev, pos, &header) != PCIBIOS_SUCCESSFUL)
return 0;
/*
* If we have no capabilities, this is indicated by cap ID,
* cap version and next pointer all being 0.
*/
if (header == 0)
return 0;
while (ttl-- > 0) {
if (PCI_EXT_CAP_ID (header) == cap && pos != start)
return pos;
pos = PCI_EXT_CAP_NEXT (header);
if (pos < PCI_CFG_SPACE_SIZE)
break;
if (pci_read_config_dword (dev, pos, &header) != PCIBIOS_SUCCESSFUL)
break;
}
return 0;
}
PCIe 的配置空间内容很多,且版本升级后又会增加。上述只介绍了配置空间中比较常见的内容,详细内容需要参考最新的 PICe Spec 文档。
参考资料
- PCIEXPRESS 体系结构导读
- PCI Express technology 3.0
- PCI_Express_Base_r3.0_10Nov10
- PCI - to - PCI Bridge Architecture Specification Revision 1.1
- PCI Express® Base Specification Revision 5.0 Version 1.0
PCIe 架构下 memory 空间、IO 空间、PCIe 配置空间简介
linjiasen 已于 2024 - 12 - 05 15 : 10 : 14 修改
1. 4 种空间迷魂阵
PCIe 架构下定义了 4 种地址空间:Memory 空间、IO 空间、配置空间和 message 空间。
我们先看一下 PCIe spec 关于这四种空间的定义:
- 配置空间 Configuration Space:One of the four address spaces within the PCI Express architecture. Packets with a Configuration Space address are used to configure Functions。
- IO 空间 I/O Space:One of the four address spaces of the PCI Express architecture. Identical to the I/O Space defined in the PCI Local Bus Specification。
- memory 空间 Memory Space:One of the four address spaces of the PCI Express architecture. Identical to the Memory Space defined in PCI 3.0。
- message 空间 Message Space:One of the four address spaces of the PCI Express architecture。
看完介绍是不是更懵 X 了,如果没有懵 X 说明这篇文章不适合你了,如果懵 X 了就继续。
message 空间其实就是用来 report 带内的 message 和 event 的(比如 error message、电源管理消息等),其实就是通过带内 message transaction 的方式来代替带外信号,用 message 的好处就是可以省去许多带外信号。
下面是 PCIe spec 中关于 message space 描述的原话:
The transaction Layer supports four address spaces: it includes the three PCI address spaces (memory, I/O, and configuration) and adds Message Space. This specification uses Message Space to supportall prior sideband signals,such as interrupts, power - management requests, and so on, as in - band Message transactions. You could think of PCI Express Message transactions as “virtual wires” since their effect is to eliminate the wide array of sideband signals currently used in a platform implementation.
事务层支持四种地址空间:它包括三种 PCI 地址空间(memory、I/O、configuration),并增加了 消息空间(Message Space)。本规范使用消息空间来支持 所有先前的边带信号,例如 中断、电源管理请求 等,作为带内消息事务。可以将 PCI Express 消息事务视为 “虚拟线”,因为它们的作用是消除当前平台实现中使用的大量边带信号。
抛开第 4 种 message 空间先不谈,先看其他三种 PCI 架构就定义的地址空间:Memory 空间、IO 空间、配置空间。说到 memory 空间和 IO 空间就不得不说统一编址和独立编址的问题。
2. 统一编址和独立编址导致 IO 空间和 Memory 空间分离
X86 采用独立编址的方式,将 memory 操作与外设 IO 操作分开了,才有了 memory 空间和 IO 空间的区分。X86 平台 CPU 内部对内存和外设寄存器访问的指令也是不同的。
-
IO 空间:
访问外部设备寄存器的地址区域,(PCI 支持 4 GB 的 IO 空间,但是 x86 平台为 64 KB),因为 X86 平台只支持 64 KB 的 IO 空间,Endpoint 为了能在 X86 上使用,只能把自己的消耗的 IO 资源限制在 64 KB 以内。由于 Endpoint 纷纷把 IO 资源的消耗限制在 64 KB,因此,大部分其他架构的 CPU 也把 IO 空间限制到 64 KB 以内了(不是无法支持,而是根本用不着这么大 IO 空间)。
-
memory 空间:
访问 memory 的地址空间,32 位平台为 4 GB。** 此 memory 空间和 main memory(平时常说的内存或者主存)是两个概念**,32 bit 平台下 CPU memory 地址总线只能寻址到 4 GB,这 4 GB 空间包括 main memory、外设 IO 空间映射(MMIO)等,不能全给 main memory,因此 32 bit 的 CPU 是无法配置 4 GB 内存的。 -
PCIe 配置空间:
PCIe spec 规定了所有 PCIe 设备(除了 host bus bridge 外)必须实现配置空间,说白了就是 PCI - SIG 规定了一种独立于 memory 空间的 PCIe 设备访问(读写、配置)机制(说白了就是一堆按规则排列的 reg)。PCI - SIG 详细规定了 PCIe 设备 reg 的排列(每个 capability id reg 的后面 4 Byte 会保存 next capability 的起始地址,这样方便芯片厂商扩展 reg,并且 PCIe 驱动软件天然可以使用 list 来管理这些 reg)。
3. 三角关系
X86 的 CPU 可以直接访问 memory 空间和 IO 空间,但是不能直接访问 PCIe 配置空间(原因很简单,X86 的 CPU 只有 memory 指令和 IO 指令,没有配置指令)。因此,** 需要把 PCIe 配置空间映射到 memory 空间或者 IO 空间**(一般不推荐映射到 IO 空间)。或者说 CPU 访问 PCIe 配置空间需要一个翻译官(RC)。这个翻译官是干好事的(帮 CPU 的 memory 访问或者 IO 访问转换成 PCIe 域的请求),不是给鬼子带路的汉奸。
下面是 PCIe system architecture 对 PCIe 三种空间的描述:
32 bit memory address 系统中,memory address space 是 4 GB,64 bit memory address 系统中是 16 EB。
-
PCI 支持多达 4 GB 的 IO address space,但是很多平台会限制 IO space 到 64 KB(16 bit),因为 X86 只能支持 64 KB IO address space。为什么要这么做呢?因为 endpoint 设备做出来肯定要在大部分 CPU 系统中都可以使用,为了在 X86 上使用,所有的 endpoint 设备消耗的 IO BAR(如果是 legacy 的 endpoint,其 IO BAR 也就是一两百个 bytes)都非常小,那么其他的 CPU 平台也就没有必须把自己支持的 IO space 做得超过 64 KB。
PCIe device configuration reg 被 map 到 一个 space,这个 space 就是 configuration address space。每个 PCI function 有 256 Byte 的 configuration address space,256 Byte x 256 bus x 32 device x 8 function = 16 MByte(** 注意是 PCI,不是 PCIe**)。
X86 CPU 可以访问 memory 空间或者 IO 空间但是不能直接访问 configuration space。CPU 需要通过 host bridge(North bridge)里面的 IO mappedaddress port 和data port 来访问 PCI configuration space。Address Port 在 IO address 的 CF8 - CFB,Data Port 在 IO address 的 CFC - CFF。
step1: CPU 向 Address Port(IO address 0xCF8)发起一个 IO wirte。
step2: CPU 向 Data Port(IO address 0xCFC)发起一个 IO read/IO wirte。North bridge 会在 PCI bus 上产生一个 configuration read 或者 configuration write。
在 X86 系统中,IO 方式的翻译官就是 CONFIG_DATA 和 CONFIG_ADDRESS,这两个位于 IO 空间的端口。这就是所谓的 PCI 的 CAM 方式,只能访问 PCI 兼容的配置空间(前面 256 Byte,从下图中也可看到 register number 只有 6 bit,但是可以访问 256 Byte(下面会详细讲为啥 6 bit 可以访问 256 Byte)。但是有些 CPU 芯片比较鸡贼,会把 reserved 的 bit24 到 30 用起来,使用 bit24 - 27 作为 extended register number,和 bit2 - 7 的 register number 组合这样可以扩展到 4 K,不过这种扩展属于芯片的私有行为,并不是所有芯片都支持)。
Configuration Address Port 的 register number(bit7 - 2)只有 6 bit,为什么能访问 256 Byte 的 PCI compatible space 空间?
原因是,256 Byte 的 PCI compatible space 空间被分割成很多 reg 段,每个 reg 段的大小是 4 Byte,这样就有了 256 / 4 = 64 个 reg 段。Configuration Address Port 的 register number(bit7 - 2)的 value 就是 register index,register index 需要乘以 4 才是 register offset,这样 6 bit 的 register number(bit7 - 2)就访问了 256 Byte 的 PCI compatible space。
寄存器 index 0 对应 configuration space 的字节 0 - 3。
寄存器 index 1 对应 configuration space 的字节 4 - 7。
……
寄存器 index 63 对应字节 configuration space 的 252 - 255。
至于为啥每个 reg 字段的大小是 4 Byte,是因为 Configuration Data Port 是 32 bit reg,每次 IO read 或者 IO write 只能发送 4 Byte 的数据。
举个例子比如你想访问配置空间的 register offset 为 0x4 的偏移(command and status reg),那么你需要的 register index 就是 0x4 / 4 = 0x1,你需要往 Configuration Address Port 的 bit7 - 2 写 0x1,0x1 << 2 = 0x4,也就是 bit7 - 0 写 0x4。
更简单的一种理解方式是把 Configuration Address Port 的低 bit7 - 0 理解成 register offset(只是这个 offset 必须 4 byte 对齐的,Configuration Address Port 的最后 2 bit 是 hardware 成 0 的),这样比把 bit7 - 2 理解成 register index,再通过 register index * 4 计算出 register offset 更简单。
下面是写了一个 test 代码来说明,代码中的 offset 参数就是 bit7 - 0。
下面是源码
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/io.h>
#define PCI_CONFIG_ADDRESS (0xCF8)
#define PCI_CONFIG_DATA (0xCFC)
// 生成 PCI 配置空间地址的宏
#define PCI_CONF_ADDRESS (bus, device, function, offset) \
(0x80000000 | ((bus) << 16) | ((device) << 11) | ((function) << 8) | ((offset) & 0xFC))
// 读取 PCI 配置空间的函数
uint32_t pci_read_config_dword (uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
uint32_t address = PCI_CONF_ADDRESS (bus, device, function, offset);
printf ("bus: 0x%02X, device: 0x%02X, funtion: 0x%02X, offset: 0x%02X: generate IO address: 0x%08X\n", bus, device, function, offset, address);
outl (address, PCI_CONFIG_ADDRESS); // 向地址端口写入地址
return inl (PCI_CONFIG_DATA); // 从数据端口读取数据
}
// 获取用户输入的函数
void get_pci_parameters (uint8_t *bus, uint8_t *device, uint8_t *function, uint8_t *offset) {
unsigned int bus_temp, device_temp, function_temp, offset_temp;
printf ("Please input (bus),(device),(function) and (offset: must align with 4.\n");
scanf ("% x % x % x % x", &bus_temp, &device_temp, &function_temp, &offset_temp);
if (offset_temp % 4 != 0) {
fprintf (stderr, "offset must align with 4.\n");
exit (1);
}
*bus = (uint8_t) bus_temp;
*device = (uint8_t) device_temp;
*function = (uint8_t) function_temp;
*offset = (uint8_t) offset_temp;
}
int main () {
// 检查 I/O 端口访问权限
if (iopl (3) != 0) {
perror ("iopl");
return 1;
}
uint8_t bus, device, function, offset;
// 获取用户输入的 PCI 参数
get_pci_parameters (&bus, &device, &function, &offset);
// 读取并打印配置空间的内容
uint32_t value = pci_read_config_dword (bus, device, function, offset);
printf ("bus: 0x%02X, device: 0x%02X, funtion: 0x%02X, offset: 0x%02X: value: 0x%08X\n", bus, device, function, offset, value);
return 0;
}
在 X86 系统中,Memory 方式的翻译官就是 MCFG(memory mapped configuration space base address description table 可以通过cat /proc/iomem 查看 PCI MMCONFIG 得到在 memory 空间的映射)其实就是 bus 0 dev 0 function 0 的 BASE 地址。这就是所谓的 PCIe 的 ECAM 方式,可以访问 PCIe 全部 4 K 配置空间。
可以看到从 0xe0000000 - 0xefffffff,总共 256 M 大小的 memory 空间被映射成了配置空间,大小是 256 M 的原因是 PCIe spec 规定 bus 占用 8 bit(256 个 bus),device 占用 5 bit(32 个 device),function 占用 3 bit(8 个 function),每个 function 有 4 KB 的配置空间。总共 = 256 bus * 32 device * 8 function * 4 KB = 256 M。
可以通过 cat /proc/iomem | grep MMCONFIG 得到 MMCONFIG 的 base 地址,然后使用 busbox 下面的 devmem 按照表格 7 - 1 的规则访问配置空间(下面程序找了 0:3.1 和 0:0.0 做了下实验),** 但是这种方式存在顺序问题,见 PCIe Spec**。
ECAM 把 memory 事务从 host CPU 转换成 PCIe fabric 配置请求。这种转换对应软件来说存在潜在的 顺序问题,因为写 memory 地址是典型的 post 事务(不需要 completion),但是写配置空间是 non - post 的请求(需要 completion)。
软件无法知道什么时候,完成者完成了 post 事务。这种场景下(ECAM 访问),软件必须要知道完成者已经完成了 post 请求,软件通常用回读刚写过 location 的这种方式来确定完成者是否完成。对于遵守 PCI order 规则的系统,read 事务必须要在 post 写完成后才能完成。然而,由于 PCI order 规则允许 non - post 写事务和 read 事务进行乱序处理,CPU 必须等待 PCIe fabric 上 non - post 写请求完成来确保完成者完成了这个事务(也就说,由于允许乱序 non - post 写事务插了 read 的队)。
举个例子,软件期望通过 ECAM 的方式写 device 的 Base address reg,然后读取 memory - map 的区域的 base address reg 的位置。如果软件发出 memory - map 读请求乱序了,在配置写请求达到前就达到,将会引起不可预知的结果。
为了阻止这个问题,处理器和主桥必须确保有一种方式可以让软件确定什么时候使用 ECAM 的写请求被完成者完成。
下面我们通过 0:3.1 和 0:0.0 两个设备,验证下 config read 和 ECAM 的 read 是否一致。我们可以看到系统中 mmconfig 的地址是 0xf800_0000,0:3.1 按照 table 7 - 1 算出的 offset 是 0x1_9000,我们访问 0xf801_9000 的地址就是 0:3.1 的配置空间的 0 地址,也就是 deviceid 和 vendorid。可以看出 ECAM 方式访问和配置方式访问的值是一致的。
BAR(base address registers)就是为了把设备的内部各种资源映射(芯片内部寄存器或者 DDR)到 IO 空间(IO BAR)或者 memory 空间(memory BAR)。
关于 IO 方式和 memory 方式访问 PCIe 配置空间的具体实现,参考
-
PCI/PCIe 的那些事 - 配置空间(Configuration Space)_mmcfg base 怎么设置 - CSDN 博客
https://blog.csdn.net/huangkangying/article/details/50570612 -
如何访问 pcie 整个 4k 的配置空间 - CSDN 博客
-
Linux 下用 memory 方式访问 PCIE 空间_pci firmware specification revison-CSDN 博客
-
PCIE 总线的地址问题 - 知乎
https://zhuanlan.zhihu.com/p/34047690 -
PCI/PCI Express Configuration Space Access Advanced Micro Devices, Inc(链接已沉寂)
http://developer.amd.com/wordpress/media/2012/10/pci - pci express configuration space access.pdf
via:
-
PCIe 学习笔记之 pcie 结构和配置空间_pcie class code-CSDN 博客
https://blog.csdn.net/yhb1047818384/article/details/106676528 -
PCIe 总线 - 配置空间介绍_pcie 配置空间 - CSDN 博客
https://blog.csdn.net/u011037593/article/details/138016148 -
PCIe 架构下 memory 空间、IO 空间、PCIe 配置空间简介_pcie register number-CSDN 博客
https://blog.csdn.net/linjiasen/article/details/87944672