Monday, June 29, 2015

Even More Ideas on Coverage Extendability

In parts one and two of this series we looked at how to use policy classes to implement an extendable coverage model, where ignore bins can be tweaked. The first post looked at how to use these policies as parameters for a parameterizable coverage collector (the so called static flavor), while the second post focused on using them as constructor arguments for the collector (the so called dynamic flavor).

Since SystemVerilog and UVM have become almost synonymous terms, let's look at how these two approaches for implementing coverage extendability interact with UVM features such as the factory.

Typically, coverage collectors are UVM subscribers that are connected to monitors. Let's start as before with the static implementation, that relies on a parameterizable class:

We had the foresight to create our coverage collector using the factory. Now, when we want to verify the CPU variant without a multiplier, we just need to make sure that we create an instance of the appropriate type by setting a factory override:

While this code would compile, it will fail at run time. The reason is because even though no_mul_cg_ignore_bins_policy is a sub-class of cg_ignore_bins_policy, this relationship doesn't trickle down through the parameterization of cov_collector. The two different variants of the coverage collector are totally unrelated to each other from an OOP point of view and cannot be swapped for each other.

The classic solution to this problem is to create a common base class from which the parameterizations will inherit:

This will work, but it's not terribly elegant in my opinion. We needed to create the extra level of class hierarchy just so we could get the compiler to do our bidding. I'm also not particularly thrilled with directly starting out with a factory override in the base environment.

The dynamic version of the code is much more straightforward. This time we need to make sure we can switch out policy class instances for each other, so we'll need to make them UVM objects:

In the vanilla SystemVerilog example from the previous post we passed an instance of the policy class as the coverage collector's constructor argument. This isn't possible in UVM because a component's constructor has a fixed signature, taking only its name and its parent. However, there isn't anything stopping us from creating a policy object internally:

You may have noticed something slightly off about the previous code snippet. Even though policy is a uvm_object at heart, we pass a uvm_component parent to the create(...) call. What that does is create the policy object inside the coverage collector's scope. This makes it possible to override the policy class type using the following instance override:

A type override would have also worked, but this is a nifty little trick to know. The dynamic variant ended up being far shorter than the static one. I also like this one more because it feels closer to the Gang of Four's "favor object composition over class inheritance" principle. The main disadvantage is that, as we saw last time, it might suffer from more simulator limitations.

As always, you can download the code for this and the previous two posts from SourceForge.

I hope this series of posts convinced you that a certain degree of coverage extendability is also possible in SystemVerilog. As with all other things, as opposed to e, this requires pre-planning. Because of the overhead required, this approach might not catch traction for many testbenches, but it is ideal when developing UVCs. There are still some kinks that need ironing: the syntax for coverpoints isn't fully described and simulator support might be limited for now. While this approach currently resembles a dangerous trek through uncharted waters of the LRM, once the seas of SystemVerilog vendor interoperability calm down it's sure to really start to shine.

5 comments:

Nice series.I like your e - view, and the way you're trying to recreate the capabilities you miss.

I use a lot of macro generated types, where the basic covergroup with coverpoint per item are defined. when I want to add a specific cross I inherit the types, add my cross, and sure, factory override it!your post got me thinking to add cross coverage to already defined covergroup using the existing coverpoints,

Another possibility to use dynamic "Policy" (dynamic is called algorithm pattern, I think) is to put the object in a configuration object. which is also done in build phase. Not that I'm against overrides, I would have done the override straight from the top, without going into the build. Objects/Components are heavy with factory infrastructure that propagates the override into your object, your super.build() is searching this list anyway. So why not use it? declare that override from the test, without touching your build phase.

Looking back at the last example I notice that it isn't really that good. In a normal use case, you'd have the coverage collector already created in the base (generic) environment. Somewhere in the testbench we'd need to replace the policy object. That could be done anywhere: in the build of the test, of the testbench env, etc. Like you point out, the policy could also be a part of the config class and the testbench would override it when it's configuring the CPU env.

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.