PCIe Series — PCIe-05: TLP Structure — Common Header Fields — VLSI Trainers
PCIe Series · PCIe-05

TLP Structure — Common Header Fields

Every TLP starts with the same first Doubleword. This post dissects it bit by bit — Fmt, Type, TC, LN, TH, TD, EP, Attr (RO, NS, IDO), AT, and Length — with worked examples, hex decoding, and what each field means in Gen 6.

📋 What a TLP Is — Structure Overview

A Transaction Layer Packet (TLP) is how PCIe devices talk to each other. Every read request, write, completion, configuration access, interrupt signal, and error report travels as a TLP. The Transaction Layer builds TLPs on the transmit side and decodes them on the receive side. Everything below the Transaction Layer (Data Link Layer, Physical Layer) is plumbing — the TLP is the message.

TLP — Four Parts, DW0 Is Always First DW0 Common Header Always present · 4 bytes THIS POST ↑ DW1 + DW2 (or DW1 + DW2 + DW3) Requester ID · Tag · BEs Address / ID / Message fields → PCIe-06 through PCIe-09 Data Payload 0–1024 DW · optional Present when Fmt bit[1]=1 MWr, CplD, IOWr, CfgWr ECRC 4 bytes · optional Present when TD = 1 End-to-end · survives routing Data Link Layer wraps the TLP: prepends 12-bit Sequence Number · appends 32-bit LCRC Physical Layer adds framing: Gen 1/2 → STP/END K-characters · Gen 3–5 → 128b/130b sync headers Gen 6 → packed into 256-byte flit with FEC parity appended
Figure 1 — TLP structure. DW0 (this post) is always the first 4 bytes of every TLP and carries all the type and control information. The type-specific header fields (DW1 onward), payload, and ECRC are covered in later posts. Data Link Layer and Physical Layer additions are transparent to the Transaction Layer.
Why every TLP starts with the same DW0. When a receiver’s Transaction Layer dequeues a packet from the Physical Layer, the very first thing it does is parse DW0. From Fmt it knows immediately how many more header DWs to read and whether to expect a payload. From Type it knows exactly which kind of packet this is. All of this decision-making happens from the first 4 bytes — before the receiver has read anything else.

📋 DW0 — Full Bit Map

DW0 is 32 bits. Each field lives at a fixed bit position. The receiver always parses bits 31 downto 0 in this exact layout. Reserved bits must be written as 0 and should be ignored on receipt.

TLP Header DW0 — Bit-by-Bit Map (bit 31 → bit 0, left to right) 31 30:28 27:24 23 22:20 19 18 17 16 15 14 13 12:11 10 9:0 R Fmt [30:28] 3 bits Type [27:24] 5 bits (was 4:0) R TC [22:20] 3 bits R LN [18] 1 bit TH [17] 1 bit TD [16] 1 bit EP [15] 1 bit Attr [2] IDO [14] Attr [1] RO [13] Attr [0] NS [12] AT [11:10] 2 bits Length [9:0] 10 bits · DW count Colour key Blue — TLP type and size info (Fmt, Type, Length) Green — QoS and address handling (TC, AT) Purple — Processing hints (LN, TH) Amber — Ordering and cache hints (TD, Attr: IDO, RO, NS) Red — Error signalling (EP) Grey — Reserved (must be 0) Byte boundaries within DW0 Byte 0 (bits 31:24) Byte 1 (bits 23:16) Byte 2 (bits 15:8) Byte 3 (bits 7:0)Spec references fields by byte+bit position: e.g. TC = Byte 1 bits 6:4 · EP = Byte 2 bit 6 · Length = Byte 2 bits 1:0 + Byte 3 bits 7:0
Figure 2 — DW0 complete bit map with colour coding by field group. Bit 31 is the MSB on the left; bit 0 is the LSB on the right. The byte boundaries divide DW0 into Byte 0 (Fmt+Type), Byte 1 (TC+LN+TH), Byte 2 (TD+EP+Attr+AT+Length[9:8]), Byte 3 (Length[7:0]).

📋 Fmt [30:28] — Format and Header Size

Fmt is a 3-bit field at DW0 bits 30:28. It tells the receiver two things simultaneously: how many DWs the header occupies (3 or 4) and whether a data payload follows the header. The receiver reads Fmt first — before anything else — to know how many bytes to collect for the header and whether to expect payload bytes after.

