PCIe Series — PCIe-08: Configuration TLPs — VLSI Trainers
PCIe Series · PCIe-08

Configuration TLPs

CfgRd0, CfgRd1, CfgWr0, CfgWr1 — how the 3DW configuration header works, what Type 0 vs Type 1 means, how Bus/Device/Function addressing targets config space, Type 1 to Type 0 conversion at switches, Extended Register Number, and how all of this applies in Gen 6.

📋 Configuration TLPs — What They Are

Configuration TLPs are how software reads and writes the configuration registers of every PCIe device in the system. During boot, the BIOS or OS uses them to discover every device, assign bus numbers, program Base Address Registers (BARs), enable bus mastering, and set up interrupt routing. At runtime, drivers use them to configure device-specific features.

There are four configuration TLP types:

Four Configuration TLP Types CfgRd0 Fmt=000 · Type=0_0100 Read from device on the Secondary Bus CfgWr0 Fmt=010 · Type=0_0100 Write to device on the Secondary Bus CfgRd1 Fmt=000 · Type=0_0101 Read — forwarded downstream until converted to Type 0 CfgWr1 Fmt=010 · Type=0_0101 Write — forwarded downstream until converted to Type 0
Figure 1 — Four configuration TLP types. Fmt=000 means no data payload (read request). Fmt=010 means data payload present (write request — the register value to write follows the header). Type=0_0100 is Type 0 (targets the Secondary Bus). Type=0_0101 is Type 1 (forwarded further downstream).

All four share the same header layout. The only difference is in DW0: Fmt distinguishes read from write, and Type bit[0] distinguishes Type 0 from Type 1. Configuration TLPs are always non-posted — both reads and writes require a completion to return.

Config writes are non-posted (unlike memory writes). MWr is posted — fire and forget. CfgWr is non-posted — it waits for a Cpl (completion without data) to confirm the write landed before the software continues. This is essential: BIOS must know each register write succeeded before it programs the next register. Ordering guarantees collapse if config writes were posted.

📋 Type 0 vs Type 1 — the Core Distinction

The single bit that separates Type 0 from Type 1 is bit 0 of the Type field. Type 0 means “this is the final destination — the device on my Secondary Bus should respond.” Type 1 means “I need to forward this further downstream.” Every bridge or switch port along the path from the Root Complex to the target device converts the Type 1 into a Type 0 at the right moment.

Type 1 → Type 0 Journey — Config TLP Traversing the Topology RC Bus 0 Generates CfgRd1 for target Bus 3 Dev=0, Fn=0 CfgRd1 (Type 1) Target BDF: 03:00.0 Switch Upstream: Bus 1 Receives CfgRd1 Checks: target Bus=03 Port 2: Sec=3, Sub=5 Bus 3 is within 3–5 → Bus 3 = Secondary → Convert to Type 0! Flip Type bit[0]: 1→0 CfgRd0 (Type 0) Converted at switch port Endpoint BDF 03:00.0 Receives Type 0 Reads config register Returns CplD to RC Type field bit 0 — the only difference between Type 0 and Type 1 Type 1 (CfgRd1/CfgWr1): Type[4:0] = 0_0101 → bit[0] = 1 → forward downstream Type 0 (CfgRd0/CfgWr0): Type[4:0] = 0_0100 → bit[0] = 0 → device responds
Figure 2 — Type 1 to Type 0 journey. The RC sends CfgRd1 with the target BDF embedded in DW2. The switch checks which downstream port’s Secondary/Subordinate range contains Bus 3. When that port’s Secondary bus number exactly matches the target bus, it flips Type bit[0] from 1 to 0, converting the TLP to CfgRd0 before forwarding it out. The endpoint sees only Type 0 — it never sees a Type 1.
The conversion rule from the spec: a bridge converts Type 1 to Type 0 when the target bus number exactly matches the Secondary bus number of the forwarding port. If the target bus is downstream of (greater than) the Secondary bus but still within the Subordinate range, the bridge forwards it unchanged as Type 1. Only when target bus = Secondary bus does conversion happen.

📋 DW0 Special Rules for Config TLPs

Several DW0 fields behave differently for configuration TLPs compared to memory TLPs. These restrictions come directly from Table 5-6 of the MindShare PCIe specification:

