Home > Community > Blogs > Functional Verification > using macros for repetitive coding tasks
 
Login with a Cadence account.
Not a member yet?
Create a permanent login account to make interactions with Cadence more conveniennt.

Register | Membership benefits
Get email delivery of the Functional Verification blog (individual posts).
 

Email

* Required Fields

Recipients email * (separate multiple addresses with commas)

Your name *

Your email *

Message *

Contact Us

* Required Fields
First Name *

Last Name *

Email *

Company / Institution *

Comments: *

Using Macros for Repetitive Coding Tasks

Comments(0)Filed under: Functional Verification, OVM, Coverage-Driven Verification, Specman, OVM e, Aspect Oriented Programming, AOP, tech tips, team specman, macros

For this post welcome guest blogger Hilmar van der Kooij. Hilmar is a Cadence Application Engineer for our Metric Driven Verification solutions. He has expertise in applying Metric Driven Verification both in simulation as well as formal analysis. Himar is based in beautiful Stockholm, Sweden. Besides living the Jetset Verification Lifestyle, he enjoys playing around with an old-fashioned film photocamera.

Thanks Hilmar for the great post!

Team Specman

Using Macros for Repetitive Coding Tasks

When writing advanced verification code, often we come across something that requires quite a few repetitive statements. What is more is that these repetitive statements may also be required in more than one area in our code thus resulting in more repetition.  If you're like me, you want to avoid these repetitive, error-prone, coding tasks. One great way to do this is with macros.

Maybe you haven't used macros, because they may appear a bit daunting at first, so let's have a look at a very simple example. Let's assume we want to cover a one-hot encoded set of grant lines from an arbiter. This set of grant lines is sampled by the monitor as a uint, and typical coverage code might look like this:

cover arbiter_grant_e is {
    item grants using ranges = {
        range([1], "Bit 0");
        range([2], "Bit 1");
        range([4], "Bit 2");
        range([8], "Bit 3");
        range([16], "Bit 4");
        range([32], "Bit 5");
        range([64], "Bit 6");
        range([128], "Bit 7");
        range([256], "Bit 8");
        range([512], "Bit 9");
        range([1024], "Bit 10");
        range([2048], "Bit 11");
        range([4096], "Bit 12");
    };
};

In my view, this becomes really old, really fast, especially when you have to do this in different places. So let's write a macro to assist you. This macro will create new code computed out of its input parameters, so a define as computed macro is what we'll write. A define as computed macro returns the code as a string and to start we'll need the covergroup and item we'd like to use as inputs:

define <one_hot_item'struct_member> "one_hot_item <group'name> <item'name>" as computed {
      result = append("cover ",<group'name>," is { item ", <item'name>, "; };");
};

This macro isn't complete yet, but let's have a look at what we've generated in these few lines. First we define a new struct member which is tagged one_hot_item. The input syntax we've provided so far requires our newly introduced keyword one_hot_item followed by a two names: One for the cover group, the second for the coverage item. The string that's returned is a basic concatenation of the keyword cover followed by the group name and the group will contain nothing but the item we give. In our previous example, the returning code of "one_hot_item arbiter_grant_e grants" would look like:

cover arbiter_grant_e is {
    item grants;

};

It doesn't look like much now, does it? But we have already written a significant part of the macro! The first thing we'll need to add are the ranges and while we're at it, why not specify the number of buckets needed as well?

define <one_hot_item'struct_member> "one_hot_item <group'name> <item'name> <size'exp>" as computed {

    result = append("cover ",<group'name>," is { item ", <item'name>, " using ranges = {");
    for i from 0 to <size'exp>.as_a(uint)-1 do 
        result = append(result,"range([",ipow(2,i),"]);");
    };
    result = append(result,"};};");

};

So what's happened here? We've added one additional parameter for our macro execution, the <size'exp>, which we'll use to generate the buckets. For this we used ranges, containing a single bucket representing the individual bit. To avoid code cluttering, we've omitted the nicer descriptions for the ranges:

define <one_hot_item'struct_member> "one_hot_item <group'name> <item'name> <size'exp>" as computed {

    result = append("cover ",<group'name>," is { item ", <item'name>, " using ranges = {");
    for i from 0 to <size'exp>.as_a(uint)-1 do 
        result = append(result,"range([",ipow(2,i),"], \"Bit ",i,"\");");
    };
    result = append(result,"};};");

};

 This macro will give you the result that we were looking for initially, but we aren't there yet. Why not make it a little more user friendly by also allowing extension of a cover group? Below we've added an option for the word extend:

define <one_hot_item'struct_member> "one_hot_item[ <extend'any>] <group'name> <item'name> <size'exp>" as computed {
   //set up the cover group definition using optional extension
   if (<extend'any> == "extend") {
      result = append("cover ",<group'name>," is also { item ", <item'name>, " using ranges = {");
   } else {
      result = append("cover ",<group'name>," is { item ", <item'name>, " using ranges = {");
   };
  
   //add the buckets
   for i from 0 to <size'exp>.as_a(uint)-1 do {
      result = append(result,"range([",ipow(2,i),"], \"Bit ",i,"\");");
   };
  
   result = append(result,"};};");
};

And why not add support for zero-one-hot while we're at it? It only makes our macro more user friendly.

define <one_hot_item'struct_member> "one_hot_item[ <extend'any>] <group'name> <item'name> <size'exp>[ <zero'any>]" as computed {
   //set up the cover group definition using optional extension
   if (<extend'any> == "extend") {
      result = append("cover ",<group'name>," is also { item ", <item'name>, " using ranges = {");
   } else {
      result = append("cover ",<group'name>," is { item ", <item'name>, " using ranges = {");
   };
  
   //add the buckets with optional bucket for zero_onehot
   if (<zero'any> == "has_zero_one_hot") {
      result = append(result,"range([0], \"No bit selected\");");
   };
   for i from 0 to <size'exp>.as_a(uint)-1 do {
      result = append(result,"range([",ipow(2,i),"], \"Bit ",i,"\");");
   };
  
   result = append(result,"};};");
};

Now it's time to use our new macro in some code. Let's assume we have an arbiter interface and want to add zero-one-hot coverage for the grant signals coming out of your arbiter to an existing coverage group, similar to the initial example:

extend my_arbiter_monitor_u {

    //this extends the arbiter_grante_e cover group with the grants signals in a zero-one-hot fashion

    one_hot_item extend arbiter_grant_e grants 13 has_zero_one_hot;

};

 

The final code is available as a download on the Functional Verification Shared Code Forum here: (http://www.cadence.com/community/forums/T/12340.aspx). It contains a script to start up specman and show a tiny demo, showing the generated code.

Happy coding!

Hilmar van der Kooij

Comments(0)

Leave a Comment


Name
E-mail (will not be published)
Comment
 I have read and agree to the Terms of use and Community Guidelines.
Community Guidelines
The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.