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 Path
  • Cache Coherency Glossary
  • The Shared Course Model
  • Cache Coherency Revision Sheet
  • Why Coherence Exists8
  • States and Protocols12
  • Transactions12
  • Races and Controller Design8
  • Progress and the Fabric16
    • Virtual Networks and Progress
    • Filtering and Snoop Pruning
    • Latency, Fairness and QoS
      • Anatomy of a Miss Latency
      • Fairness on a Contended Line
      • QoS Classes and Coherence
      • Starvation Cases
    • Hierarchical and Chiplet Coherence
  • Ordering and Synchronization12
  • Performance and Scalability12
  • Real Protocols12
  • SoC Integration12
  • Power, Clocking and Reliability12
  • Verification12
  • Debug and Bring-up12
  • Interview and Design Review17
  • Capstone: A Coherent Model Lab5
Loading lesson

‹Latency, Fairness and QoS
  1. Topics
  2. Cache Coherency
  3. Progress and the Fabric
  4. Latency, Fairness and QoS
  5. Fairness on a Contended Line

Part 5 · Progress and the Fabric · All levels

Fairness on a Contended Line

Distinguish fair transaction admission from completing the active holder's obligations.

Can one core keep winning forever?

Two cores cannot do it on their own: a core that already holds M has no reason to send another GetM. So this experiment uses three cores. C0 and C2 alternate GetM on X (a lock ping-pong): each takes the line from the other, then requests it again as soon as it loses it. Fixed priority ranks both above C1, so H always has a higher-priority eligible GetM and C1's queued GetM is never selected. Keep the course's single conflicting transaction at H: each selected transaction completes before the next decision. C1's request stays queued and eligible throughout.

A fair policy needs an explicit definition of eligibility. A request dropped for lack of a pending entry never enters the ingress queue (the queue a request enters before arbitration), so fairness at that arbiter does not protect it. Likewise, an admitted request does not complete merely because its selection was fair: its Invs or Fwds, responses, data storage and grant still need service.

Policy, three cores

Selection rule

C1 consequence

Fixed priority, C0 and C2 above C1

Choose C0 or C2 whenever either is eligible

C1 is bypassed indefinitely while C0 and C2 keep alternating.

Three-source round-robin

After each selection, the next source in the order C0, C1, C2 gets top priority

A continuously eligible C1 is selected within three decisions: at most two other transactions precede it.

Round-robin only inside the ingress queue

Rotate only among requests already in that queue

Says nothing about a C1 request unable to enter that queue.

A request that stays queued needs admission, protocol service and grant acceptance before the next transaction decision.
Fairness is needed at each relevant stage, not just at a visible front-end arbiter.
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
ORDER = [0, 1, 2]           # round-robin order C0, C1, C2
RANK = {0: 0, 2: 1, 1: 2}   # fixed priority: C0 and C2 above C1

def run(policy, start, steps=12):
    holder, pointer, c1_waiting, winners = None, start, True, []
    for _ in range(steps):
        # Lock ping-pong: whichever of C0, C2 lacks M asks again.
        eligible = [c for c in (0, 2) if c != holder]
        if c1_waiting:
            eligible.append(1)
        if policy == "fixed":
            winner = min(eligible, key=RANK.get)
        else:
            winner = next(ORDER[(pointer + k) % 3] for k in range(3)
                          if ORDER[(pointer + k) % 3] in eligible)
            pointer = (winner + 1) % 3
        winners.append(winner)
        holder = winner
        if winner == 1:
            c1_waiting = False
    return winners

fixed = run("fixed", 0)
assert fixed == [0, 2] * 6 and 1 not in fixed   # C1 starves
for start in ORDER:
    rr = run("rr", start)
    assert rr.index(1) < 3                       # within three decisions
print("PASS: fixed priority starves C1 under ping-pong, round-robin serves it")

The program checks finite prefixes; the rotating rule supplies the three-decision argument for this three-source setup. Eventual completion also assumes eventual reliable delivery, fair service at each receiving node, reserved completion resources and eventual acceptance by each receiver. Without an upper bound on that work, three decisions is not a cycle bound.

Arbitration decides when C1's request starts, never whether C0's newest value may be skipped: see QoS Classes and Coherence for the dirty-holder case.

Common pitfall

Resetting a request's age whenever a retry occurs, then calling the internal queue fair. A persistently unlucky request may never keep enough age to be selected.

Interview

State where C1 waits, why it remains eligible, what advances priority, and which service assumptions turn selection into completion.

Check yourself Why does a fair request arbiter not prove that dirty data returns?

The response can still be blocked by routing, receive capacity or unfair service at the receiving node. Admission fairness addresses one stage, not the whole dependency path.

Related topics

  • Shared course model

  • Capacity can block a selected transaction

Previous

55 / 167

Next: QoS Classes and Coherence

Up next

Up nextQoS Classes and Coherence

Sign in to save progress

Practice this lessonReport an error

See also

  • Starvation Cases