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. On/Off Flow Control

Part 4 · Flow Control · Working (W)

On/Off Flow Control

Stop-and-go flow control: the receiver says stop when it is nearly full. Where to put the threshold so that everything already on its way still fits, and why hysteresis matters.

On this page

    Note

    Level: Working (W) · Answers: How much may I send before I hear back?

    Before this: Why Flow Control

    A receiver with a 32-word buffer sends "stop" to its sender when the buffer is full. In the lab, the buffer overflows anyway, by several words, every time traffic is heavy. The stop message works: the sender does stop. It just stops too late.

    When should a receiver say stop, so that everything already on its way still fits?

    The obvious answer, and why it breaks

    The obvious answer is to say stop when the buffer is full. But "stop" takes time to reach the sender, and during that time the sender keeps sending. Then the words that were already on the wire when the sender stopped keep arriving. All of those land in a buffer that was already full. The stop signal must be sent early, with room left for everything still coming.

    The idea: stop early, by one round trip

    In on/off (also called XON/XOFF, or PAUSE) flow control, the receiver sends "stop" when its fill level reaches a threshold, and "go" when it has drained to a lower threshold. The room above the stop threshold must hold everything that arrives after the decision:

    room above threshold≥rate×(stop delay+in-flight delay)−drained meanwhile

    With a one-way delay of D cycles in each direction, the stop message takes D cycles to reach the sender and the words already on the wire take another D to arrive: a full round trip of words at the sender's rate. The go threshold is set lower than the stop threshold, called hysteresis, so the link does not flip between stop and go on every word.

    After the receiver decides to stop, words keep arriving for one round trip. The threshold must leave room for them.

    Build it: find the safe threshold

    The model has a sender at one word per cycle, a receiver draining one word every other cycle into a 32-word buffer, and a one-way delay of 8 cycles. It tries five stop thresholds (with go at half of each) and reports the peak fill.

    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    """On/off (XON/XOFF) flow control and where to put the threshold.
    
    The sender sends one word per cycle unless stopped. The receiver drains one word every
    other cycle into a buffer of B = 32 words. When the buffer reaches the XOFF threshold,
    the receiver sends "stop"; it takes D = 8 cycles to reach the sender, and words already
    on the wire keep arriving for another D cycles. "Go" is sent when the buffer falls to
    the XON threshold. Find which XOFF thresholds overflow the buffer.
    """
    from collections import deque
    
    B, D = 32, 8
    
    
    def run(xoff, xon, cycles=3000):
        buf, peak, overflow = 0, 0, False
        stopped_at_sender = False
        wire = deque([0] * D)              # words on their way to the receiver
        ctrl = deque([None] * D)           # stop/go messages on their way to the sender
        sent_stop = False
        for t in range(cycles):
            msg = ctrl.popleft()
            if msg is not None:
                stopped_at_sender = msg == "stop"
            send = 0 if stopped_at_sender else 1
            wire.append(send)
            buf += wire.popleft()
            if t % 2 == 0 and buf > 0:     # receiver drains one word every other cycle
                buf -= 1
            if buf > B:
                overflow = True
            peak = max(peak, buf)
            if not sent_stop and buf >= xoff:
                ctrl.append("stop"); sent_stop = True
            elif sent_stop and buf <= xon:
                ctrl.append("go"); sent_stop = False
            else:
                ctrl.append(None)
        return peak, overflow
    
    
    print("XOFF threshold  peak fill (buffer 32)  overflow?")
    results = {}
    for xoff in (8, 16, 20, 24, 28):
        peak, ovf = run(xoff, xon=xoff // 2)
        results[xoff] = ovf
        print(f"{xoff:14d}  {peak:20d}  {'yes' if ovf else 'no'}")
    # Words still arriving after XOFF is sent: about D cycles for the message to arrive plus
    # D cycles of words already in flight, minus what the receiver drains meanwhile.
    assert not results[16] and results[28]
    print("PASS: the threshold must leave room for a round trip of words; XOFF at 28 overflows")

    It prints:

    Diagram
    XOFF threshold  peak fill (buffer 32)  overflow?
                 8                    15  no
                16                    23  no
                20                    27  no
                24                    31  no
                28                    35  yes
    PASS: the threshold must leave room for a round trip of words; XOFF at 28 overflows

    In every case the peak is 7 words above the threshold: 16 cycles of round trip at one word per cycle, minus the 8 or so words the receiver drained in that time, minus one for when the threshold is noticed. A threshold of 24 leaves exactly enough room in a 32-word buffer; 28 overflows by 3. So the receiver's stop threshold is not "full" but "full minus a round trip of net arrivals".

    The design space

    Choice

    What it costs

    When it wins

    On/off with threshold and hysteresis

    Room above the threshold is unused most of the time

    Simple links; few traffic classes; moderate distances

    Lower stop threshold (more margin)

    Even more idle buffer; sender stops more often

    Uncertain delays

    On/off per traffic class

    One threshold set per class

    Links where one class must not stop the others (priority flow control)

    Credits instead (lesson 4.3)

    Counters and credit messages

    Long links, many classes, or when buffers are too precious to leave idle

    Compared with credits, on/off wastes buffer: the room above the threshold is reserved for the worst case even when traffic is light. Its advantage is simplicity: the sender only obeys two messages.

    How real protocols chose

    Protocol

    Its on/off scheme

    Ethernet

    IEEE 802.3x defines PAUSE frames, which ask the link partner to stop sending for a time; IEEE 802.1Qbb priority flow control pauses each priority separately, so one class of traffic can be stopped without stopping the rest.

    UART

    Optional software flow control with XON and XOFF characters, or hardware flow control with dedicated wires, in both cases stop-and-go.

    Ethernet's priority flow control is the same idea extended to traffic classes, the problem credits solve with separate credit pools (lesson 4.6).

    What breaks

    The opening overflow, as debug steps (illustrative):

    • Symptom: buffer overflow under heavy traffic, always by a few words, despite a working stop signal.

    • Evidence: the stop was sent when the buffer was full; the overflow equals the words that arrived during the round trip after it.

    • Root cause: threshold at full, not at full minus a round trip of net arrivals.

    • Fix: move the stop threshold down by the round trip times the rate (minus what drains), and set the go threshold lower still to avoid oscillation. Recompute whenever the path's latency changes.

    Why this matters

    Senior lens. On/off thresholds hide the round trip in a number that someone set once and nobody revisits. When a path gains pipeline stages, a retimer or a longer cable, the threshold silently becomes unsafe. In reviews, ask for the threshold to be derived, in the specification or in a parameter, from the round-trip latency and the rate, and for an overflow counter that would show if the derivation ever stops holding.

    Interview

    Asked: "Where do you set the XOFF threshold?"

    Weak answer: When the buffer is full.

    Strong answer: Below full by everything that can still arrive after the decision: the stop message's delay to the sender plus the words already in flight, that is one round trip at the sender's rate, minus what the receiver drains in that time. Then set XON lower to add hysteresis so the link doesn't flip on every word. And re-derive it whenever the path latency changes.

    Follow-ups: (1) Why does on/off waste buffer compared with credits? (2) What does priority flow control add?

    Check yourself A link has 4 cycles of delay each way; the sender sends one word per cycle and the receiver drains nothing while stopped. How much room above the stop threshold is needed?

    One round trip of words: 4 cycles for the stop to arrive plus 4 cycles of words in flight, so 8 words.

    Check yourself Why is the go threshold set below the stop threshold?

    Hysteresis: without it, the fill level hovering at one threshold would make the receiver send stop and go alternately on every word.

    Check yourself In the model, why is the peak always 7 words above the threshold?

    In the 16-cycle round trip about 16 words arrive and about 8 are drained, and the threshold is detected one cycle late, so the fill rises a further 7 or so above the threshold.

    Key takeaways

    • On/off flow control stops the sender when the receiver's fill reaches a threshold, and restarts it at a lower one.

    • The stop threshold must leave room for a round trip of net arrivals: the stop's delay plus the words already in flight.

    • It is simple but leaves buffer idle; thresholds must be re-derived whenever the path latency changes.

    Keep going

    Next: Lesson 4.3, Credit-Based Flow Control. Grant room in advance instead of saying stop afterwards: credits.

    Related topics

    • Part 4 overview: Flow Control

    Previous

    52 / 234

    Next: Credit-Based Flow Control

    Up next

    Up nextCredit-Based Flow Control

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Credit Initialisation
    • The Credit Loop and Buffer Sizing
    • Credit Classes and Pools