Comprehensive Coverage Tutorial with Examples
Table of Contents
- Introduction to Functional Coverage
- Covergroup Basics
- Coverpoint and Bins
- Sampling Methods
- Cross Coverage
- Transition Coverage
- Coverage Options
- Conditional Coverage (iff)
- Bins Expressions and Filters
- Coverage Queries and Reporting
- Advanced Techniques
- UVM Integration
- Best Practices
1. Introduction to Functional Coverage
What is Functional Coverage?
Functional coverage is a user-defined metric that measures how much of the design’s intended functionality has been exercised by the test stimulus.
Types of Coverage:
- Code Coverage: Structural (line, branch, FSM, expression)
- Functional Coverage: Behavioral (features, corner cases, scenarios)
Why Functional Coverage?
- Measures test quality
- Identifies coverage holes
- Enables coverage-driven verification
- Proves verification completeness
2. Covergroup Basics
2.1 Basic Covergroup Syntax
module coverage_basic;
// Variables to cover
logic [7:0] addr;
logic write;
logic [31:0] data;
// Define covergroup
covergroup transaction_cg;
// Simple coverpoint - covers all values
addr_cp: coverpoint addr;
// Coverpoint with label
write_cp: coverpoint write;
// Coverpoint for data
data_cp: coverpoint data;
endgroup
// Instantiate covergroup
transaction_cg cg_inst;
initial begin
// Create covergroup instance
cg_inst = new();
// Generate stimulus and sample
repeat(100) begin
addr = $urandom_range(0, 255);
write = $urandom();
data = $urandom();
// Sample the covergroup
cg_inst.sample();
#10;
end
// Display coverage
$display("Coverage = %0.2f%%", cg_inst.get_coverage());
$finish;
end
endmodule
2.2 Covergroup in Class
class transaction;
rand bit [7:0] addr;
rand bit [31:0] data;
rand bit write;
// Covergroup inside class
covergroup cg;
coverpoint addr;
coverpoint write;
coverpoint data;
endgroup
function new();
cg = new(); // Create covergroup
endfunction
function void post_randomize();
cg.sample(); // Auto-sample after randomization
endfunction
endclass
module test;
initial begin
transaction txn;
txn = new();
repeat(1000) begin
assert(txn.randomize());
// Coverage automatically sampled in post_randomize()
end
$display("Coverage = %0.2f%%", txn.cg.get_coverage());
end
endmodule
3. Coverpoint and Bins
3.1 Explicit Bins
covergroup bins_explicit;
coverpoint addr {
// Individual value bins
bins zero = {0};
bins one = {1};
bins two = {2};
// Multiple values in one bin
bins low_addr = {3, 4, 5, 6, 7};
// Range bin
bins mid_range = {[8:15]};
// Multiple ranges
bins high_addr = {[16:31], [64:127]};
}
endgroup
3.2 Automatic Bins
covergroup bins_automatic;
coverpoint addr {
// Auto bins - one bin per value
bins addr_bins[] = {[0:15]};
// Creates: addr_bins[0], addr_bins[1], ..., addr_bins[15]
}
coverpoint status {
// Auto bins for enum
bins status_bins[] = {IDLE, ACTIVE, DONE, ERROR};
// Creates: status_bins[IDLE], status_bins[ACTIVE], etc.
}
endgroup
3.3 Range Bins with Auto
covergroup range_bins;
coverpoint data[7:0] {
// Create 4 bins covering ranges
bins range[4] = {[0:255]};
// Creates 4 bins: [0:63], [64:127], [128:191], [192:255]
}
coverpoint addr {
// Create 8 equal-sized bins
bins addr_range[8] = {[0:255]};
// Each bin covers 32 values
}
endgroup
3.4 Wildcard Bins
covergroup wildcard_bins;
coverpoint opcode[3:0] {
// Wildcard matching (? = don't care)
bins load_ops = {4'b00??}; // 0000, 0001, 0010, 0011
bins store_ops = {4'b01??}; // 0100, 0101, 0110, 0111
bins alu_ops = {4'b10??}; // 1000-1011
bins branch = {4'b11??}; // 1100-1111
}
coverpoint addr {
wildcard bins aligned_4k = {16'b????_0000_0000_0000};
// Matches addresses aligned to 4KB boundary
}
endgroup
3.5 Ignore and Illegal Bins
covergroup special_bins;
coverpoint addr {
bins valid_low = {[0:127]};
bins valid_high = {[128:255]};
// Ignore these values (don't count toward coverage)
ignore_bins reserved = {254, 255};
// Illegal bins (will trigger warning/error if hit)
illegal_bins forbidden = {[256:511]};
}
coverpoint state {
bins valid_states = {IDLE, ACTIVE, DONE};
// Should never reach these states
illegal_bins invalid_states = {UNKNOWN, CORRUPT};
}
endgroup
3.6 Default Bins
covergroup default_bins_example;
coverpoint command {
bins read = {READ};
bins write = {WRITE};
bins nop = {NOP};
// Catch all other values not explicitly binned
bins other_commands = default;
}
coverpoint addr {
bins special_addr = {0, 0xFF, 0x100, 0x1FF};
// All other addresses go here
bins normal_addr = default;
}
endgroup
4. Sampling Methods
4.1 Explicit Sampling
module explicit_sampling;
bit [7:0] addr;
bit write;
covergroup cg;
coverpoint addr;
coverpoint write;
endgroup
cg cg_inst;
initial begin
cg_inst = new();
repeat(100) begin
addr = $urandom();
write = $urandom();
// Explicitly call sample()
cg_inst.sample();
#10;
end
end
endmodule
4.2 Event-Based Sampling
module event_sampling;
bit clk;
bit [7:0] addr;
bit write;
// Sample on clock edge
covergroup cg @(posedge clk);
coverpoint addr;
coverpoint write;
endgroup
cg cg_inst;
// Clock generation
always #5 clk = ~clk;
initial begin
cg_inst = new();
repeat(100) begin
@(posedge clk);
addr = $urandom();
write = $urandom();
// Automatically sampled on posedge clk
end
end
endmodule
4.3 Sampling with Arguments
class transaction;
// Covergroup with sample function
covergroup cg with function sample(bit[7:0] a, bit w);
coverpoint a;
coverpoint w;
endgroup
function new();
cg = new();
endfunction
function void drive(bit[7:0] addr, bit write);
// Sample with arguments
cg.sample(addr, write);
endfunction
endclass
module test;
initial begin
transaction txn = new();
repeat(100) begin
txn.drive($urandom(), $urandom());
end
$display("Coverage = %0.2f%%", txn.cg.get_coverage());
end
endmodule
4.4 Sampling in Class Methods
class packet;
rand bit [7:0] addr;
rand bit [31:0] data;
rand bit write;
covergroup packet_cg;
coverpoint addr;
coverpoint data;
coverpoint write;
endgroup
function new();
packet_cg = new();
endfunction
// Method 1: Sample in post_randomize
function void post_randomize();
packet_cg.sample();
endfunction
// Method 2: Sample in custom method
function void send();
// Send packet logic...
packet_cg.sample(); // Sample when sent
endfunction
endclass
5. Cross Coverage
5.1 Basic Cross Coverage
covergroup cross_basic;
addr_cp: coverpoint addr {
bins low = {[0:3]};
bins mid = {[4:7]};
bins high = {[8:15]};
}
write_cp: coverpoint write {
bins read = {0};
bins write = {1};
}
// Cross coverage: addr × write
addr_write_cross: cross addr_cp, write_cp;
// Creates 6 bins:
// <low, read> <low, write>
// <mid, read> <mid, write>
// <high, read> <high, write>
endgroup
5.2 Cross Coverage with Multiple Variables
covergroup three_way_cross;
addr_cp: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
size_cp: coverpoint size {
bins byte = {8};
bins word = {16};
bins dword = {32};
}
write_cp: coverpoint write {
bins rd = {0};
bins wr = {1};
}
// Three-way cross: addr × size × write
full_cross: cross addr_cp, size_cp, write_cp;
// Creates 12 bins:
// 2 (addr) × 3 (size) × 2 (write) = 12 combinations
endgroup
5.3 Cross Coverage with Ignore
covergroup cross_with_ignore;
addr_cp: coverpoint addr {
bins zero = {0};
bins low = {[1:127]};
bins high = {[128:255]};
}
write_cp: coverpoint write {
bins read = {0};
bins write = {1};
}
// Cross with ignore bins
addr_write_cross: cross addr_cp, write_cp {
// Ignore reading from address 0
ignore_bins ignore_zero_read = binsof(addr_cp.zero) &&
binsof(write_cp.read);
// Illegal to write to high addresses
illegal_bins no_high_write = binsof(addr_cp.high) &&
binsof(write_cp.write);
}
endgroup
5.4 Selective Cross Coverage
covergroup selective_cross;
addr_cp: coverpoint addr {
bins addr_bins[] = {[0:15]};
}
data_cp: coverpoint data {
bins data_bins[] = {[0:7]};
}
// Cross only specific bins
partial_cross: cross addr_cp, data_cp {
// Only cover when addr is even AND data is < 4
bins selected = binsof(addr_cp) intersect {0, 2, 4, 6, 8, 10, 12, 14} &&
binsof(data_cp) intersect {[0:3]};
}
endgroup
6. Transition Coverage
6.1 Simple Transitions
typedef enum {IDLE, ACTIVE, WAIT, DONE} state_t;
covergroup state_transitions;
coverpoint state {
// Single transition
bins idle_to_active = (IDLE => ACTIVE);
bins active_to_wait = (ACTIVE => WAIT);
bins wait_to_done = (WAIT => DONE);
bins done_to_idle = (DONE => IDLE);
}
endgroup
6.2 Multi-Step Transitions
covergroup complex_transitions;
coverpoint state {
// Two-step transition
bins normal_flow = (IDLE => ACTIVE => DONE);
// Three-step transition
bins wait_flow = (IDLE => ACTIVE => WAIT => DONE);
// Four-step complete cycle
bins full_cycle = (IDLE => ACTIVE => WAIT => DONE => IDLE);
}
endgroup
6.3 Wildcard Transitions
covergroup wildcard_transitions;
coverpoint state {
// From any state to ERROR
bins any_to_error = (* => ERROR);
// From IDLE to any state
bins idle_to_any = (IDLE => *);
// From any to any (all transitions)
bins all_transitions = (* => *);
// ERROR to IDLE (recovery)
bins error_recovery = (ERROR => IDLE);
}
endgroup
6.4 Repetition in Transitions
covergroup repetition_transitions;
coverpoint state {
// Stay in ACTIVE for exactly 3 cycles
bins stay_active_3 = (ACTIVE [*3]);
// Stay in WAIT for 2 to 5 cycles
bins stay_wait_range = (WAIT [*2:5]);
// Stay in IDLE for 1 or more cycles
bins stay_idle_plus = (IDLE [+]);
// Consecutive transitions with repetition
bins burst_sequence = (IDLE => ACTIVE [*3] => DONE);
// Goto repetition (repeat 0 or more times)
bins optional_wait = (IDLE => ACTIVE [->1:3] => DONE);
}
endgroup
6.5 Non-Consecutive Transitions
covergroup non_consecutive;
coverpoint state {
// Eventually reach DONE (states can occur in between)
bins eventually_done = (IDLE [-> 1] => DONE);
// IDLE appears 3 times (not necessarily consecutive)
bins idle_appears_3 = (IDLE [-> 3]);
// Complex: IDLE, then ACTIVE appears 2 times, then DONE
bins complex_flow = (IDLE [-> 1] => ACTIVE [-> 2] => DONE);
}
endgroup
7. Coverage Options
7.1 Covergroup Options
covergroup options_example;
// Set options for entire covergroup
option.name = "my_coverage"; // Name for reports
option.per_instance = 1; // Separate coverage per instance
option.at_least = 10; // Each bin needs 10 hits minimum
option.auto_bin_max = 64; // Max number of auto bins
option.cross_num_print_missing = 10; // Print 10 missing cross bins
option.goal = 90; // Coverage goal (%)
option.comment = "APB transaction coverage";
coverpoint addr {
bins values[] = {[0:255]};
}
endgroup
7.2 Coverpoint Options
covergroup coverpoint_options;
coverpoint addr {
option.weight = 5; // Weight in total coverage (default=1)
option.goal = 100; // Goal for this coverpoint
option.at_least = 5; // Each bin needs 5 hits
option.auto_bin_max = 32; // Max auto bins for this coverpoint
bins low = {[0:127]};
bins high = {[128:255]};
}
coverpoint data {
option.weight = 1; // Lower weight
option.goal = 80; // Lower goal acceptable
bins values[] = {[0:15]};
}
// Total coverage weighted by coverpoint weights
endgroup
7.3 Cross Coverage Options
covergroup cross_options;
addr_cp: coverpoint addr {
bins values[] = {[0:7]};
}
write_cp: coverpoint write {
bins rd = {0};
bins wr = {1};
}
addr_write_cross: cross addr_cp, write_cp {
option.weight = 10; // High weight for cross
option.goal = 100; // Want full cross coverage
option.at_least = 3; // Each cross bin hit 3 times
}
endgroup
8. Conditional Coverage (iff)
8.1 Coverpoint with iff
module conditional_coverage;
bit enable;
bit valid;
bit [7:0] addr;
bit [31:0] data;
covergroup cg;
// Only sample addr when enable is high
coverpoint addr iff (enable) {
bins values[] = {[0:15]};
}
// Only sample data when both valid and enable are high
coverpoint data iff (valid && enable) {
bins low = {[0:255]};
bins high = {[256:511]};
}
endgroup
cg cg_inst;
initial begin
cg_inst = new();
repeat(100) begin
enable = $urandom();
valid = $urandom();
addr = $urandom();
data = $urandom();
cg_inst.sample();
// addr only sampled if enable=1
// data only sampled if enable=1 AND valid=1
#10;
end
end
endmodule
8.2 Cross Coverage with iff
covergroup cross_conditional;
coverpoint addr {
bins values[] = {[0:7]};
}
coverpoint write {
bins rd = {0};
bins wr = {1};
}
// Cross only sampled when transaction is valid
addr_write_cross: cross addr, write iff (valid_txn);
endgroup
9. Bins Expressions and Filters
9.1 Bins with Expression
covergroup bins_with_expression;
coverpoint addr {
// Filter: only even addresses
bins even[] = {[0:99]} with (item % 2 == 0);
// Filter: only multiples of 4
bins aligned_4[] = {[0:100]} with (item % 4 == 0);
// Filter: powers of 2
bins powers_of_2[] = {[1:128]} with ($countones(item) == 1);
}
coverpoint data {
// Filter: only values where bit[7] is set
bins msb_set[] = {[0:255]} with (item[7] == 1);
// Filter: values in specific range
bins filtered[] = {[0:255]} with (item > 10 && item < 200);
}
endgroup
9.2 Advanced Filtering
covergroup advanced_filtering;
coverpoint addr[7:0] {
// Prime numbers (simple check)
bins primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};
// Palindrome addresses (reads same forwards and backwards)
bins palindrome[] = {[0:255]} with (
(item[7:4] == {item[0], item[1], item[2], item[3]}) &&
(item[6:5] == {item[1], item[2]})
);
}
coverpoint data {
// All ones patterns
bins all_ones = {32'hFFFFFFFF};
// Alternating pattern
bins alternating = {32'hAAAAAAAA, 32'h55555555};
// Walking ones
bins walking_ones[] = {32'h1, 32'h2, 32'h4, 32'h8, 32'h10, 32'h20};
}
endgroup
10. Coverage Queries and Reporting
10.1 Coverage Query Methods
class coverage_queries;
bit [7:0] addr;
bit write;
covergroup cg;
addr_cp: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
write_cp: coverpoint write {
bins read = {0};
bins write = {1};
}
cross_cp: cross addr_cp, write_cp;
endgroup
cg cg_inst;
function new();
cg_inst = new();
endfunction
function void report_coverage();
real total_cov, addr_cov, write_cov, cross_cov;
// Get overall coverage
total_cov = cg_inst.get_coverage();
$display("Total Coverage: %0.2f%%", total_cov);
// Get coverage per coverpoint
addr_cov = cg_inst.addr_cp.get_coverage();
$display("Address Coverage: %0.2f%%", addr_cov);
write_cov = cg_inst.write_cp.get_coverage();
$display("Write Coverage: %0.2f%%", write_cov);
// Get cross coverage
cross_cov = cg_inst.cross_cp.get_coverage();
$display("Cross Coverage: %0.2f%%", cross_cov);
// Get instance coverage (if per_instance=1)
$display("Instance Coverage: %0.2f%%", cg_inst.get_inst_coverage());
endfunction
endclass
10.2 Coverage Reporting
module coverage_reporting;
bit [7:0] addr;
bit write;
covergroup transaction_cg;
option.name = "Transaction Coverage";
option.comment = "APB transaction functional coverage";
addr_cp: coverpoint addr {
bins low = {[0:63]};
bins mid = {[64:127]};
bins high = {[128:255]};
}
write_cp: coverpoint write {
bins read = {0};
bins write = {1};
}
cross_cp: cross addr_cp, write_cp;
endgroup
transaction_cg cg;
initial begin
cg = new();
// Generate stimulus
repeat(1000) begin
addr = $urandom();
write = $urandom();
cg.sample();
end
// Coverage report
$display("\
========================================");
$display("Coverage Report");
$display("========================================");
$display("Total Coverage : %0.2f%%", cg.get_coverage());
$display("Address Coverage : %0.2f%%", cg.addr_cp.get_coverage());
$display("Write Coverage : %0.2f%%", cg.write_cp.get_coverage());
$display("Cross Coverage : %0.2f%%", cg.cross_cp.get_coverage());
$display("========================================\
");
// Check coverage goal
if (cg.get_coverage() >= 90.0)
$display("PASS: Coverage goal met!");
else
$display("FAIL: Coverage goal not met (need 90%%)");
end
endmodule
11. Advanced Techniques
11.1 Generic Covergroup
class generic_coverage #(type T = int);
T value;
covergroup cg with function sample(T val);
coverpoint val;
endgroup
function new();
cg = new();
endfunction
function void sample_value(T v);
cg.sample(v);
endfunction
endclass
// Usage
module test_generic;
initial begin
generic_coverage#(bit[7:0]) byte_cov = new();
generic_coverage#(int) int_cov = new();
byte_cov.sample_value(8'hAA);
int_cov.sample_value(12345);
end
endmodule
11.2 Covergroup Arrays
module covergroup_arrays;
bit [7:0] addr[4]; // 4 addresses
covergroup addr_cg(int index);
coverpoint addr[index] {
bins low = {[0:127]};
bins high = {[128:255]};
}
endgroup
// Create array of covergroups
addr_cg cg[4];
initial begin
// Instantiate one covergroup per array element
for (int i = 0; i < 4; i++) begin
cg[i] = new(i);
end
// Sample each
repeat(100) begin
foreach(addr[i]) begin
addr[i] = $urandom();
cg[i].sample();
end
#10;
end
// Report each
foreach(cg[i]) begin
$display("Coverage[%0d] = %0.2f%%", i, cg[i].get_coverage());
end
end
endmodule
11.3 Dynamic Covergroups
class dynamic_coverage;
bit [7:0] addr;
bit enable_coverage;
covergroup cg;
coverpoint addr {
bins values[] = {[0:15]};
}
endgroup
function new();
// Conditionally create covergroup
if (enable_coverage)
cg = new();
endfunction
function void sample_coverage();
if (cg != null)
cg.sample();
endfunction
function void start_coverage();
if (cg == null) begin
cg = new();
$display("Coverage started");
end
endfunction
function void stop_coverage();
if (cg != null) begin
$display("Final Coverage: %0.2f%%", cg.get_coverage());
cg.stop(); // Stop sampling
end
endfunction
endclass
11.4 Covergroup with Bins Based on Another Variable
class correlated_coverage;
bit [7:0] addr;
bit [1:0] burst_len; // 1, 2, 4, or 8
covergroup cg;
burst_cp: coverpoint burst_len {
bins single = {0}; // burst_len=1
bins burst2 = {1}; // burst_len=2
bins burst4 = {2}; // burst_len=4
bins burst8 = {3}; // burst_len=8
}
// Address bins depend on burst length
addr_cp: coverpoint addr {
// For single transfers, any address OK
bins any_addr_single = {[0:255]} iff (burst_len == 0);
// For burst2, address must be 2-byte aligned
bins aligned_2[] = {[0:255]} with (item % 2 == 0)
iff (burst_len == 1);
// For burst4, address must be 4-byte aligned
bins aligned_4[] = {[0:255]} with (item % 4 == 0)
iff (burst_len == 2);
// For burst8, address must be 8-byte aligned
bins aligned_8[] = {[0:255]} with (item % 8 == 0)
iff (burst_len == 3);
}
// Cross to ensure all combinations tested
burst_addr_cross: cross burst_cp, addr_cp;
endgroup
function new();
cg = new();
endfunction
endclass
12. UVM Integration
12.1 Coverage in UVM Subscriber
class apb_coverage extends uvm_subscriber#(apb_transaction);
`uvm_component_utils(apb_coverage)
// Covergroup
covergroup transaction_cg;
addr_cp: coverpoint trans.addr {
bins low = {[0:255]};
bins medium = {[256:511]};
bins high = {[512:1023]};
}
write_cp: coverpoint trans.write {
bins read = {0};
bins write = {1};
}
data_cp: coverpoint trans.wdata {
bins zero = {0};
bins ones = {32'hFFFFFFFF};
bins other = default;
}
// Cross coverage
addr_write_cross: cross addr_cp, write_cp;
endgroup
apb_transaction trans; // For covergroup
function new(string name, uvm_component parent);
super.new(name, parent);
transaction_cg = new();
endfunction
// Called by analysis port
virtual function void write(apb_transaction t);
trans = t;
transaction_cg.sample();
endfunction
// Report coverage in report_phase
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("COV_REPORT",
$sformatf("\
Functional Coverage Report:\
%s",
"─────────────────────────────────────────\
",
"Total Coverage : %0.2f%%\
",
"Address Coverage : %0.2f%%\
",
"Write Coverage : %0.2f%%\
",
"Data Coverage : %0.2f%%\
",
"Cross Coverage : %0.2f%%\
",
"─────────────────────────────────────────",
transaction_cg.get_coverage(),
transaction_cg.addr_cp.get_coverage(),
transaction_cg.write_cp.get_coverage(),
transaction_cg.data_cp.get_coverage(),
transaction_cg.addr_write_cross.get_coverage()),
UVM_LOW)
endfunction
endclass
12.2 Coverage in UVM Monitor
class apb_monitor extends uvm_monitor;
`uvm_component_utils(apb_monitor)
virtual apb_if vif;
uvm_analysis_port#(apb_transaction) ap;
// Coverage inside monitor
covergroup protocol_cg @(posedge vif.pclk);
// Protocol state coverage
state_cp: coverpoint {vif.psel, vif.penable} {
bins idle = {2'b00};
bins setup = {2'b10};
bins access = {2'b11};
illegal_bins invalid = {2'b01}; // Invalid state
}
// State transitions
trans_cp: coverpoint {vif.psel, vif.penable} {
bins idle_to_setup = (2'b00 => 2'b10);
bins setup_to_access = (2'b10 => 2'b11);
bins access_to_idle = (2'b11 => 2'b00);
}
// Ready signal coverage
ready_cp: coverpoint vif.pready iff (vif.psel && vif.penable) {
bins ready = {1};
bins not_ready = {0};
}
endgroup
function new(string name, uvm_component parent);
super.new(name, parent);
protocol_cg = new();
ap = new("ap", this);
endfunction
// Coverage sampled automatically on posedge clk
// due to @(posedge vif.pclk) in covergroup definition
endclass
12.3 Coverage-Driven Test
class coverage_driven_test extends uvm_test;
`uvm_component_utils(coverage_driven_test)
apb_env env;
apb_coverage cov;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = apb_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
apb_sequence seq;
real coverage_val;
int iteration = 0;
phase.raise_objection(this);
// Run until coverage goal met
while (1) begin
seq = apb_sequence::type_id::create("seq");
seq.start(env.agent.seqr);
iteration++;
// Check coverage every 100 transactions
if (iteration % 100 == 0) begin
coverage_val = env.coverage.transaction_cg.get_coverage();
`uvm_info("COV_CHECK",
$sformatf("Iteration %0d: Coverage = %0.2f%%",
iteration, coverage_val),
UVM_LOW)
// Stop when 95% coverage achieved
if (coverage_val >= 95.0) begin
`uvm_info("COV_DONE",
"Coverage goal met! Stopping test.",
UVM_LOW)
break;
end
// Safety: Stop after 10000 iterations
if (iteration >= 10000) begin
`uvm_warning("COV_TIMEOUT",
$sformatf("Max iterations reached. Coverage = %0.2f%%",
coverage_val))
break;
end
end
end
phase.drop_objection(this);
endtask
endclass
13. Best Practices
13.1 Coverage Model Organization
//=============================================================================
// GOOD PRACTICE: Separate coverage class
//=============================================================================
class apb_functional_coverage extends uvm_subscriber#(apb_transaction);
`uvm_component_utils(apb_functional_coverage)
// Transaction handle for sampling
apb_transaction trans;
//--------------------------------------------------------------------------
// Address Space Coverage
//--------------------------------------------------------------------------
covergroup address_cg;
option.name = "address_coverage";
option.per_instance = 1;
option.goal = 100;
addr_cp: coverpoint trans.addr {
bins low_mem = {[32'h0000_0000 : 32'h0000_0FFF]};
bins mid_mem = {[32'h0000_1000 : 32'h0000_FFFF]};
bins high_mem = {[32'h0001_0000 : 32'h000F_FFFF]};
bins peripheral = {[32'h4000_0000 : 32'h4FFF_FFFF]};
illegal_bins reserved = {[32'h5000_0000 : 32'hFFFF_FFFF]};
}
endgroup
//--------------------------------------------------------------------------
// Transaction Type Coverage
//--------------------------------------------------------------------------
covergroup transaction_type_cg;
option.name = "transaction_type";
option.goal = 100;
write_cp: coverpoint trans.write {
bins read = {0};
bins write = {1};
}
size_cp: coverpoint trans.size {
bins byte = {0}; // 8-bit
bins halfword = {1}; // 16-bit
bins word = {2}; // 32-bit
}
// Cross: All combinations of write × size
write_size_cross: cross write_cp, size_cp;
endgroup
//--------------------------------------------------------------------------
// Data Pattern Coverage
//--------------------------------------------------------------------------
covergroup data_pattern_cg;
option.name = "data_patterns";
option.goal = 90;
data_cp: coverpoint trans.wdata {
bins all_zeros = {32'h0000_0000};
bins all_ones = {32'hFFFF_FFFF};
bins alternating1 = {32'hAAAA_AAAA};
bins alternating2 = {32'h5555_5555};
bins walking_ones[] = {32'h1, 32'h2, 32'h4, 32'h8, 32'h10, 32'h20, 32'h40, 32'h80};
bins other_patterns = default;
}
endgroup
//--------------------------------------------------------------------------
// Protocol Coverage
//--------------------------------------------------------------------------
covergroup protocol_cg;
option.name = "protocol_coverage";
// Wait states
wait_states_cp: coverpoint trans.wait_cycles {
bins no_wait = {0};
bins short_wait = {[1:3]};
bins long_wait = {[4:10]};
bins max_wait = {[11:$]};
}
// Response types
response_cp: coverpoint trans.response {
bins okay = {OKAY};
bins error = {ERROR};
bins retry = {RETRY};
}
endgroup
//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
function new(string name, uvm_component parent);
super.new(name, parent);
address_cg = new();
transaction_type_cg = new();
data_pattern_cg = new();
protocol_cg = new();
endfunction
//--------------------------------------------------------------------------
// Write method (called by analysis port)
//--------------------------------------------------------------------------
virtual function void write(apb_transaction t);
trans = t;
// Sample all covergroups
address_cg.sample();
transaction_type_cg.sample();
data_pattern_cg.sample();
protocol_cg.sample();
endfunction
//--------------------------------------------------------------------------
// Report Phase
//--------------------------------------------------------------------------
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("COVERAGE_REPORT",
$sformatf("\
" +
"╔════════════════════════════════════════════════╗\
" +
"║ Functional Coverage Report ║\
" +
"╠════════════════════════════════════════════════╣\
" +
"║ Address Coverage : %6.2f%% ║\
" +
"║ Transaction Type : %6.2f%% ║\
" +
"║ Data Pattern Coverage : %6.2f%% ║\
" +
"║ Protocol Coverage : %6.2f%% ║\
" +
"╠════════════════════════════════════════════════╣\
" +
"║ TOTAL COVERAGE : %6.2f%% ║\
" +
"╚════════════════════════════════════════════════╝",
address_cg.get_coverage(),
transaction_type_cg.get_coverage(),
data_pattern_cg.get_coverage(),
protocol_cg.get_coverage(),
(address_cg.get_coverage() +
transaction_type_cg.get_coverage() +
data_pattern_cg.get_coverage() +
protocol_cg.get_coverage()) / 4.0),
UVM_LOW)
endfunction
endclass
13.2 Coverage Closure Strategy
class coverage_closure_strategy;
// Track uncovered bins
bit [7:0] uncovered_addresses[$];
covergroup addr_cg with function sample(bit[7:0] a);
coverpoint a {
bins values[] = {[0:255]};
}
endgroup
function new();
addr_cg = new();
endfunction
// Identify coverage holes
function void find_coverage_holes();
uncovered_addresses.delete();
for (int i = 0; i < 256; i++) begin
// Check if this value was covered
// (This is conceptual - actual implementation varies by tool)
if (!is_bin_covered(i)) begin
uncovered_addresses.push_back(i);
end
end
$display("Uncovered addresses: %p", uncovered_addresses);
endfunction
// Helper function (tool-specific implementation)
function bit is_bin_covered(int addr);
// Tool-specific API to check if bin is covered
// Example only - actual implementation depends on simulator
return 1; // Placeholder
endfunction
// Generate directed test for holes
function void generate_directed_stimulus();
foreach(uncovered_addresses[i]) begin
// Create directed test for this address
$display("Generating directed test for addr: 0x%0h",
uncovered_addresses[i]);
end
endfunction
endclass
13.3 Layered Coverage
//=============================================================================
// Multi-layer coverage model
//=============================================================================
class system_coverage;
//--------------------------------------------------------------------------
// Layer 1: Transaction-level coverage
//--------------------------------------------------------------------------
covergroup transaction_layer_cg with function sample(
bit[31:0] addr,
bit write,
bit[31:0] data
);
option.name = "transaction_layer";
coverpoint addr {
bins ranges[] = {[0:32'hFFFF]};
}
coverpoint write {
bins rd = {0};
bins wr = {1};
}
endgroup
//--------------------------------------------------------------------------
// Layer 2: Protocol-level coverage
//--------------------------------------------------------------------------
covergroup protocol_layer_cg with function sample(
bit psel,
bit penable,
bit pready
);
option.name = "protocol_layer";
coverpoint {psel, penable, pready} {
bins idle = {3'b000};
bins setup = {3'b100};
bins access_ready = {3'b111};
bins access_wait = {3'b110};
illegal_bins invalid = {3'b010, 3'b011};
}
endgroup
//--------------------------------------------------------------------------
// Layer 3: Feature-level coverage
//--------------------------------------------------------------------------
covergroup feature_layer_cg with function sample(
bit burst_mode,
bit error_injection,
bit back_to_back
);
option.name = "feature_layer";
coverpoint burst_mode;
coverpoint error_injection;
coverpoint back_to_back;
// All feature combinations
feature_cross: cross burst_mode, error_injection, back_to_back;
endgroup
function new();
transaction_layer_cg = new();
protocol_layer_cg = new();
feature_layer_cg = new();
endfunction
function void report_layered_coverage();
$display("\
╔════════════════════════════════════════╗");
$display("║ Layered Coverage Report ║");
$display("╠════════════════════════════════════════╣");
$display("║ Transaction Layer : %6.2f%% ║",
transaction_layer_cg.get_coverage());
$display("║ Protocol Layer : %6.2f%% ║",
protocol_layer_cg.get_coverage());
$display("║ Feature Layer : %6.2f%% ║",
feature_layer_cg.get_coverage());
$display("╚════════════════════════════════════════╝\
");
endfunction
endclass
Summary
This comprehensive guide covered:
- ✅ Covergroup basics – Syntax and instantiation
- ✅ Coverpoint and bins – Explicit, automatic, range, wildcard
- ✅ Sampling methods – Explicit, event-based, with arguments
- ✅ Cross coverage – 2-way, 3-way, with filters
- ✅ Transitions – Simple, multi-step, wildcards, repetitions
- ✅ Coverage options – Covergroup, coverpoint, cross options
- ✅ Conditional coverage – iff clause usage
- ✅ Bins expressions – Filtering with expressions
- ✅ Coverage queries – get_coverage(), reporting
- ✅ Advanced techniques – Generic, arrays, dynamic
- ✅ UVM integration – Subscriber, monitor, coverage-driven
- ✅ Best practices – Organization, closure, layering
Key Takeaways:
- Use covergroups to measure feature coverage
- Sample at appropriate points in your testbench
- Use cross coverage for combinations
- Track transitions for protocol verification
- Integrate with UVM using uvm_subscriber
- Drive tests based on coverage feedback
- Organize coverage in layers