FieldNormal memory TLPsConfiguration TLPs
TC [22:20] 0–7 (any traffic class) Must be 000 (TC 0 only). Ensures config traffic never interferes with high-priority traffic.
Attr[2] / IDO [18] Optional — can be set Reserved · must be 0. ID-based ordering does not apply.
TH [17] Optional — can be set Reserved · must be 0. TLP Processing Hints not applicable.
Attr[1] / RO [13] Optional — can be set Must be 0. Relaxed ordering not applicable to config.
Attr[0] / NS [12] Optional — can be set Must be 0. No Snoop not applicable to config registers.
AT [11:10] 00, 01, or 10 Must be 00. Address Translation Services not applicable.
Length [9:0] 1–1024 DW Always exactly 1 (= 0b00_0000_0001). Config space is always accessed one DW at a time.

📋 DW1 — Requester ID, Tag, Byte Enables

DW1 of a configuration TLP is structured identically to that of a memory request — Requester ID (16-bit BDF), Tag (8-bit), Last DW BE (4-bit), First DW BE (4-bit). The differences are in how the byte enable fields must be set:

FieldBytesValue for config TLPsWhy
Requester ID 4–5 BDF of the Root Complex port generating the request Completion routes back to the RC using this BDF
Tag 6 8-bit tag value unique among pending config requests CplD echoes it to match the completion to the outstanding request
Last DW BE 7 bits [7:4] Must be 0x0 (all zeros) Config requests are always 1 DW — there is no “last” DW separate from the “first” DW
First DW BE 7 bits [3:0] Any pattern, including all zeros, is valid Selects which bytes within the 1 DW access are valid. All-zero means “no bytes” — effectively a flush operation.

📋 DW2 — Target BDF and Register Address

DW2 of a configuration TLP does not carry a memory address. Instead it carries the Bus/Device/Function number of the target device and the DW offset of the register being accessed within that device’s 4 KB configuration space.

DW2 — Target BDF and Register Address Bytes 8–11 · 32 bits total · forms a 10-bit DW address into 4 KB config space Bus Number Byte 8 bits [7:0] Target bus 0–255 Device Byte 9 bits [7:3] 5 bits · Dev 0–31 Fn Byte 9 bits [2:0] 3 bits · Fn 0–7 Rsvd Byte 10 [7:4] Ext Reg No Byte 10 [3:0] upper 4 bits of 10-bit addr Reg No [7:2] Byte 11 [7:2] lower 8 bits of 10-bit addr 10-bit Config Space DW Address = ExtRegNo[3:0] : RegNo[7:2] ExtRegNo (4 bits) forms the upper half · RegNo (6 bits) forms the lower half 10-bit address → 1024 DW = 4096 bytes = the full PCIe configuration space per function For PCI-compatible config space only (first 256 bytes): ExtRegNo must be 0000 · RegNo selects DW 0–63
Figure 4 — DW2 fields. Bus + Device + Function identify the target device. The combined 10-bit register address (ExtRegNo[3:0] concatenated with RegNo[7:2]) selects a specific DW within the 4 KB configuration space. Bits [1:0] of the address are always 00, forcing DW-aligned register access.

📋 Register Number and Extended Config Space

The Register Number field selects which DW of configuration space is being accessed. The two-level addressing system exists for backward compatibility with PCI:

Addressing modeExtRegNo [3:0]RegNo [7:2]DW rangeByte range
PCI-compatible 0x0 (must be zero) 0x00–0x3F (6 bits) DW 0–63 Bytes 0–255 (first 256 bytes)
PCIe Extended 0x1–0xF (4 bits) 0x00–0x3F (6 bits) DW 64–1023 Bytes 256–4095
Why two fields instead of one 10-bit field? When PCIe was introduced, existing PCI software only knew about the 6-bit Register Number that addressed the first 256 bytes of config space. PCIe extended config space to 4 KB by adding the 4-bit Extended Register Number (ExtRegNo) as a separate field in Byte 10. Software that only knew about PCI would leave Byte 10 = 0x00, automatically falling back to PCI-compatible config space access. PCIe-aware software sets ExtRegNo to access the additional 3840 bytes of extended config space where PCIe capabilities live.

📋 4 KB Configuration Space Layout

Every PCIe function has 4 KB of configuration space, accessed one DW at a time via configuration TLPs. The space is divided into a PCI-compatible region (first 256 bytes) and a PCIe-extended region (bytes 256–4095).

