Sign In
Forgot Password?
Sign In | | Create Account

Verification Horizons

Merging SystemVerilog Covergroups by Example

by Eldon Nelson M.S. P.E., Verification Engineer, Micron Technology

SystemVerilog has the concept of covergroups that can keep track of conditions observed during a simulation. If you have a single instance of a covergroup in your design, you don’t need to deal with merging coverage across all of the instances of that covergroup. If you do have multiple instances of a covergroup and want to merge coverage, there are implementation details that can make a big difference on what information you will be able to collect.

An incorrect application of a covergroup might collect extraneous information that slows down the simulation and the merging process. Or, an incorrect application of a covergroup could result in covergroups that cannot be merged with other instances because of where the covergroup was defined. You can avoid making these mistakes by making informed choices when implementing a covergroup.

The choices for implementing a covergroup start with where the covergroup is actually defined: in a separate coverage object or in the module itself. The choices continue with the options that define the behavior of the covergroup.

This article describes the effect of those choices on how the resulting covergroup behaves when merged. The source code of the examples, along with their structural diagrams, as well as screen shots of the resulting covergroups, taken from Mentor Questa, will help illustrate those choices in a simple way.

The source code of the examples shown and how to execute them is available on GitHub (link available in References).

The Base Example Union_Merge.sv

covergroup c1_cg (ref bit x);
option.per_instance = 1;
type_option.merge_instances = 1;
x : coverpoint x {
bins _0 = {1’h0};
bins _1 = {1’h1};
}
endgroup

module dut (input bit x, input bit clk);
c1_cg c1_cg_inst = new(x);
always @(posedge clk) begin
c1_cg_inst.sample();
end
endmodule
module tb ();
bit clk = 0;
dut duta(1’h0, clk);
dut dutb(1’h1, clk);
initial begin
#10; clk = 1;
#10; clk = 0;
#10; clk = 1;
#10
$finish();
end
endmodule

Figure 1: Complete source of union_merge.sv

The SystemVerilog above creates a covergroup type called c1_cg (purple box in Figure 2) that has a single coverpoint that will keep track of the one bit vector x that is passed into it. The coverpoint creates two bins named _0 for the zero value and _1 for the one value. The name of the single coverpoint is: x.

The example then instantiates the covergroup inside a module named dut (green box). The testbench (blue box) instantiates two copies of dut named: duta and dutb that has their x input tied to either a constant one or constant zero. The covergroup is sampled on the positive edge of clk.

This Union Merge

The first example “union_merge.sv” merges the two instances of the covergroup c1_cg_inst in a way where if either instance meets a specific condition, the condition is marked as met. This could be thought of as the union merge of both covergroup instances.

You might be a person who likes looking directly at the code or if you are like me, you would get a lot more out of the structural block diagram below.

Figure 2:Structural Diagram of union_merge.sv

The implementation detail that is important is that you are setting the values of: option.per_instance and type_option. merge_instances both equal to one; which will properly setup a union merge.

What does Coverage look like in Mentor Questa?

Seeing how the covergroups are interpreted in a simulator will help make this clearer. Below, is a screen capture of how Mentor Questa interprets the covergroups in “union_ merge.sv”. You can see that there are two INST lines in the Covergroups window. This is showing the c1_cg_inst covergroup that is in both duta and dutb.

We can see that the duta instance only had hits for the bin _0 while dutb only had hits for the bin _1 – exactly as we expected, we tied them to constants of course. So each INST is 50% covered because each is hitting half of their described bins. But, because we set the covergroup options to do a union merge of all possible covergroups of this type, we get the “overall coverpoint” called c1_cg::x being marked as 100%.

The coverpoint c1_cg::x also shows the number of hits it had for each bin: 2 in this case. Which is the union of duta and dutb. In this example, you can use the INST and the c1_cg::x coverage as both measurements are available; both measurements also include full bin information.

Weighted Average Merge

Let’s do a small change where we set the values of: option.per_instance and type_option.merge_instances to zero - this is handled in “weighted_merge.sv”. Now, when we look at the overall coverpoint c1_cg::x, we see that the simulator now shows the coverpoint c1_cg::x as a greyed-out un-expandable entry (in the previous example it was expandable). The coverpoint c1_cg::x now has 50% coverage opposed to 100% previously. That is because it is not doing a union merge.

Figure 3:Mentor Questa View of Covergroups in union_merge.sv

Figure 4:Mentor Questa View of Covergroups in weighted_merge.sv

