可以把这个问题理解成Linux/BIOS 并不是直接“选择某根 PCIe 线发包”而是访问了某个 Root Complex/Host Bridge 的配置访问窗口主板/SoC 的地址译码把这个 CPU 访问送到对应 PCIe 控制器PCIe 控制器再从自己连接的物理 PCIe Lane 发出去。也就是说路径是软件枚举逻辑 ↓ CPU 发起配置空间访问 ↓ 命中某个 PCIe RC / Host Bridge 的配置访问窗口 ↓ RC 硬件生成 Configuration Read / Write TLP ↓ 通过该 RC 连接的 PCIe PHY / Lane 发出去 ↓ Endpoint 响应 Completion1. 软件不是直接发 TLP而是访问“配置空间窗口”Linux 枚举 PCIe 时代码里看起来像是在读pci_bus_read_config_dword(bus,devfn,offset,val);但 CPU 不会自己生成 PCIe TLP。CPU 做的是读/写某个特殊的 MMIO 地址这个 MMIO 地址属于PCIe 配置空间访问窗口常见叫法包括ECAM MMCONFIG PCI configuration space window如果是标准 ECAM配置空间地址大致按下面方式编码ECAM_BASE Bus Number × 1MB Device Number × 32KB Function Number × 4KB Register Offset例如软件想读Bus 1, Device 0, Function 0, Offset 0x00也就是读 Vendor ID / Device ID。它实际可能访问ECAM_BASE (1 20) (0 15) (0 12) 0x00这个 CPU 读操作会进入主机内部地址总线。然后主机地址译码发现这个地址属于 PCIe RC 的 ECAM 配置窗口于是把访问送到对应的 PCIe Root Complex 硬件。2. “怎么知道通过板子上的 PCIe 硬件接口传出去”靠的是主机地址映射 PCIe Host Bridge 注册信息 硬件连接关系。你可以这样理解CPU 并不知道板子上哪根 PCIe Lane 接了 FPGA Linux 也不是直接控制物理 Lane 真正知道的是 这个 MMIO 配置窗口属于哪个 PCIe Root Complex 这个 Root Complex 的 PHY/Lane 物理上连到了哪个插槽/接口比如主板上有两个 PCIe 控制器PCIe RC0 → 接 M.2 插槽 PCIe RC1 → 接 FPGA 板卡插槽系统启动时BIOS/UEFI 或 Device Tree / ACPI 会告诉 LinuxRC0 的配置访问窗口在哪里 RC0 的 bus range 是多少 RC0 的 MMIO resource window 是多少 RC1 的配置访问窗口在哪里 RC1 的 bus range 是多少 RC1 的 MMIO resource window 是多少Linux 注册两个 Host BridgePCI domain 0000, bus 00-3f → RC0 PCI domain 0001, bus 00-3f → RC1之后 Linux 扫描 RC1 时访问的是 RC1 的配置窗口。所以配置访问自然进入 RC1然后从 RC1 的 PCIe 硬件接口发出去。不是软件手动说从这根 PCIe 线发而是访问了哪个 RC 的配置窗口就由哪个 RC 发3. 关键硬件路径地址译码主机内部有地址映射。例如简化成这样CPU 地址空间 0x0000_0000_0000 ~ 0x0000_FFFF_FFFF DRAM 0x8000_0000_0000 ~ 0x8000_0FFF_FFFF PCIe RC0 ECAM 0x9000_0000_0000 ~ 0x9000_0FFF_FFFF PCIe RC1 ECAM 0xA000_0000_0000 ~ 0xA000_FFFF_FFFF PCIe RC0 MMIO outbound window 0xB000_0000_0000 ~ 0xB000_FFFF_FFFF PCIe RC1 MMIO outbound window如果 CPU 访问0x9000_0010_0000地址译码发现这是 PCIe RC1 的 ECAM 配置空间于是访问进入PCIe RC1然后 RC1 根据访问地址中的Bus / Device / Function / Register Offset生成 PCIe Configuration TLP。所以问题的答案是不是数据包自己知道去哪而是 CPU 地址访问先被主机地址译码送到某个 RCRC 再把访问转换成 PCIe TLP从自己的物理 PCIe 端口发出去。4. Linux 内核怎么知道每个 RC 的配置窗口来源一般有两类。4.1 PC / x86 平台通常来自BIOS / UEFI ACPI MCFG 表 _CRS 资源描述其中 MCFG 会告诉系统PCIe ECAM 配置空间基地址 PCI segment/domain bus rangeLinux 读取这些信息后就知道哪个 PCI domain 的配置空间窗口在哪里4.2 ARM / 嵌入式平台通常来自 Device Tree。比如设备树里可能有pciefd0e0000 { compatible vendor,pcie-host; reg 0x0 0xfd0e0000 0x0 0x100000; bus-range 0x00 0xff; ranges ...; interrupts ...; };这些信息告诉 LinuxPCIe 控制器寄存器在哪里 配置访问窗口在哪里 outbound memory window 怎么映射 中断怎么接 bus number 范围是多少Linux 的 PCIe host controller driver 会根据这些信息初始化 RC然后把它注册成一个 PCI Host Bridge。5. Linux 内部是怎么分发到对应 RC 的Linux 里每个 PCI bus 都属于某个 host bridge。简化结构是pci_host_bridge ↓ pci_bus ↓ pci_dev每个 host bridge 会带一组配置访问函数通常抽象成structpci_ops{int(*read)(structpci_bus*bus,unsignedintdevfn,intwhere,intsize,u32*val);int(*write)(structpci_bus*bus,unsignedintdevfn,intwhere,intsize,u32 val);};当 Linux 要读某个设备配置空间时pci_bus_read_config_dword() ↓ 找到这个 bus 对应的 pci_ops ↓ 调用该 RC/host bridge 的 read() ↓ read() 访问对应 RC 的 ECAM 或 RC 控制器寄存器 ↓ RC 生成 PCIe Configuration TLP所以如果设备属于 RC1 管理的 busLinux 就调用 RC1 的配置访问函数。这就是软件层面的“知道走哪个 PCIe 控制器”。6. RC 生成什么 TLP枚举时主要是Configuration Read Request Configuration Write Request Completion with Data Completion without Data例如软件想读 Vendor ID / Device IDCPU 读 ECAM 地址 ↓ RC 生成 Configuration Read Request TLP ↓ Endpoint 收到配置读请求 ↓ Endpoint 返回 Completion with Data ↓ RC 把 Completion 数据返回给 CPU从软件角度看只是一次普通的 MMIO read。从 PCIe 总线上看则是Configuration Read Request ↓ Completion with Data7. Type 0 和 Type 1 配置 TLP 怎么决定PCIe 配置请求有两类Configuration Type 0 Configuration Type 1大致可以这样理解类型用途Type 0访问当前下游总线上的设备Type 1穿过 PCIe Bridge / Switch访问更下级总线比如RC / Root Port ↓ Endpoint直接挂在 Root Port 后面的 Endpoint最终下发到链路上时通常是 Type 0 配置请求。如果中间有 SwitchRC / Root Port ↓ PCIe Switch ↓ Endpoint访问 Switch 后面的设备时可能先发 Type 1 请求让 Switch 根据 bus number 继续转发到了目标下游端口再变成 Type 0。所以路由依据主要是Bus Number Device Number Function Number Bridge 的 secondary/subordinate bus number也就是 PCIe 的 BDF 路由体系。8. 多个 PCIe 口时系统怎么区分假设主机有两个 Root PortRoot Port A → 插槽 A Root Port B → 插槽 B系统可能分配Root Port A 下游 bus 01 Root Port B 下游 bus 02那么访问 01:00.0 ↓ 走 Root Port A 访问 02:00.0 ↓ 走 Root Port B这个映射关系是在枚举时建立的。Root Port 本身像一个 PCI-to-PCI Bridge有Primary Bus Number Secondary Bus Number Subordinate Bus Number例如 Root Port APrimary Bus 00 Secondary Bus 01 Subordinate Bus 01Root Port BPrimary Bus 00 Secondary Bus 02 Subordinate Bus 02所以当软件访问 bus 01 的设备时PCI core/RC/bridge routing 会把配置请求导向 Root Port A。当访问 bus 02 时会导向 Root Port B。9. 普通 Memory TLP 也是类似逻辑但依据是地址窗口枚举时主要是配置 TLP。枚举完成后Linux 会给设备 BAR 分配地址。例如FPGA BAR0 分配到 Host 物理地址 0xB000_0000驱动中bar0pci_iomap(pdev,0,0);之后 CPU 写writel(0x1,bar0DMA_CTRL);从总线角度看CPU 写某个 MMIO 地址 ↓ 地址译码命中 PCIe outbound memory window ↓ RC 生成 Memory Write TLP ↓ TLP 通过 PCIe link 到 FPGA ↓ FPGA PCIe IP 根据 BAR hit 把写请求交给用户逻辑所以配置阶段和普通 MMIO 阶段类似阶段CPU 访问什么RC 生成什么 TLP路由依据枚举阶段配置空间窗口 ECAMConfiguration Read/WriteBDF驱动访问 BARBAR 对应 MMIO 地址Memory Read/WriteAddressDMA 阶段FPGA 主动访问 Host 内存Memory Read/WriteAddress10. 对 FPGA Endpoint 来说你看到的是什么你的 FPGA PCIe IP 会收到来自主机的 TLP。枚举阶段FPGA PCIe IP 主要处理Configuration Read Configuration Write这些通常由 PCIe IP 内部配置空间模块响应。比如主机读Vendor ID / Device ID BAR MSI Capability PCIe Capability多数情况下Xilinx/Intel PCIe Endpoint IP 已经帮你实现了配置空间响应。你的用户逻辑通常不需要自己解析这些配置 TLP。驱动阶段主机访问 BAR 时FPGA 侧会看到Memory Read / Memory Write TLP这时 PCIe IP 会把 BAR 命中的访问转换成 AXI-Lite、Avalon-MM 或 AXI-Stream 接口给你的逻辑。所以枚举配置空间多由 PCIe IP 内部处理 BAR 寄存器访问交给你的 FPGA 用户逻辑处理 DMA TLP由你的 DMA 引擎/PCIe IP 用户接口处理11. 你可以用一个完整例子串起来假设 FPGA 插在 PCIe 插槽上。阶段一链路建立主机上电 ↓ PCIe Refclk 稳定 ↓ PERST# 释放 ↓ RC 和 FPGA PCIe IP 进行 LTSSM 链路训练 ↓ Link up这一步是硬件链路层完成的。阶段二Linux/BIOS 枚举Linux PCI core 想扫描某个 bus/dev/function ↓ 访问对应 RC 的 ECAM 配置窗口 ↓ 主机地址译码把访问送到 PCIe RC ↓ RC 生成 Configuration Read TLP ↓ TLP 从 RC 的 PCIe PHY/Lane 发出 ↓ FPGA PCIe IP 收到配置读 ↓ 返回 Vendor ID / Device ID ↓ Linux 知道这个 BDF 上有设备阶段三分配 BARLinux 读取 BAR 大小 ↓ 给 BAR0 分配 Host 物理地址 ↓ 通过 Configuration Write TLP 写入 BAR 寄存器 ↓ FPGA PCIe IP 记录 BAR base/size 信息阶段四驱动访问 BAR你的 pci_driver probe() ↓ pci_request_regions() ↓ pci_iomap() ↓ writel(DMA_START, bar0 offset) ↓ CPU 写 BAR 对应 MMIO 地址 ↓ RC 生成 Memory Write TLP ↓ FPGA PCIe IP BAR hit ↓ 用户逻辑收到寄存器写操作12. 最核心结论你的问题RC PCIe 枚举的过程发送的数据包怎么知道要通过板子上的 PCIe 硬件接口传出去答案是1. Linux/BIOS 枚举时不是直接手写 TLP 2. 它访问某个 PCIe Host Bridge / RC 的配置空间窗口 3. CPU 地址译码把这个访问送到对应 RC 硬件 4. 这个 RC 硬件把配置访问转换成 PCIe Configuration TLP 5. TLP 只能从这个 RC 物理连接的 PCIe PHY/Lane 发出去 6. 如果有多个 Root Port则根据 host bridge、bus number、bridge bus range 选择对应路径 7. Endpoint 收到配置 TLP 后返回 Completion 8. Linux 得到 Vendor ID / Device ID 后创建 pci_dev 并继续枚举。你可以记成一句话软件通过“访问哪个 RC 的配置窗口”来选择 PCIe 控制器硬件通过“地址译码和 Root Port 拓扑”决定 TLP 从哪个 PCIe 物理接口发出去。