콘텐츠로 이동

Module 04 — DLLP, Flow Control, ACK/NAK

학습 목표

이 모듈을 마치면:

  • List DLLP 의 종류 (Ack, Nak, FC Init/Update, PM_*, Vendor) 와 8 byte 포맷을 나열한다.
  • Trace ACK/NAK 시퀀스와 Replay Buffer 동작을 timeline 으로 추적한다.
  • Compute P/NP/Cpl 별 (Header credit + Data credit) 모델로 송신 가능 여부를 판정한다.
  • Diagram Gen6 의 FLIT mode 가 ACK/NAK 메커니즘을 어떻게 단순화하는지 그릴 수 있다.
  • Distinguish ACKFC Update 의 의미 차이를 정확히 구분해 stall 원인을 분류한다.

사전 지식

  • Module 02 (DLL 책임)
  • Module 03 (TLP 의 P/NP/Cpl 분리)

1. Why care? — 이 모듈이 왜 필요한가

1.1 시나리오 — Cpl credit 부족 으로 stall

당신의 PCIe link 가 link up OK, 그런데 throughput 0 Mbps. Stall.

원인 추적: - TLP 발송 시도 → FC credit 부족 → block. - Credit 종류: P (Posted), NP (Non-Posted), Cpl (Completion). - 각각 Header (H), Data (D) 별도 카운트.

현재 상태: - PH credit: 100 (충분). - NPH credit: 0 (없음). ← 여기. - CplH credit: 50.

NP (Memory Read) 보내려는데 NPH credit 0 → block.

원인: Peer 가 FC Update DLLP 안 보냄. NP request 처리 후 credit return 안 함.

FC credit 6 종류 × header/data 분리는 부분적 stall 진단을 위한 핵심.

Link 가 동작하지 않을 때 80% 는 DLL 영역의 이슈 (FC credit 부족, ACK/NAK loop, Replay buffer overflow). DLLPFC 의 동작을 정확히 알아야 packet trace 에서 stall 의 원인을 찾을 수 있습니다.

이 모듈의 어휘 — Sequence# / LCRC / Replay Buffer / FC credit (PH/PD/NPH/NPD/CplH/CplD) / ACK coalescing — 가 이후 stall debug, AER correctable counter 해석, Gen6 FLIT 의 차이를 이해하는 기반. 한 번 정확히 잡으면 packet trace 만 봐도 "이건 NP credit 부족" 처럼 즉시 분류할 수 있습니다.


2. Intuition — 음식점 좌석 비유와 한 장 그림

💡 한 줄 비유

Flow Control credit음식점 좌석 예약.
좌석이 비어야 (credit 있음) 손님 (TLP) 보낼 수 있음. 좌석 종류 (P/NP/Cpl) 마다 별도 카운트 — VIP 좌석 (Cpl) 이 비어도 일반 (P) 이 차면 못 받음. 손님이 식사 끝내면 (TL 처리 완료) 좌석 반환 (FC Update DLLP). 처음에 "좌석 N 개" 알려줌 (FC Init).

한 장 그림 — DLL 의 두 트랙

Sender DLL[Replay Buffer: Seq=12,13,14]Receiver DLL[RX TLP buf · PH/PD/NPH/NPD/CplH/CplD credit] TLP (Seq 12) + LCRCLCRC OKTLP (Seq 13) + LCRCLCRC FAIL NAK (Seq 13)TLP (Seq 13) re-sendLCRC OKACK (Seq 14)UpdateFC (P, …)
Sender DLL[Replay Buffer: Seq=12,13,14]Receiver DLL[RX TLP buf · PH/PD/NPH/NPD/CplH/CplD credit] TLP (Seq 12) + LCRCLCRC OKTLP (Seq 13) + LCRCLCRC FAIL NAK (Seq 13)TLP (Seq 13) re-sendLCRC OKACK (Seq 14)UpdateFC (P, …)

두 트랙이 분리된 layer 의 두 트랙:

  • Reliability 트랙 — Seq# / LCRC / ACK / NAK / Replay. "내가 보낸 packet 이 정상 도착했는가."
  • Flow Control 트랙 — credit per (P/NP/Cpl) × (Header/Data). "다음 packet 을 보낼 buffer 자리가 있는가."

