1. PCIe 基础概念:总线拓扑、设备层级、配置空间与 BAR 空间
PCI Express(PCIe)作为现代计算机系统中最主流的高速串行互连标准,是 DMA 传输与环形缓冲区设计的物理基础。理解 PCIe 的总线拓扑、设备层级、配置空间与 BAR 空间,是编写高效 PCIe 设备驱动的第一步。本节将系统梳理这些核心概念,为后续的 DMA 引擎编程与环形缓冲区管理奠定坚实的硬件抽象基础。
1.1 PCIe 总线拓扑结构
PCIe 采用 树形拓扑,由 根复合体(Root Complex, RC)、交换器(Switch) 和 端点(Endpoint, EP) 组成。与传统的并行 PCI 总线不同,PCIe 使用点对点串行链路,每个设备通过独立的逻辑通道与上游通信,消除了共享总线的仲裁开销。
- 点对点连接: 每个链路只连接两个设备,不再共享总线。
- 交换器实现扇出: 交换器内部包含多个虚拟 PCI 桥,将上游端口扩展到多个下游端口。
- 树形结构: 根复合体位于树根,端点位于树叶,交换器位于中间节点。
- 层级唯一性: 每个设备由 总线号(Bus)、设备号(Device) 和 功能号(Function) 唯一标识(BDF)。
struct pci_dev 结构体代表一个 PCIe 设备(端点或桥)。通过 pci_get_slot() 或遍历 pci_devices 链表,驱动可以获取设备的 BDF 信息,进而访问其配置空间。
1.2 设备层级与功能组织
PCIe 设备在逻辑上被组织为 设备(Device) 和 功能(Function) 两个层级。一个物理设备最多可包含 8 个独立的功能(Function 0 到 Function 7),每个功能拥有独立的配置空间、BAR 空间和中断能力。
| 层级 | 位宽 | 取值范围 | 说明 |
|---|---|---|---|
| 总线号 (Bus) | 8 位 | 0 – 255 | 标识系统中的一条 PCIe 总线,根复合体通常为 Bus 0。 |
| 设备号 (Device) | 5 位 | 0 – 31 | 标识总线上的一个物理设备。 |
| 功能号 (Function) | 3 位 | 0 – 7 | 标识设备内的一个独立功能,Function 0 必须存在。 |
- Function 0: 控制与状态寄存器(CSR)接口,用于驱动初始化与配置。
- Function 1: DMA 引擎,负责主机与设备间的批量数据传输。
- Function 2: 中断控制器,管理 MSI-X 中断的分配与路由。
struct pci_dev 实例。
1.3 配置空间与 Capability 结构
PCIe 配置空间是驱动与设备交互的“门牌号”。每个 PCIe 功能拥有 4 KB 的配置空间(传统 PCI 只有 256 字节),前 64 字节为 配置头(Configuration Header),包含 Vendor ID、Device ID、Class Code、BAR 寄存器等关键字段。剩余空间用于存放 Capability 链表,描述设备支持的扩展特性。
| 偏移 | 字段 | 位宽 | 说明 |
|---|---|---|---|
| 0x00 | Vendor ID | 16 位 | 设备制造商 ID(如 0x8086 为 Intel)。 |
| 0x02 | Device ID | 16 位 | 设备型号 ID。 |
| 0x08 | Class Code | 24 位 | 设备类别(基类、子类、编程接口)。 |
| 0x10 – 0x24 | BAR0 – BAR5 | 32 位 × 6 | 基地址寄存器,描述设备内存/I/O 空间。 |
| 0x34 | Capabilities Pointer | 8 位 | 指向 Capability 链表的起始偏移。 |
pci_read_config_*() / pci_write_config_*() 系列函数访问,不能直接内存映射。Capability 链表通过 pci_find_capability() 或 pci_find_ext_capability() 遍历,用于检测 MSI-X、PCIe 电源管理、ARI 等高级特性。
1.4 BAR 空间与地址映射
基地址寄存器(Base Address Register, BAR) 是 PCIe 设备向系统声明其所需地址空间的“窗口”。每个功能最多拥有 6 个 BAR(BAR0 – BAR5),每个 BAR 可以映射为 内存空间(Memory Space) 或 I/O 空间(I/O Space)。在 DMA 驱动设计中,BAR 空间通常用于映射设备内部的 控制寄存器、状态寄存器 和 描述符队列。
| 特性 | 内存空间 (Memory Space) | I/O 空间 (I/O Space) |
|---|---|---|
| 访问方式 | 通过 ioremap() 映射到内核虚拟地址,使用 readl()/writel() 访问。 |
通过 pci_ioport_map() 映射,使用 inl()/outl() 访问。 |
| 地址范围 | 32 位或 64 位(取决于 BAR 的 Prefetchable 位)。 | 16 位(x86 平台 I/O 地址空间有限)。 |
| 性能 | 高,支持 CPU 缓存和写合并(Write-Combine)。 | 低,每次访问需通过 I/O 控制器,不可缓存。 |
| 典型用途 | DMA 描述符队列、控制寄存器、数据缓冲区。 | 传统设备控制端口(已逐渐淘汰)。 |
- 调用
pci_request_regions()声明设备的所有 BAR 资源。 - 调用
pci_ioremap_bar()将指定 BAR 映射到内核虚拟地址空间。 - 通过映射后的虚拟地址读写设备寄存器,完成初始化配置。
- 在驱动卸载时调用
pci_iounmap()和pci_release_regions()释放资源。
1.5 本章小结
PCIe 总线拓扑、设备层级、配置空间与 BAR 空间构成了驱动开发的“硬件坐标系”。掌握这些基础概念,意味着你能够:
- 理解设备在总线树中的位置(BDF),并正确枚举设备。
- 通过配置空间读取设备能力,判断是否支持 MSI-X、64 位地址等 DMA 相关特性。
- 通过 BAR 空间映射设备寄存器,为后续的 DMA 描述符队列和环形缓冲区编程做好准备。