MindShare 官网,地址如下:
MindShare
Charpter 3: Configuration 概述
主要介绍 PCIe 驱动对 PCIE 设备中 function 的 Config Header 的访问.
1. 总线、设备与功能定义
每一个 PCIE function 都是独一无二的,通过设备号与总线号区分。
2. PCIe 总线
PCIe 最多可以分配 256 个总线总线号,总线 0 在 Root Complex 中,由硬件分配。总线 0 下挂一个或多个 PCI-PCI bridge。
每一个 PCI-PCI bridge 都会创建一个新总线。
pcie 驱动从 bus0,device0, funcion0 开始查找,如果查找到一个 PCIE Bridge,PCIE 驱动就分配一个新的总线号。(枚举过程)
3. PCIe 设备
允许 32 个设备连接到同一个 PCIE 总线,但是 PCIE 是点对点的连接,意味着只有一个设备可以连接到 PCIE 链路,这个设备总是设备 0.
但是 Root Complex 与 Switch 内部存在一条虚拟总线,这个虚拟总线允许多个设备连接。
所以需要使用 Switch 来扩展 pcie 拓扑结构
4. PCIe 功能
每个设备都必须实现 function 0, 每一个设备最多实现 8 个 function, function 编号不必连续。
每一个 function 都有自己的配置空间。
5.示例的 PCIe 拓扑结构
下图展示了一个 PCIe 示例拓扑结构中 bus, device, function 分配与使用:
6. 配置地址空间
PCIe 设备为每一个 function 定义了一个专用的配置空间块。配置空间前 64 字节为 PCI 兼容配置空间。
配置空间的剩余 192 字节可以实现一些可选的功能结构。
PCIE 必须实现如下的功能结构:
- PCIe 功能结构
- 电源管理
- MSI、MSI-X
7. 64 字节 PCI 兼容配置空间
64 字节 PCI 兼容配置空间如下:
8. 扩展配置空间- 4KB PCIe Configuration Space
PCIe 诞生时,256 字节配置空间不足以包含所有的功能结构,所以将 256 字节配置空间扩展为 4KB,前 64 字节依旧为 PCI 兼容配置空间:
9. Host-PCI Bridge 配置空间
Host-PCI Bridge 是一个特殊的设备,位于 Root Complex 中,接受 CPU 的请求,并转换为标准 PCI 请求。代表 CPU 与下游 PCIe 设备通信。
Host-PCI Bridge 的配置空间实现是基于芯片的,不同 SOC 厂商也许有不同的实现,且通过内存映射的寄存器访问。
10. 只有 Root-Complex 可以发起配置请求
Root Complex 作为 CPU 代言人,只有 Root Complex 可以发起配置请求。
Root Complex 基于设备的总线号、设备号与功能号配置下游的设备。
11. 配置空间请求事务的生成
CPU 由于只能访问地址,所以不能直接生成 PCI 的配置读、写请求,需要 Root Complex 将地址访问翻译为对应的 pci 配置请求。
配置空间能通过两种方式访问:
1. 传统 PCI 配置机制,使用 I/O 间接访问(x86 处理器?);
2. 增强型的配置机制,使用内存映射访问。
12. 传统 PCI 配置机制 - I/O 间接访问(x86? ARM不使用)
使用两个 4 字节的 I/O 地址空间的寄存器实现配置空间的间接访问。
- Configuration Address Port: 0xCF8
- Configuration Data Port: 0xCFC
Configuration Address Port 寄存器位域说明如下:
13. 总线比较与数据端口访问
PCI bridge 实现一个 Secondary Bus Number Register (次级总线号寄存器)和一个 Subordinate Bus Number Register (下属总线号寄存器)。
次级总线号是 bridge 之下的第一个总线号;
下属总线号是 bridge 之下的最大的一个活跃目标总线号
当 Bridge 收到一个配置请求,会对比目标总线号是否在 secondary Bus number 与 Subordinate Bus number 之间,如果在,说明目标设备是当前总线的下游设备。
如果目标总线号与次级总线号相等,那么 bridge 会发送 Type 0 Request(硬件数据包类型)。
如果目标总线号在 secondary Bus number 与 Subordinate Bus number 之间,那么 bridge 会发送 Type 1 Request.
下图为次级总线号与下属总线号分配示例:
14. 增强型配置访问机制 - 地址映射访问(ARM,我们主要使用)
增强型配置访问机制使用地址映射的机制访问配置空间。
使用增强型配置访问机制,每一个 function 需要 4KB 配置空间,一个 PCIE 架构需要 256MB 配置空间。
地址映射中位域定义如下表:
位域 | 描述 |
---|---|
bit[63:28] | 配置空间 256MB 对齐,表示配置空间的高位,具体值由 SOC 芯片厂商确定 |
bit[27:20] | Bus Number - [0 : 255] |
bit[19:15] | Device Number - [0 : 31] |
bit[14:12] | Function Number - [0 : 7] |
bit[11: 2] | 4KB 配置空间内寄存器偏移,4字节对齐 |
bit[ 1: 0] | 定义访问的大小与字节使能设置(不清楚,默认0?)。 |
直接访问上述位域组成的地址,可访问任一 bus / dev / func 的配置空间。
比如:
mov ax,[E0400000h];memory-mapped Config read,16bit 读访问
上述 16 位内存读请求,表示访问 Bus 4, Dev0, Function 0, Register 0(Vendor ID 寄存器)。
PCIe 配置读请求发送流程如下图:
15. 配置空间访问请求
这是硬件相关的,软件不需要太关心。
Bridge 可以翻译生成 Type0 与 Type1 两种配置请求,当目标总线是当前 bridge 的次级总线时,生成 Type 0 请求,当目标请求是下属总线时,生成 Type 1 请求。
16. PCIe 系统枚举
系统上电后,pcie 驱动必须扫描整个 pcie 拓扑结构,用于分配 Bus Number,扫描过程称为枚举。
PCI-PCI bridge 的上行端口连接主级(上级)总线,bridge 的下行端口连接次级总线。
系统上电后,驱动只能确定 Root Complex 中的 Host/PCI Bridge 与 bus 0 存在。
Root Complex 中的 Host-PCI Bridge 没有上级总线,只有作为 bus 0 的次级总线。
17. 查找 function 是否存在
因为每一个 function 都有 4KB 的 configuration space。
所以 pcie 驱动通常使用读 Vendor ID 寄存器的方法来发现一个设备。Vendor ID 由 PCI-SIG 分配,并被硬件编码进 Vendor ID 寄存器。
通过组合不同的 bus, device, function,读对应的配置空间 Vendor ID 寄存器,驱动可以查找到系统存在的所有设备。
PCIe 驱动读 Vendor ID 寄存器时,返回 0xFFFF, 即表示当前设备不存在。
(也存在一种情况,设备存在,但未准备好响应配置读请求。)
18. 确定 function 类型:Endpint 或 bridge
标准的 PCIe 枚举处理能够确定一个function 是否为终端或 bridge。
Header Type 位域的低 7 位,可以用于确定 Function 类型:
-
0 为终端设备;
-
1 表示 PCI- PCI bridge
-
2 表示 CardBus Bridge(不再使用)
位域图如下:
19. 上电枚举示例
首先贴出如下的上电枚举完成后 ,bus 分配结果图:
上电启动后,驱动执行枚举:
- 软件更新 Host-PCI Bridge 的 Secondary Bus Number 寄存器为 0,并设备 Subordinate Bus Number 寄存器为 255.(这是 Root Complex Controller 的内存控制寄存器?操作外设控制器?)
- 从 bus 0- device 0 - function 0 开始枚举, 尝试读取 Vendor ID, 如果 Vendor ID 存在,那么说明当前 device 存在且包含至少 1 个 function, 如果没有返回有效的 Vendor ID, 那么说明当前设备不存在,因为每一个 device 必须实现 function 0。
- bus 0- device 0 - function 0 的配置空间对应 Bridge A 的配置头,Header Type 位域值为 01h, 表示这是一个桥,Header Type 位域 bit7 = 0, 表示这是一个单 function 设备。
- 驱动的枚举过程查找到了一个 bridge 桥,那么分配总线号:
- Primary Bus Number Register = 0 : 上级总线号为 0 号总线
- Secondary Bus Number Register = 1 : 下级总线号为 1 号总线
- Subordinate Bus Number Register = 255 : 最大活跃下级总线号暂时设置为 255(以便执行一个深度优先的枚举)
- 驱动必须执行一个深度优先的枚举,所以此时应该优先枚举 bus1 下的设备,而不是 bus 0 的其他设备。
- 驱动继续枚举 bus 1 - device 0 - function 0, 尝试读取 Vendor ID, 返回 Bridge C 的 Vendor ID,Header Type 位域值为 01h, 表示这是一个桥,Header Type 位域 bit7 = 0, 表示这是一个单 function 设备。
- Bridge C 的 Header Type 位域值为 0x01, 表示这是一个 PCI Bridge,Header Type 位域 bit7 = 0, 表示这是一个单 function 设备。
- 驱动的枚举过程查找到了一个 bridge, 那么分配总线号:
- Primary Bus Number Register = 1 : 上级总线号为 1 号总线
- Secondary Bus Number Register = 2 : 下级总线号为 2 号总线
- Subordinate Bus Number Register = 255 : 最大活跃下级总线号暂时设置为 255(以便执行一个深度优先的枚举)
- 驱动继续执行一个深度优先的枚举,枚举 bus 2 - device 0 - function 0, 也就是对应 Bridge D.
…
Chapter 4: 地址空间与传输路由
本章描述 Endpoint/Bridge 的 function 通过 BAR 寄存器请求地址空间,驱动必须设置所有 bridge 的 Base/Limit 寄存器来正确路由 TLP。
1. 三种内存地址空间
大多数 PCIE 设备都存在内部寄存器和存储区, 意味着 PCIE 设备需要把内部寄存器与存储区映射到 CPU 可寻址的内存地址空间。
PCIE 支持三种内存地址空间:
- Configuration Space
- Memory Space
- I/O Space
2. 配置空间
前面的章节已经说明,配置空间用来控制与检查设备状态,每一个 PCIE 设备的 Function 都有专门的配置空间。
3. Memory 与 I/O 空间
早期的 intel 处理器,通过 I/O 地址空间来访问 I/O 设备的内部寄存器和存储区。
但如今 I/O 设备的寄存器和存储区被映射到内存地址空间,称为 MMIO(内存映射 I/O 设备)。
PCIE 规范也不鼓励使用 I/O 地址空间来映射设备寄存器和存储区。
PCIe 可以支持将内存地址映射到 64bit Mem 空间。
不仅 Endpoint 可以使用 MMIO 来访问,switch 与 Root Complex 也可以使用 MMIO 访问特有的寄存器(Root Complex 也包含 PCI-PCI Bridge)。
4. Prefetchable 与 Non-Prefetchable Mem 空间
Perfetchable Memory 存在下列优良的属性:
- 读取没有副作用
- 允许写合并
如果一个内存区域被定义为 Perfetchable,那么该区域的数据被允许提前准备好,以便请求者下一步读取(类似 CPU 的提取预测,用于提升性能)。
问:什么样的内存空间读取存在副作用?
答:内存映射的状态寄存器,如果设计为读清除时,那么读取可能存在副作用。
Pcie 设备内存映射示例:
5. Base Address Registers (BARs)
每一个 PCIE 设备可能需要映射不同的地址空间类型和大小。
pcie 设备不能决定自身内部寄存器与存储区在内存空间的位置,这应该由系统的 pcie 驱动决定.
因此 pcie 设备需要提供一种方式,供驱动确定设备需要的地址空间类型与大小。
如果可以满足 pcie 设备的地址要求,那么驱动会分配合适的地址类型与大小。
pcie 设备通过 Configuration Header 的 Base Address Registers(BAR)指示 PCIE 驱动应该分配地址类型与大小。
Type 0 Header (Endpoint) 有 6 个 32 位的 BAR 寄存器。
Type 1 Header (Bridge) 有 2 个 32 位的 BAR 寄存器。
Pcie 设备的设计者将 pcie 设备所需要的地址与类型信息硬件编码到 BARs 的低位。
Type 0 Header 拥有 6 个 BAR,那么最多可以为设备分配 6 个不同的地址空间(只要 pcie 设备对 BAR 的低位进行了硬件编码)
如果一个 BAR 寄存器被硬件编码为全 0(写 1 无效),那么表示驱动不需要为当前 BAR 进行内存映射。
BAR 寄存器在 Header 中的位置如下:
一旦 BAR 被编码,那么可以通过内存映射的形式访问 pcie 设备的寄存器和地址空间。内存访问的地址必须是 BAR 设置的地址范围之内。
6. 32 位内存空间映射请求 - BAR 配置示例
在本例中,PCIe 设备的 function 的 BAR0 请求一个 4KB 的 Non-Prefetchable memory(NP-MMIO) 用于映射本身的寄存器与存储区。
BAR 配置图解如下:
BAR 配置过程如下:
-
在上图的(1)中展示了未初始化的 BAR0,BAR0 的低位保存着 pcie 设备设计者硬件编码的内存映射大小与类型。PCIE 驱动首先写全 1 到每一个 BAR 来设置所有的可写位(低位的硬件编码位不受影响)。
-
写每一个 BAR 寄存器为 1 后,驱动读回 BAR 寄存器, 如上图中的(2),从 BAR0 开始,来确定 pcie 设备请求的地址空间大小与类型。 写全 1 后,最低有效位指示了 pcie 设备需要的内存地址空间(低 4 位不算最低有效位)。
- 上例中,读回的结果为 0xFFFF F000, 各位域描述如下图:
BAR 位域 描述 bit 0 0 表示请求映射 Mem 空间,1表示请求映射 I/O 空间,本例中,读回为 0 bit[2:1] 00 表示请求映射 32 位 Mem, 10 表示请求映射 64 位 Mem, 本例中,读回为 0b00 bit3 0 表示请求映射 non-prefetchable mem, 1 表示请求映射 prefetchable Mem, 本例中读回为 0 bit[11: 4] 本例中硬件编码为全0,读回为0 bit[31:12] 本例中读回为全1,最低有效位 bit12 = 1, 表示请求映射的内存大小 2^12 = 4KB -
最后一步是为当前 BAR 分配合适的地址范围, 如上图中的(3), 本例中 BAR0 地址映射请求被映射到 0xF900_0000 - 0xF900_0FFF。
BAR0 寄存器编码后,一旦驱动使能 Command Register(04h),pcie 设备会接收内存访问请求。
7. 64位内存空间映射请求 - BAR 配置示例
当 BAR 指示请求映射的内存类型为 64 位内存时,此时使用两个连续的 BAR 被用来支持 64 位地址映射请求。
在本例中, BAR1 与 BAR2 被用来请求 64MB 的 64bit Prefetchable-Memory 用于映射自身的寄存器与存储区.
BAR 配置图解如下:
BAR 配置过程如下:
-
上图中,(1)展示了 BAR 未初始化的状态内容,pcie 设备设计者硬件编码了 BAR1 的低位,用来指示请求映射的地址类型与大小。上图中,(2)展示了BAR1 与 BAR2 已被全写为 1 的寄存器内容。
-
PCIE 驱动读回 BAR1,发现 BAR1 请求 64 bit 地址空间,所以驱动会将 BAR2 作为 64-bit 的高位,并读取 BAR2, 此时 BAR2 的低 4 位不在指示请求映射的地址类型。读回的寄存器位域解释如下:
BAR1/2 位域 描述 BAR1- bit0 读回为 0,指示请求映射 Memory 内存空间 BAR1- bit[2:1] 读回为 0b10, 指示请求的是 64bit Mem, 此时驱动会将 BAR2 会作为 64bit 的高 32 位 BAR1- bit3 读回为 1, 指示请求映射的是 prefetchable Mem BAR1- bit[25:4] 读回为全 0,硬件编码为 0,表示请求映射的内存大小 BAR1- bit[31:26] 读回为全 1,最低有效位为 1,表示请求映射的内存大小为 2^26 = 64MB BAR2-bit[31:0] 读回为全1,64bit Mem 的高32位 -
最后一步是驱动分配合适的地址空间类型与大小,并设置 BAR2-BAR1。
BAR 配置完成后,只要驱动使能 Command Register(04h),那么可以进行内存访问。
8. I/O 空间映射请求 - BAR 配置示例
(略, ARM 不需要配置)
9. 所有的 BAR 寄存器必须按顺序配置
如果不存在地址映射请求,那么 BAR 会被硬件编码为全0,也就是写 1 无效。
即使存在一个 BAR 为全0,依然要配置接下来的 BAR,即所有的 BAR 都需要尝试配置,因为 function 设计者可能不会选择 BAR0 作为设备的地址映射请求。
10. Bridge 设备 Header 中的 Base 与 Limit 寄存器
只要 function 的 BARs 被编码,那么 function 就获得使用的地址映射范围。
每一个 bridge 需要知道下游设备的地址映射范围,以便于将上级总线的事务,转发给下级总线。
在 Type 1 Header 中存在 Base 与 Limit 寄存器用于保存下游设备的地址映射范围,也就是 BAR 映射的地址范围.
Bridge 的存在 3 类 Base 与 Limit 寄存器需要设置, 对应三种内存空间:
- Prefetchable Mem
- Non-Prefetchable Mem
- I/O Space
11. Bridge 设备中 Prefetchable Mem Base/Limit 寄存器
Type 1 header 拥有两对 prefetchable memory base/limit register:
- Prefetchable Memory Base/Limit registers
- Prefetchable Memory Base/Limit registers upper 32 bits
两对 prefetchable memory base/limit register 位置如下图,寄存器值设置为上述(第 7 点)配置的值:
寄存器解释如下:
寄存器 | 值 | 描述 |
---|---|---|
Prefetchable Memory Base | 0x4001 | 16位的寄存器,高 12 位用于保存地址映射的 bit[31:20], 低 4 位,为 1 时,表示使用 64 bit Mem,为 0 时表示使用 32位 Mem |
Prefetchable Memory Limit | 0x43f1 | 16位的寄存器,高 12 位用于保存地址映射的 bit[31:20], 低 4 位,为 1 时,表示使用 64 bit Mem,为 0 时表示使用 32位 Mem |
Prefetchable Memory Base Upper 32 Bits | 0x2 | 32 Bit 寄存器, 保存 64 位地址映射的 bit[63:32] |
Prefetchable Memory Limit Upper 32 Bits | 0x2 | 32 Bit 寄存器, 保存 64 位地址映射的 bit[63:32] |
(只有 prefetchable 内存支持 64 bit 地址映射)
12. Non-Prefetchable Mem Base/Limit 寄存器
bridge 的不可预取内存映射范围。
type 1 header 只支持 32 位的 Non-Perfetchable Mem.只有一对 16 位的 BASE/Limit 寄存器对用于指示地址范围:
- Memory Base
- Memory Limit
Memory base/limit register 位置如下图,寄存器值设置为上述(第 6 点)配置的值:
寄存器解释如下:
寄存器 | 值 | 描述 |
---|---|---|
(Non‐Prefetchable) Memory Base | 0xF900 | 16 位的寄存器,高 12 位存储不可预取内存地址的 Bit[31:20], 低 4 位固定为 0 |
(Non‐Prefetchable) Memory Limit | 0xF900 | 16 位的寄存器,高 12 位存储不可预取内存地址的 Bit[31:20], 低 4 位固定为 0 |
bridge 对于内存的最小配置范围是 1MB ,那么即使下游端点使用不到 1MB,空余的地址范围依旧不允许分配给其他设备。
13. Bridge 设备中 I/O 范围
类似 Mem Base/Limit, Type 1 Header也有两对 Base/Limit 用于配置 I/O 空间。
寄存器解释:略
14. Bridge 未使用 Base 与 Limit 寄存器
并不是所有的 pcie 设备会使用 3 种类型的地址空间,实际上 pcie 规范不鼓励使用 I/O 空间。
如果 Bridge 的下游 endpoint 没有请求 I/O 地址空间,那么可以把 I/O Base 设置为比 I/O Limit 大,以此种形式来表明,当前 bridge 的下游设备未映射 I/O 空间。
15. PCIE 事务通过 Base/Limit 实现地址路由,将 pcie 数据包发送给对应设备
系统枚举的分配 bus 号用来干嘛?(读/写配置空间)
当 cpu 内存访问的地址在某一个 BASE:Limit 范围内时, brige 会路由pcie 数据到下级总线。示例如下图: