2. PCIe配置空间

PCIe配置空间是软件(驱动)与硬件(PCIe设备)之间进行初始化、资源分配和状态查询的核心接口。理解配置空间的结构,是编写正确PCIe驱动的基础。本章将深入剖析Type0/Type1配置空间的布局、Capability链表的遍历机制,以及PCIe独有的扩展配置空间。

2.1 配置空间基础:256字节的“身份证”

每个PCIe设备(包括Endpoint、Switch、Root Complex)都必须实现一个标准的配置空间。传统PCI配置空间大小为256字节,由PCI规范定义,PCIe完全兼容这一部分。配置空间通过配置读写总线周期(CFG Read/Write)进行访问,在VxWorks中通常通过pciConfigInLong/pciConfigOutLong等函数操作。

配置空间分为两个逻辑部分:Header(64字节)和Capability链表(192字节)。Header决定了设备的类型和基本功能。

关键概念: PCIe配置空间的访问地址由 Bus Number(总线号)、Device Number(设备号)、Function Number(功能号)和 Register Offset(寄存器偏移)共同决定。VxWorks 的 PCI 库封装了这些细节,驱动开发者只需提供 BDF 和偏移量。

2.2 Type0 与 Type1 配置空间 Header

PCIe规范定义了两种Header类型,由配置空间偏移0x0E处的Header Type寄存器标识:

  • Type0 (Header Type = 0x00):用于Endpoint设备(非桥设备)。
  • Type1 (Header Type = 0x01):用于PCIe-to-PCI桥或PCIe Switch的Upstream/Downstream端口(桥设备)。

两种Header的前16字节布局相同(Vendor ID、Device ID、Command、Status、Revision ID、Class Code等),但从偏移0x10开始,布局出现显著差异。

偏移 (Bytes) Type0 (Endpoint) 寄存器 Type1 (Bridge) 寄存器
0x00 - 0x03 Vendor ID (16b) + Device ID (16b) Vendor ID (16b) + Device ID (16b)
0x04 - 0x07 Command (16b) + Status (16b) Command (16b) + Status (16b)
0x08 - 0x0B Revision ID (8b) + Class Code (24b) Revision ID (8b) + Class Code (24b)
0x0C - 0x0F Cache Line Size + Latency Timer + Header Type + BIST Cache Line Size + Latency Timer + Header Type + BIST
0x10 - 0x27 6 x Base Address Registers (BARs) 2 x BARs + 8B Expansion ROM Base
0x28 - 0x3F CardBus CIS Pointer / Subsystem ID / Expansion ROM Bridge-specific: Primary Bus #, Secondary Bus #, Subordinate Bus #, I/O & Memory Limits/Base
实战要点: 驱动在读取BAR之前,必须先判断Header Type。对于Type1设备,其BAR数量较少(通常只有2个),但多了用于配置下游总线范围的寄存器(Primary/Secondary/Subordinate Bus Number),这是枚举PCIe树的关键。

2.3 Capability链表:扩展功能的“索引目录”

传统PCI配置空间的前64字节(Header)定义了基本功能,但现代PCIe设备需要支持电源管理(PM)、消息 signaled 中断(MSI/MSI-X)、PCIe原生特性(如Max Payload Size)等。这些扩展功能通过Capability链表机制实现。

Capability链表起始于配置空间偏移0x34处的Capabilities Pointer寄存器(8位)。该指针指向第一个Capability结构的起始偏移。每个Capability结构包含:

  • Capability ID (1 Byte):标识功能类型(0x01 = PM, 0x05 = MSI, 0x10 = PCIe)。
  • Next Capability Pointer (1 Byte):指向下一个Capability结构的偏移,若为0x00则表示链表结束。
  • Capability-specific Data (N Bytes):根据Capability ID不同,后续数据长度和格式各异。

驱动必须遍历此链表来发现设备支持的所有高级特性。

/* VxWorks 中遍历 Capability 链表示例 */
LOCAL STATUS pcieFindCapability (UINT16 bdf, UINT8 capId, UINT8 *pCapOffset)
{
    UINT8   capPtr;
    UINT8   nextPtr;
    UINT16  status;

    /* 1. 读取 Status 寄存器,检查 Capabilities List 位 (bit 4) */
    pciConfigInWord (bdf, PCI_CFG_STATUS, &status);
    if (!(status & PCI_STATUS_CAP_LIST)) {
        return ERROR; /* 设备不支持 Capability 链表 */
    }

    /* 2. 读取 Capabilities Pointer (偏移 0x34) */
    pciConfigInByte (bdf, PCI_CAPABILITY_LIST_PTR, &capPtr);

    /* 3. 遍历链表 */
    while (capPtr != 0) {
        UINT8 id;
        pciConfigInByte (bdf, capPtr, &id);
        if (id == capId) {
            *pCapOffset = capPtr;
            return OK;
        }
        pciConfigInByte (bdf, capPtr + 1, &nextPtr);
        capPtr = nextPtr;
    }
    return ERROR;
}
避坑指南: 某些老式或非标准设备可能未正确实现Capability链表(Status寄存器的Cap List位为0)。驱动在遍历前必须检查该位,否则读取无效的Capabilities Pointer可能导致系统挂死或访问违例。

