Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
  • Part 4: Flow Control17
    • Why Flow Control
    • On/Off Flow Control
    • Credit-Based Flow Control
    • Credit Initialisation
    • The Credit Loop and Buffer Sizing
    • Credit Classes and Pools
    • Credit Return Latency and Batching
    • Credit Bugs
    • Window Flow Control
    • Rate-Based Shaping
    • Hop-by-Hop or End-to-End
    • Lossless or Lossy
    • Switching Modes
    • Head-of-Line Blocking
    • Choosing Flow Control
    • Protocols Compared: Flow Control
    • Drills: Flow Control
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 4: Flow Control
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 4: Flow Control
  4. Credit Return Latency and Batching

Part 4 · Flow Control · Senior (S)

Credit Return Latency and Batching

Every cycle a credit spends on its way back lengthens the loop. Why late or batched credit returns cut throughput, and how to trade return bandwidth against buffering.

On this page

    Note

    Level: Senior (S) · Answers: How much may I send before I hear back?

    Before this: The Credit Loop and Buffer Sizing

    A link was sized carefully: 12 receiver credits against a 10-cycle credit round trip, comfortably full throughput. To save bandwidth on the return path, a later change sends credits back in groups of eight instead of one at a time. Throughput drops to about 60%, and the change was "only a return-path optimisation".

    How much does returning credits late, or in batches, cost, and when is it worth it?

    The obvious answer, and why it breaks

    The obvious view is that credit returns are bookkeeping: sending them in batches saves messages and changes nothing important, because the credits all arrive eventually. But lesson 4.5 showed that a credit is useless while it is not at the sender. A credit that waits at the receiver for its batch to fill is, for that time, still "in the loop". Batching lengthens the round trip of every credit by up to the time it takes to fill the batch.

    The idea: the loop includes every cycle a credit waits

    Count a credit's round trip including the time it sits at the receiver waiting to be returned. With batches of K, a freed credit may wait for up to K − 1 more slots to free before the batch goes out. At one word per cycle, the effective loop grows by up to K − 1 cycles, so full throughput now needs:

    B≥RTT+(K−1)

    With fewer credits than that, throughput falls below one word per cycle. When B is not a multiple of K, there is a further loss: up to K − 1 credits can be stranded at the receiver, waiting for a batch that the remaining credits are too few to complete quickly.

    Build it: batch size against throughput

    The model is lesson 4.5's credit loop with 4 stages each way (round trip 10 cycles) and 12 credits, so batches of one give full throughput. It then returns credits only in groups of K and measures steady-state throughput.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    """Credit return batching: return credits in groups of K instead of one at a time.
    
    Same loop as lesson 4.5: L = 4 stages each way, receiver buffer B = 12 (the round
    trip without batching is 2L + 2 = 10 cycles, so 12 credits give full throughput).
    Now the receiver holds freed credits until it has K of them, then returns all K at once.
    """
    from collections import deque
    
    
    def simulate(L, B, K, cycles=6000):
        credits, buf, pending, sent_late = B, 0, 0, 0
        fwd, back = deque([0] * L), deque([0] * L)
        for t in range(cycles):
            arrive, crd_in = fwd[-1], back[-1]
            send = 1 if credits > 0 else 0
            pop = 1 if buf > 0 else 0
            pending += pop
            ret = 0
            if pending >= K:
                ret, pending = K, pending - K
            credits += crd_in - send
            buf += arrive - pop
            fwd.rotate(1); fwd[0] = send
            back.rotate(1); back[0] = ret
            assert credits + sum(fwd) + buf + pending + sum(back) == B
            if t >= cycles // 2:
                sent_late += send
        return sent_late / (cycles - cycles // 2)
    
    
    BATCH = [1, 2, 4, 6, 8, 12]
    row = [simulate(4, 12, k) for k in BATCH]
    print("batch size K:  " + " ".join(f"{k:5d}" for k in BATCH))
    print("throughput:    " + " ".join(f"{x:5.3f}" for x in row))
    assert row[0] == 1.0 and row[-1] < row[0]
    print("PASS: batching holds credits back; once B no longer covers RTT + K - 1, throughput falls")

    It prints:

    Diagram
    batch size K:      1     2     4     6     8    12
    throughput:    1.000 1.000 0.923 0.800 0.615 0.572
    PASS: batching holds credits back; once B no longer covers RTT + K - 1, throughput falls
    The model's numbers. Batches of 1 or 2 are free; beyond that, throughput falls as the batch delays returning credits.

    Batches of 1 and 2 cost nothing: 12 ≥ 10 + 1. At 4, 12 < 10 + 3 and throughput is 12/13. At 6 it is 12/15 = 0.80. At 8 it falls further than the formula's 12/17, to 0.615, because four credits are stranded: the receiver holds them waiting for a batch of eight. At 12, all credits travel as one batch and the loop is 21 cycles: 12/21 ≈ 0.57.

    Credit loop simulator

    Loading credit loop simulator…

    The opening example: 12 credits, a 10-cycle round trip and batches of 8, so about 62%. Step through to see four freed credits wait at the receiver; then set the batch to 2, or raise the credits to 17.

    Worked numbers: return bandwidth versus buffer

    Batching exists because credit messages cost bandwidth on the return path. With illustrative numbers:

    Diagram
    Forward traffic: one 64-byte flit per cycle; credit message: 8 bytes, carrying a count
    Return one credit per message:   8 bytes per cycle on the return path (12.5% of the forward rate)
    Return 4 credits per message:    2 bytes per cycle (3.1%), but B must grow by K - 1 = 3 flits
                                     = 3 x 64 = 192 bytes of extra buffer to keep full throughput

    The trade is explicit: return bandwidth against buffer. Where the return path also carries other traffic (responses, for example), saving return bandwidth can be worth a few extra buffer entries; where the buffer is SRAM-limited, returning promptly is cheaper.

    Other ways credits come back late

    • The return path is shared and credit messages wait behind other traffic: give credits priority, or carry them in the headers of reverse-direction packets.

    • The receiver frees slots late, for example only after its consumer has fully processed a word: return the credit as soon as the slot itself is free.

    • A clock-domain crossing on the return path adds synchroniser cycles to every credit: count them in the round trip.

    The design space

    Return policy

    Return bandwidth

    Buffer needed for full rate

    One credit per message, immediately

    Highest

    RTT

    Batches of K

    1/K of the above

    RTT + K − 1

    Counts carried in reverse-direction traffic

    Almost none extra

    RTT plus the wait for a reverse packet; needs a fallback when there is none

    Cumulative totals ("I have freed N in all")

    Like batches, and a lost message is repaired by the next

    As for the batch interval

    How real protocols chose

    Protocol

    Credit return

    PCI Express

    Credits are returned with UpdateFC data link layer packets, separate from data traffic; how often they are sent is a design choice within the specification's limits, and it is exactly the trade above.

    What breaks

    The opening story (illustrative): batching credits by eight lengthened the effective loop from 10 to 17 cycles and stranded four credits, so a link with 12 credits ran at about 60%. The fix was a hybrid many designs use: return credits in batches when the link is busy in both directions, but return any pending credits immediately when the sender is running low or the return path is idle, so batching never starves the sender.

    Why this matters

    Senior lens. When you size a credit loop, write down every term in it, including the receiver's return policy, and re-check the sizing when any term changes. Return-path optimisations are the classic silent regression: they are made by a different team, for a good reason, and they only show up as lower throughput. Expose the credit count at the sender for debug; a counter that sits at zero while the receiver holds unreturned credits is the immediate signature.

    Interview

    Asked: "Why not return credits one at a time?"

    Weak answer: It wastes bandwidth.

    Strong answer: One at a time is the best for throughput, because each credit gets back to the sender as soon as possible, but it costs a message per credit on the return path. Batching K credits cuts that bandwidth by K, but each credit may wait up to K − 1 slots at the receiver, so the effective loop grows and full throughput needs B ≥ RTT + K − 1; non-multiples can also strand credits. So it's a trade: return bandwidth against buffer. A common compromise is to batch when busy and flush immediately when the sender is short.

    Follow-ups: (1) What is a stranded credit? (2) How would you notice a batching regression in silicon?

    Check yourself A loop has RTT 20 and the receiver batches credits in groups of 4. How many credits give full throughput?

    At least RTT + K − 1 = 20 + 3 = 23 credits.

    Check yourself In the model, why is throughput at K = 8 lower than 12/17?

    Twelve is not a multiple of eight, so after a batch of eight goes out, four freed credits wait at the receiver for four more, and the sender works with fewer credits for longer than the simple formula assumes.

    Check yourself Why are cumulative credit totals more robust than individual credit pulses?

    If one message is lost, the next total still tells the sender everything freed so far; a lost pulse is a credit lost for ever (lesson 4.8).

    Key takeaways

    • A credit's round trip includes any time it waits at the receiver before being returned.

    • Batching K credits saves return bandwidth but needs B ≥ RTT + K − 1 for full throughput, and can strand credits.

    • Every return-path change belongs in the credit-loop sizing; batch when busy, flush when the sender runs short.

    Keep going

    Next: Lesson 4.8, Credit Bugs. Leaks, double returns and resets: the bugs that make credit loops decay, and the one invariant that catches them.

    Related topics

    • Part 4 overview: Flow Control

    Previous

    57 / 234

    Next: Credit Bugs

    Up next

    Up nextCredit Bugs

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Why Flow Control
    • On/Off Flow Control
    • Credit-Based Flow Control