Skip to Content

Functional Coverage in UVM: A Comprehensive Guide

1.Introduction to Functional Coverage

Functional coverage measures what has been tested in the design, unlike code coverage, which measures how much code has been executed. It ensures that all specified functionalities are verified. code vs functional coverage

Key Concepts:

  • Covergroup: A container for coverage points, crosses, and options.
  • Coverpoint: A variable or expression being tracked.
  • Bins: Value ranges or transitions for coverpoints.
  • Cross Coverage: Tracks combinations of multiple coverpoints.
  • Sampling: Triggering coverage collection at specific events.

Coverpoint Bins Explained in Simple Terms

Let me break down coverpoints and bins in the easiest way possible.

What is a Coverpoint?

Imagine you're testing a TV remote control. A coverpoint is like making a checklist for each button:

  • Power button
  • Volume up/down
  • Channel up/down

Each of these is a separate thing you want to test.

What are Bins?

Bins are like boxes where you group similar tests together.

Real-World Example: Testing a Volume Control

Your TV has volume levels from 0 to 100. Instead of checking every single number (which would take forever), you group them into bins:

coverpoint volume {
  bins mute     = {0};          // Only volume 0
  bins low      = {[1:30]};     // Volume 1-30 in one box
  bins medium   = {[31:70]};    // Volume 31-70 in another box
  bins max      = {100};        // Only volume 100
}

This way, instead of tracking 100 separate numbers, you only track 4 bins:

  1. Mute (0)
  2. Low (1-30)
  3. Medium (31-70)
  4. Max (100)

Different Types of Bins (With Simple Examples)

1. Fixed Value Bins

You care about specific exact values.

Example: Testing traffic lights:

coverpoint traffic_light {
  bins red    = {0};  // 0 = Red
  bins yellow = {1};  // 1 = Yellow
  bins green  = {2};  // 2 = Green
}
2. Range Bins

Group a range of values together.

Example: Testing temperature sensor:

coverpoint temperature {
  bins freezing = {[-20:0]};    // -20°C to 0°C
  bins cold     = {[1:15]};     // 1°C to 15°C
  bins warm     = {[16:25]};    // 16°C to 25°C
  bins hot      = {[26:50]};    // 26°C to 50°C
}
3. Wildcard Bins (Don’t Care About Some Bits)

Use ? to ignore bits.

Example: Testing a 4-bit command where last 2 bits don’t matter:

