PCIe Series — PCIe-12: Data Link Layer — DLLPs and Reliability — VLSI Trainers
PCIe Series · PCIe-12
Data Link Layer — DLLPs and Reliability
How the Data Link Layer guarantees reliable TLP delivery — DLLP types and formats, sequence numbers, LCRC, the Replay Buffer, the ACK/NAK protocol, REPLAY_TIMER, and how Gen 6 flit-based reliability changes the picture.
📋 What the Data Link Layer Does
The Data Link Layer (DLL) sits between the Transaction Layer above it and the Physical Layer below it. Its single most important job is guaranteed delivery of TLPs across one link hop. If a TLP is corrupted or lost in the Physical Layer, the DLL detects this and automatically retransmits the packet — without any involvement from software, drivers, or the Transaction Layer.
Figure 1 — The Data Link Layer wraps every TLP with reliability metadata before sending it to the Physical Layer. The Transaction Layer sees only clean, error-free TLPs — it has no visibility into the ACK/NAK protocol happening below it.
The DLL also manages flow control credit updates (carried in UpdateFC DLLPs) and link power management signalling (PM DLLPs). These topics are covered in later posts. This post focuses on the reliability mechanism — the ACK/NAK protocol.
📋 How DLL Wraps a TLP
Every TLP that the Transaction Layer hands down to the Data Link Layer gets two additions before it is passed to the Physical Layer for transmission:
Figure 2 — DLL wrapping. The Sequence Number is prepended (2 bytes, top 4 bits reserved, lower 12 bits the counter value). The LCRC is appended (4 bytes, CRC-32 covering all fields including SeqNo). The Transaction Layer’s TLP content is unchanged — DLL adds reliability metadata around it.
📋 DLLPs — What They Are
DLLPs (Data Link Layer Packets) are the private language of the Data Link Layer. They carry ACK and NAK signals, flow control credit updates, and power management transitions. DLLPs are fundamentally different from TLPs in several ways:
Local traffic only. A DLLP travels exactly one hop — from one device’s DLL to the adjacent device’s DLL. Switches do not route DLLPs; each switch port has its own independent DLL that terminates and generates DLLPs for its link.
No flow control. DLLPs cannot be blocked by flow control. They are always accepted and immediately processed by the receiving DLL. If a DLLP is corrupted, it is discarded — there is no replay mechanism for DLLPs themselves.
Fixed 8-byte size. Every DLLP is exactly 8 bytes: 4 bytes of content (type + fields) plus a 2-byte CRC, plus 2 bytes of framing symbols in Gen 1/2 (or a 2-byte SDP token in Gen 3+).
Not visible to the Transaction Layer. DLLPs never reach the Transaction Layer. They are generated, consumed, and discarded entirely within the Data Link Layer.
📋 DLLP Format
Figure 3 — DLLP structure. Byte 0 is the type code. Bytes 1–3 carry type-specific content (sequence number for ACK/NAK, credit counts for flow control). Bytes 4–5 are the 16-bit CRC protecting bytes 0–3. Physical Layer adds framing — the total transmitted size including framing is 8 bytes in all generations.
📋 DLLP Types
DLLP Type
Type byte encoding
Purpose
Ack
0000 0000b
Positive acknowledgement — the receiver tells the transmitter which TLPs arrived correctly. Carries a 12-bit Sequence Number representing the last successfully received TLP.
Nak
0001 0000b
Negative acknowledgement — a TLP failed LCRC check or arrived out of sequence. Transmitter must replay from the indicated Sequence Number.
PM_Enter_L1
0010 0000b
Request to enter L1 link power state.
PM_Enter_L23
0010 0001b
Request to enter L2/L3 link power state.
PM_Active_State_Request_L1
0010 0011b
Request for ASPM L1 entry.
PM_Request_Ack
0010 0100b
Acknowledge a power management request.
Vendor Specific
0011 0000b
Vendor-defined content. Bytes 1–3 are vendor-defined.
InitFC1-P/NP/Cpl
0100–0110 0xxxb
Flow control initialisation phase 1 — advertise receive buffer sizes.
InitFC2-P/NP/Cpl
1100–1110 0xxxb
Flow control initialisation phase 2 — confirm values.
UpdateFC-P/NP/Cpl
1000–1010 0xxxb
Update flow control credits as receive buffers drain.
xxx = VC number (0–7). Flow control DLLPs are covered in PCIe-13. PM DLLPs are covered in PCIe-16.
📋 Sequence Numbers
Every TLP that the Data Link Layer transmits gets a 12-bit Sequence Number stamped on it before it is sent. The Sequence Number counter starts at 0 after reset and increments by 1 for each TLP. When it reaches 4095 it wraps back to 0. This wrap is expected and handled correctly — “later” in sequence terms means the next sequential value after wrap-around, not necessarily numerically larger.
Figure 4 — 12-bit Sequence Number counter. Counts 0 through 4095 then wraps back to 0. “Later” is defined as the next value in counting order, accounting for wrap-around. Transmitter and receiver must never be more than 2048 apart or the numbering scheme becomes ambiguous.
Three outcomes when a TLP arrives at the receiver
Sequence Number comparison
Meaning
Action
SeqNo == NEXT_RCV_SEQ (expected)
Good TLP — this is exactly the packet we were waiting for
Accept, forward to Transaction Layer, increment NEXT_RCV_SEQ, schedule ACK
SeqNo < NEXT_RCV_SEQ (earlier)
Duplicate — we have already received and accepted this TLP
Silently drop. Send ACK with NRS−1 so transmitter knows current progress
SeqNo > NEXT_RCV_SEQ (later)
Gap — a TLP was lost or dropped between this one and the last good one
Discard this TLP. Send NAK with NRS−1. Set NAK_SCHEDULED flag
📋 LCRC — Link CRC
The LCRC (Link CRC) is a 32-bit CRC appended by the transmitter DLL to every TLP before it is sent. The receiver DLL recalculates the same CRC over the received bits and compares the result with the received LCRC. Any single-bit or burst error in the TLP body will produce a mismatched CRC and trigger a NAK.
The LCRC covers: Sequence Number + TLP Header + Data Payload (if any) + ECRC (if present). It does not cover the Physical Layer framing symbols.
LCRC is per-hop only. When a TLP passes through a switch, the switch’s DLL checks the incoming LCRC, strips it, and the switch’s DLL appends a freshly calculated LCRC before forwarding the TLP out the egress port. This means LCRC only detects errors on the individual link segment. Errors that corrupt a TLP inside a switch’s internal fabric — after the LCRC check but before the new LCRC is calculated — are invisible to LCRC. That is exactly why ECRC (the Transaction Layer’s optional end-to-end CRC, enabled by the TD bit in DW0) exists.
📋 Replay Buffer
Before sending any TLP, the transmitting DLL stores a complete copy of it — including the prepended Sequence Number and appended LCRC — in the Replay Buffer. This copy stays in the buffer until an ACK DLLP arrives confirming successful reception. Only then is it purged.
Figure 5 — Replay Buffer holds all sent-but-unacknowledged TLPs. When ACK(9) arrives, everything with SeqNo ≤ 9 is purged. The buffer must be large enough to hold all in-flight TLPs for a round-trip ACK latency without stalling the Transaction Layer.
Sizing the Replay Buffer
The Replay Buffer is not a fixed hardware size mandated by the spec — it is a design choice. It needs to be large enough to hold all TLPs sent during one ACK round-trip without stalling. The round-trip latency includes: physical link propagation delay, ACK generation time at the receiver, and ACK transmission delay back to the sender. For a Gen 6 x16 link, the ACK round-trip allows many large TLPs to be in flight simultaneously.
📋 ACK/NAK Protocol — Normal Operation
Under normal conditions (no errors), the protocol is simple: transmitter sends TLPs with incrementing Sequence Numbers, receiver accepts them, and periodically sends ACK DLLPs to confirm delivery.
Figure 6 — Normal ACK operation. Three TLPs are sent with SeqNos 5, 6, 7. All pass LCRC and SeqNo checks at the receiver. The ACK latency timer coalesces them into a single ACK(7). Transmitter purges SeqNos ≤ 7 from its Replay Buffer. No separate ACK is needed for each TLP.
The key efficiency feature is cumulative acknowledgement. A single ACK DLLP with SeqNo=N tells the transmitter that TLPs with SeqNo 0 through N all arrived successfully. This means heavy traffic with many TLPs per round trip generates only one ACK per batch, keeping DLLP overhead low.
📋 NAK and Replay — Error Recovery
Figure 7 — NAK recovery. TLP 6 fails LCRC at the receiver. TLPs 7 and 8 are discarded because they arrived out of sequence. Receiver sends NAK(5) — meaning SeqNo 5 was the last good TLP. Transmitter purges everything ≤ 5 and replays TLPs 6, 7, 8. All three pass on replay. Receiver sends ACK(8).
Important NAK rules
Only one NAK outstanding at a time. The NAK_SCHEDULED flag prevents the receiver from sending additional NAKs until the problematic TLP is successfully replayed. This prevents the transmitter from being NAK-bombed into an infinite replay loop.
Replay starts at the first bad TLP, not just the bad one. When a NAK arrives, the transmitter replays all TLPs still in the Replay Buffer starting from the one that failed — including any TLPs that may have been sent after the bad one. This is correct behaviour because the receiver discards everything after a SeqNo gap.
Duplicate TLPs on replay are handled silently. If TLP 5 was already acknowledged before the replay arrives, the receiver discards the duplicate and sends an ACK. No error.
📋 REPLAY_TIMER and REPLAY_NUM
The REPLAY_TIMER is a watchdog that runs whenever there are unacknowledged TLPs in the Replay Buffer. If it expires without receiving any ACK or NAK that makes forward progress, the transmitter automatically replays everything in the buffer — even without receiving a NAK. This handles scenarios where the NAK itself was corrupted and discarded.
Figure 8 — REPLAY_TIMER fires when no ACK/NAK arrives in time. REPLAY_NUM counts replay attempts. After 4 failed replays (REPLAY_NUM rolls over from 11 to 00), the DLL forces link retraining — something is seriously wrong with the physical link and it needs to re-establish timing and equalization.
📋 ACK Latency Timer
The receiver’s ACK Latency Timer controls how long the receiver may hold off sending an ACK in order to batch multiple TLPs into one acknowledgement. When the timer expires, the receiver must send an ACK covering all TLPs it has received since its last ACK.
The timer value is calculated from the link speed, link width, and maximum payload size — it is set long enough to guarantee that a fast transmitter can keep its link busy without stalling waiting for ACKs, but short enough that the transmitter’s REPLAY_TIMER never expires before an ACK arrives during normal operation.
Link configuration
Approximate ACK latency (typical)
Gen 1 x1, 128B max payload
~237 symbol times ≈ 950 ns
Gen 1 x16, 128B max payload
~48 symbol times ≈ 190 ns
Gen 3 x16, 256B max payload
Much shorter due to 128b/130b and higher speed
Gen 6 x16
Flit-level ACKs — see Gen 6 section below
📋 All Error Cases and How They Resolve
Error scenario
Detection
Resolution
TLP has LCRC error
Receiver DLL: recalculated CRC ≠ received LCRC
Discard TLP. Send NAK(NRS−1). Transmitter replays from the failed TLP onward.
TLP lost en route (Physical Layer drops it)
Next arriving TLP has a SeqNo gap (greater than expected)
Discard the out-of-sequence TLP. Send NAK(NRS−1). Transmitter replays from the missing TLP.
No immediate action. Wait for next ACK or NAK. If REPLAY_TIMER expires, replay all. Often resolves harmlessly — next successful ACK covers all prior TLPs.
Receiver will not send another NAK (NAK_SCHEDULED prevents it). REPLAY_TIMER eventually expires, triggering replay. Receiver accepts replayed TLPs, clears NAK_SCHEDULED, sends ACK.
No ACK/NAK arrives at all
REPLAY_TIMER expires at transmitter
Replay entire buffer. Increment REPLAY_NUM. After 4 consecutive failures, force link retraining.
4 replay attempts all fail
REPLAY_NUM rolls from 11b to 00b
DLL forces LTSSM to Recovery state. Link retrains. If link cannot recover, link goes down — hardware error escalation to AER.
Duplicate TLP arrives (replay after already-acked TLP)
SeqNo < NEXT_RCV_SEQ at receiver
Silently discard duplicate. Send ACK(NRS−1) so transmitter knows current progress. No error raised.
📋 Transmit Priority Order
When the DLL has multiple things to transmit simultaneously, it must prioritise them correctly. The recommended order (highest priority first):
Complete any TLP or DLLP already in progress on the link
Ordered Sets (from the Physical Layer)
NAK DLLPs
ACK DLLPs
Flow Control Update DLLPs
Replayed TLPs from the Replay Buffer
New TLPs from the Transaction Layer
All other DLLPs (power management, vendor)
NAKs and ACKs are prioritised above new TLPs to ensure the transmitter on the remote end receives error feedback quickly. A delayed NAK means the remote transmitter must keep more TLPs in its Replay Buffer for longer — potentially stalling its Transaction Layer.
⚡ Data Link Layer in Gen 6
Gen 6 introduces flit-based framing at the Physical Layer, and this changes how the Data Link Layer’s reliability mechanism operates. The core concepts — Sequence Numbers, LCRC, Replay Buffer, ACK/NAK — are all preserved but operate at a different granularity.
Flit-level vs TLP-level reliability
Figure 9 — Gen 1–5 assign Sequence Numbers per TLP. Gen 6 assigns Sequence Numbers per flit. Multiple TLPs are packed into each 256-byte flit. FEC corrects most errors without any replay. Only uncorrectable flit errors trigger ACK/NAK replay at the flit level. A replayed flit brings all its TLPs with it.
How Gen 6 DLL reliability works
FEC is the first line of defence. Gen 6’s Reed-Solomon FEC corrects up to 15 symbol errors per flit. The vast majority of physical link errors that would have triggered ACK/NAK replay in Gen 1–5 are silently corrected by FEC in Gen 6 without any DLL involvement.
Sequence Numbers are per flit. The DLL assigns a Sequence Number to each flit, not to each TLP. The Sequence Number counter and the Replay Buffer operate at flit granularity.
ACK/NAK is per flit. A NAK causes the replay of the entire flit that failed — including all TLPs packed inside it. If a flit contained TLPs 5, 6, and 7, a NAK on that flit causes all three to be replayed.
LCRC is per flit. The FEC parity block appended to each Gen 6 flit serves a similar integrity-checking role. If FEC cannot correct the errors (too many symbol errors), the DLL treats it as a CRC failure and triggers replay.
REPLAY_TIMER and REPLAY_NUM logic is unchanged. The same timeout and escalation mechanism applies, now operating at flit granularity rather than TLP granularity.
DLLPs in Gen 6. ACK, NAK, and UpdateFC DLLPs are carried inside flits alongside TLPs, not in separate framing gaps. The flit header indicates which bytes carry DLLPs. Their content and semantics are unchanged.
Gen 6 in practice: fewer replays, simpler ACK/NAK. Because FEC handles the vast majority of bit errors silently, Gen 6 links experience far fewer NAK-triggered replays than Gen 5 links operating on the same physical channel. The ACK/NAK protocol is still there — it handles the rare uncorrectable flit errors — but the typical Gen 6 link operates almost entirely in the FEC-correction path, with the DLL replay mechanism as a fallback for catastrophic errors. This means the Replay Buffer in a Gen 6 device can be smaller than an equivalent Gen 5 device operating at the same throughput.
📋 Quick Reference
Concept
Key Point
DLL purpose
Guaranteed per-hop TLP delivery. Detects and automatically corrects transmission errors without software involvement.
32-bit CRC per TLP. Per-hop only — stripped and recalculated at each switch. Does not protect against in-switch fabric corruption (that is ECRC’s job).
Replay Buffer
Stores copy of every sent-but-unacked TLP. Purged when ACK covers the TLP. Replayed on NAK or REPLAY_TIMER expiry.
NAK_SCHEDULED flag
Set when receiver sends a NAK. Cleared only when the expected TLP arrives correctly. Only one NAK outstanding at a time.
REPLAY_TIMER
Watchdog. Fires if no forward progress from ACK/NAK. Triggers full buffer replay. Resets when ACK makes progress or buffer empties.