Monday, June 29, 2015

Some More Ideas on Coverage Extendability

In the previous post, we looked at how to use policy classes as parameters for a highly configurable coverage collector. This allows us to easily implement different variations of what bins to ignore. If you haven't read that one yet, I'd encourage you to do so before continuing with this post.

Truthfully, using policy classes wasn't the first approach I tried. I had to go down a wrong path and fail before coming to that idea. Let's have a look at how I started out. You may remember the following code snippet showing the use of a function to specify what values are supposed to belong to a certain bin:

Since I had defined the covergroup for the CPU instructions inside a coverage collector class, it made the most sense to have the ignore bin function definition also part of that class. At the same time, class member function are overridable... What if we defined the ignore expression inside a virtual function? Inside the base class we don't want to ignore anything yet, so the function would be empty. We could then extend this class and override this function to ignore MUL and DIV. The code for the generic coverage collector would look like this:

Unfortunately, this wasn't supported by the simulator. After a bit more fiddling I gave up on trying to use functions with the with syntax, since that didn't seem to be supported. I wasn't about to give up yet. I figured I could pass the list of ignore bins as a constructor parameter to the covergroup. Instead of having a function that tells us whether a certain value should be ignored we could write a function that returns a list of values that should be ignored:

That also gave some cryptic error. If it didn't like passing the list of ignore bins as a constructor argument, maybe it would go for having it as a class member. After some more trial and error, I eventually got it to work by declaring it as a static field:

For some whatever reason this worked, but only if the ignore list was static. Ignoring the fact that it's a hack, now we're back in business! We can declare our coverage collector sub-class that ignores MULs and DIVs:

After firing up the simulator GUI and having a look at the generated bins, we'll get a big surprise. We'll see that MUL and DIV are, in fact, not being ignored. What's the reason for this? Well, remember that we defined the get_operation_ignore_bins() function as virtual and that we are calling this function from inside the constructor. It turns out that calling virtual functions from constructors is generally frowned upon inside the programming community, for reasons that I'm not going to list here. What's happening in our case is that the covergroup is being constructed inside the base class's constructor. The version of the bin generation function that's getting called is the one from inside that same class. Conceptually, at that point, the object being constructed is still of type cov_collector, not no_mul_cov_collector. The same behavior is also specified in the C++ standard.

Bonus points go to whoever noticed from the get-go that this whole thing was a bad idea!

In any case, we should ignore calling a class's virtual methods from its constructor altogether. This is because I've seen other simulators that will behave differently from what I've described above. I seem to remember reading that SystemVerilog should work like C++ in this respect, but I couldn't find any reference inside the LRM. If anyone could point out the appropriate section in the comments, that would be great!

I guess we'll have to scratch that idea... Not necessarily. We can still make use of polymorphism, but we just need to take the method that generates the bins out of the coverage collector class and put it into some other class. We'll call this a policy class:

We can now subclass the ignore bin generation policy as we like and pass different types of objects to the coverage collector. The simulator will take care at run time that the appropriate get_operation_ignore_bins() function gets called. For example, to create a coverage collector that ignores MULs and DIVs we can create a policy that specifies those as ignore bins:

You may notice that this approach is remarkably similar to the one we used in the previous post. Before, the type of policy that the coverage collector used was specified at compile time, by passing the policy class as a parameter and relying on static methods. Now, the coverage collector has to wait until run time to get the policy via a constructor argument and it makes use of virtual methods and polymorphism. We could describe the former approach as static and the latter as dynamic.

The dynamic approach seems to suffer from worse tool support, though. We've only got it running by employing various hacks. Barring that we could say that the two are, for all intents and purposes, equivalent.

Or are they? Make sure to read the next post where we'll explore how the two styles fare in the context of a UVM environment.

2 comments:

In the coverage collector, for the arrays containing the ignore bins (the ones we defined static due to simulator limitations), some simulators might complain that they can't be used inside the coverpoint bin definitions, because they aren't constant (which is true). In those cases, try replacing 'static' with 'constant' and it should work.

About

I am a Verification Engineer at Infineon Technologies, where I get the chance to work with both e and SystemVerilog.
I started the Verification Gentleman blog to store solutions to small (and big) problems I've faced in my day to day work. I want to share them with the community in the hope that they may be useful to someone else.