SystemVerilog Functional Coverage – Complete Guide

Comprehensive Coverage Tutorial with Examples

Table of Contents

  1. Introduction to Functional Coverage
  2. Covergroup Basics
  3. Coverpoint and Bins
  4. Sampling Methods
  5. Cross Coverage
  6. Transition Coverage
  7. Coverage Options
  8. Conditional Coverage (iff)
  9. Bins Expressions and Filters
  10. Coverage Queries and Reporting
  11. Advanced Techniques
  12. UVM Integration
  13. 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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top