4 KB Configuration Space — Byte 0 to Byte 4095 0x000 0x040 0x100 0xFFF PCI-Compatible Header Bytes 0x000–0x03F (64 bytes) VendorID, DeviceID, Command, Status, ClassCode, BARs, SubsystemID… PCI Capability Structures Bytes 0x040–0x0FF · MSI, Power Management, VPD… PCIe Extended Capability Structures Bytes 0x100–0xFFF (3840 bytes) AER, VSEC, PASID, ATS, SR-IOV, LTR, Secondary PCIe, Gen 6 features… How to Address Each Region PCI-compatible header (0x00–0x3F): ExtRegNo = 0x0 · RegNo = 0x00–0x0F Address = {0x0, RegNo[5:0], 00} PCI capabilities (0x40–0xFF): ExtRegNo = 0x0 · RegNo = 0x10–0x3F Address = {0x0, RegNo[5:0], 00} PCIe extended caps (0x100–0xFFF): ExtRegNo = 0x1–0xF · RegNo = any 10-bit address = {ExtRegNo, RegNo, 00}
Figure 5 — 4 KB configuration space. The first 64 bytes are the PCI-compatible header (common across all function types). Bytes 64–255 hold PCI capability structures. Bytes 256–4095 are PCIe-only extended capabilities — AER, ATS, SR-IOV, and Gen 6-specific features like FRS and Flit Mode capability. Extended capabilities require ExtRegNo ≠ 0.

📋 Type 1 → Type 0 Conversion — Exact Spec Rule

The conversion rule from the MindShare spec (Chapter 5, Configuration Requests section):

target_bus vs bridge registersBridge actionTLP type sent downstream
target_bus == Secondary Convert: flip Type bit[0] Type 0 (CfgRd0 / CfgWr0)
Secondary < target_bus ≤ Subordinate Forward unchanged Type 1 (CfgRd1 / CfgWr1)
target_bus outside Secondary–Subordinate range Discard silently Not forwarded
BDF capture on Type 0 write. From the spec: “All devices capture their Bus and Device Number from the target BDF numbers in the Request whenever they receive a Type 0 configuration write.” This is how a device learns its own BDF during enumeration — it does not have a hardwired BDF. When the BIOS programs a CfgWr0 targeting Bus=3, Dev=0, Fn=0, the device at the end of that link latches Bus=3, Dev=0 as its own identity. From that point it uses 03:00.x as its Requester ID in all TLPs it sends.

📋 How Config TLPs Are Routed

Configuration TLPs use ID routing — they are routed based on the Bus/Device/Function number in DW2, not on a memory address. This makes config routing simpler than memory routing: no Base/Limit register comparison is needed.

Config TLP Routing Decision at Every Bridge target_bus == Secondary? Convert Type 1→0 send Type 0 downstream YES NO target_bus in (Secondary, Subordinate]? Forward Type 1 unchanged downstream within range outside range → discard silently
Figure 6 — Routing decision at every bridge in the path. Compare target bus to Secondary: equal → convert and send Type 0. Greater than Secondary but within Subordinate → forward Type 1. Outside range → discard. No UR completion is generated for out-of-range config requests during enumeration.

📋 Config Completions — CplD and Cpl

Configuration TLPs always generate completions. The completion format is the same standard 3DW completion header described in PCIe-07 — no special config completion type exists.

Config TLP typeCompletion returnedPayloadNotes
CfgRd0 / CfgRd1 CplD (completion with data) 1 DW — the register value Always exactly 1 DW, never split. Byte Count = 4. Lower Address = 0.
CfgWr0 / CfgWr1 Cpl (completion without data) None Status-only confirmation. SC = write accepted. UR or CA = write failed.
Any config TLP CRS (Config Request Retry Status) None (no data with CRS) Device not yet ready — RC must retry. Device has up to 1 second after reset to stop returning CRS.

CRS (Config Request Retry Status) is unique to configuration completions — it is not valid in response to memory or IO TLPs. When the RC receives CRS during a CfgRd of the VendorID register and CRS Software Visibility is enabled, it synthesises a VendorID return value of 0x0001 to software — a reserved value that tells the OS “this device is not ready yet, try again later” without blocking the entire boot sequence.

📋 BDF Capture During Enumeration

PCIe devices do not have hardwired BDF addresses. A device at the end of a link genuinely does not know its own bus number until the BIOS tells it. The mechanism is elegant: any Type 0 configuration write that reaches a device causes that device to latch the Bus and Device number from DW2 of the CfgWr0 header as its own identity.