Fmt[30:28] — All Encodings with Visual Packet Shapes 000 3DW no data DW0 DW1 DW2 = 12 bytes total · no payload Used by: MRd, MRdLk, IORd (32-bit address) 001 4DW no data DW0 DW1 DW2 DW3 = 16 bytes total · no payload Used by: MRd, MRdLk (64-bit address), Msg 010 3DW + data DW0 DW1 DW2 Data (1–1024 DW) Used by: MWr (32-bit), CplD, IOWr, CfgWr Bit interpretation Bit 30 (Fmt[2]): 0 = 3DW header (12 bytes) 1 = 4DW header (16 bytes) Bit 29 (Fmt[1]): 0 = no data payload 1 = data payload present Bit 28 (Fmt[0]): TLP Prefix flag (Gen 3+)
Figure 3 — Fmt encoding. Three common values (000, 001, 010) cover the vast majority of TLPs. The 4DW variant (011) is identical to 010 but with 16-byte header for 64-bit addresses. Bit 29 is the “data present” flag — the receiver checks this to know whether to expect payload bytes after the header DWs.
Address rule: a target address below 4 GB (32-bit) must use the 3DW header format (Fmt bit 30 = 0). Using a 4DW header with a 32-bit address where the upper 32 bits are zero is technically undefined behaviour in the spec — receivers may discard it as malformed. Address ≥ 4 GB requires the 4DW header.

📋 Type [27:24] — TLP Type

Type is a 5-bit field at DW0 bits 27:24. Combined with Fmt it uniquely identifies the TLP. The receiver uses Type to select the correct parser for the remaining header DWs and to decide what action to take (route, consume, generate completion, etc.).

The full Type encoding table is covered in PCIe-06 through PCIe-09 as part of each specific TLP format. Below is a summary grouped by category:

Fmt[1:0]Type[4:0]TLP NameHeaderPayload?
000_0000MRd — Memory Read Request3DWNo
010_0000MRd — Memory Read Request4DWNo
000_0001MRdLk — Memory Read Locked3DWNo
100_0000MWr — Memory Write Request3DWYes
110_0000MWr — Memory Write Request4DWYes
000_0010IORd — IO Read Request3DWNo
100_0010IOWr — IO Write Request3DWYes
000_0100CfgRd0 — Config Read Type 03DWNo
100_0100CfgWr0 — Config Write Type 03DWYes
000_0101CfgRd1 — Config Read Type 13DWNo
100_0101CfgWr1 — Config Write Type 13DWYes
000_1010Cpl — Completion without Data3DWNo
100_1010CplD — Completion with Data3DWYes
011_0rrrMsg — Message (rrr = routing)4DWNo
111_0rrrMsgD — Message with Data4DWYes
100_1100FetchAdd — AtomicOp3DWYes
100_1101Swap — AtomicOp3DWYes
100_1110CAS — AtomicOp Compare-and-Swap3DWYes

📋 TC [22:20] — Traffic Class

TC is a 3-bit field at DW0 bits 22:20. It encodes a priority value from 0 (lowest, default) to 7 (highest). The TC travels unmodified with the TLP from requester to completer — it is never changed by a switch. Completions must carry the same TC value as the request that caused them.

At every switch port, the TC value is used to select which Virtual Channel (VC) buffer the TLP enters. TC 0 always maps to VC0. TC 1–7 can be mapped to VC0 or any implemented VC, configured via the TC/VC mapping register. TLPs of different TCs have no ordering relationship with each other — ordering rules only apply within the same TC.

TC[22:20] — Priority Ladder and VC Mapping TC 0 Default → VC0 always TC 1 → VC0 or VC1 TC 2 → any VC TC 3 TC 4 TC 5 TC 6 TC 7 Highest priority → dedicated VC Key Rules • TC 0 → VC0 (mandatory, always) • VC0 is the only mandatory VC • Completions inherit requester TC • Cross-TC ordering: none Example: video stream TC 7 → dedicated VC with guaranteed bandwidth · background DMA TC 0 → VC0 best-effort
Figure 4 — TC priority ladder. TC 0 is the default and always maps to VC0. TC 7 is the highest priority. Most real-world systems use only VC0 with all TC values mapped to it. High-performance video or audio systems may use multiple VCs to prevent high-priority traffic from being delayed by lower-priority DMA.

📋 LN [18] — Lightweight Notification

LN is a 1-bit field at DW0 bit 18. It is also referred to as Attr[2] in some spec tables — though in the Gen 3 spec it was separated from the older Attr field to make room for IDO. When LN = 1, the TLP is tagged as a Lightweight Notification request — a mechanism that allows a downstream component to notify the Root Complex of specific events without generating a full Message TLP or interrupt.

