A single coverpoint tells you whether a variable took a particular value at some point during the test. It says nothing about which values happened together. Cross coverage fills that gap. It tracks combinations of bins across two or more coverpoints, which is exactly what you need to catch corner cases that only appear when several conditions line up at once.

8 min read | Intermediate Level

What You'll Learn

  • What cross coverage measures and how its automatic bins are created
  • The syntax of a cross statement and the rules for what it can contain
  • How to build custom cross bins with binsof and use intersect correctly
  • How illegal_bins and ignore_bins change the cross coverage total

What Is Cross Coverage?

Cross coverage observes the combination of states between two or more coverpoints. Instead of asking "did master_id ever equal 2?" it asks "did master_id equal 2 while slave_id equalled 3?" This is essential for verifying interactions such as every opcode with every addressing mode, or every command type with every address region.

Syntax

Cross coverage is declared with the cross keyword inside a covergroup:


[label :] cross item1, item2 [, itemN] [iff (expression)] [{
    bins         bin_name = select_expression;
    ignore_bins  bin_name = select_expression;
    illegal_bins bin_name = select_expression;
}];
  • label (optional): names the cross and creates a scope for its custom bins
  • item1, item2 ... (required, two or more): coverpoint names, or plain variables in the same covergroup
  • iff (expression) (optional): the cross is sampled only when the expression is true
  • body (optional): user-defined, ignored and illegal cross bins

Some rules to remember:

  • When a plain variable is listed in a cross, SystemVerilog creates an implicit coverpoint for it with automatic bins.
  • An expression such as a + b cannot be crossed directly. Define it as a named coverpoint first, then cross the coverpoint.
  • All items in a cross must belong to the same covergroup.

A Basic Cross Coverage Example

Imagine a system where different masters can initiate transactions with different slaves. Individual coverpoints on master_id and slave_id are useful, but they do not tell you whether master 0 ever actually talked to slave 1. A cross answers exactly that:


covergroup master_slave_interaction @(posedge clk);
  master_id_cp: coverpoint master_id {
    bins master0 = {0};
    bins master1 = {1};
    bins master2 = {2};
    bins master3 = {3};
  }
  slave_id_cp: coverpoint slave_id {
    bins slave0 = {0};
    bins slave1 = {1};
    bins slave2 = {2};
    bins slave3 = {3};
  }

  ms_cross: cross master_id_cp, slave_id_cp; // Cross coverage declaration
endgroup

ms_cross automatically creates one cross product bin for every combination of the two coverpoints' bins. Since both coverpoints have 4 bins, the cross produces 4 x 4 = 16 bins: <master0,slave0>, <master0,slave1>, and so on up to <master3,slave3>. Each bin counts one specific master-slave interaction.

The label before the colon becomes the cross's name and the scope for any custom bins. If you omit it, the tool generates a name for reporting, but that name cannot be referenced in your code.

Label every cross. It makes coverage reports readable and lets you refer to the cross from coverage options and custom bins.

Quick Check

Q: If master_id_cp had 5 bins and slave_id_cp had 3 bins, how many automatic cross bins would the cross create?

Show Answer

15. The number of automatic cross bins is the product of the participating coverpoints' bin counts, 5 x 3 in this case.

Selecting Cross Products with binsof and intersect

Custom cross bins are built from select expressions. The binsof construct picks bins from one coverpoint, and the optional intersect clause narrows that pick to bins whose values overlap a given value or range list in braces.

Select expressionSelects cross products where...
binsof(cp.b)bin b of cp is part of the combination
binsof(cp) intersect {[0:1]}the bin of cp contains at least one value in 0 to 1
!binsof(cp) intersect {[0:1]}the bin of cp contains no value in 0 to 1
sel1 && sel2both selections are true
sel1 || sel2either selection is true

The right-hand side of intersect is always a brace-enclosed value or range list, never another binsof(). To combine conditions on two different coverpoints, join two select expressions with && or ||.

Defining User-Defined Cross Coverage Bins

User-defined bins group one or more cross products under a single name. Say you care about master 0 accessing slave 1, and separately about any master accessing slave 3:


covergroup master_slave_interaction_refined @(posedge clk);
  master_id_cp: coverpoint master_id {
    bins master0 = {0};
    bins master1 = {1};
    bins master2 = {2};
    bins master3 = {3};
  }
  slave_id_cp: coverpoint slave_id {
    bins slave0 = {0};
    bins slave1 = {1};
    bins slave2 = {2};
    bins slave3 = {3};
  }

  ms_cross: cross master_id_cp, slave_id_cp {
    bins master0_slave1    = binsof(master_id_cp.master0) && binsof(slave_id_cp.slave1);
    bins any_master_slave3 = binsof(slave_id_cp.slave3);
    // Selects  : 4 cross products grouped into one bin
  }
endgroup
  • master0_slave1 combines two binsof selections with &&, so it matches only the single product <master0,slave1>.
  • any_master_slave3 uses binsof(slave_id_cp.slave3) on its own. Because no condition is placed on master_id_cp, every master paired with slave 3 is selected. The bin is covered as soon as any one of those four combinations is hit.

Here is the same idea written with intersect, to cover only the low-numbered masters talking to slave 3:


ms_cross: cross master_id_cp, slave_id_cp {
  // master bins whose values overlap 0..1 (master0, master1), paired with slave3
  bins low_masters_slave3 = binsof(master_id_cp) intersect {[0:1]} &&
                            binsof(slave_id_cp) intersect {3};
}

Any cross product not included in a user-defined bin still gets its own automatic bin. With the two bins in the refined example above, the cross reports 2 user-defined bins plus 11 automatic bins (16 - 1 - 4).

Where Is Cross Coverage Used?