왜 이 디자인인가 — Design rationale

세 가지 요구가 동시에 풀려야 했습니다.

  1. Hop-level reliability (PHY BER ≠ 0) → Seq# + LCRC + ACK/NAK + Replay.
  2. Receiver buffer overflow 방지 (TL 의 처리 속도 ≠ link 속도) → P/NP/Cpl 별 credit-based FC.
  3. Producer-Consumer ordering 보장 (TL 의 ordering rule 과 호환) → P/NP/Cpl 분리된 credit pool.

세 요구의 교집합이 DLLP (8B link-level packet) + 12-bit Seq# + 6 credit groups 입니다.


3. 작은 예 — 3 TLP 중 1 개 LCRC error 가 replay 까지 가는 과정

가장 단순한 시나리오. Sender 가 Seq=10/11/12 의 TLP 3 개를 연속 송신, 12 가 LCRC error 로 깨짐. 그 후 13/14 가 추가 송신.

Sender (RC)[Replay Buf]Receiver (EP) Seq=10 TLPLCRC OK → next expected = 11Seq=11 TLPLCRC OK → next expected = 12Seq=12 TLPLCRC FAIL → NAK send NAK Seq=12Seq=12 TLP (re-send)LCRC OK → next expected = 13Seq=13 TLPLCRC OKACK Seq=13
Sender (RC)[Replay Buf]Receiver (EP) Seq=10 TLPLCRC OK → next expected = 11Seq=11 TLPLCRC OK → next expected = 12Seq=12 TLPLCRC FAIL → NAK send NAK Seq=12Seq=12 TLP (re-send)LCRC OK → next expected = 13Seq=13 TLPLCRC OKACK Seq=13

단계별 의미

Step 누가 무엇을
Sender DLL Seq=10/11/12 의 TLP 를 Replay Buffer 에 저장 + 송신 ACK 받기 전까지 보관 필수
Receiver DLL Seq=10, 11 의 LCRC OK → next_expected 증가 hop-level 무결성 통과
Receiver DLL Seq=12 의 LCRC FAIL → NAK with seq=12 재전송 요청
Sender DLL NAK Seq=12 수신 → Replay Buffer 에서 Seq=12 부터 재송신 "그 지점부터 모두 다시"
Receiver DLL Seq=12 재수신 → OK → 13 도 OK → ACK Seq=13 (누적 ACK) ACK coalescing
Sender DLL ACK Seq=13 → Replay Buffer 에서 Seq ≤ 13 retire 메모리 회수
// 의사코드 — Sender DLL 의 NAK 처리
void on_nak(uint16_t bad_seq) {
    // NAK 받으면 그 seq 부터 모두 재송신
    for (uint16_t s = bad_seq; s != next_transmit_seq; s = (s + 1) % 4096) {
        struct tlp t = replay_buffer_get(s);
        send_phy_with_seq(t, s);
    }
    replay_count++;
    if (replay_count > REPLAY_MAX) {
        // Replay 한도 초과 → DL_Inactive → LTSSM Recovery
        dl_state = DL_INACTIVE;
        trigger_ltssm_recovery();
    }
}

// Sender DLL 의 ACK 처리
void on_ack(uint16_t cumulative_seq) {
    // 누적 ACK — cumulative_seq 까지 모두 retire
    while (replay_buffer_oldest_seq() <= cumulative_seq) {
        replay_buffer_retire_oldest();
    }
    replay_count = 0;  // 정상 진행 → reset
}

여기서 잡아야 할 두 가지

(1) NAK 은 단일 packet 이 아니라 그 Seq 부터 끝까지 의 재송신을 트리거한다. 그래서 단일 비트 오류가 늦게 발견되면 retransmission 비용이 큼 — ACK 가 일찍 돌아와야 효율.
(2) Replay Buffer 가 차면 sender 가 stall 한다 — credit 처럼 동작. RTT 가 큰 link (long-distance, retimer 다수) 에서는 작은 buffer 가 throughput 의 발목.


4.1 DLL 의 3 책임

