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
  • Meet DCRouter
  • Glossary
  • Part 0: What Verification Is8
  • Part 1: What Can Go Wrong11
  • Part 2: Specifications as Checkable Rules8
  • Part 3: Observability and Controllability7
  • Part 4: Stimulus11
  • Part 5: Checking11
    • Self-Checking Testbenches
    • Where to Check
    • Assertions as Local Checkers
    • Scoreboards
    • How Precise a Reference Model?
    • Ordering and Timing in Checks
    • End-of-Test Checks
    • False Passes
    • Checking Performance
    • Hunting the Zoo with Checkers
    • Drills: Checking
  • Part 6: Coverage10
  • Part 7: Formal and Simulation10
  • Part 8: Verification Levels and Reuse8
  • Part 9: Planning and Risk9
  • Part 10: Emulation and Prototyping8
  • Part 11: Post-Silicon Validation8
  • Part 12: Bug Economics8
  • Part 13: Debug7
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 5: Checking
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 5: Checking
  4. Checking Performance

Part 5 · Checking · Senior (S)

Checking Performance

Performance bugs deliver every packet correctly, just late, slowly or unfairly, so functional checks pass. Performance must be specified as numbers and checked as properties: latency bounds, throughput, fairness. A model of a 4-input round-robin output with and without a stuck pointer: both serve the same number of packets, and only a head-of-line wait checker sees the difference.

