Introduction
PCI Express (PCIe) devices expose a standardized Configuration Space that allows software to discover, identify, and configure hardware. Accessing this space involves multiple layers:
- CPU MMIO access (ECAM)
- Root Complex translation (iATU)
- PCIe Transaction Layer (Config TLPs)
This article explains how these pieces fit together with a concrete example.
What is Config Space
Every PCIe device implements a Configuration Space—a standardized register layout used to describe device capabilities and control its behavior.
- Minimum size: 256 bytes
- PCIe Extended Configuration Space: up to 4 KB per function
The Linux PCI subsystem uses this space to:
- Discover devices (by reading Vendor ID / Device ID)
- Determine resource requirements (by probing BARs)
- Configure devices (by programming BARs and control registers)
Configuration space is accessed by issuing Configuration Read/Write TLPs on the PCIe link. From the CPU perspective, these accesses are initiated as MMIO transactions, which the Root Complex translates into PCIe TLPs.
For Coniguration Space layout, refer to PCIe Overview
BDF Address Scheme
Each PCIe function is uniquely identified using the BDF (Bus:Device:Function) tuple:
BDF = Bus[7:0] : Device[4:0] : Function[2:0]
Examples:
-
00:00.0→ Root Complex integrated endpoint (typical) -
00:01.0→ Endpoint on the root bus
Field breakdown:
- Bus: 8 bits → up to 256 buses
- Device: 5 bits → up to 32 devices per bus
- Function: 3 bits → up to 8 functions per device
Enhanced Configuration Access Mechanism (ECAM)
ECAM provides a standardized way to access PCIe configuration space using CPU MMIO. It maps configuration space into a contiguous region of the CPU’s physical address space.
ECAM Address Formula
Physical Address = ECAM_BASE
+ (Bus << 20)
+ (Device << 15)
+ (Function << 12)
+ Register
Field mapping:
- Bus → bits [27:20]
- Device → bits [19:15]
- Function → bits [14:12]
- Register offset → bits [11:0]
Each function occupies exactly 4 KB of address space.
ECAM Region Size
Maximum possible ECAM size:
256 buses × 32 devices × 8 functions × 4KB = 256 MB
ECAM in Device Tree
pcie@10000 {
reg = <0x10000000 0x1000>, /* DBI registers */
<0x20000000 0x10000000>, /* ECAM window */
reg-names = "dbi","config"
}
- DBI region → internal controller registers
- ECAM region → CPU-visible configuration space window
Internal Address Translation Unit (iATU)
The iATU is a hardware block within the Root Complex responsible for translating CPU physical addresses into PCIe transaction attributes.
Role of iATU
When the CPU accesses an address within the ECAM window:
- The transaction is routed to the Root Complex
- iATU compares the address against configured regions
- Based on the matched region, the controller generates a Configuration TLP
The mapping between ECAM address bits and BDF follows the ECAM specification, while the iATU region configuration determines:
- Transaction type (Cfg0 / Cfg1 / Memory)
- Target PCIe address space
iATU Regions
Outbound (CPU → PCIe):
- Region 0 → Config Type-0 (same bus devices)
- Region 1 → Config Type-1 (downstream buses via bridges)
- Region 2 → Memory transactions (BAR access)
Inbound (PCIe → CPU):
- Used for endpoint-initiated transactions (e.g., DMA writes)
Example iATU Programming (Config Type-0)
Region 0:
LOWER_BASE_ADDR = 0x20000000
UPPER_BASE_ADDR = 0x00000000
LIMIT_ADDR = 0x200FFFFF (1 MB window for Bus 0)
REGION_CTRL_1 = TYPE = CFG0
REGION_CTRL_2 = ENABLE = 1
-
1 MB corresponds to Bus 0 ECAM space:
- 32 devices × 8 functions × 4KB
Example: Config Read Transaction
Read Vendor ID (offset 0x00) from device: 00:01.0
Step 1: CPU Issues MMIO Read
ECAM address = 0x20000000
+ (0 << 20)
+ (1 << 15)
+ (0 << 12)
+ 0x000
= 0x20008000
Step 2: Address Hits Root Complex
- AXI interconnect routes the access to RC
- iATU checks region match:
0x20000000 ≤ 0x20008000 ≤ 0x200FFFFF → Match
Step 3: ECAM Address Decoding
From address:
Bus = bits [27:20] = 0
Device = bits [19:15] = 1
Function = bits [14:12] = 0
Register = bits [11:0] = 0x000
Step 4: Transaction Layer Generates CfgRd0 TLP
CfgRd0 TLP Header (3 DW):
DW0
- Fmt =
000(3 DW, no data) - Type =
00100(CfgRd0) - Length =
1 DW
DW1
- Requester ID =
00:00.0(Root Complex) - Last BE =
0000 - First BE =
0011(16-bit access → Vendor ID)
DW2
[31:24] Bus Number = 0x00
[23:19] Device Number = 0x01
[18:16] Function Number = 0x00
[15:12] Register Number = 0x0
[11:2] Register Number = 0x00
[1:0] Reserved = 00
- Register offset =
0x00 - Extended register field =
0x0(within first 256 bytes)
End-to-End Flow
CPU MMIO Read (ECAM)
↓
AXI Interconnect
↓
Root Complex (Host Bridge)
↓
iATU Region Match
↓
CfgRd0 TLP Generation
↓
PCIe Link
↓
Endpoint
Top comments (0)