LN is an optional feature. Most devices and most TLPs leave it at 0. LN = 1 requires both the sender and intermediate nodes to declare support for LN through configuration registers. For general-purpose PCIe design, treat LN = 0 as the normal state.

📋 TH [17] — TLP Processing Hints

TH is a 1-bit field at DW0 bit 17. When TH = 1, the TLP contains TLP Processing Hints (TPH) in the header — additional bytes that give the platform a hint about how to handle caching for this transaction. The TPH mechanism is aimed at optimising NUMA (Non-Uniform Memory Access) systems where a DMA device should write data to the memory controller closest to the CPU that will process it.

Like LN, TH = 1 is an optional feature that requires explicit hardware support. For IO and legacy TLP types the TH bit is always reserved (must be 0). For most general-purpose PCIe devices in a standard server or desktop, TH = 0.

📋 TD [16] — TLP Digest (ECRC Present)

TD is a 1-bit field at DW0 bit 16. When TD = 1, a 4-byte End-to-End CRC (ECRC) is appended to the end of the TLP — after the data payload, before the LCRC that the Data Link Layer adds. When TD = 0, no ECRC is present.

TD = 1 — ECRC vs LCRC: Two Different CRCs, Two Different Purposes LCRC — Link CRC (always present) Added by: Data Link Layer of the transmitter Covers: SeqNo + entire TLP header + payload Checked at: every hop — receiver DLL checks on arrival On error: NAK DLLP sent, TLP replayed from sender Switch behaviour: checks incoming LCRC, strips it, recalculates new LCRC for outgoing link — does NOT propagate original LCRC ECRC — End-to-End CRC (TD=1, optional) Added by: Transaction Layer of the original sender Covers: TLP header + payload (from sender’s TL) Checked at: final destination TL only (endpoint or RC) On error: error reported to AER — packet may still be accepted Switch behaviour: forwards ECRC unchanged — must not modify it even if it performs optional ECRC checking
Figure 5 — LCRC (Link CRC) is mandatory and per-hop; it catches link transmission errors and triggers automatic replay. ECRC (End-to-End CRC, enabled by TD=1) is optional and survives routing — it catches errors that happen inside a switch’s fabric, which LCRC cannot detect because LCRC is stripped and recalculated at each switch boundary.
Why ECRC matters. Suppose data gets silently corrupted inside a switch’s crossbar fabric — after the incoming LCRC passes but before the outgoing LCRC is calculated. That error is invisible to LCRC (the outgoing LCRC will be calculated on the already-corrupted data). ECRC covers this gap — it is calculated from the original sender’s data and only checked at the final receiver, so any mid-path corruption is caught.

ECRC support is optional for endpoints but can be enabled via the AER Extended Capability registers. Both sender and receiver must agree to use ECRC. A device that does not support ECRC checking must still forward TLPs with TD=1 unchanged.

📋 EP [15] — Error Poisoned Data

EP is a 1-bit field at DW0 bit 15. When EP = 1, the payload data accompanying this TLP is marked as intentionally invalid. The TLP is allowed to complete normally — routing, flow control, and acknowledgement proceed as usual — but the final destination is told: “do not use this data, it is corrupt.”

When is EP set?

EP does not stop the TLP. A TLP with EP=1 is not dropped or NAKed. It travels all the way to the destination. Only at the destination (and at AER-capable intermediate nodes) is the error reported. This design allows the system to maintain flow control and ordering even when data is known to be bad — and lets software decide how to recover.

📋 Attr [14:12] — Relaxed Ordering, No Snoop, IDO

Attr is a 3-bit field spanning DW0 bits 14 through 12. The three bits have independent meanings and are named separately in the spec:

Attr[14:12] — Three Independent Bits, Three Separate Meanings Attr[2] = bit 14 — IDO ID-Based OrderingWhen IDO=1: packets with different Requester IDs may reorder past each other. Switch treats them as independent, unrelated streams. Use when: devices share no state Avoid: shared memory buffers Enabled via Device Control 2 Introduced in PCIe 2.1 Bit position: DW0 bit 14 Attr[1] = bit 13 — RO Relaxed OrderingWhen RO=1: switch MAY allow this TLP to pass earlier posted writes. Software guarantees no ordering dependency on prior transactions. Use when: GPU reads, scatter-gather Avoid: flag writes after data writes Advisory — switch may ignore it Forwarded unchanged by switches Bit position: DW0 bit 13 Attr[0] = bit 12 — NS No SnoopWhen NS=1: RC may skip CPU cache snoop for this memory access. Software guarantees: target memory is uncacheable or caches are clean. Use when: DMA/NVMe/GPU buffers Avoid: cacheable memory regions Saves 50–200 ns snoop round-trip RC skips cache coherency check Bit position: DW0 bit 12
Figure 6 — The three Attr bits. IDO (bit 14) enables reordering between different Requester ID streams. RO (bit 13) permits a switch to bypass earlier posted writes. NS (bit 12) skips CPU cache snoops at the Root Complex. All three are optional performance hints that require software awareness of their safety conditions.

