4. Configuration TLP:Type0与Type1配置请求TLP格式、Bus/Device/Function编号机制、配置空间访问流程
配置请求TLP(Configuration TLP)是PCIe总线枚举与设备发现的基础。主机桥(Root Complex)通过发送配置请求TLP来读取或写入下游设备的配置空间。根据目标设备所在的位置,配置请求分为Type0和Type1两种格式,其核心区别在于寻址方式和路由行为。
4.1 Type0与Type1配置请求TLP格式
配置请求TLP属于Posted(写)或Non-Posted(读)事务。其TLP前缀(Fmt/Type字段)标识了请求类型。两种格式的TLP头部均为3 DW(12字节),但内部字段布局不同。
4.1.1 Type0配置请求TLP头部格式
Type0用于访问本总线上的设备配置空间。其头部格式如下:
| Byte Offset | Bit 31:24 | Bit 23:16 | Bit 15:8 | Bit 7:0 |
|---|---|---|---|---|
| DW0 | Fmt[2:0] + Type[4:0] | R | TC[2:0] | Attr[2:0] + TH/TD/EP/ATTR |
| DW1 | Requester ID (Bus[7:0] | Device[4:0] | Function[2:0]) | |||
| DW2 | Register Offset[7:2] + 0b00 | |||
关键字段说明:
- Fmt/Type: 对于Type0配置写,Fmt=010, Type=00100;对于Type0配置读,Fmt=000, Type=00100。
- Requester ID: 发起请求的PCIe设备的BDF编号(Bus:Device.Function)。
- Register Offset: 配置空间内的双字偏移地址(低2位固定为0,按4字节对齐)。
- R: 保留位,必须为0。
pci_read_config_dword()和pci_write_config_dword()最终会构造Type0配置请求TLP,通过Root Port发送到目标设备。
4.1.2 Type1配置请求TLP头部格式
Type1用于访问下游总线上的设备。当主机桥或交换机收到Type1请求时,如果目标总线号等于其次级总线号(Secondary Bus Number),则将其转换为Type0请求转发;否则继续向下游转发Type1。
| Byte Offset | Bit 31:24 | Bit 23:16 | Bit 15:8 | Bit 7:0 |
|---|---|---|---|---|
| DW0 | Fmt[2:0] + Type[4:0] | R | TC[2:0] | Attr[2:0] + TH/TD/EP/ATTR |
| DW1 | Requester ID (Bus[7:0] | Device[5:0] | Function[2:0]) | |||
| DW2 | Register Offset[7:2] + 0b00 | |||
与Type0的关键区别:
- Fmt/Type: Type1配置写Fmt=011, Type=00101;Type1配置读Fmt=001, Type=00101。
- 路由机制: Type1不包含目标总线号字段,而是通过ID路由(ID Routing)机制,由桥设备根据总线号(Bus Number)决定转发路径。
- 转换规则: 当Type1到达一个桥设备,且目标总线号等于该桥的次级总线号时,桥设备将其转换为Type0,并发送到次级总线。
4.2 Bus/Device/Function编号机制
PCIe总线使用层次化拓扑,每个设备由BDF(Bus:Device.Function)三元组唯一标识。BDF编号是配置空间访问和TLP路由的基础。
4.2.1 BDF编号结构
| 字段 | 位宽 | 范围 | 说明 |
|---|---|---|---|
| Bus Number | 8 bit | 0-255 | 标识PCIe总线编号。总线0通常为Root Complex内部总线。 |
| Device Number | 5 bit | 0-31 | 标识总线上的设备编号。每个总线最多支持32个设备。 |
| Function Number | 3 bit | 0-7 | 标识设备内的功能编号。每个设备最多支持8个功能。 |
4.2.2 总线枚举与编号分配
系统启动时,主机桥(Root Complex)通过深度优先搜索(DFS)算法遍历PCIe拓扑,为每个桥设备分配总线号。过程如下:
- 初始化: 主机桥将自身配置为总线0,并扫描总线0上的设备。
- 发现桥设备: 如果扫描到Type1配置头(即桥设备),则为其分配一个新的总线号(次级总线号)。
- 递归扫描: 主机桥通过发送Type1配置请求,递归扫描新分配的总线,直到所有桥设备都被配置。
- 分配Device/Function: 在每个总线上,通过读取设备的Vendor ID寄存器(偏移0x00)来探测设备是否存在,并记录其Device Number和Function Number。
Primary Bus Number: 桥设备上游的总线号。
Secondary Bus Number: 桥设备下游的总线号(即桥所连接的总线)。
Subordinate Bus Number: 桥设备下游可达的最大总线号(用于判断是否转发Type1请求)。
4.3 配置空间访问流程
配置空间访问流程涉及软件层(操作系统/驱动)和硬件层(Root Complex/交换机)的协同工作。以下以Linux内核为例,描述一次配置读取的完整路径。
4.3.1 软件访问接口
Linux内核提供标准API用于访问PCIe配置空间:
// 读取配置空间双字
int pci_read_config_dword(struct pci_dev *dev, int offset, u32 *value);
// 写入配置空间双字
int pci_write_config_dword(struct pci_dev *dev, int offset, u32 value);
// 底层实现:通过pci_bus_read_config_dword()调用
// 最终调用架构相关的配置空间访问函数(如x86的pci_conf1_read())
4.3.2 硬件访问流程(以Type0读取为例)
当驱动调用pci_read_config_dword()时,硬件层执行以下步骤:
- 构造TLP: Root Complex根据目标设备的BDF和偏移地址,构造一个Type0配置读取TLP。
- ID路由: TLP通过ID路由机制,根据目标总线号在内部交换网络中进行路由。
- 到达目标设备: TLP到达目标设备所在的PCIe端口(Root Port或Switch Downstream Port)。
- 配置空间访问: 目标设备接收TLP,解析Register Offset,从内部配置空间读取对应双字数据。
- 完成TLP(Completion TLP): 目标设备构造一个Completion TLP,包含读取的数据,沿原路径返回Root Complex。
- 返回软件: Root Complex将Completion TLP中的数据返回给驱动。
4.4 配置请求TLP路由与转换SVG图
下图展示了Type1配置请求从Root Complex出发,经过交换机转换为Type0并到达目标Endpoint的完整流程。
4.5 配置空间访问流程SVG图
下图详细描述了从驱动调用到硬件完成配置读取的完整流程,包括TLP构造、路由、完成返回等关键步骤。
4.6 配置空间访问常见问题与避坑
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 配置读取返回0xFFFFFFFF | 目标设备不存在、未上电、或链路未训练成功 | 检查BDF编号是否正确;确认设备电源状态;检查链路状态寄存器 |
| 配置写入无响应 | 目标设备配置空间被硬件锁定(如BAR寄存器在枚举后锁定) | 确认写入时机(应在枚举阶段或设备复位后写入) |
| Type1请求未转换为Type0 | 桥设备的Secondary/Subordinate Bus Number配置错误 | 检查桥设备的总线号寄存器;重新枚举总线 |
| Completion超时 | 目标设备未返回Completion(可能因设备故障或TLP损坏) | 检查TLP校验和(ECRC);使用PCIe分析仪抓包 |
pci_read_config_dword()),因为这些函数可能触发睡眠等待Completion。应使用非阻塞或异步方式访问,或提前在初始化阶段缓存所需配置数据。
4.7 本章小结
- Type0 vs Type1: Type0用于本总线设备,Type1用于下游总线设备,由桥设备负责转换。
- BDF编号: 8位总线号 + 5位设备号 + 3位功能号,唯一标识PCIe设备。
- 配置空间访问流程: 驱动调用 → TLP构造 → ID路由 → 设备响应 → Completion返回。
- 关键寄存器: 桥设备的Primary/Secondary/Subordinate Bus Number寄存器决定路由行为。
- 性能与安全: 配置访问延迟较高,避免在热路径频繁调用;注意原子上下文限制。