HomeProblemsTheoryUVM
Advanced3 min readChapter 14

Coverage-Driven Verification

Use functional coverage to measure verification completeness and close coverage holes systematically.

Coverage-Driven Verification

Coverage-driven verification (CDV) uses functional coverage metrics to decide when verification is complete. Instead of running a fixed number of tests, you run until coverage goals are met.


The CDV Flow

text
Define Coverage Model Run Constrained Random Tests Measure Coverage ──▶ Met target? ──▶ Done! ✅ │ │ │ No │ Yes ▼ │ Analyze Holes │ │ │ ▼ │ Adjust Constraints ───────┘ or Write Directed Tests

Writing a Coverage Collector

systemverilog
class apb_coverage extends uvm_subscriber #(apb_txn); `uvm_component_utils(apb_coverage) apb_txn txn; covergroup apb_cg; // Cover address ranges addr_cp: coverpoint txn.addr[31:28] { bins low = {[0:3]}; bins mid = {[4:7]}; bins high = {[8:15]}; } // Cover read vs write dir_cp: coverpoint txn.write { bins read = {0}; bins write = {1}; } // Cover special data patterns data_cp: coverpoint txn.data { bins zero = {0}; bins all_ones = {32'hFFFF_FFFF}; bins walking1 = {32'h0000_0001, 32'h0000_0002, 32'h0000_0004, 32'h0000_0008}; bins other = default; } // Cross: every address range × every direction addr_x_dir: cross addr_cp, dir_cp; endgroup function new(string name, uvm_component parent); super.new(name, parent); apb_cg = new(); endfunction // Called automatically for each broadcast transaction function void write(apb_txn t); txn = t; apb_cg.sample(); endfunction function void report_phase(uvm_phase phase); real cov = apb_cg.get_coverage(); `uvm_info("COV", $sformatf("Coverage: %.1f%%", cov), UVM_NONE) endfunction endclass

Connecting to the Monitor

systemverilog
// In environment's connect_phase: agent.monitor.ap.connect(coverage.analysis_export);

Coverage Strategies

Transaction-Level Coverage

systemverilog
covergroup txn_cg; op_cp: coverpoint txn.op_type { bins read = {READ}; bins write = {WRITE}; bins rmw = {RMW}; } size_cp: coverpoint txn.size { bins byte_ = {1}; bins half = {2}; bins word = {4}; } // Cross: every operation × every size op_x_size: cross op_cp, size_cp; endgroup

Error Scenario Coverage

systemverilog
covergroup error_cg; resp_cp: coverpoint txn.response { bins okay = {OKAY}; bins slverr = {SLVERR}; bins decerr = {DECERR}; } endgroup

Protocol-Level Coverage

systemverilog
covergroup axi_protocol_cg; burst_cp: coverpoint txn.burst_type { bins fixed = {FIXED}; bins incr = {INCR}; bins wrap = {WRAP}; } len_cp: coverpoint txn.burst_len { bins single = {0}; bins short_ = {[1:3]}; bins medium = {[4:15]}; bins long_ = {[16:255]}; } burst_x_len: cross burst_cp, len_cp { // WRAP only supports 2, 4, 8, or 16 beats ignore_bins invalid = binsof(burst_cp) intersect {WRAP} && binsof(len_cp) intersect {[2:$]}; } endgroup

Closing Coverage Holes

When random tests don't hit all bins, you have two options:

Option 1: Adjust Constraints

systemverilog
// Bias toward uncovered scenarios constraint bias_c { burst_type dist { WRAP := 50, // Increase weight for wrap INCR := 30, FIXED := 20 }; }

Option 2: Write Directed Sequences

systemverilog
class wrap_burst_seq extends uvm_sequence #(axi_txn); `uvm_object_utils(wrap_burst_seq) function new(string name = "wrap_burst_seq"); super.new(name); endfunction task body(); axi_txn txn = axi_txn::type_id::create("txn"); start_item(txn); if (!txn.randomize() with { burst_type == WRAP; burst_len inside {1, 3, 7, 15}; }) `uvm_fatal("RAND", "Failed to randomize") finish_item(txn); endtask endclass

Best Practices

PracticeWhy
Define coverage goals before writing testsFocus your effort
Use cross coverageCatches interaction bugs
Set realistic targets (90-95%)100% is rarely needed
Review exclusions carefullyDon't exclude real scenarios
Automate regression + coverage mergingTrack progress over time