Cross coverage is a natural fit for protocol verification: confirming every command type was issued to every address region, every burst length was used with every data width, or every opcode was executed in every privilege mode. It is also used in stress testing to confirm that high-traffic or boundary combinations were hit, not just each field in isolation.

Specifying Illegal and Ignore Cross Products

Not every combination is valid or relevant. Two special bin types handle these cases:

  • illegal_bins: combinations that should never occur. Hitting one produces a run-time error, and these products are excluded from the coverage total.
  • ignore_bins: combinations that are valid but not part of your coverage goals. They are excluded from the coverage total.

covergroup master_slave_interaction_with_exceptions @(posedge clk);
  ...

  ms_cross: cross master_id_cp, slave_id_cp {
    bins master0_slave1 = binsof(master_id_cp.master0) && binsof(slave_id_cp.slave1);
    bins any_master_slave3 = binsof(slave_id_cp.slave3);
    illegal_bins bad_master_slave_config = binsof(master_id_cp.master0) && binsof(slave_id_cp.slave0); // Master 0 should not talk to Slave 0
    ignore_bins unused_master_slave_combo = binsof(master_id_cp.master3) && binsof(slave_id_cp.slave2); // Valid, but not critical to cover
  }
endgroup

If master 0 accesses slave 0, the simulator reports an error. The combination of master 3 and slave 2 simply does not contribute to the coverage percentage. If a product falls into both an illegal_bins and an ignore_bins selection, it is still treated as illegal and the error is reported.

The diagram below shows how all 16 cross products of ms_cross are classified:

Cross coverage bin map showing user-defined, illegal, ignore and automatic bins for a 4x4 master-slave cross

Quick Check

Q: In the example above, how many bins count toward the coverage percentage of ms_cross?

Show Answer

11. Out of 16 products, 1 belongs to master0_slave1, 4 to any_master_slave3, 1 is illegal and 1 is ignored. The remaining 9 become automatic bins, giving 2 user-defined + 9 automatic = 11 bins.

Cross Coverage in Design and Verification

Covergroups are a verification construct and are not synthesizable. In a testbench they are usually placed in a monitor or subscriber that samples transaction fields, which is the most common use of cross coverage.

Design engineers can still use cross coverage to capture design intent, for example crossing FSM state with an input request to confirm every request was seen in every state. A covergroup can be declared inside an RTL module or interface, or attached with bind, as long as it is excluded from synthesis.


`ifndef SYNTHESIS
  covergroup fsm_cg @(posedge clk);
    state_cp : coverpoint state;      // enum-typed FSM state
    req_cp   : coverpoint req;
    state_x_req : cross state_cp, req_cp;
  endgroup
  fsm_cg fsm_cg_inst = new();
`endif

Leaving a covergroup outside a synthesis guard can cause errors or warnings in synthesis tools. Always wrap design-embedded coverage in `ifndef SYNTHESIS or keep it in a separate bind file.

Design-Side Coverage in Practice

RTL designers often add small covergroups or cover property statements next to complex control logic such as arbiters and FIFO controllers. Crossing arbiter grant with requester count, for example, shows whether the fairness logic was exercised under contention, not just in idle conditions.

Common Beginner Mistakes

Mistake #1: Trying to cross an expression directly

cross only accepts coverpoint names or plain variables. Why? To cross something like mode + cfg, first define a named coverpoint for the expression, then reference that coverpoint in the cross.

Mistake #2: Expecting intersect to select only bins fully inside the range

binsof(cp) intersect {3} selects every bin of cp that contains the value 3, even if the bin also holds other values. A bin defined as {[0:7]} is selected. Why? intersect checks for overlap, not containment. If you need exact selection, refer to the bin directly with binsof(cp.bin_name).

Mistake #3: Assuming invalid combinations are excluded automatically

A cross creates automatic bins for every combination, whether or not it makes sense for the design. Why? The tool has no knowledge of your protocol rules. Unreachable combinations must be marked with illegal_bins or ignore_bins, otherwise coverage can never reach 100%.

Practice Exercise

Challenge: Cross Two 2-Bin Coverpoints

Given a coverpoint cp_dir with 2 bins (read, write) and a coverpoint cp_region with 2 bins (low, high), write a labeled cross between them. How many automatic cross bins does it create? As a bonus, add a user-defined bin that collects all writes using intersect.

Hint

Follow the label: cross cp1, cp2; pattern and multiply the bin counts. For the bonus, write is the bin holding the value 1.

Solution

covergroup cg @ (posedge clk);
  cp_dir : coverpoint dir {
    bins read  = {0};
    bins write = {1};
  }
  cp_region : coverpoint region {
    bins low  = {0};
    bins high = {1};
  }

  dir_region_cross : cross cp_dir, cp_region;
endgroup

2 bins x 2 bins = 4 automatic cross bins: <read,low>, <read,high>, <write,low> and <write,high>.

Bonus: give the cross a body with bins all_writes = binsof(cp_dir) intersect {1};. It groups <write,low> and <write,high> into one bin, leaving 2 automatic bins for the reads.

Summary

In this tutorial, you learned:

  • Cross coverage tracks combinations of bins across two or more coverpoints, not each one in isolation
  • A cross accepts coverpoints or plain variables from the same covergroup; expressions need their own coverpoint first
  • The number of automatic cross bins is the product of the participating coverpoints' bin counts
  • binsof selects bins of one coverpoint, and intersect narrows that selection to bins overlapping a value or range list in braces
  • Conditions on different coverpoints are combined with && and ||; products not in any user-defined bin still get automatic bins
  • illegal_bins flag combinations that must never happen; ignore_bins drop valid but irrelevant ones; both are excluded from the coverage total
  • Covergroups are not synthesizable, but designers can embed cross coverage in RTL behind a synthesis guard
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5