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
  • Part 6: Coverage10
    • What Coverage Measures
    • Code Coverage
    • Functional Coverage Models
    • Crosses and the Combinatorial Explosion
    • Closure Curves
    • Holes and Unreachable Bins
    • Coverage-Driven Stimulus
    • Mutation Testing
    • A Coverage Model for DCRouter
    • Drills: Coverage
  • 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 6: Coverage
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 6: Coverage
  4. Coverage-Driven Stimulus

Part 6 · Coverage · Senior (S)

Coverage-Driven Stimulus

Blind random keeps spending most of its effort on bins already hit. Coverage-driven stimulus reads the holes and steers the next tests toward them. A model on DCRouter's 32-bin header cross: steering half of each test at a hole closes the cross in 12.4 tests on average against 54.9 blind, and caps the worst case at 17 instead of 180.

On this page

    Note

    Level: Senior (S) · Answers: How do we know we have tested enough?

    Before this: Closure Curves, Distributions and Bias

    Halfway through closure, a regression has hit 28 of DCRouter's 32 header bins. The four holes are all length-0 headers at particular destinations and priorities, each about one packet in 400 under the random distribution. Every new test spends ninety-nine packets out of a hundred re-hitting bins that are already full. The coverage report knows exactly which four bins are missing; the stimulus generator does not read it.

    How can coverage feedback make closure faster?

    The obvious answer, and why it breaks

    The obvious fixes are more tests or a directed test per hole. More tests pay at the coupon collector's slow rate (lesson 4.2). A directed test per hole works for a handful of holes, but holes keep appearing as the model grows, seeds change and the design changes, and hand-writing a test for each does not scale. The information needed to steer is already in the coverage database.

    The idea: close the loop

    • Read the holes after each test or batch.

    • Turn a hole into constraints: a bin (destination 2, priority 1, length 0) becomes inline constraints for some of the next packets.

    • Keep part of each test blind, so breadth and unexpected combinations continue.

    • Repeat until the reachable bins are closed; what remains goes to triage (lesson 6.6).

    Build it: blind against driven

    Both generators send 20 packets per test until all 32 header bins are hit. The driven one sends 10 packets at a randomly chosen hole and 10 blind packets each test. Each is run 400 times.

    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
    """Coverage-driven stimulus against blind random, on DCRouter's 32 header bins.
    
    The coverage model is lesson 6.9's header cross: 4 destinations x 2 priorities x 4 length
    classes (len0, len1, len2-14, len15) = 32 bins. Each test sends 20 packets.
      blind    every packet from lesson 4.10's distribution: len0 2%, len1 10%, len2-14 70%,
               len15 18%; priority and destination uniform
      driven   after each test, look at the holes; the next test sends 10 packets constrained to
               one randomly chosen hole and 10 blind packets
    Both run until all 32 bins are hit; 400 runs each. The model prints average coverage after
    each number of tests and the average number of tests to close.
    """
    import random
    
    LEN_W = [("len0", 0.02), ("len1", 0.10), ("len2-14", 0.70), ("len15", 0.18)]
    BINS = [(d, p, l) for d in range(4) for p in range(2) for l, _ in LEN_W]
    
    def blind_packet(rng):
        u, acc = rng.random(), 0.0
        for l, w in LEN_W:
            acc += w
            if u < acc:
                break
        return (rng.randrange(4), rng.randrange(2), l)
    
    def run(driven, rng, checkpoints):
        seen, t, curve = set(), 0, {}
        target = None
        while len(seen) < len(BINS):
            t += 1
            for k in range(20):
                pkt = target if (driven and target and k < 10) else blind_packet(rng)
                seen.add(pkt)
            if t in checkpoints:
                curve[t] = len(seen)
            holes = [b for b in BINS if b not in seen]
            target = rng.choice(holes) if holes else None
        for c in checkpoints:
            curve.setdefault(c, len(BINS))
        return t, curve
    
    CK = [1, 2, 5, 10, 20, 40, 80]
    rng = random.Random(9)
    results = {}
    for name, driven in [("blind", False), ("driven", True)]:
        tests, curves = [], []
        for _ in range(400):
            t, c = run(driven, rng, CK)
            tests.append(t)
            curves.append(c)
        avg = [sum(c[k] for c in curves) / len(curves) / len(BINS) * 100 for k in CK]
        results[name] = (sum(tests) / len(tests), max(tests), avg)
    
    print("tests:            " + " ".join(f"{k:>6d}" for k in CK))
    for name, (mean, worst, avg) in results.items():
        print(f"{name:6s} coverage % " + " ".join(f"{a:6.1f}" for a in avg) + f"   tests to close: mean {mean:.1f}, worst {worst}")
    speed = results["blind"][0] / results["driven"][0]
    print(f"coverage-driven closes {speed:.1f} times faster on average")
    
    assert results["driven"][0] < results["blind"][0] / 2
    print("PASS: steering half of each test at a hole closes the header cross in less than half the tests")

    It prints:

    Diagram
    tests:                 1      2      5     10     20     40     80
    blind  coverage %   36.7   51.5   70.7   82.3   90.4   96.6   99.6   tests to close: mean 54.9, worst 180
    driven coverage %   36.8   47.9   69.7   91.9  100.0  100.0  100.0   tests to close: mean 12.4, worst 17
    coverage-driven closes 4.4 times faster on average
    PASS: steering half of each test at a hole closes the header cross in less than half the tests
    The model's numbers: both start near 37%; coverage-driven reaches 100% by 20 tests on average, blind reaches 90.4% at 20 tests and 99.6% at 80.

    How to read it: blind random needs 54.9 tests on average, and 180 in its worst run, because the last length-0 bins arrive by luck. Driven closes in 12.4 on average and never more than 17. Note the early cost: after two tests driven is slightly behind (47.9% against 51.5%), because half its packets went to a single hole. Steering trades breadth for focus, which pays off once only holes remain.

    Ways to steer

    Technique

    How it uses coverage

    Cost

    Hand-written directed tests

    An engineer reads the holes and writes tests

    Engineering time per hole

    Automatic constraint feedback

    A script turns holes into inline constraints or test parameters

    Infrastructure; works for value and cross bins

    Weight adjustment

    Distribution weights shift toward values in empty bins

    Simple; slower for combinations

    Test selection

    Run more of the seeds and tests that hit rare bins (lesson 9.6)

    Needs per-test coverage data

    Formal cover

    A formal tool finds an input sequence that reaches the bin, or proves none exists (Part 7)

    Tool time; best for deep or very rare bins

    Cautions

    • Hitting a bin is not testing it: steered stimulus still needs active checks (lesson 6.1).

    • Keep blind stimulus in the mix: steering only toward the model finds only what the model names.

    • Event and sequence bins are harder to steer than value bins; they often need scenarios (lesson 4.6).

    How real teams use coverage-driven stimulus

    Practice

    Purpose

    Coverage-guided test generation in the regression flow

    Rare bins closed without a test per hole

    Formal cover for the deepest holes

    Reach or prove unreachable

    Regression ranking by coverage contribution

    Spend simulation on the tests that add coverage (lesson 9.6)

    What breaks

    A team (illustrative) steered all of its stimulus toward coverage holes and closed its model quickly. The model happened to omit back-pressure on one interface, and the steered tests, now narrow, never produced it; earlier blind tests had produced it occasionally. A bug there escaped. Keeping half of each test blind, as the model above does, preserves the breadth that finds what the model forgot.

    Why this matters

    Senior lens. Once the closure curve bends, connect the coverage database to the stimulus: holes become constraints automatically, and engineers spend their time on holes that resist steering. Keep a blind share in every test, and never confuse a steered hit with a checked behaviour.

    Interview

    Asked: "How do you close coverage faster?"

    Weak answer: Add more seeds to the regression.

    Strong answer: Feed the holes back into the stimulus. After each batch, turn empty bins into constraints for part of the next tests, keep the rest blind for breadth, and send the holes that resist steering to directed tests or formal cover. In a model of DCRouter's 32-bin header cross, steering half of each test at a hole cut the average tests to closure from 54.9 to 12.4 and the worst case from 180 to 17. More seeds only pay at the coupon-collector rate.

    Follow-ups: (1) Why keep part of each test blind? (2) Which bins are hard to steer toward?

    Check yourself Why is coverage-driven stimulus slightly behind blind random after two tests?

    Half of each driven test targets a single hole, so early on, when most bins are still empty, it hits fewer distinct bins than fully random packets would.

    Check yourself Why does blind random's worst case (180 tests) matter more than its average?

    A regression is planned against what it must reliably achieve; a long tail of slow closure means some weeks or seeds leave rare bins open far longer than the average suggests.

    Key takeaways

    • Coverage-driven stimulus turns holes into constraints for the next tests.

    • On DCRouter's header cross it closed 4.4 times faster on average and capped the worst case at 17 tests.

    • Keep a blind share for breadth, send stubborn holes to directed tests or formal, and keep checks active.

    Keep going

    Next: Lesson 6.8, Mutation Testing. Measuring the testbench itself by the bugs it kills.

    Related topics

    • Part 6 overview: Coverage

    Previous

    68 / 160

    Next: Mutation Testing

    Up next

    Up nextMutation Testing

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • What Coverage Measures
    • Code Coverage
    • Functional Coverage Models