Part 5 · Functional Coverage · Intermediate
Closing Techniques
Constraint steering toward holes, directed tests for stubborn corners, seed sweeps, bin adjustments, and the diminishing-returns curve.
Technique 1 — steer constraints toward the holes
When triage says needs-constraint-change, the cheapest fix is usually an inline constraint that biases randomization onto the hole's ranges — either in a dedicated 'closure sequence' or via randomize() with at the call site. The base constraints stay untouched; the steering is additive and visible.
// Hole: <incr, max_len, unaligned> never hit — alignment starved it.
// Closure sequence: same transaction class, steered at the call.
task run_closure_burst(int n);
repeat (n) begin
dma_txn t = new();
if (!t.randomize() with {
btype == INCR;
blen == 16; // the max_len bin
addr[1:0] != 2'b00; // force unaligned
})
$fatal(1, "closure randomize failed");
drive(t);
end
endtask
// Distribution steering for a rare-but-not-empty bin (below at_least):
constraint c_close_fixed { btype dist { FIXED := 6, INCR := 3, WRAP := 1 }; }Inline with constraints compose with (and can be contradicted by) class constraints — a randomize() failure here means the hole may be constraint-unreachable: back to triage.
dist reshaping closes below-at_least holes without excluding the rest of the space.
Keep steering in clearly named closure sequences/tests — reviewers must be able to see that mainline stimulus was not narrowed.
Techniques 2 & 3 — directed tests and seed sweeps
Some corners resist steering: multi-step setups (fill the FIFO, then collide two events), exact timing races, or error recovery chains. These get directed tests — short, explicit, named after the hole they close. Before writing one, the cheaper intermediate is a seed sweep : more seeds of an existing test whose stimulus is in the right neighborhood. Sweeps help when hit probability per seed is small but not vanishing; they do not help when the probability is structurally near zero.
// Directed test for hole: timeout recovery (cp_fsm XFER => IDLE on timeout)
// Random stimulus can't hold the bus idle long enough; force it.
task test_timeout_recovery();
cfg.timeout_cycles = 50; // shortened via config — legal knob
start_burst(INCR, 8);
stall_ready(100); // hold ready low past the timeout
wait (fsm_state == IDLE); // recovery observed
check_abort_status(); // closure AND checking
start_burst(INCR, 4); // prove the DUT still works after
endtaskChoosing between the three
per-seed hit probability tool of choice
──────────────────────────────────────────────────────
~0 (structurally blocked) directed test (or triage
says unreachable)
tiny (one setup in millions) steered constraints
small (1 in ~10 seeds) seed sweep
fine (already hit, < at_least) dist reshapingTechnique 4 — adjust the bins (carefully) and know when to stop
Sometimes the right fix is in the model: add bins when triage keeps finding interesting scenarios between your buckets (a 'mid' range hiding a boundary), and widen or merge bins when distinctions carry no verification meaning (16 length bins where the design only distinguishes single/burst). Widening to make a hole disappear is only legitimate when the distinction was meaningless — that judgement belongs in review, not in a deadline commit.
THE DIMINISHING-RETURNS CURVE
coverage %
100 ┤ ____________ ●●● waiver/
95 ┤ _________/ directed-only
90 ┤ ______/ ▲ each point here costs
85 ┤ _____/ days of engineer time
80 ┤ ___/
70 ┤ __/ ▲ here a constraint tweak buys 5%
50 ┤ _/
30 ┤ / ▲ here every seed adds whole percents
0 ┤ /
└──┬─────┬─────┬─────┬─────┬────────► effort (seeds + engr time)
day1 week1 week2 week3 week4
Strategy by region:
0–80% : run seeds, fix wrong-bins — coverage is cheap here
80–95% : triage seriously; constraint steering and dist reshaping
95–99% : directed tests for named stubborn corners only
last 1%: every remaining hole is individually known — close it,
waive it with documentation, or it blocks sign-offInterview angle: "How do you close the last 5%?" is asked precisely because brute force stops working there. The expected answer is technique selection by hole class and hit probability — steering for starved ranges, directed tests for structural corners, sweeps only where probability justifies them, bin fixes where the model is wrong — plus the honesty that some of the last percent is waivers, properly documented.
Key takeaways
Match technique to triage class: steering for starved, directed for structural, sweeps for low-probability, bin fixes for model bugs.
Keep closure steering in named sequences/tests so mainline stimulus visibly stays broad.
A randomize()-with failure during steering is information: the hole may be constraint-unreachable.
Know where you are on the returns curve — the right tool at 60% is the wrong tool at 97%.
Common pitfalls
Closing holes by narrowing base-class constraints — every other test now generates less diverse stimulus.
Seed sweeps against structurally-blocked corners — thousands of CPU-hours, zero new bins.
Directed tests that hit the bin but check nothing — coverage without checking is a scenario observed, not verified.
Widening bins at deadline to delete holes — reviewers diff the model; silent goal-shrinking torpedoes trust.