All three Attr bits are forwarded unchanged through the fabric — a switch must not modify them. Completions must carry the same Attr values as the request that triggered them (with the exception that IDO may be set independently by the completer).

📋 AT [11:10] — Address Type

AT is a 2-bit field at DW0 bits 11:10. It supports Address Translation Services (ATS) and IOMMUs in virtualised systems. The field encodes whether the address in the TLP has been translated by an IOMMU or is still a “virtual” device address.

AT[1:0]NameMeaning
00Default / UntranslatedNormal address — not translated. This is the value for all standard transactions in non-virtualised systems.
01Translation RequestThe TLP is asking the IOMMU (via the Root Complex) to translate this address. Used only for ATS Translation Requests.
10TranslatedThe address has already been translated by the IOMMU. The device is using a physical host address directly. Requires prior ATS negotiation.
11ReservedMust not be used. Receivers may treat it as a Malformed TLP.

AT only applies to Memory and AtomicOp TLPs. For IO, Config, Message, and Completion TLPs, AT bits must be 00 (they are reserved or don’t apply). The AT field is covered in depth in PCIe-11 alongside ATS and the Page Request Interface (PRI).

📋 Length [9:0] — Payload Size in Doublewords

Length is a 10-bit field spanning DW0 bits 9:0 (Byte 2 bits 1:0, Byte 3 bits 7:0). It specifies the number of Doublewords (4-byte units) of data payload that follow the header. It applies only to TLPs that have a data payload (Fmt bit 29 = 1).

Length[9:0] — Encoding Table and Key Rules Length Encoding Binary value Payload size Bytes 00_0000_0001 1 DW 4 bytes 11_1111_1111 1023 DW 4092 bytes 00_0000_0000 1024 DW (max!) 4096 bytes Note: 0x000 wraps to 1024 — not zero Key Rules (from the spec) ① Length = 0 means 1024 DW (4096 bytes) — not zero payload ② For read requests (MRd): Length = the number of DWs requested, not 0 ③ Max payload limited by Max_Payload_Size in Device Control register (128 B, 256 B, 512 B, 1 KB, 2 KB, or 4 KB — must not exceed) ④ No 4 KB boundary crossing: (start_addr + length) must not span a 4096-byte boundary (receiver may discard as Malformed TLP)
Figure 7 — Length field encoding and rules. The 10-bit field naturally wraps — value 0x000 means 1024 DW (the maximum), not zero. The device’s Max_Payload_Size configuration register places a further hardware limit. No single TLP payload may cross a 4 KB address boundary.
Length for read requests vs write requests. For a write (MWr), Length counts the DWs of data being sent. For a read (MRd), Length counts the DWs of data being requested — there is no payload in the request TLP itself, but Length tells the completer how much data to return. The completer may split this into multiple CplD TLPs if needed.

Worked Example — Decoding a Real DW0

Let’s decode the hex value 0x40000010 — a typical DW0 from an MRd (Memory Read Request) for a 32-bit address.

Decode 0x40000010 — Step by Step 0x40 00 00 10 Binary: 0100 0000 0000 0000 0000 0000 0001 0000 Byte 0 = 0x40 = 0100 0000 · Byte 1 = 0x00 · Byte 2 = 0x00 · Byte 3 = 0x10 = 0001 0000 Fmt[30:28] Byte 0 bits 7:5 0x40 = 0100 0000 bits 7:5 = 010 010 = 3DW header with data payload Type[27:24] Byte 0 bits 4:0 0x40 = 0100 0000 bits 4:0 = 00000 0_0000 = MWr Memory Write Byte 1 = 0x00 TC[22:20] = 000 LN=0 · TH=0 Byte 2 = 0x00 → TD=0 EP=0 Attr=000 AT=00 TC 0 · No ECRC No poison · No RO/NS/IDO Length[9:0] Byte 2[1:0] + Byte 3 0x00 0x10 = 0x010 = 16 decimal 16 DW payload = 64 bytes of data DW0 = 0x40000010 → MWr · 3DW header · 64 bytes payload · TC 0 · No ECRC · No RO · Untranslated address A memory write targeting a 32-bit address, best-effort traffic class, standard ordering — the most common TLP type in any PCIe system
Figure 8 — Decoding 0x40000010. Byte 0 = 0x40 encodes Fmt=010 (3DW+data) and Type=00000 (MWr). Byte 1 = 0x00 means TC=0, LN=0, TH=0. Byte 2 = 0x00 means TD=0, EP=0, all Attr bits = 0, AT=00. Byte 3 = 0x10 = 16 decimal = Length of 16 DW = 64 bytes of payload data.

