- 配置请求 = 找设备、设参数、读 BAR、枚举 BDF(靠BDF 路由)
- 存储器请求 = 真正读写寄存器、数据交互(靠地址 + ATU 路由)
PCIe 只有两种核心请求
- 配置请求(Configuration Request)
- 存储器请求(Memory Request)
配置请求 Config Request
用途:找设备、管设备
什么时候用?
- 内核扫描 PCIe 拓扑
- 读 Vendor ID/Device ID
- 读 / 写BAR寄存器
- 配置BDF 路由表(Primary/Secondary/Subordinate Bus)
- 配置MSI 中断、电源、链路速度
- 访问ECAM 地址触发的就是它
路由方式:
只看 BDF,按 Bus 号路由 → 桥只转发,不看 Dev/Func
走哪里:
CPU → ECAM → Root Port →BDF 路由表→ 桥 → 设备配置空间
关键:
和地址无关,和 BAR 无关,和 ATU 无关!
存储器请求 Memory Request
用途:真正干活(读写寄存器、DMA)
- 驱动
ioread32 / iowrite32 - 读写设备内部寄存器
- 读写设备片上 RAM
- 设备 DMA 读写内存
路由方式:
只看地址 → 走 ATU 翻译 → 地址路由
走哪里:
CPU →BAR 物理地址→ Root Port →ATU 翻译→ PCIe 总线地址 → 设备
关键:
和 BDF 路由表无关!只认地址。
核心区别
| 项目 | 配置请求 Config | 存储器请求 Memory |
|---|---|---|
| 干啥 | 枚举、配置、读 BAR、设 BDF | 读写寄存器、DMA、数据交互 |
| 路由依据 | BDF(Bus 号) | CPU 物理地址(BAR) |
| 谁转发 | 桥的 BDF 路由表 | Root Port / ATU |
| 地址类型 | ECAM 地址 | BAR 物理地址 |
| 是否走 ATU | 不走 | 必须走 ATU |
| 硬件行为 | 桥逐级转发 | 地址翻译 + 直达设备 |
| 触发来源 | 内核 PCI 子系统 | 驱动 / 设备 DMA |
- 配置请求 = 给设备上户口、分配地址、设门牌号
- 存储器请求 = 按门牌号直接上门访问
IO 请求
远古遗留的 “独立小地址空间” 访问请求
- x86 才有(64KB IO 空间)
- ARM64 根本没有
- 现代 PCIe 基本不用
- 路由方式和存储器请求几乎一样,只是地址空间不同
访问操作
- CPU 执行:
inb/outb/inw/outw/inl/outl - 访问独立的 IO 地址空间(不是内存,不是配置)
- 最大64KB(0x0000 ~ 0xFFFF)
- 设备通过IO BAR声明自己要用这段空间
它和 Memory 请求唯一区别:
- 地址空间不一样
- 路由、转发逻辑几乎一样(都是地址路由)
- 都不走 BDF 路由
IO 请求 硬件流程(x86)
CPU 执行 outl(0x3F8, val) ↓ 发出 IO 地址 0x3F8 ↓ 送到 Root Port ↓ 地址路由(匹配 IO 范围) ↓ 发到 PCIe 总线(IO 报文) ↓ 设备识别:这是我的 IO 地址 ↓ 响应没有 ATU 翻译,没有 BDF 查表,纯地址路由;
IO 请求 = 访问 x86 独有的 64KB IO 空间;
真实硬件流程对比
① 配置请求流程(内核扫设备)
- CPU 访问ECAM 地址
- 解析出BDF = 01:00.0
- Root Port 查BDF 路由表
- 桥按 Bus 号逐级转发
- 到达设备配置空间
- 读 / 写BAR、命令寄存器
② 存储器请求流程(驱动读寄存器)
- 驱动访问BAR 虚拟地址
- 转为CPU 物理地址
- Root Port 查ATU
- 翻译成PCIe 总线地址
- 直接发到目标设备
- 读写设备寄存器
总结
- ECAM → 触发配置请求
- BDF 路由表 → 配置请求专用
- BAR → 分配存储器请求的地址
- ATU → 存储器请求地址翻译
- Root Port → 同时管两种请求
- DMA → 存储器请求反向
- 配置请求 = 管设备(BDF 路由)
- 存储器请求 = 用设备(地址 + ATU)