Part 1 · Foundations · Intermediate
Verification Plan Basics: Features, Goals, and Traceability
How the verification plan drives UVM testbench structure — mapping features to tests, checks to scoreboards, and coverage goals to covergroups with full traceability.
The plan comes before the code
A verification plan (vplan) is the contract that says what 'done' means for a block. It enumerates every feature to be verified, the checks that prove correctness, and the coverage goals that measure progress. UVM testbench structure is a direct realization of that plan — not the other way around.
Engineers who skip the plan tend to write a pile of random tests and then cannot answer the only question that matters at sign-off: 'how do we know we are done?' The plan answers that by tying every TB artifact back to a documented intent.
The three columns of a plan row
Feature — a specific, testable behavior from the spec (e.g. 'back-to-back write transfers with no idle cycles').
Check — how correctness is proven (scoreboard rule, assertion, or RAL mirror check).
Coverage — how you measure that the feature was actually exercised (coverpoint, bin, or cross).
Mapping the plan onto a UVM testbench
Each plan element has a natural home in the UVM environment. The mapping is what makes a vplan actionable:
VPLAN ELEMENT -> UVM ARTIFACT
feature / scenario -> test + sequence (often a virtual sequence)
legal input space -> sequence_item constraints
correctness check -> scoreboard rule / SVA / RAL mirror
"was it exercised?" -> covergroup coverpoint / bin / cross
sign-off goal -> coverage closure target (e.g. 100% of plan bins)A concrete plan-to-TB example
// Plan feature F2: "back-to-back transfers exercise ready stalls"
// -> Test that runs a stress sequence
class burst_stress_test extends base_test;
`uvm_component_utils(burst_stress_test)
function new(string name, uvm_component parent); super.new(name, parent); endfunction
task run_phase(uvm_phase phase);
burst_stress_vseq seq = burst_stress_vseq::type_id::create("seq");
phase.raise_objection(this, "F2 burst stress");
seq.start(env.v_sqr);
phase.drop_objection(this, "F2 done");
endtask
endclass
// -> Coverage that proves F2 was actually hit (ready stalls observed)
class bus_cov extends uvm_subscriber #(bus_txn);
`uvm_component_utils(bus_cov)
bus_txn tr;
covergroup cg;
cp_stall: coverpoint tr.ready_stall_cycles {
bins none = {0};
bins short = {[1:3]};
bins long = {[4:$]}; // F2 closure needs this bin hit
}
endgroup
function new(string name, uvm_component parent); super.new(name, parent); cg = new(); endfunction
function void write(bus_txn t); tr = t; cg.sample(); endfunction
endclassFeature F2 becomes a named test/sequence so regressions can target it.
The 'long stall' coverage bin is the measurable proof F2 was exercised.
Tag the test and covergroup with the plan ID (F2) so traceability tooling can link them.
Traceability and sign-off
Traceability means every plan row can be traced forward to the test(s), check(s), and coverage that implement it — and every coverage hole can be traced back to a plan row. This is what lets a lead say, with evidence, that the block is verified.
Give each plan feature a stable ID (F1, F2, ...).
Tag tests, assertions, and covergroups with that ID (in names or metadata).
Merge coverage across the regression and map covered bins back to plan rows.
Sign-off = all plan rows have passing checks and closed coverage (or documented waivers).
Key takeaways
UVM structure is a realization of the verification plan; write the plan first.
Every feature maps to a test, every check to a scoreboard/assertion, every goal to coverage.
Traceability (plan ID on tests and covergroups) is what makes sign-off defensible.
Common pitfalls
Writing the env first and back-filling a plan after weeks of coding.
Using line/code coverage as the sole metric while functional coverage holes remain.
Orphan tests with no documented feature, check, or coverage intent.