Logo

Coverage Construct in SV

28 Oct 2023
7 mins

In the previous article we focused on the theoretical aspect of coverage and learnt about code and functional coverage. We saw that code coverage is automatically calculated by tool, but functional coverage needs cover groups to be written. In this article we will learn about the constructs provided by System Verilog which helps in achieving functional coverage.

What are functional coverage constructs?

Functional coverage constructs are based on the concept of covergroups, which are collections of coverpoints and crosses. A coverpoint is a variable or expression that can take a range of values, and a cross is a combination of two or more coverpoints. Each coverpoint and cross can have a set of bins, which are subranges of values that are of interest for coverage. For example, a coverpoint for an 8-bit data bus can have bins for all zeros, all ones, even parity, odd parity, etc. A cross between the data bus and a 2-bit control signal can have bins for different combinations of data and control values.

How to define a covergroup?

The syntax for defining a covergroup is as follows:

covergroup <name> [with function sample (<arguments>)]
[option.<option_name> = <option_value>; ...];
  <coverage_event>;
  coverpoint <coverpoint_expression> [option.<option_name> = <option_value>; ...] {
    bins <bin_name> = <bin_expression>;
    ...
  }
  ...
  cross <coverpoint_name1>,<coverpoint_name2>,  ... [option.<option_name> = <option_value>; ...] {
    [bins <bin_name> = <bin_expression>;]
    ...
  }
  ...
endgroup

The with function sample clause is optional and specifies the arguments that are passed to the covergroup when it is sampled. Generally, covergroups are defined in a different component which receives a transaction packet from monitor. In these scenarios, covergroup can sample from the transaction packet and thus with function sample is not generally used.

The option statements are also optional and allow setting various parameters for the covergroup, such as the weight, goal, type, etc. We will see different options in detail later in this article.

The <coverage_event> is a defined event in which the sampling it done. If we do not provide a coverage_event then we need to explicitly call sample function to sample the covergroup. The sampling happens immediately when the event is triggered. coverpoint or a cross statement defines the variables which we are covering in the covergroup. The bins statements define the bins for each coverpoint and cross.

Providing bins for the coverpoints or cross is optional. By default, bins will be created for all possible values of a variable and for cross a bin will be created for all possible combinations. If a variable has a considerable number of possible values, it can lead to the creation of lots of bins. It is always best practice to define bins for coverpoints. For cross, we generally define bins if there are some invalid combinations which we don’t want to cover.

How to use a covergroup?

To use a covergroup, it must be first declared as a type or an object, and then instantiated with the new keyword. The covergroup is sampled by calling its sample method if we have not provided a default coverage_event. Also we need to pass argument with sample if we have used with function samplewhile defining covergroups.

Example
cg cg1 = new();
cg1.sample(); // if no default event is passed.
Cg1.sample(<args>) // if with function sample is used

Coverage Bins

Defining bins is an important part of the coverage in SV and the final coverage metrics depend on the bins we have defined. In this section we will learn about bins in detail. SV provides several types of bins which we can use in the coverpoints and cross.

Writing Bins

To define bins, we use the word bins followed by the name we want to give to the bin. Then some value is assigned to the bin. Whenever the variable attains the specified value, we say that the bin has been covered.

A bin can also be assigned a range of values that we specify using different words and symbols, such as inside, outside, or intersect. We can also list the exact values that we want the bin to have. For example, we can write bins even = {0, 2, 4, 6, 8}; to create a bin named even that has only those five values.

Sometimes, we may want to have more than one bin with the same range or list of values. To do this, we can use square brackets [] after the word bin. This creates an array of bins, which means a group of bins that are numbered from zero to the size of the array minus one. If we do not write the size of the array inside the brackets, then System Verilog will create one bin for each of the values we assign. If we write a number inside the brackets, then System Verilog will create that many bins and divide the values equally among them. For example, we can write bins odd[2] = {1, 3, 5, 7}; to create two bins named odd[0] and odd[1] that have two values each.

Example

bins data[] = {[1:10]} -> This will create 10 bins with each bin having 1 value. data[0] will have value 1, data[1] will have value 2 and so on.

bins data[5] = {[1:10]} -> This will create 5 bins by splitting the values equally in each bin. Thus data[0] will have {1,2}, data[1] will have {3,4} and so on.

bins data[4] = {[1:10]} -> This will create 4 bins but the last bin will have only 1 value as 10 values cannot be divided equally in 4 bins.

When System Verilog divides the values into bins, it tries to make each bin have the same number of values. But sometimes, this is not possible because the list of values is not a multiple of the number of bins. In that case, System Verilog will make the last bin have fewer values than the others.

Different types of bins

Illegal Bins

Illegal bins are bins which are used to define invalid bins. If the signal or variable takes this value an error will be generated. These are generally used to check the validity of the input or output signals and detect any invalid behavior. Though we have checker for this, but checker only checks if output is correct for a particular input scenario. Through illegal bins we can validate if the signal has taken some error values.

Illegal bins are defined using illegal_bins keyword.

Ignore bins

Ignore bins are bins which are used to specify the values that should not be counted for coverage. These are basically the bins which are not important for the verification of the DUT and thus can be ignored.

Ignore bins are defined using ignore_bins keyword.

Wildcard bins

Wildcard bins are used to define bins which will match for a specific pattern with wildcard characters. These are mostly used when we are interested in some of the bits of the signal and rest can be don’t care.

Most basic example of this scenario is a priority encoder. If we want to cover the inputs of the priority encoder, we can create bins as follow:

wildcard bins bin1 = 4’b1???; // if 3rd bit is 1, it will be given priority.

wildcard bins bin2 = 4’b01??; // if 3rd bit is 0 & 2nd bit is 1, then output will be for 2nd input.

Transition bin

Transition bins are used to capture transitions from value to another. We can use this sample for any specific transition or even to sample invalid transitions.

Syntax

bins <bin_name> = ( {val1} => {val2}); - This will increment the count of bin whenever the variable goes from {val1} to {val2}.

There are some modifiers that can be used with transition bins to specify more complex transitions, such as:

  • *: This modifier indicates a repetition of the same value. For example, transition_bins stable = (* => *); will match any transition where the value does not change.
  • +: This modifier indicates one or more occurrences of the same value. For example, transition_bins glitch = (0 => 1+ => 0); will match any transition where the value changes from 0 to 1 and then back to 0, with one or more 1s in between.
  • [*<min>:<max>]: This modifier indicates a range of occurrences of the same value. For example, transition_bins pulse = (0 => 1[*2:4] => 0); will match any transition where the value changes from 0 to 1 and then back to 0, with two to four 1s in between. This is known as repetition operator. We will learn more about repetition operator in assertions topic.
We can combine transition bins with wildcard bins to write complex transition bins.
Both transition bins and wildcard bins can be used with illegal_bins and ignore_bins to create an illegal and ignore bin, respectively.