When defining coverage bins for coverage items, the number and size of bins depend on the item type. People seek ways to define the MIN and MAX value of the type automatically, rather than typing the numbers.
For example, to ensure edge values are covered, we want to have a bin of the MIN value of the type, a bin of the MAX value of the type, and the rest of the values – to be grouped in 10 bins. If the field is of unsigned integer of 16 bits, the edge values of this type are 0 and 0xffff, and the coverage can be defined like this:
typeaddress_t : uint ( bits : 16);
unit monitor {
address : address_t;
cover ended is {
item address : address_t = address using ranges = {
range([0], "First address");
range([1..0xfffe], "", 0x1999);
range([0xffff], "Last address");
};
};
};
But do we really want to perform this calculation for every coverage item?
Once a type definition changes, say to be of 32 bits, will we remember to update the definition of all coverage items that are using this type?
And what if this code is used in multiple configurations – in some of them address_t is of 32 bits, and in others of 64. How do we implement this coverage item?
To solve all these problems, we can replace usage of hard coded numbers with calling set_of_values().
This method returns the set of all values of a given type. Then, you can query the set - get its minimum and maximum values (set.min()), set.max()), and its size - how many values there are in this set (set.uint_size()).
Here are some examples of querying a type:
You can also call this method for user defined types -
By the way, in this snapshot you can see the difference between all_values(), returning a list, and set_of_values() - returning a set.
Using set_of_values(), we can define the coverage bins without specifying hard coded numbers. If the type definition changes – the coverage model will change accordingly.
cover ended is {
item address : address_t = address
using ranges = {
range([set_of_values(address_t).min()], "First address");
range([set_of_values(address_t).min() + 1..set_of_values(address_t).max() - 1], "",
set_of_values(address_t).uint_size() / 10);
range([set_of_values(address_t).max()], "Last address");
};
};
The problems raised above are solved – if address_t definition changes – set_of_values() will return the new set. No need to edit the coverage model.
I know what you think – ok, this works. But such a long code; why not use macros?
You are right. By implementing MIN_VALUE, MAX_VALUE and RANGE_SIZE macros, we can write a shorter code:
cover ended is {
item address : address_t = address
using ranges = {
range([MIN_VALUE(address_t)], "First address");
range([MIN_VALUE(address_t) + 1..MAX_VALUE(address_t) - 1], "",
RANGE_SIZE(address_t) / 10);
range([MAX_VALUE(address_t)], "Last address");
};
};
The one question left is: How do we define this macro?
The simplest set of macros for implementing MIN/MAX value of a type is:
define<my_min_value'exp> "MIN_VALUE[ ]\(<type>\)" as {
set_of_values(<type>).uint_min().as_a(<type>);
};
define<my_max_value'exp> "MAX_VALUE[ ]\(<type>\)" as {
set_of_values(<type>).uint_max().as_a(<type>);
};
define<my_range_size'exp> "RANGE_SIZE[ ]\(<type>\)" as {
set_of_values(<type>).uint_size();
};
These macros can be improved to have better performance, and to support more types (the way they are implemented here, they do not support longs).
For an example of full implementation of these macros, supporting all types with maximum efficiency, see:
https://github.com/efratcdn/e_type_min_max
You can download this small utility from the github, and then use as is or improve per your requirements and preferences.
Enjoy verification,
Team Specman