DW0 in Gen 6 — What Changes

The answer is: nothing changes in DW0. Every field — Fmt, Type, TC, LN, TH, TD, EP, Attr (IDO, RO, NS), AT, Length — is identical in Gen 6 as in Gen 1. The TLP header format did not change across any PCIe generation.

In Gen 6, TLPs are physically packed into 256-byte flits by the Physical Layer before being sent over the link. From the Transaction Layer’s perspective, it builds exactly the same DW0 it always has and hands the complete TLP to the Data Link Layer. The Data Link Layer adds SeqNo and LCRC. The Physical Layer packs it into a flit. The receiver’s Physical Layer unpacks the flit and delivers the TLP upward — and the Transaction Layer decodes the same DW0 it has always decoded.

The practical rule for RTL and firmware designers: if you are writing logic that builds or parses TLP DW0 — the bit positions, field widths, and encodings are the same for Gen 1, Gen 3, and Gen 6. Your TLP parsing code does not need a generation flag. The physical transport changes with generation; the TLP header does not.

📋 Quick Reference — All DW0 Fields

FieldBits (DW0)Byte:BitsWidthPurpose
Fmt30:28Byte 0 [7:5]3 bitsHeader size (3DW vs 4DW) and whether data payload is present. 010=3DW+data, 001=4DW-no-data, etc.
Type27:24Byte 0 [4:0]5 bitsTLP type: MRd (0_0000), MWr (0_0000+Fmt), IORd/IOWr (0_0010), CfgRd/Wr0 (0_0100), CplD (0_1010), Msg (1_0rrr)…
Reserved23Byte 1 [7]1 bitMust be 0. Ignored on receipt.
TC22:20Byte 1 [6:4]3 bitsTraffic Class 0 (default) to 7 (highest). Selects Virtual Channel. Completions must match request TC.
Reserved19Byte 1 [3]1 bitMust be 0.
LN18Byte 1 [2]1 bitLightweight Notification. Optional. 0 in most TLPs.
TH17Byte 1 [1]1 bitTLP Processing Hints present. Optional NUMA placement hint for DMA. 0 in most TLPs.
TD16Byte 1 [0]1 bitTLP Digest — ECRC appended at end of TLP. End-to-end CRC survives routing. Optional.
EP15Byte 2 [7]1 bitError Poisoned — data payload is known bad. TLP still delivered; destination must not use data. Propagated by devices that forward poisoned data.
Attr[2] / IDO14Byte 2 [6]1 bitID-Based Ordering. Different Requester IDs may reorder past each other. Enabled in Device Control 2.
Attr[1] / RO13Byte 2 [5]1 bitRelaxed Ordering. Switch MAY allow this TLP to bypass earlier posted writes. Advisory.
Attr[0] / NS12Byte 2 [4]1 bitNo Snoop. RC skips CPU cache snoop. Safe only for uncacheable DMA regions.
AT11:10Byte 2 [3:2]2 bitsAddress Type: 00=Untranslated (default), 01=Translation Request, 10=Translated (ATS). 00 for all non-memory TLPs.
Reserved9Byte 2 [1]1 bitPart of Length[9:0] field — not separately reserved. Length spans Byte 2 bits [1:0] and Byte 3 [7:0].
Length9:0Byte 2 [1:0] + Byte 3 [7:0]10 bitsData payload size in DW. 0x001=1DW(4B) … 0x3FF=1023DW … 0x000=1024DW(4096B, maximum). For MRd: bytes requested. For MWr: bytes sent.
Coming next — PCIe-06: Memory Read and Write TLPs — the full 3DW and 4DW header formats for MRd, MWr, and MRdLk, 32-bit vs 64-bit addressing, the Byte Enable fields, payload alignment rules, and worked packet diagrams.
Scroll to Top