coverpoint command {
  wildcard bins start_cmd = {4'b10??}; // Matches 1000, 1001, 1010, 1011
}
4. Illegal Bins (Should Never Happen)

Mark values that are not allowed.

Example: A 2-bit mode where 11 is invalid:

coverpoint mode {
  bins valid[] = {0, 1, 2};  // 00, 01, 10
  illegal_bins invalid = {3}; // 11 is illegal
}

If this happens, the simulator will report an error.

5. Transition Bins (Sequences)

Track if a signal changes in a certain way.

Example: Testing a power state machine:

coverpoint power_state {
  bins off_to_on = (0 => 1);  // Must go from OFF(0) to ON(1)
  bins on_to_off = (1 => 0);  // Must go from ON(1) to OFF(0)
}

How Does This Work in Real Verification?

Let’s say you’re testing a car dashboard:

Step 1: Define Coverpoints

You care about:

  • Speed (0-200 km/h)
  • Fuel level (Empty, Low, Medium, Full)
  • Engine temperature (Cold, Normal, Hot)
Step 2: Group into Bins
covergroup car_dashboard_cg;
  // Speed bins
  speed_cp: coverpoint speed {
    bins stopped = {0};
    bins slow    = {[1:60]};
    bins medium  = {[61:120]};
    bins fast    = {[121:200]};
  }

  // Fuel level bins
  fuel_cp: coverpoint fuel {
    bins empty  = {0};
    bins low    = {[1:30]};
    bins medium = {[31:70]};
    bins full   = {[71:100]};
  }

  // Cross coverage: Speed + Fuel
  speed_x_fuel: cross speed_cp, fuel_cp;
endgroup
Step 3: What the Simulator Checks

The simulator will track:

  • Did the car go fast (121-200 km/h) with low fuel (1-30%)?
  • Did it ever stop (0 km/h) with full tank (71-100%)?
  • Did we miss any combination?
Key Takeaways
  1. Coverpoint = A feature to track (e.g., speed, temperature).
  2. Bins = Groupings of values (e.g., "slow speed" = 1-60 km/h).
  3. Cross coverage = Checks combinations (e.g., "high speed + low fuel").
  4. Illegal bins = Values that should never happen (error if they do).
  5. Transition bins = Tracks sequences (e.g., "off → on").

2. Types of Functional Coverage

(A) Covergroup-Based Coverage

  • Point Coverage: Tracks individual variables.
  • Cross Coverage: Tracks relationships between variables.
  • Transition Coverage: Tracks sequences of values.

(B) UVM Callback-Based Coverage

  • Used for higher-level scenario coverage (e.g., protocol states, error injection).

3. Covergroup Syntax & Usage

Basic Syntax:

covergroup <covergroup_name> [ ( [args] ) ] [ @(event) ];
  [<coverpoint_definitions>]
  [<cross_definitions>]
  [<options>]
endgroup

Example:

covergroup addr_cov @(posedge clk);
  addr_cp: coverpoint addr {
    bins low = {[0:15]};
    bins mid = {[16:31]};
    bins high = {[32:47]};
  }
endgroup

4. Coverpoint Binning Strategies

(A) Automatic Binning

If no bins are specified, SystemVerilog creates bins automatically.

coverpoint data; // Auto-bins for all possible values (0-255 for 8-bit)

(B) Explicit Binning

Manually define ranges or specific values.

coverpoint cmd {
  bins read = {0};
  bins write = {1};
  bins reset = {2};
  illegal_bins invalid = {3}; // Marks invalid values
}

(C) Range Binning

coverpoint addr {
  bins low = {[0:31]};
  bins mid = {[32:63]};
  bins high = {[64:127]};
}

(D) Wildcard Binning

Use wildcard to ignore certain bits.

coverpoint status {
  wildcard bins even = {3'b??0}; // Any value where LSB=0
}

(E) Transition Coverage

Tracks value sequences.

coverpoint state {
  bins s0_to_s1 = (0 => 1);
  bins s1_to_s2 = (1 => 2);
}

5. Cross Coverage

Tracks combinations of two or more coverpoints.

Basic Syntax:

cross <coverpoint1>, <coverpoint2> [, ...];

Example:

covergroup cmd_addr_cov;
  cmd_cp: coverpoint cmd {
    bins read = {0};
    bins write = {1};
  }
  addr_cp: coverpoint addr {
    bins low = {[0:31]};
    bins high = {[32:63]};
  }
  cmd_x_addr: cross cmd_cp, addr_cp;
endgroup

Ignoring Unwanted Crosses

cmd_x_addr: cross cmd_cp, addr_cp {
  ignore_bins wr_high = binsof(addr_cp.high) && binsof(cmd_cp.write);
}

6. Sampling Functional Coverage

(A) Using Events

covergroup data_cov @(posedge clk);
  coverpoint data;
endgroup

(B) Manual Sampling

covergroup data_cov;
  coverpoint data;
endgroup

data_cov cg = new();
cg.sample(); // Manually trigger coverage

(C) Sampling in UVM Monitor

class my_monitor extends uvm_monitor;
  packet pkt;
  packet_cg cg;

  virtual task run_phase(uvm_phase phase);
    cg = new();
    forever begin
      @(posedge vif.clk);
      pkt = get_packet_from_interface();
      cg.sample(); // Sample coverage
    end
  endtask
endclass

7. UVM Functional Coverage Flow (Full Example)

Step 1: Define a Transaction with Coverage

class packet extends uvm_sequence_item;
  rand bit [3:0] cmd;
  rand bit [7:0] addr;
  rand bit [31:0] data;

  // Define covergroup
  covergroup pkt_cg;
    option.per_instance = 1; // Track per instance
    option.comment = "Packet coverage";

    // Coverpoints
    cmd_cp: coverpoint cmd {
      bins read = {0};
      bins write = {1};
      bins burst = {2};
      illegal_bins invalid = {[3:15]};
    }

    addr_cp: coverpoint addr {
      bins low = {[0:127]};
      bins high = {[128:255]};
    }

    // Cross coverage
    cmd_x_addr: cross cmd_cp, addr_cp;
  endgroup

  function new(string name = "packet");
    super.new(name);
    pkt_cg = new(); // Instantiate covergroup
  endfunction

  `uvm_object_utils(packet)
endclass

Step 2: UVM Monitor Sampling Coverage

class my_monitor extends uvm_monitor;
  `uvm_component_utils(my_monitor)

  uvm_analysis_port #(packet) ap;
  virtual my_intf vif;
  packet pkt;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    ap = new("ap", this);
  endfunction

  task run_phase(uvm_phase phase);
    forever begin
      @(posedge vif.clk);
      pkt = packet::type_id::create("pkt");
      // Capture signals
      pkt.cmd = vif.cmd;
      pkt.addr = vif.addr;
      pkt.data = vif.data;
      pkt.pkt_cg.sample(); // Sample coverage
      ap.write(pkt); // Send to scoreboard
    end
  endtask
endclass

Step 3: UVM Scoreboard with Scenario Coverage

class my_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(my_scoreboard)

  uvm_analysis_imp #(packet, my_scoreboard) sb_export;

  // Scenario coverage
  covergroup scenario_cov;
    burst_read: coverpoint {packet.cmd == 2 && packet.addr inside {[100:200]}};
    back_to_back_write: coverpoint {packet.cmd == 1} {
      bins consecutive = (1 => 1);
    }
  endgroup

  function new(string name, uvm_component parent);
    super.new(name, parent);
    sb_export = new("sb_export", this);
    scenario_cov = new();
  endfunction

  function void write(packet pkt);
    // Check transaction
    if (pkt.cmd == 0 && pkt.addr == 8'hFF)
      `uvm_error("SCBD", "Read from reserved address")

    // Sample scenario coverage
    scenario_cov.sample();
  endfunction
endclass

Step 4: UVM Environment Integration

class my_env extends uvm_env;
  `uvm_component_utils(my_env)

  my_agent agent;
  my_scoreboard scbd;

  function void build_phase(uvm_phase phase);
    agent = my_agent::type_id::create("agent", this);
    scbd = my_scoreboard::type_id::create("scbd", this);
  endfunction

  function void connect_phase(uvm_phase phase);
    agent.monitor.ap.connect(scbd.sb_export);
  endfunction
endclass

8. Coverage Options & Methods

(A) Common Covergroup Options

OptionDescription
per_instance=1Tracks coverage per instance
weight=valueAssigns weight to covergroup
comment="text"Adds a comment for reports

(B) Coverage Methods

MethodDescription
sample()Triggers coverage sampling
get_coverage()Returns coverage percentage
start() / stop()Enables/disables coverage
$coverage_save("file.ucdb")Saves coverage to a file

9. Generating Coverage Reports

(A) Using Simulator Commands

# Questa
vsim -coverage -c top -do "run -all; coverage save coverage.ucdb; quit"
# VCS
urg -dir simv.vdb -report coverage_report

(B) UVM Report Hook

function void end_of_elaboration_phase(uvm_phase phase);
  $coverage_save("final_coverage.ucdb");
endfunction

10. Best Practices

  1. Focus on functional requirements, not just RTL code.
  2. Use meaningful bins (avoid auto-binning unless necessary).
  3. Cross coverage should be relevant (don’t cross everything).
  4. Use illegal_bins to catch invalid scenarios.
  5. Review coverage holes and update tests accordingly.

Conclusion

Functional coverage in UVM ensures that all specified design functionalities are verified. By following this guide, you can:

  • Define covergroupscoverpoints, and cross coverage.
  • Sample coverage in UVM monitors and scoreboards.
  • Generate reports to track verification progress.

This structured approach ensures high-quality verification with measurable results. 🚀

in AXI
Functional Coverage in UVM: A Comprehensive Guide
Prachi Goyal 8 April 2025
Share this post
Tags
Archive