책임 메커니즘 DLLP 종류
Reliability Seq# (12-bit) + LCRC (32-bit) + Replay Buffer + ACK/NAK Ack, Nak
Flow Control P/NP/Cpl × Header/Data 의 6 credit 그룹 (per VC) InitFC1, InitFC2, UpdateFC
Link State DL_Inactive → DL_Init → DL_Active (state machine, no DLLP)
Power Mgmt 신호 PM_Enter_L1, PM_Request_Ack, … PM_xxx

4.2 Sequence Number 모델

   범위: 0..4095 (modulo 4096)

   Sender
     - Next Transmit Seq# (NTS): 송신할 Seq#
     - Acknowledged Seq# (AckSeq): 마지막 ACK 받은 #

   Receiver
     - Next Receive Seq# (NRS): 받을 것으로 기대하는 Seq#
     - Receive Buffer 의 last good Seq#

   조건: 항상 (NTS - AckSeq) mod 4096 < buffer_size

→ Window size = 2048 (modulo 의 절반) 정도가 정상 운영 범위. modulo arithmetic 이라 "앞뒤" 비교 시 절반 window 만 안전.

4.3 ACKFC Update — 두 메커니즘의 분리

   ACK :    "이 Seq# 까지 LCRC 검증 OK 로 받았다"
            → Sender 의 Replay Buffer entry retire
            → Reliability 트랙

   FC Update: "이만큼 credit 이 다시 비었다, 이 만큼 더 보내도 된다"
            → Sender 의 송신 가능 여부 판단
            → Flow Control 트랙

두 메커니즘이 같은 방향 (receiver → sender) 의 DLLP 라는 공통점이 있지만, 하나는 packet integrity, 다른 하나는 buffer occupancy. 헷갈리면 stall 분석에서 잘못된 결론.


5. 디테일 — DLLP 카탈로그, FC credit rule, Gen6 FLIT

5.1 DLLP 구조 (8 byte 고정)

   Byte 0 (Type)   Byte 1-3 (Type-specific) Byte 4-7 (CRC + LCRC)
   ┌──────────┬─────────────────────────────────────┐
   │ Type 8b  │   payload (3 byte)                  │
   ├──────────┼─────────────────────────────────────┤
   │   16-bit DLLP CRC (separate from TLP LCRC)     │
   └────────────────────────────────────────────────┘
Type DLLP 설명
0000_0000 ACK 누적 ACK — Sequence # 까지 수신 OK
0001_0000 NAK Sequence Number 부터 재송 요청
0010_VVVV PM_xxx Power Management (Enter_L1, Request_Ack, ...)
0011_xxxx Vendor Specific 벤더 확장
01_VC_TYPE InitFC1 / InitFC2 / UpdateFC FC initialization / update — VC 와 type (P/NP/Cpl) 인코딩

InitFC1 → InitFC2 → UpdateFC 의 3 단계로 FC initialization 완료.

5.2 ACK / NAK 타임라인 (전체)

Sender[Replay Buf]Receiver Seq=10 TLPLCRC OK → next expected = 11Seq=11 TLPLCRC OK → next expected = 12Seq=12 TLPLCRC FAIL → NAK send NAK Seq=12Seq=12 TLP (re-send)LCRC OK → next expected = 13Seq=13 TLPLCRC OKACK Seq=13
Sender[Replay Buf]Receiver Seq=10 TLPLCRC OK → next expected = 11Seq=11 TLPLCRC OK → next expected = 12Seq=12 TLPLCRC FAIL → NAK send NAK Seq=12Seq=12 TLP (re-send)LCRC OK → next expected = 13Seq=13 TLPLCRC OKACK Seq=13

Replay Buffer

  • 송신 후 ACK 받기 전까지 TLP 보관.
  • NAK 시 그 Seq# 부터 재송신.
  • ACK 시 그 Seq# 까지의 entry retire (메모리 회수).
  • Buffer 가 차면 sender stall — credit 처럼 동작.

ACK Coalescing

매 packet 마다 ACK 보내면 비효율. Receiver 는 일정 packet 누적 또는 timer 만료 시 누적 ACK 1 개. spec 의 AckFactor parameter 가 양 끝의 협상 결과를 결정.

Replay Number Rollover

