Credit-based flow control algorithms, equations, initialization, and Flit Mode credit blocks
Flow control is a mechanism that prevents buffer overflow by ensuring a transmitter does not send more TLPs than the receiver can accept. PCIe uses a credit-based flow control system where:
| Credit Type | Abbreviation | Description | TLP Types |
|---|---|---|---|
| Posted Header | PH | Credits for Posted request headers | MWr, Msg, MsgD |
| Posted Data | PD | Credits for Posted request data (4 DW units) | MWr data, MsgD data |
| Non-Posted Header | NPH | Credits for Non-Posted request headers | MRd, CfgRd, CfgWr, IORd, IOWr |
| Non-Posted Data | NPD | Credits for Non-Posted request data | CfgWr data, IOWr data |
| Completion Header | CplH | Credits for Completion headers | Cpl, CplD |
| Completion Data | CplD | Credits for Completion data | CplD data |
CREDITS_CONSUMED[type] = Total credits used by transmitted TLPs
CREDIT_LIMIT[type] = Last received credit limit from receiver
CREDITS_AVAILABLE[type] = CREDIT_LIMIT[type] - CREDITS_CONSUMED[type]
CREDITS_ALLOCATED[type] = Total credits provided to transmitter
CREDITS_RECEIVED[type] = Credits for TLPs received and processed
CREDITS_TO_RETURN = CREDITS_RECEIVED[type] - CREDITS_ADVERTISED[type]
For each TLP transmitted:
Header_Credits_Consumed = 1 (one header credit per TLP)
Data_Credits_Consumed = CEIL(Payload_Size_Bytes / 16)
Where:
- Each data credit = 4 DW = 16 bytes
- CEIL rounds up to nearest integer
- Example: 100 byte payload = CEIL(100/16) = 7 credits
CanTransmit(TLP) =
(CREDITS_AVAILABLE[header_type] >= 1) AND
(CREDITS_AVAILABLE[data_type] >= Data_Credits_Required(TLP))
Where header_type and data_type depend on TLP type:
- Posted Request: PH, PD
- Non-Posted Request: NPH, NPD
- Completion: CplH, CplD
Credit counters use modular arithmetic with specific bit widths:
| Credit Type | Counter Width | Modulus |
|---|---|---|
| Header Credits (PH, NPH, CplH) | 8 bits | 256 |
| Data Credits (PD, NPD, CplD) | 12 bits | 4096 |
Credits_Available = (Credit_Limit - Credits_Consumed) MOD 2^N
Where N = 8 for headers, 12 for data
Transmit allowed if Credits_Available > 0
(accounting for wraparound)
Flow control initialization uses a two-phase handshake:
Port A Port B
│ │
│──────── InitFC1 (P) ──────────────────────►
│◄─────── InitFC1 (P) ─────────────────────│
│──────── InitFC1 (NP) ─────────────────────►
│◄─────── InitFC1 (NP) ────────────────────│
│──────── InitFC1 (Cpl) ────────────────────►
│◄─────── InitFC1 (Cpl) ───────────────────│
│ │
│ ═══ Phase 1 Complete ═══ │
│ │
│──────── InitFC2 (P) ──────────────────────►
│◄─────── InitFC2 (P) ─────────────────────│
│──────── InitFC2 (NP) ─────────────────────►
│◄─────── InitFC2 (NP) ────────────────────│
│──────── InitFC2 (Cpl) ────────────────────►
│◄─────── InitFC2 (Cpl) ───────────────────│
│ │
│ ═══ Phase 2 Complete ═══ │
│ │
│ Flow Control Initialized │
| Field | Bits | Description |
|---|---|---|
| Type | [7:0] | DLLP Type (InitFC1 or InitFC2) |
| VC ID | [2:0] | Virtual Channel identifier |
| HdrFC | [7:0] | Header credits (0-255) |
| DataFC | [11:0] | Data credits (0-4095) |
After initialization, UpdateFC DLLPs return credits to transmitters:
Transmitter Receiver
│ │
│────── TLP (consumes credits) ───────►│
│ │ Process TLP
│ │ Free buffer space
│◄──────── UpdateFC ──────────────────│
│ (returns credits) │
│ │
Send UpdateFC when ANY of:
1. Credits_To_Return >= Threshold (implementation specific)
2. Timer expires (< 30μs since last UpdateFC)
3. Credit limit has changed
4. Recovery from low-credit situation
A receiver can advertise "infinite" credits to indicate unlimited buffer space:
| Credit Type | Infinite Value | Meaning |
|---|---|---|
| Header (HdrFC) | 0x00 | Unlimited header credits |
| Data (DataFC) | 0x000 | Unlimited data credits |
Flit Mode uses a different credit model based on 64-byte credit blocks:
Credit_Block_Size = 64 bytes (fixed)
Credits_Consumed = CEIL(TLP_Size / 64)
Where TLP_Size includes header + data + any OHC
Flit Mode supports both shared and dedicated credit pools:
| Credit Type | Description | Use Case |
|---|---|---|
| Shared Credits | Single pool for all TLP types | Default, simpler management |
| Dedicated Credits | Separate pools per type | Guaranteed bandwidth |
The Flit Mode Local TLP Prefix indicates dedicated credit usage:
TLP_Blocks = CEIL((Header_Size + Payload_Size + OHC_Size) / 64)
For Shared Credits:
Shared_Credits_Consumed += TLP_Blocks
For Dedicated Credits:
Dedicated_Credits_Consumed[type] += TLP_Blocks
Where type = P, NP, or Cpl
PCIe can deadlock if completion credits are exhausted while non-posted requests are pending:
DEADLOCK SCENARIO:
Device A Device B
│ │
│──── NP Request ────────────────►│ B receives request
│ │ B needs to send Cpl
│ │ B has no Cpl credits to A!
│ │
│◄─── (cannot send Cpl) ──────────│
│ │
│ A waiting for Cpl │ B waiting for Cpl credits
│ A cannot free Cpl credits │
│ === DEADLOCK === │
Min_CplH_Credits >= Max_Outstanding_NP_Requests
Min_CplD_Credits >= Max_NP_Request_Size * Max_Outstanding_NP_Requests
Where Max_NP_Request_Size depends on Max_Read_Request_Size
┌─────────────┐
│ IDLE │
│ (Reset) │
└──────┬──────┘
│ DL_Up
▼
┌─────────────┐
│ INIT_FC1 │◄──────────┐
│ (Phase 1) │ │
└──────┬──────┘ │
│ All InitFC1 │ Timeout/Error
│ Received │
▼ │
┌─────────────┐ │
│ INIT_FC2 │───────────┘
│ (Phase 2) │
└──────┬──────┘
│ All InitFC2
│ Received
▼
┌─────────────┐
│ FC_ACTIVE │◄──────────┐
│ (Normal) │ │
└──────┬──────┴───────────┘
│ │
│ UpdateFC │ Link
│ Exchange │ Active
└──────────────────┘
TLP: MWr with 256 bytes data payload
Non-Flit Mode:
PH Credits = 1 (one header)
PD Credits = CEIL(256 / 16) = 16 credits
Flit Mode (Shared):
Header = 16 bytes (4 DW)
Payload = 256 bytes
Total = 272 bytes
Credit Blocks = CEIL(272 / 64) = 5 blocks
TLP: CplD with 512 bytes data
Non-Flit Mode:
CplH Credits = 1
CplD Credits = CEIL(512 / 16) = 32 credits
Flit Mode:
Header = 12 bytes (3 DW)
Payload = 512 bytes
Total = 524 bytes
Credit Blocks = CEIL(524 / 64) = 9 blocks