The merge used in this example, is taking all of the instances of the covergroup and giving you the average across every coverpoint – the weighted average merge. It doesn’t have visibility into what bins are covered anymore; that is why you can’t expand it and see the _0 and _1 bins. In this example, you can use either the INST or the c1_cg::x as both measurements are available; but, bin coverage is only available for INST instance coverage.

Figure 5:Structural Diagram of weighted_merge.sv

Covergroups Defined in a Module - Watch out!

The next example, and the one that confused me the most when I first started implementing coverage, is the diagram below “module_merge.sv”. This example defines the covergroup within the dut module. This is a perfectly legal thing to do, but you will see that even if you setup the union merge you will not get the result you may think you are getting.

Figure 6:Structural Diagram of module_merge.sv

What you see below is that Mentor Questa did not pull out what I called the “overall coverpoint” that was named c1_cg::x as it did before. It doesn’t know that the duta and dutb covergroups are the same; therefore, it can’t pull out the covergroup c1_cg as a shared coverage component.

Figure 7:Mentor Questa View of Covergroups in module_merge.sv

You cannot do a union merge or a weighted average merge if you define your covergroup this way. The reasoning why the covergroups cannot be merged is best described in Dave Rich’s blog post “SystemVerilog Coding Guidelines: Package import versus `include.” The problem with merging these has to do with the type equivalence of a covergroup. The covergroup type gets a unique class name prefixed by the module instance name, which names the covergroups differently and won’t allow merging to occur. The more robust way of doing this example is to define a covergroup within a package and then import the covergroup into the module; which will allow merging of coverage across the instances of the covergroup.

Merging Systemverilog Covergroups for Efficiency

So far, we are merging covergroups and, in all cases, keeping track of the covergroup instances INST (the Mentor Questa notation) data as well. This is perfectly fine if you have only a few repeated instances. But, what if you have thousands of instances in your design? The coverage database can get bloated. Bloated, being that you have redundant data if all you want to know is the union of the total coverage.

If you just want to have a single number, the union, to represent your coverage, you can turn off an option for the covergroup:

option.per_instance = 0

type_option.merge_instances = 1

Figure 8:Structural Diagram for efficient_merge.sv

Figure 9:Mentor Questa View of Covergroups in efficient_merge.sv

In Figure 9 we can see there is no longer any INST data being kept. You just get a single number.

Why do it

The reason to do this is to reduce the amount of data that the coverage database has to hold. Imagine having 1024 cores in your design that each has an instance of your covergroup in them. You would have 1024 INST values plus the “overall covergroup”.

There are also problems when merging across simulations. If you have to merge those 1024 cores’ covergroups, the simulator is going to update each of the 1024 covergroups INST coverage data as well as calculating the “overall covergroup” for every simulation. That takes a lot more time than just updating the bins of the “overall covergroup”. If you have multiple layers of environments and test benches you have to drag along all of the covergroup INST data with you too; you would likely also have to do tricks in your simulator to normalize the path to the INST coverage to merge the coverage databases properly.

There are times when it makes sense to keep INST data; such as, if you need to answer the question: “did you send command x to every core in the design?” That question could not be answered without INST data. Right now, with this implementation, you could only answer with: “we sent the command x to at least one core in the design and we sent that command n times.”

There is a balancing act of keeping the tools and coverage database reasonable and meeting the completeness requirements.

The Table of Covergroup Possibilities

The table below describes the four types of coverage explored in this article: Union Merge with INST, Weighted Average Merge, Only Instance Coverage, and Union Merge without INST with the required values of the covergroup options and where the covergroup is defined. (A grey “-“ means the value could be any value including not defined all.

Figure 10 Table of Covergroup Possibilities
option.
per_instance
type_option.
merge_instances
covergroup defined
inside a module
Union Merge with INST
union_merge.sv
1 1 0
Weighted Average Merge
weighted_merge.sv
0 0 0
Only Instance Coverage
module_merge.sv
- - 1
Union Merge w/o INST
efficient_merge.sv
0 1 0


Summary

This article through: source code, diagrams, and screen captures from Mentor Questa showed how the choices in implementing a covergroup effect what data will be available in the coverage database. The covergroup choices that can be made are summarized in a provided table that allows for quick reference of the common ways of configuring covergroups that will meet the required need. Covergroups are a powerful verification tool to see what conditions the design has encountered. Informed with a few examples of how the simulator uses the configuration options of covergroups, your next covergroup will be even more effective at capturing exactly what you need to verify.

References

 
Online Chat