Replay 횟수가 한도 (보통 4) 초과 → DL 가 link recovery 트리거. LTSSM Recovery 단계로 빠짐.

Spec 인용

PCIe Base Spec 의 "Data Link Layer Specification > Retry Buffer" 와 "Ack/Nak Protocol" 섹션. (Spec 자체는 PCI-SIG 회원사 비공개)

5.3 Sequence Number 12-bit

   범위: 0..4095 (modulo 4096)

   Sender
     - Next Transmit Seq# (NTS): 송신할 Seq#
     - Acknowledged Seq# (AckSeq): 마지막 ACK 받은 #

   Receiver
     - Next Receive Seq# (NRS): 받을 것으로 기대하는 Seq#
     - Receive Buffer 의 last good Seq#

   조건: 항상 (NTS - AckSeq) mod 4096 < buffer_size

→ Window size = 2048 (modulo 의 절반) 정도가 정상 운영 범위.

5.4 Flow Control — 6 Credit Groups

   각 Virtual Channel (VC) 마다 독립적으로:
                Header   Data
                ──────   ────
   Posted   :   PH       PD
   Non-Posted:  NPH      NPD
   Completion:  CplH     CplD
  • Header credit: 1 unit = 4 DW (header size)
  • Data credit: 1 unit = 4 DW = 16 byte

Receiver 의 advertised credit = receiver 의 RX buffer 가 받을 수 있는 양.

Sender 의 송신 조건:

   if  TLP 의 header 가 1 unit 이고 payload 가 N DW 이면:
       (used_PH + 1) ≤ credit_limit_PH
       and (used_PD + ceil(N/4)) ≤ credit_limit_PD
   → 만족 시 송신 가능

만족 못 하면 stall — receiver 가 UpdateFC 로 credit 풀어주기를 기다림.

Infinite Credit (∞)

일부 카테고리는 spec 가 무한 credit 을 허용 (특히 Completion 의 Header).

FC Init1 / Init2 의 "credit advertised value" field 가 0 인 경우 ∞ 의미.

FC Initialization

SenderReceiver InitFC1 (P, advertised_credit)InitFC1 (NP, …)InitFC1 (Cpl, …) InitFC1 (P, …)InitFC1 (NP, …)InitFC1 (Cpl, …)InitFC2 (P, …)InitFC2 (NP, …)InitFC2 (Cpl, …)InitFC2 (P)InitFC2 (NP)InitFC2 (Cpl)
SenderReceiver InitFC1 (P, advertised_credit)InitFC1 (NP, …)InitFC1 (Cpl, …) InitFC1 (P, …)InitFC1 (NP, …)InitFC1 (Cpl, …)InitFC2 (P, …)InitFC2 (NP, …)InitFC2 (Cpl, …)InitFC2 (P)InitFC2 (NP)InitFC2 (Cpl)

UpdateFC: 현재 credit consumed 의 총합 (modulo) 을 주기적으로 송신.

5.5 디버그 — Stall 의 원인 분류

   Sender 가 packet 못 보냄
       ├─ Replay Buffer full ?
       │      → ACK 늦거나 NAK 빈발
       │      → Receiver LCRC error 확인
       │      → Link 의 BER 또는 PHY 문제
       ├─ FC credit 부족 ?
       │      → 어느 그룹? (PH/PD/NPH/NPD/CplH/CplD)
       │      → Receiver 의 TL 이 packet 처리 못 따라가는가
       │      → UpdateFC 주기가 비정상으로 느린가
       ├─ Outstanding NP 한도 초과 ?
       │      → Tag pool 고갈
       │      → 가장 흔한 원인: Completion 늦게 오거나 timeout
       └─ DL_Inactive 로 빠짐 ?
              → LTSSM Recovery 발생
              → 더 깊은 PHY 진단 필요 (Module 05)

5.6 Gen6 FLIT mode

   Pre-Gen6:   가변 길이 TLP/DLLP
   Gen6 FLIT:  고정 256-byte FLIT 프레임
               ┌──────────┬──────────────────────┬──────┬──────┐
               │ FLIT Hdr │ TLP / DLLP payload   │ CRC  │ FEC  │
               │  6 byte  │      236 byte        │ 8B   │ 6B   │
               └──────────┴──────────────────────┴──────┴──────┘