2.4 PCIe扩展配置空间:4KB的“增强引擎”

传统256字节配置空间对于PCIe引入的大量新特性(如AER、VC、ACS、SR-IOV)来说远远不够。PCIe规范将配置空间从256字节扩展至4KB(偏移0x000 - 0xFFF)。前256字节(0x000 - 0x0FF)与PCI配置空间完全兼容,而扩展部分(0x100 - 0xFFF)称为PCIe扩展配置空间。

扩展配置空间通过Enhanced Capability链表组织,其结构与传统Capability链表类似,但起始位置固定为偏移0x100,且每个Enhanced Capability Header为4字节:

  • Capability ID (16 bits):标识扩展功能(如0x0001 = AER, 0x0010 = ACS)。
  • Capability Version (4 bits):版本号。
  • Next Capability Offset (12 bits):指向下一个Enhanced Capability的偏移(从配置空间基址0x000开始计算)。

访问扩展配置空间需要通过PCIe特有的ECAM (Enhanced Configuration Access Mechanism)机制,即内存映射IO方式。在VxWorks中,BSP通常会在系统启动时将ECAM空间映射到一段连续的虚拟地址,驱动通过直接内存访问方式读写扩展配置空间。

/* VxWorks 中通过 ECAM 访问扩展配置空间示例 */
/* 假设 BSP 已将 ECAM 基址映射到 pEcamBase */
volatile UINT8 *pEcamBase = (UINT8 *)ECAM_VIRT_BASE;

/* 计算 BDF 对应的 ECAM 地址偏移 */
/* 公式: offset = (Bus << 20) | (Device << 15) | (Function << 12) | Register */
UINT32 ecamOffset = (busNum << 20) | (devNum << 15) | (funcNum << 12) | regOffset;

/* 读取扩展配置空间中的 AER Capability 的 Uncorrectable Error Status 寄存器 */
UINT32 *pAerReg = (UINT32 *)(pEcamBase + ecamOffset + 0x104); /* AER 通常从 0x100 开始 */
UINT32 uncorrStatus = *pAerReg;
性能与兼容性: 传统256字节配置空间通过IO端口(CF8/CFC)访问,而扩展配置空间必须通过MMIO(ECAM)访问。在VxWorks中,如果BSP未正确初始化ECAM映射,驱动将无法访问任何扩展Capability。建议在系统初始化阶段通过sysPciEcamInit()确认ECAM基址有效。

2.5 配置空间结构总览图

下图展示了从传统PCI配置空间到PCIe扩展配置空间的完整结构,以及Capability链表与Enhanced Capability链表的关系。

PCIe 配置空间结构总览 传统 PCI 配置空间 (256 Bytes) 偏移 0x000 - 0x0FF Header (64 Bytes) 偏移 0x00 - 0x3F Type0: 6 BARs / Type1: 2 BARs + Bridge Regs Capabilities Pointer @ 0x34 Capability 链表 (192 Bytes) 偏移 0x40 - 0xFF Cap ID: 0x01 (PM) Next Ptr → 0x50 Cap ID: 0x05 (MSI) Next Ptr → 0x70 Cap ID: 0x10 (PCIe) Next Ptr → 0x00 Cap Ptr PCIe 扩展配置空间 (4KB) 偏移 0x000 - 0xFFF 兼容区 (256 Bytes) 偏移 0x000 - 0x0FF 与左侧传统配置空间完全一致 Enhanced Capability 链表 偏移 0x100 - 0xFFF ECap ID: 0x0001 (AER) Next Offset → 0x200 ECap ID: 0x0010 (ACS) Next Offset → 0x300 ECap ID: 0x0012 (SR-IOV) Next Offset → 0x000 兼容 ECAM (MMIO) 访问方式,偏移 0x100 开始为 Enhanced Capability

2.6 关键寄存器与驱动开发对照表

下表总结了驱动开发中需要重点关注的配置空间寄存器及其在VxWorks中的访问方法:

偏移 寄存器名称 大小 驱动开发用途 VxWorks 访问函数
0x00 Vendor ID / Device ID 32-bit 设备识别与匹配 pciConfigInLong(bdf, 0x00, &val)
0x04 Command / Status 32-bit 使能IO/Memory/Bus Master;检查Cap List位 pciConfigInWord/OutWord
0x08 Class Code / Revision ID 32-bit 设备分类(存储、网络、显示等) pciConfigInLong
0x10 - 0x24 Base Address Registers (BAR0-5) 32-bit each 获取设备MMIO/I/O基址与大小 pciConfigInLong/OutLong
0x34 Capabilities Pointer 8-bit Capability链表遍历入口 pciConfigInByte
0x3C Interrupt Line / Pin 16-bit 中断号分配与路由 pciConfigInWord/OutWord
0x100 PCIe Extended Capability Header 32-bit Enhanced Capability链表遍历入口 ECAM 内存直接访问
实战建议: 在VxWorks BSP开发中,建议封装一组统一的配置空间访问接口,自动判断是使用传统CF8/CFC方式(用于256字节内)还是ECAM方式(用于扩展空间)。这样可以提高驱动代码的可移植性,并避免因BSP差异导致的访问错误。