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.
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.
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.
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 Name
Header
Payload?
00
0_0000
MRd — Memory Read Request
3DW
No
01
0_0000
MRd — Memory Read Request
4DW
No
00
0_0001
MRdLk — Memory Read Locked
3DW
No
10
0_0000
MWr — Memory Write Request
3DW
Yes
11
0_0000
MWr — Memory Write Request
4DW
Yes
00
0_0010
IORd — IO Read Request
3DW
No
10
0_0010
IOWr — IO Write Request
3DW
Yes
00
0_0100
CfgRd0 — Config Read Type 0
3DW
No
10
0_0100
CfgWr0 — Config Write Type 0
3DW
Yes
00
0_0101
CfgRd1 — Config Read Type 1
3DW
No
10
0_0101
CfgWr1 — Config Write Type 1
3DW
Yes
00
0_1010
Cpl — Completion without Data
3DW
No
10
0_1010
CplD — Completion with Data
3DW
Yes
01
1_0rrr
Msg — Message (rrr = routing)
4DW
No
11
1_0rrr
MsgD — Message with Data
4DW
Yes
10
0_1100
FetchAdd — AtomicOp
3DW
Yes
10
0_1101
Swap — AtomicOp
3DW
Yes
10
0_1110
CAS — AtomicOp Compare-and-Swap
3DW
Yes
📋 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.
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.
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?
Memory controller detects an ECC error in a read: the RC cannot return correct data, but it still returns a CplD. It sets EP=1 so the requester knows to treat the data as garbage.
An MWr arrives with EP=1: the completer (memory controller, device register, etc.) must still accept the write’s flow-control credit and process it — but it should discard the data payload and report an error. It must not update the target register or memory with poisoned data.
Propagation rule: if a device receives a read CplD with EP=1 and then uses that data in a subsequent write, it must set EP=1 in the write TLP too — propagating the poison flag forward so downstream devices know not to trust that data either.
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:
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]
Name
Meaning
00
Default / Untranslated
Normal address — not translated. This is the value for all standard transactions in non-virtualised systems.
01
Translation Request
The TLP is asking the IOMMU (via the Root Complex) to translate this address. Used only for ATS Translation Requests.
10
Translated
The address has already been translated by the IOMMU. The device is using a physical host address directly. Requires prior ATS negotiation.
11
Reserved
Must 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).
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.
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
Field
Bits (DW0)
Byte:Bits
Width
Purpose
Fmt
30:28
Byte 0 [7:5]
3 bits
Header size (3DW vs 4DW) and whether data payload is present. 010=3DW+data, 001=4DW-no-data, etc.
Traffic Class 0 (default) to 7 (highest). Selects Virtual Channel. Completions must match request TC.
Reserved
19
Byte 1 [3]
1 bit
Must be 0.
LN
18
Byte 1 [2]
1 bit
Lightweight Notification. Optional. 0 in most TLPs.
TH
17
Byte 1 [1]
1 bit
TLP Processing Hints present. Optional NUMA placement hint for DMA. 0 in most TLPs.
TD
16
Byte 1 [0]
1 bit
TLP Digest — ECRC appended at end of TLP. End-to-end CRC survives routing. Optional.
EP
15
Byte 2 [7]
1 bit
Error Poisoned — data payload is known bad. TLP still delivered; destination must not use data. Propagated by devices that forward poisoned data.
Attr[2] / IDO
14
Byte 2 [6]
1 bit
ID-Based Ordering. Different Requester IDs may reorder past each other. Enabled in Device Control 2.
Attr[1] / RO
13
Byte 2 [5]
1 bit
Relaxed Ordering. Switch MAY allow this TLP to bypass earlier posted writes. Advisory.
Attr[0] / NS
12
Byte 2 [4]
1 bit
No Snoop. RC skips CPU cache snoop. Safe only for uncacheable DMA regions.
AT
11:10
Byte 2 [3:2]
2 bits
Address Type: 00=Untranslated (default), 01=Translation Request, 10=Translated (ATS). 00 for all non-memory TLPs.
Reserved
9
Byte 2 [1]
1 bit
Part of Length[9:0] field — not separately reserved. Length spans Byte 2 bits [1:0] and Byte 3 [7:0].
Length
9:0
Byte 2 [1:0] + Byte 3 [7:0]
10 bits
Data 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.