변화 효과
고정 프레임 Framing 단순화 (STP/END token 불필요)
FEC (Forward Error Correction) PAM4BER 증가를 보완, single-bit 정정
통합 ACK/NAK FLIT 단위로 매 frame ACK 가능 → 늦은 ACK 문제 ↓
FLIT mode 만 지원 Gen6 이상 link 는 FLIT mode 강제

→ Gen5 이하의 ACK/NAK 메커니즘은 그대로 살아 있지만, Gen6 부터 spec 의 default 가 FLIT mode.

5.7 검증 (DV) 시 자주 보는 시나리오

시나리오 목표
Replay 강제 LCRC corruption inject → NAK → replay 정상 동작 확인
Replay overflow ACK 일부러 drop → Replay 횟수 한도 → Recovery 진입
FC stall Receiver TL 의 처리 속도 강제 저하 → credit 부족 → sender stall 검증
Tag exhaustion NP 8b/10b tag pool 고갈 직전 시나리오
FC update timing UpdateFC 주기 변화에 따른 throughput 영향
Sequence # wraparound 4096 packet 보내 wrap 동작 확인
DLLP CRC fail DLLP 자체에 CRC corruption — 어떻게 처리되는지

6. 흔한 오해 와 DV 디버그 체크리스트

흔한 오해

❓ 오해 1 — 'ACKFC Update 는 같은 것이다'

실제: 서로 다른 layer 의 다른 메커니즘.

  • ACK: "이 sequence # 까지 LCRC 검증 OK 로 받았다" — DLL의 reliability.
  • FC Update: "이만큼 credit 이 다시 비었다, 이 만큼 더 보내도 된다" — TL 의 flow control.

하나는 packet integrity, 다른 하나는 buffer occupancy. 헷갈리면 stall 분석에서 잘못된 결론.

왜 헷갈리는가: 둘 다 receiver → sender 방향 DLLP 라는 공통점 때문.

❓ 오해 2 — '한 NAK 은 한 packet 만 재송신을 트리거한다'

실제: NAK 은 그 Seq# 이후 모두 의 재송신을 트리거. cumulative semantics. 단일 비트 오류가 늦게 발견되면 large 한 retransmit overhead 가 발생 — 그래서 ACK timer 가 너무 길지 않아야.
왜 헷갈리는가: TCP-style "selective ACK" 와의 혼동.

❓ 오해 3 — 'Cpl credit 은 항상 무한이라 신경 쓸 필요 없다'

실제: spec 가 Cpl 의 Header/Data credit 을 ∞ 로 광고하도록 허용 하지만 강제 하지는 않음. Implementation 에 따라 finite credit 을 광고할 수 있고, 그 경우 Cpl stall 도 발생. FC Init1 의 광고값을 직접 확인 필요.
왜 헷갈리는가: 일부 자료가 "Cpl 은 ∞" 로 단순화.

❓ 오해 4 — 'Replay Buffer size 는 spec 에 정해져 있다'

실제: spec 는 최소 만 규정 (RTT 와 ACK timer 를 견딜 정도). 실제 size 는 implementation 결정. 큰 size → throughput, 작은 size → 면적/전력 — trade-off. RTT 큰 link (retimer 다수) 에서는 작은 buffer 가 stall 의 주범.

❓ 오해 5 — 'Gen6 FLIT 이라 ACK/NAK 이 사라진다'

실제: Gen6 FLIT 도 reliability 가 필요 — 단지 프레임 단위 가 가변 → 256B 고정 으로 바뀐 것. ACK/NAK 메커니즘은 단순화되지만 사라지지 않음. FLIT mode 의 ACK 는 매 FLIT 단위로 가능해 latency 가 줄어드는 장점.

DV 디버그 체크리스트 (이 모듈 내용으로 마주칠 첫 실패들)