On this page

    Note

    Level: Senior (S) · Answers: How do we know the output is right?

    Before this: Ordering and Timing in Checks, Performance Bugs

    Through all of Part 4, Bug Zoo BUG_5 escaped every testbench. Its round-robin pointer never advances, so the lowest-numbered waiting input always wins. Under DCRouter's traffic every packet still arrived, intact and in order, and every counter matched. The bug does not make anything wrong. It makes some packets wait much longer than the specification allows, and nothing in the testbench was measuring waiting.

    How do you check a design's performance, not just its function?

    The obvious answer, and why it breaks

    The obvious check is throughput: count packets per input and compare. Below saturation, a design that delivers everything eventually has the same throughput however unfairly it schedules, because every packet is served in the end. Unfairness shows only in when packets are served: in the tail of the latency distribution. Averages hide it too: a few packets waiting a very long time barely move a mean.

    The idea: performance as checked properties

    Property

    Specified as

    Checked by

    Latency

    A bound, often on a percentile or the maximum

    Timestamps per transaction, compared with the bound

    Throughput

    Minimum rate under a stated load

    Counting over a window at saturation

    Fairness

    Bounded waiting: at most N others served first

    Head-of-line wait, or a count of skips per requester

    Starvation freedom

    Every request eventually served, within a bound

    Maximum wait against the bound

    The specification must give numbers. "Round-robin" implies one: with 4 inputs, a waiting packet lets at most 3 others go first.

    Build it: a head-of-line wait checker

    One output serves 4 inputs at 96% offered load; each packet occupies it for 4 cycles. The checker measures each packet's head-of-line wait, from reaching the front of its queue to being granted, and asserts the round-robin bound of 16 cycles (3 other packets of 4 cycles, plus the one already being sent).

    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
    57
    58
    59
    60
    """Checking performance: a head-of-line wait checker on a 4-input round-robin output.
    
    One output serves 4 inputs; each packet occupies it for 4 cycles. Each input receives a new
    packet with probability 0.06 per cycle (total offered load 0.96 of the output). Two arbiters:
      round-robin   the pointer moves past each winner (DCRouter's rule R6)
      stuck pointer the pointer never moves, so the lowest-numbered waiting input always wins
                    (Bug Zoo BUG_5)
    For every packet the checker measures its head-of-line wait: cycles from reaching the front of
    its input queue to being granted. Round-robin guarantees at most 3 other packets go first, so
    the wait is at most 3 x 4 cycles plus the 4-cycle packet already being sent: 16 cycles. The
    checker asserts that bound and prints the median, 99th percentile and maximum per input.
    """
    import random
    
    SERVICE, RATE, CYCLES, BOUND = 4, 0.06, 20000, 16
    
    def simulate(stuck, seed=2):
        rng = random.Random(seed)
        queues = [[] for _ in range(4)]      # arrival cycles
        head_since = [None] * 4              # cycle the current head reached the front
        waits = [[] for _ in range(4)]
        ptr, busy = 0, 0
        for t in range(CYCLES):
            for i in range(4):
                if rng.random() < RATE:
                    queues[i].append(t)
                    if len(queues[i]) == 1:
                        head_since[i] = t
            if busy > 0:
                busy -= 1
            if busy == 0:
                order = [(ptr + k) % 4 for k in range(4)]
                win = next((i for i in order if queues[i]), None)
                if win is not None:
                    waits[win].append(t - head_since[win])
                    queues[win].pop(0)
                    head_since[win] = t if queues[win] else None
                    busy = SERVICE
                    if not stuck:
                        ptr = (win + 1) % 4
        return waits
    
    def pct(xs, q):
        xs = sorted(xs)
        return xs[min(len(xs) - 1, int(q * len(xs)))]
    
    results = {}
    for name, stuck in [("round-robin", False), ("stuck pointer", True)]:
        w = simulate(stuck)
        results[name] = w
        print(f"{name}: head-of-line wait in cycles (median / 99th percentile / max), packets served")
        for i in range(4):
            print(f"  input {i}: {pct(w[i], 0.5):4d} / {pct(w[i], 0.99):4d} / {max(w[i]):4d}   {len(w[i]):5d}")
        viol = sum(x > BOUND for xs in w for x in xs)
        print(f"  packets over the {BOUND}-cycle bound: {viol}")
    
    rr, st = results["round-robin"], results["stuck pointer"]
    assert max(max(x) for x in rr) <= BOUND
    assert max(st[3]) > 10 * BOUND
    print(f"PASS: round-robin never exceeds {BOUND} cycles; with the stuck pointer input 3 waits up to {max(st[3])} cycles")

    It prints:

    Diagram
    round-robin: head-of-line wait in cycles (median / 99th percentile / max), packets served
      input 0:   12 /   16 /   16    1201
      input 1:   12 /   16 /   16    1263
      input 2:    9 /   16 /   16    1165
      input 3:   10 /   16 /   16    1159
      packets over the 16-cycle bound: 0
    stuck pointer: head-of-line wait in cycles (median / 99th percentile / max), packets served
      input 0:    2 /    4 /    4    1201
      input 1:    3 /   17 /   27    1263
      input 2:    4 /   43 /   68    1165
      input 3:    4 /  128 /  208    1159
      packets over the 16-cycle bound: 338
    PASS: round-robin never exceeds 16 cycles; with the stuck pointer input 3 waits up to 208 cycles
    The model's numbers: round-robin's worst wait is 16 cycles on every input; with the stuck pointer it grows from 4 cycles on input 0 to 208 on input 3.

    How to read it: both arbiters serve exactly the same packets per input (1201, 1263, 1165, 1159), so a throughput check sees nothing. The medians do not reveal the bug either; the stuck pointer's medians are actually lower. The tail does: round-robin never exceeds 16 cycles, while with the stuck pointer input 3's 99th percentile is 128 cycles and its worst wait 208, and 338 packets break the bound. A checker on the maximum against a specified bound catches it in one run.

    Writing performance checks

    • Get numbers into the specification: bounds on latency, minimum throughput, a fairness guarantee.

    • Check tails, not averages: maximum or high percentiles against the bound.

    • Create the load the bound is about: saturation and hot spots (lesson 4.6); a quiet test cannot violate a fairness bound.

    • Measure at the pins where possible, and where the decision is hidden, judge only where timing is certain (lesson 5.5).

    • Keep performance checks separate from functional ones, so a slow design is reported as slow, not as wrong.

    How real teams check performance

    Practice

    Purpose

    Latency and bandwidth monitors in the testbench

    Every transaction timestamped; statistics per test

    Performance regressions with bounds

    A change that slows the design fails a test

    Architecture models as performance references

    Expected numbers to compare against (lesson 8.7)

    What breaks

    A team (illustrative) checked average latency against a target and met it comfortably. In the system, one low-priority client occasionally waited thousands of cycles and timed out. The average was dominated by thousands of fast transactions; the starvation lived in the tail. A maximum-wait check under a hot-spot load would have failed in block simulation.

    Why this matters

    Senior lens. Treat performance as part of correctness: if the specification gives a bound, check it; if it does not, get one. Check the maximum or a high percentile under the load the bound is about, and report it separately from functional failures. Fairness bugs pass every functional check by construction.

    Interview

    Asked: "How do you verify performance?"

    Weak answer: Run a long test and look at the throughput.

    Strong answer: As checked properties with numbers: latency bounds on the maximum or a high percentile, minimum throughput at a stated load, and fairness as bounded waiting. I timestamp transactions, drive the load the bound is about (saturation and hot spots), and compare tails with the bound, because throughput and averages hide unfairness. In a model of a 4-input round-robin output, a stuck pointer served exactly the same packets per input as the correct arbiter; only the worst-case wait (208 cycles against a 16-cycle bound) exposed it.

    Follow-ups: (1) Why don't averages catch starvation? (2) Where does the 16-cycle bound come from?

    Check yourself Why do both arbiters serve the same number of packets per input?

    The load is below saturation, so every packet is served eventually; unfairness changes when packets are served, not whether.

    Check yourself Why is the round-robin bound 16 cycles in this model?

    Once a packet is at the head, at most 3 other inputs' packets (4 cycles each) go first, plus up to 4 cycles for the packet already being sent: 16.

    Key takeaways

    • Performance bugs pass functional checks; they must be specified as numbers and checked.

    • Check tails (maximum, high percentiles) under the load the bound is about.

    • A stuck round-robin pointer served the same packets per input; only the wait tail revealed it.

    Keep going

    Next: Lesson 5.10, Hunting the Zoo with Checkers. DCRouter step 7: every checker together against the Bug Zoo.

    Related topics

    • Part 5 overview: Checking

    Previous

    59 / 160

    Next: Hunting the Zoo with Checkers

    Up next

    Up nextHunting the Zoo with Checkers

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Self-Checking Testbenches
    • Where to Check
    • Assertions as Local Checkers