[Edit of Image1]
Hey it's a me again @drifter1!
Today we continue with the Logic Design series on SystemVerilog in order to talk about Functional Coverage.
So, without further ado, let's get straight into it!
For years, measuring the quality of verification has been done using an approach known as Functional Coverage. When combined with constrained random verification (CRV) that we've explained thoroughly in the previous parts, it's possible to identify which set of features has been covered by the tests.
Of course, there are limitations to this approach. It will only be as good as the code written for it by the programmer. This means that unimplemented features of the system can be overlooked by the tests very easily as no functional coverage code will be written for them. It is important for the design specification to be analyzed correctly, so that all the required information is extracted from it.
The functional coverage code is written alongside the testbench and there are 4 main places for it to be coded in. This basically gives us 4 types of functional coverage points, which are:
Coverage models in SystemVerilog are enclosed within the covergroup and endgroup keywords. Such a covergroup is quite similar to a class, and so once defined (basically anywhere) it can then be instantiated just like any object using new().
A covergroup definition is associated to the identifier that is provided along with it and contains:
There are two ways of triggering when to sample:
sample() methodThe clocking event of a covergroup is specified similar to how the trigger event is specified in always blocks. So, it's a @ followed by the event, which can be something like posedge clk (on every positive edge of the clock signal) or a completely custom event:
covergroup covergroup_name @ (customEvent);
When left unspecified, coverage sampling must be triggered manually through the build-in sample() method. The method is called on the specific coverpoint that needs to be sampled within the group:
covergroup_name.coverpoint_name.sample();
Within a coverage group multiple coverage points are defined. They can be variables or expressions, and are mentioned using the coverpoint keyword. Each coverpoint can also include an optional label, which is put before the coverpoint keyword and is followed by a colon (:).
opt_cp_label : coverpoint var;
opt_cp_label2 : coverpoint (expr);
Each coverpoint can include bins which basically separate the range of possible values. The bins will be "covered" when the coverpoint variable or expression is equal to the corresponding values. So, they can be used as a sort of feature "hit".
The various bins are enclosed within { } that follow the coverpoint's variable or expression. Each separate bin starts with the bins keyword, followed by an identifier and then the valid range. This range can be expressed in many ways, some of which are shown below.
coverpoint var {
// separate bin for a given value
bins zero = {0};
// separate bin for a given range
bins range = {[1 : 2]}
// automatic separate bins for a given range
bins separate[] = {[3 : 7]}
// fixed number of automatic bins (range is split into 3)
bins fixed_auto[3] = {[0 : $]}
// store remaining values that haven't been covered
bins remaining = default;
}
There are two main ways of conditionally enabling coverage:
iff construct in the corresponding coverpointstart() and stop() functions on the covergroup objectThe iff construct looks like this:
covergroup covergroup_name;
coverpoint var iff (condition) {
// bins
}
endgroup
A cross coverage between coverpoints is specified using the cross construct followed by the labels of the crosspoints separated by comma (,). It can also optionally include a label on its own.
opt_cc_label : cross cp_label1, cp_label2;
SystemVerilog includes various options that can be added to coverage groups and points. They can affect either a specific instance or all of the instances, and are accessed through the option sub-object with dot (.) notation.
covergroup covergroup_name;
option.option_name = option_value;
...
endgroup
The percentage of coverage is returned by the get_coverage() method, which needs to be called on the corresponding covergroup object. It's also possible to call the $get_coverage() system task to get the coverage for all coverage groups.
Block diagrams and other visualizations were made using draw.io
And this is actually it for today's post!
It was basically only an overview of what Functional Coverage is and what tools SystemVerilog provides us with in order to implement it. For more in-depth information on the various constructs head over to ASIC World.
Next time we will cover Assertions in a similar fashion...
See Ya!

Keep on drifting!