📋 Alternative Routing Identifier (ARI)

Standard PCIe addressing uses 5 bits for Device and 3 bits for Function in DW2. ARI (Alternative Routing Identifier) is an optional PCIe capability that reinterprets DW2 byte 9: instead of 5 bits Device + 3 bits Function, ARI uses 8 bits for Function only (Device is always 0). This extends the per-bus function count from 8 to 256.

ModeByte 9 bits [7:3]Byte 9 bits [2:0]Max functions per bus
Standard Device [4:0] Function [2:0] 32 devices × 8 functions = 256 total per bus
ARI enabled Function [7:3] Function [2:0] Device is always 0 · 256 functions per bus

ARI requires support from both the switch downstream port (ARI Forwarding Enabled) and the endpoint (ARI Capable). It is commonly used in SR-IOV scenarios where a single physical function may expose up to 256 virtual functions, all sharing the same PCIe bus.

Configuration TLPs in Gen 6

The configuration TLP header format is completely unchanged in Gen 6. Every byte, every field, and every routing rule described in this post applies identically. Gen 6 changes only the Physical Layer — configuration TLPs are packed into flits the same way any other TLP is.

Gen 6 Extended Capabilities

Gen 6 introduces new PCIe Extended Capability structures that live in the extended config space (bytes 256–4095, requiring ExtRegNo ≠ 0). These include:

All of these are accessed using the same CfgRd0/CfgWr0 TLPs with ExtRegNo set appropriately. From a TLP format perspective, nothing changes — only the register values and capability structures at the destination differ.

The practical rule for Gen 6 configuration handling. Firmware and driver code that builds or parses configuration TLPs needs zero changes for Gen 6. The header format, BDF addressing, Type 0/1 routing, BDF capture, and completion handling are all identical. New Gen 6 capabilities appear as extended capability structures at new byte offsets in config space — they are accessed with the same CfgRd0/CfgWr0 TLPs.

📋 Quick Reference

ItemValue / Rule
CfgRd0 — Fmt, TypeFmt=000 · Type=0_0100 · no data · 12 bytes header
CfgWr0 — Fmt, TypeFmt=010 · Type=0_0100 · 1 DW payload · 12 bytes header
CfgRd1 — Fmt, TypeFmt=000 · Type=0_0101 · forwarded until Type 1→0 conversion
CfgWr1 — Fmt, TypeFmt=010 · Type=0_0101 · forwarded then converted
Type 0 vs Type 1Type bit[0]=0 → Type 0 (device on Secondary Bus responds) · bit[0]=1 → Type 1 (forwarded downstream)
TC fieldMust be 000 (TC 0 only) for all config TLPs
Length fieldAlways 0b00_0000_0001 (= 1 DW) for all config TLPs
RO, NS, IDO, AT bitsAll must be 0 for config TLPs
Last DW BEAlways 0x0 for config TLPs (Length = 1 DW)
First DW BEAny pattern including all zeros · selects bytes within the 1 DW register access
DW2 target addressBus[31:24] · Device[23:19] · Function[18:16] · Rsvd[15:12] · ExtRegNo[11:8] · RegNo[7:2] · 00
Register address10-bit DW address = {ExtRegNo[3:0], RegNo[5:0]} → selects 1 of 1024 DWs in 4 KB config space
ExtRegNo = 0PCI-compatible config space (bytes 0–255)
ExtRegNo ≠ 0PCIe extended capabilities (bytes 256–4095)
Type 1→0 conversion ruletarget_bus == Secondary → convert · Secondary < target ≤ Subordinate → forward Type 1 · outside → discard
BDF captureAny CfgWr0 received by a device causes it to latch its Bus and Device number from DW2
CfgRd completionCplD with 1 DW data payload · Byte Count = 4 · never split
CfgWr completionCpl (no data) · status confirms write landed
CRSConfig Request Retry Status — config-only · device not ready · RC retries · VendorID = 0x0001 if SW Visibility enabled
ARIReinterprets Byte 9 as 8-bit Function (Device=0) · 256 functions per bus · used in SR-IOV
Gen 6 impactZero — header format unchanged · flit packing transparent · new Gen 6 caps accessible via extended config space
Coming next — PCIe-09: IO and Message TLPs — IO Read and Write for legacy devices, Message TLPs replacing PCI interrupt and error sideband signals, INTx signalling, PME, ERR_COR/NONFATAL/FATAL, and message routing codes.
Scroll to Top