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
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
    • Shared Media and Topologies
    • Arbitration Policies
    • Fairness and Starvation
    • Priority Inversion
    • Arbitration Granularity
    • Distributed Arbitration
    • Quality of Service
    • Virtual Channels
    • Routing
    • Multicast and Broadcast
    • Protocols Compared: Sharing and Arbitration
    • Drills: Sharing and Arbitration
  • 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 8: Sharing and Arbitration
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 8: Sharing and Arbitration
  4. Fairness and Starvation

Part 8 · Sharing and Arbitration · Working (W)

Fairness and Starvation

Fairness means every requester is served eventually, within a bound you can state. How to measure it (bandwidth share and worst wait), why fixed priority starves, and how to prove a bound.

On this page

    Note

    Level: Working (W) · Answers: Who gets the wire?

    Before this: Arbitration Policies

    A requester on a busy fabric issues a read and never gets an answer. Nothing has failed: every transfer in the system completes correctly. This one requester never wins arbitration, because someone with higher priority always wants the same resource at the same moment. In a design review it would have been called "low priority". On the bench it is called a hang.

    How do you make sure every requester is eventually served, and how do you measure how fair an arbiter is?

    The obvious answer, and why it breaks

    The obvious notion of fairness is "everyone gets a turn eventually". But eventually is not a number. A requester that waits a million cycles has technically been served, and the system around it has long since failed. Useful fairness is a bound: an asking requester is served within a stated number of grants or cycles. And it has a second side, bandwidth: how big a share each requester gets when everyone wants more than the resource can give.

    The idea: two measures

    Measure

    Question

    What good looks like

    Worst wait

    How long can an asking requester wait before it is granted?

    A stated bound, independent of other requesters' traffic

    Bandwidth share

    When everyone asks, what fraction does each get?

    Equal, or in deliberate proportion (weights)

    Starvation is an unbounded worst wait: some pattern of other traffic can keep a requester waiting for ever. Fixed priority starves whenever higher-priority requesters can ask continuously; round-robin cannot starve anyone, because the winner always moves to the back.

    Build it: a fairness simulator

    Four requesters share a resource for 12,000 cycles. Requesters 0 and 1 always ask; 2 and 3 ask in a random 60% of cycles. The model runs fixed priority, round-robin and weighted round-robin (weights 4, 2, 1, 1) and reports each requester's share and longest wait.

    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
    51
    52
    53
    54
    55
    56
    """Grant share and worst wait for three arbitration policies.
    
    Four requesters share one resource for 12,000 cycles. Requesters 0 and 1 always
    want it; requesters 2 and 3 want it in a random 60% of cycles. Each cycle the
    arbiter grants one requester that is asking:
      fixed:    the lowest-numbered requester asking wins
      rr:       round-robin: the first asking requester after the last one granted
      weighted: weighted round-robin with weights 4, 2, 1, 1 (in each round of 8
                grants, each requester gets up to its weight, in turn)
    It reports each requester's share of grants and its longest wait while asking.
    """
    import random
    
    N, CYCLES = 4, 12000
    
    
    def run(policy):
        rng = random.Random(9)
        grants, wait, worst = [0] * N, [0] * N, [0] * N
        last, credit = N - 1, [4, 2, 1, 1]
        for _ in range(CYCLES):
            req = [True, True, rng.random() < 0.6, rng.random() < 0.6]
            g = None
            if policy == "fixed":
                g = next((i for i in range(N) if req[i]), None)
            elif policy == "rr":
                g = next(((last + k) % N for k in range(1, N + 1) if req[(last + k) % N]), None)
            else:
                if not any(req[i] and credit[i] for i in range(N)):
                    credit = [4, 2, 1, 1]                      # start a new round
                g = next(((last + k) % N for k in range(1, N + 1) if req[(last + k) % N] and credit[(last + k) % N]), None)
                if g is not None:
                    credit[g] -= 1
            for i in range(N):
                if req[i] and i != g:
                    wait[i] += 1
                    worst[i] = max(worst[i], wait[i])
                else:
                    wait[i] = 0
            if g is not None:
                grants[g] += 1
                last = g
        return [x / CYCLES for x in grants], worst
    
    
    results = {}
    for policy in ("fixed", "rr", "weighted"):
        share, worst = run(policy)
        results[policy] = (share, worst)
        print(f"{policy:8s} share: " + " ".join(f"{x:5.2f}" for x in share) + "   worst wait: " + " ".join(f"{w:5d}" for w in worst))
    fixed_share, fixed_worst = results["fixed"]
    rr_share, rr_worst = results["rr"]
    w_share, _ = results["weighted"]
    assert fixed_share[1] == fixed_share[2] == fixed_share[3] == 0 and max(rr_worst) <= 3
    assert w_share[0] > w_share[1] > w_share[2]
    print("PASS: fixed priority starves everyone below requester 0; round-robin bounds every wait at 3; weights split the bandwidth on purpose")

    It prints:

    Diagram
    fixed    share:  1.00  0.00  0.00  0.00   worst wait:     0 12000    19    17
    rr       share:  0.31  0.31  0.19  0.19   worst wait:     3     3     3     3
    weighted share:  0.50  0.25  0.12  0.12   worst wait:     3     7     7     8
    PASS: fixed priority starves everyone below requester 0; round-robin bounds every wait at 3; weights split the bandwidth on purpose
    The model's numbers: fixed priority gives requester 0 everything; round-robin shares out evenly among those asking; weights split the bandwidth in a chosen ratio.

    Under fixed priority, requester 0 asks every cycle and takes every grant; requester 1 waits all 12,000 cycles. Round-robin gives every requester a turn within 3 grants. The shares are unequal (0.31 against 0.19) only because 2 and 3 ask less often. Weighted round-robin splits bandwidth on purpose, half to requester 0, and still bounds every wait, at a few cycles more for the low-weight requesters.

    Proving a bound

    • Round-robin: after a requester starts asking, each other requester can be granted at most once before the pointer reaches it, so its wait is at most N − 1 grants.

    • Weighted round-robin: about one round of the other requesters' weights, plus a little when a new round starts: with weights 4, 2, 1, 1 the model's low-weight requesters waited at most 7 or 8 grants.

    • Multi-level systems: a requester's bound is the product of the bounds at every arbiter on its path, multiplied by how long each grant can hold the resource (lesson 8.5). Long bursts and locks turn short bounds into long ones.

    How real systems chose

    Where

    Fairness mechanism

    Interconnect arbiters

    Round-robin or weighted round-robin, sometimes with priority from QoS values plus ageing so low-priority traffic cannot starve.

    Network-on-chip routers

    Round-robin or age-based arbitration, so packets from distant sources are not starved at busy routers.

    What breaks

    A multi-level fabric (illustrative) used round-robin at every router, which looks fair. But traffic from a corner tile crossed six routers, and at each one it competed with traffic that had joined more recently and shared the same output equally. By the last router, the corner tile's share of a hot memory port was a few percent, and its latency was the worst in the system. Age-based arbitration, which favours the request that has waited longest overall, evened it out.

    Why this matters

    Senior lens. Ask every arbiter for a stated worst-wait bound and a bandwidth share under saturation, then compose them along each path: hops multiply, and long holds (bursts, locks) stretch every bound. Verify starvation freedom with saturated traffic from every requester at once, and check end-to-end shares on multi-hop fabrics, where locally fair arbiters can be globally unfair.

    Interview

    Asked: "Can fixed priority ever be fair?"

    Weak answer: No, never.

    Strong answer: Only if the higher-priority traffic is bounded. If high-priority requesters can ask every cycle, lower ones can wait for ever, which is starvation. If the high-priority traffic is rate-limited, by a token bucket or by its nature, say a display that needs a fixed bandwidth, then lower requesters get the remaining cycles and their wait is bounded. Otherwise use round-robin, weights, or priority with ageing, and state a worst-wait bound.

    Follow-ups: (1) How do you prove a round-robin wait bound? (2) Why can round-robin at every hop still be unfair end to end?

    Check yourself With round-robin among 8 requesters, what is the worst wait in grants for an asking requester?

    7 grants to others.

    Check yourself In the model, why are round-robin's shares 0.31 and 0.19 rather than all 0.25?

    Requesters 2 and 3 ask only 60% of the time, so they are sometimes skipped; when they ask they get their turn, and requesters 0 and 1 use the cycles left over.

    Key takeaways

    • Fairness has two measures: a bounded worst wait, and bandwidth shares under saturation.

    • Fixed priority starves lower requesters unless higher traffic is bounded; round-robin bounds every wait to N − 1 grants.

    • Bounds compose along a path; long holds and many hops stretch them.

    Keep going

    Next: Lesson 8.4, Priority Inversion. When a low-priority holder blocks a high-priority requester.

    Related topics

    • Part 8 overview: Sharing and Arbitration

    Previous

    119 / 234

    Next: Priority Inversion

    Up next

    Up nextPriority Inversion

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Shared Media and Topologies
    • Arbitration Granularity
    • Distributed Arbitration