증상 1차 의심 어디 보나
Sender 가 packet 0 — link up 했는데 traffic 0 FC initialization 미완료 또는 credit advertised = 0 InitFC1/2 양쪽 교환 + DL_Active 상태
Latency spike + AER correctable counter ↑ LCRC error → NAK → replay 빈발 Receiver 의 LCRC error rate, replay 횟수 통계
특정 워크로드에서만 stall Cpl credit 부족 (split 많은 read) NPH/NPD 또는 CplH/CplD credit 사용량 추적
Tag pool 고갈로 새 NP 못 보냄 Cpl 이 timeout 으로 안 옴 → outstanding 누적 Completion Timeout counter + Tag tracking
lspci 로 봤을 때 link 정상이지만 throughput 0 DL_Active 미진입 LTSSM L0 도달했는데 DLL state 가 stuck — DLLP 캡처
Sequence# wrap 후 새 packet 이 거부 12-bit modulo arithmetic 버그 sender 의 NTS-AckSeq 모듈로 비교
DLLP CRC 자체가 깨져서 ACK 무효 PHY BER 또는 DLLP CRC 계산 버그 DLLP CRC error counter (별도)
Gen6 FLIT 인데 기존 분석 도구가 표시 못 함 FLIT-aware analyzer 가 아님 FLIT mode 인지 확인 → 도구 교체

7. 핵심 정리 (Key Takeaways)

  • DLLP = 8-byte link-level packet (Ack, Nak, FC Init/Update, PM_*, Vendor) — TL 이 보지 않음.
  • ACK/NAK + Sequence # (12-bit) + Replay Buffer 가 link reliability.
  • Flow Control 은 P/NP/Cpl × Header/Data 의 6 credit 그룹 (per VC).
  • FC Init: InitFC1 → InitFC1 양방향 → InitFC2 양방향 → DL_Active.
  • Gen6 FLIT 는 256B 고정 프레임 + FEC + 단순화된 ACK 메커니즘.

실무 주의점

  • "Link up 됐는데 traffic 0" 의 99%: FC initialization 미완료 또는 credit advertised = 0 으로 시작.
  • DLLP CRCLCRCECRC 는 모두 다른 메커니즘 — packet trace 에서 명확히 구분.
  • Replay buffer 는 DUT 마다 size 가 다름. RTT 가 큰 retimer 환경에서는 작은 buffer 가 throughput 의 발목.
  • "ACK 가 안 옴" 의 원인 진단: receiver 가 link 가 down (LTSSM 빠짐) / receiver 의 ACK timer 가 너무 길게 설정 / DLLP 자체가 PHY 에서 깨짐.
  • Gen6 FLIT 모드 검증은 기존 ACK/NAK packet trace 도구가 적용 안 될 수 있음 — FLIT-aware analyzer 필요.

7.1 자가 점검

🤔 Q1 — FC credit 계산 (Bloom: Apply)

PCIe Gen4 x16. PH credit 100, PD credit 1000. Posted TLP 의 최대 outstanding MB?

정답
  • PH (Header): 100 → 100 TLP outstanding 가능.
  • PD (Data): 1000 credit × 16 byte/credit = 16 KB.

Min(100 TLP × max payload, 16 KB) = bottleneck.

Max payload 256 byte 면: 100 × 256 = 25.6 KB, PD bottleneck 16 KB. Max payload 64 byte 면: 100 × 64 = 6.4 KB, PH bottleneck.

🤔 Q2 — Replay buffer overflow (Bloom: Analyze)

Replay buffer 16 KB. Link RTT 4 µs @ Gen4 x16. Outstanding 가능 한도?

정답
  • Gen4 x16 BW = ~31.5 GB/s.
  • RTT 4 µs → in-flight data = 31.5 × 4 = 126 KB.
  • Replay buffer 16 KB → 의미: 16 KB 만 ACK 전 in-flight 허용.

실제 throughput: min(Replay buffer, FC credit, BW) — Replay buffer가 bottleneck. 늘려야 RTT 큰 link 에서 throughput 만족.

7.2 출처

External - PCIe Specification — DLL chapter - Gen6 FLIT mode specification


다음 모듈

Module 05 — Physical Layer & LTSSM: DL_Inactive 로 빠진 link 가 어떻게 LTSSM Recovery 와 EQ 로 회복되는지. PHY 의 11 state 와 4 phase EQ.

퀴즈 풀어보기 →