Verification Guild
A Community of Verification Professionals

 Create an AccountHome | Calendar | Downloads | FAQ | Links | Site Admin | Your Account  

Login
Nickname

Password

Security Code: Security Code
Type Security Code
BACKWARD

Don't have an account yet? You can create one. As a registered user you have some advantages like theme manager, comments configuration and post comments with your name.

Modules
· Home
· Downloads
· FAQ
· Feedback
· Recommend Us
· Web Links
· Your Account

Advertising

Who's Online
There are currently, 67 guest(s) and 0 member(s) that are online.

You are Anonymous user. You can register for free by clicking here

  
Verification Guild: Forums

 Forum FAQForum FAQ   SearchSearch   UsergroupsUsergroups   ProfileProfile  ProfileDigest    Log inLog in 

Implementing Functional Coverage in Vera 6

 
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Verification Guild Forum Index -> Coverage
View previous topic :: View next topic  
Author Message
Newsletter
Original Contribution


Joined: Dec 08, 2003
Posts: 1107

PostPosted: Sat Aug 09, 2003 11:00 pm    Post subject: Implementing Functional Coverage in Vera 6 Reply with quote

(Originally from Issue 4.13, Item 8.0)

From: Mike Thompson Send e-mail

We are transitioning from Vera5.2 to Vera6 and are working to define
the optimal model for defining functional coverage in Vera6.0. The
new "embedded coverage groups" in Vera6 are attractive, but we wish to
keep our coverage model external to our class definitions. The reason
for this is that a given Verification Component (VC) may be
instantiated in multiple environments and have coverage goals unique
to each environment. One way of using embedded coverage groups with
this model is to extend the class and add embedded coverage to the
extension:

enum beer_brand = golden, guinness, harp, rickards;

class beer_c {
rand beer_brand brand;
}

class beer_cov_c extends beer_c {
event randomized;

coverage_group beer_cov {
sample_event = sync(ANY, randomized); // see post_randomize()
sample brand {
state s_golden (golden);
state s_guinness (guinness);
state s_harp (harp);
state s_rickards (rickards);
at_least = 10;
coverage_goal = 100; // goal for sample
}
coverage_goal = 100; // goal for group
}

// post_randomize() is automatically called after randomization
task post_randomize() {
trigger(ONE_BLAST, randomized); // emit for coverage
}
}

The above works, but now the environment has to instantiate
beer_cov_c, not beer_c. This has the potential of creating a
maintenance situation. Are there any better ideas out there?
Back to top
View user's profile
Newsletter
Original Contribution


Joined: Dec 08, 2003
Posts: 1107

PostPosted: Sun Aug 31, 2003 11:00 pm    Post subject: Implementing Functional Coverage in Vera 6 Reply with quote

(Originally from Issue 4.14, Item 4.0)

From: Frank Armbruster Send e-mail

Well, I don't know a better way to do this in Vera, but with Specman
and e, the solution is rather natural. This is due to the support of:

- aspect oriented programming and
- coverage group definitions per instance

The first feature enables you to keep your coverage model external to
your class definitions in the VC. You are able to define, or to
change, the coverage group outside of your original verification
component (VC) code.

You can load your coverage definition on top of the VC code, which
solves your maintenance issue.

Per-instance coverage enables you to define different flavours of the
same coverage group for each instance of the Verification Component.

At the end you always end up load only one set of files containing the
basic verification component (VC), plus one coverage configuration
file that contains all the specifics for each instance of your VC.

Taking your example it looks like:

// VC code: i.e. file: beer_vc_base.e
type beer_brand [golden, guinness, harp, rickards];

struct beer_c {
brand : beer_brand;
};

// What you do here:
// - use aspect orientation to define coverage outside of base class
// VC code: i.e. file: beer_vc_cover.e
extend beer_c {
event beer_cov;

cover beer_cov is {
// need 10 samples of each enum to report 100%
item brand using at_least=10, per_instance;
};

post_generate() is also {
emit beer_cov;
};
};

// What you do here:
// 1. - use aspect orientation to change coverage group outside of
// VC code
// 2. - use coverage per instance to define different bahavior for
// each instance of the VC in the environment

// your configuration file, i.e. beer_cover_config_project1.e
// a.) want to have more guiness and less harp :-)
// b.) leave the original definition of the group for all others
extend beer_c {
cover beer_cov(brand==guiness) is also {
item using also at_least=20;
};
cover beer_cov(brand==harp) is also {
item using also at_least=5;
};
};

The import file for your verification environment looks like:

import beer_vc_base; // VC base code
import beer_vc_cover; // VC base code

import beer_cover_config_project1; // your configuration

You have seperation of the concerns pretty natural and therefore you
can maintain them in seperate files.

- Frank Armbruster, Lead Consulting Engineer
Verisity Design
Back to top
View user's profile
Newsletter
Original Contribution


Joined: Dec 08, 2003
Posts: 1107

PostPosted: Sun Aug 31, 2003 11:00 pm    Post subject: Implementing Functional Coverage in Vera 6 Reply with quote

(Originally from Issue 4.14, Item 4.1)

From: Janick Bergeron <a href="/contact.php?to=yW3ASxI7BUj-24-2bF8B8-2bF"><img src="/modules/Forums/templates/subSilver/images/lang_english/icon_email.gif" alt="Send e-mail" title="Send e-mail" border="0" /></a>

You are wise to want to keep your functional coverage model separate
from your data model. The former is DUT or test-specific whereas the
latter is reusable between tests, environments and projects.

First, let's answer the question you are asking...

What you did so far is perfectly correct, except for your choice of
beer brands and the way you have addressed the race condition between
the triggering of the "randomized" event and the sampling of the
functional coverage group.

It would be better solved by using a regular trigger and an *ASYNC*
coverage sampling. That way, you'll correctly sample consecutive
randomizations of the same instance within the same simulation
timestep:

Code:

   class beer_cov_c extends beer_c {
      event randomized;
   
   
      coverage_group beer_cov {
          sample_event = sync(ANY, randomized) async; // <-- ASYNC sample
              sample brand {
                  state s_sleemans        (sleemans);
                  state s_upper_canada    (upper_canada);
                  state s_blanche_chambly (blanche_chambly);
                  state s_rickards        (rickards);
                  at_least = 10;
                  coverage_goal = 100; // goal for sample
               }
               coverage_goal = 100; // goal for group
          }
   
   
      // post_randomize() is automatically called after randomization
      task post_randomize() {
          trigger(randomized); // emit for coverage
      }
   }


If you have designed your generator to take advantage of the
polymorphism offered by Vera's OO programming model, having the
generator use beer_cov_c instead of beer_c is easy. First of all, you
need to write your generators (or any transactors that creates
instances of beer_c) to use a factory pattern. The factory should be a
public data member initialized with a default factory instance in the
constructor:

Code:

   class beer_brewery_c {
      beer_c recipe;

      task new() {
         this.recipe = new;
      }

      function beer_c brew() {
         void = this.recipe.randomize();
         brew = new this.recipe;
      }
   }


Your environment instantiates the brewery in the environment
(preferably near a university), and supplies any recipe you wish, such
as the one containing your coverage model:

Code:

   class environment {
      brewery_c micro;

      task new() {
         beer_cov_c beer_with_cov = new;
         micro = new;
         micro.recipe = beer_with_cov;
      }
   }


The nice thing with this approach is that your individual testcases on
the environment can supply their own (different or augmented) recipes,
or supply different recipes to different breweries...

Code:

   class lager_c extends beer_cov_c {
      constraints lagers_only {
         brand in {sleemans; upper_canada};
      }
   }

   program testcase {
      environment pub = new;
      lager_c lager_with_cov = new;

      pub.micro.recipe = lager_with_cov;
      repeat (1000) {
         beer_c bottle = pub.micro.brew();
         ...
      }
   }



However, I think I need to address what I think you want, not what you
asked for...

Functional coverage should be associated with components of your
environment that have a static lifetime throughout a simulation
(i.e. get created at the begining and be destroyed only at the end)
such as transactors, bus-functional models, generators and DUT
signals. It should not be associated with the data that flows through
them or that is being covered.

If functional coverage groups are ultimately instantiated in data
objects, there will be several thousands instances, each recording few
information items. The only useful analysis is through a cumulative
analysis. Cross coverage with a stream identifier (if available) would
be the only mechanism for differentiating functional coverage on
different data streams.

If the functional coverage groups are ultimately instantiated in
transactors, they have a static lifetime throughout the simulation.
They record a lot of information about the hundred of data items that
flow through the transactor. Instance-specific metrics can be analyzed
for stream-specific coverage information. Cumulative metrics can also
be used to analyze global coverage.

Furthermore, associating functional coverage with randomization may
yeild unreliable coverage data: does the fact that an object instance
has been generated implies that it has been transmitted, as-is, to the
DUT? What if errors were subsequently injected in the data on its way
to the DUT? What if the test was aborted before? Functional coverage
should be collected as close as possible to the DUT (ideally from a
monitor/checker stack directly on the primary DUT signals) where the
relevant context information is available.

Using your example above, I would associate the functional coverage
with the drinkers, not the beer. I could then easily analyze who drank
what and how many and if they were drunk enough to be admitted to the
fraternity. Using cumulative metrics, I could see how much beer was
consumed at the party, reguardless of who drank it.


Next, to keep the functional coverage model separate from the
transactor models, functional coverage groups should be encapsulated
in their own coverage object. Each object contributes its information
model to the overall coverage model.

Code:

   class beer_cov_c {

      coverage_group beer_cov {

              sample brand {
                  state s_sleemans        (sleemans);
                  state s_upper_canada    (upper_canada);
                  state s_blanche_chambly (blanche_chambly);
                  state s_rickards        (rickards);
                  at_least = 10;
                  coverage_goal = 100; // goal for sample
               }
               coverage_goal = 100; // goal for group
          }
   }


Note that I have left out the sampling portion and only coded the
information model portion of the coverage group. That's because
sampling should be decoupled from the coverage information model, as
they may not be directly compatible. For example, the functional
coverage for a FIFO would be implemented by sampling the value of the
READ and WRITE pointers after each push and pop operation. However,
the coverage information model would be the FIFO occupancy, computed
from the difference between the two values (modulo the FIFO
size). Furthermore, the computed value would be assigned into one of 5
states: empty, near-empty, neither, near-full, full. The functional
coverage groups are coded to ease the interpretation and analysis of
the report, reguardless of the sampling.

Next, you must bridge the functional coverage information model with
the sampling mechanism. You have to decide what data is available to
be sampled, where, and at what point in time is it valid. The
functional coverage class should have a sampling interface designed to
match the available data and thus be easy to use. That interface can
be implemented using methods (virtual or not), data members and Vera's
rich coverage group sample event sets.


For example, assume your drinker is originally modeled as:

Code:

    class drinker_c {
       task drink(beer_c bottle) {
          ...
       }
    }


with the following original environment:

Code:

   class environment {
      brewery_c micro[4];
      drinker_c drinker[4];

      task new() {
         shadow integer i;
         for (i = 0; i < 4; i++) {
       this.micro[i] = new;
            this.drinker[i] = new;
            fork
          repeat (100) {
                  beer_c bottle = this.micro[i].brew();
                  this.drinker[i].drink(bottle);
               }
            join none
         }
      }
   }


The data interface for the coverage object can be implemented using a
method that, when invoked, will cause the supplied arguments to be
sampled and added to the coverage model.

Code:

   class beer_cov_c {

      local brand_e brand;
      local event   cover_it;

      task cover(beer_c bottle) {
         this.brand = bottle.brand;
         trigger(this.cover_it);
      }

      coverage_group beer_cov {

              sample brand {
            sample_event = sync(ALL, this.cover_it) async;
                 ...
               }
          }
   }


The DUT-specific coverage object can then be inserted in the original
environment:

Code:

            fork
          repeat (100) {
                  beer_c bottle = this.micro[i].brew();
                  this.beer_cov[i].cover(bottle);
                  this.drinker[i].drink(bottle);
               }
            join none


Given that functional coverage is going to be used, the transactor
themselves should be designed to offer suitable interfaces to allow
user-defined functional coverage sampling at relevant points. Many
mechanisms can be used.

The simplest mechanism is to use virtual methods that can be extended
with pre- or post-execution statements:

Code:

    class drinker_cov_c extends drinker_c {
       beer_cov_c beer_cov;
       
       virtual task drink(beer_c bottle) {
          this.beer_cov.cover(bottle);
     super.drink(bottle);
       }
    }


But that simple mechanism only works if there exists a virtual method
with the proper level of granularity to be overloaded. If the useful
information to be sampled is generated internally to a thread embedded
in a while(1) loop, it will not be so nicely visible. Transactors
should provide mechanisms explicitly designed to give access to
relevant information, internal or external. For example, a public data
member and an event signalling its validity could be used:

Code:

    class drinker_c {
       beer_c bottle;
       event  drinking;

       task drink(beer_c bottle) {
          this.bottle = bottle;
          trigger(this.drinking);
          ...
       }
    }


The advantage is that any number of functional coverage objects (or
scoreboards for that matter) can hang off the interface and sample the
data as required, without modifying or extending the original
transactor:

Code:

   class beer_cov_c {

      local drinker_c drinker;

      task new(drinker_c drinker) {
         this.drinker = drinker;
      }

      coverage_group beer_cov {

              sample brand (drinker.bottle.brand) {
            sample_event = sync(ALL, this.drinker.drinking) async;
                 ...
               }
          }
   }


Personally, I prefer to provide callback methods to give a
well-defined and well-controlled interface at relevant execution
points in transactors. These callbacks can be extended to have
user-defined code (such as coverage sampling, scoreboard integration
or error injection) executed:

Code:

    class drinker_callbacks {
       virtual drinking(beer_c beer) {}
    }

    class drinker_c {
       drinker_callbacks cb[$];

       task register_callback(drinker_callbacks cb) {
          this.cb.push_back(cb);
       }

       task drink(beer_c bottle) {
          foreach (this.cb, i) {
             this.cb[i].drinking(bottle);
          }
          ...
       }
    }


DUT-specific coverage can then be added in the environment:

Code:

   class drinker_coverage extends drinker_callbacks {
       beer_cov_c beer_cov;
       virtual drinking(beer_c beer) {
          this.beer_cov.cover(beer);
       }
   }

   class environment {
      brewery_c micro[4];
      drinker_c drinker[4];

      task new() {
         shadow integer i;
         for (i = 0; i < 4; i++) {
       this.micro[i] = new;
            this.drinker[i] = new;
            {
               drinker_coverage dc = new;
               this.drinker[i].register_callback(dc);
            }
            fork
          repeat (100) {
                  beer_c bottle = this.micro[i].brew();
                  this.drinker[i].drink(bottle);
               }
            join none
         }
      }
   }


and test-specific coverage can then be further added in the
environment as well:

Code:

   class test_coverage extends drinker_callbacks {
       beer_cov_c beer_cov;
       virtual drinking(beer_c beer) {
          this.beer_cov.cover(beer);
       }
   }

   program testcase {
      environment pub = new;
      {
         test_coverage tc = new;
         this.drinker[i].register_callback(tc);
      }
      ...
   }


In summary:

+ Keep coverage separate from your data and transactor models
+ Associate coverage with static testbench functions
+ Define the coverage group to facilitate analysis
+ Design the coverage class interface to facilitate sampling
+ Design your transactors to allow for coverage extensions

- Janick Bergeron, Principal R&D Engineer
Synopsys Inc.
Back to top
View user's profile
Newsletter
Original Contribution


Joined: Dec 08, 2003
Posts: 1107

PostPosted: Sun Sep 14, 2003 11:00 pm    Post subject: Implementing Functional Coverage in Vera 6 Reply with quote

(Originally from Issue 4.15, Item 5.0)

From: Chris Spear Send e-mail

One small note - if you make brand to be an enumerated type, you won't
have to define the states in the sample expression - Vera will do it
for you.

class beer_c extends beer_c {

enum E_BEER {SLEEMANS, UPPER_CANADA, BLANCHE_CHAMBLY, RICKARDS};

E_BEER brand;
}

class beer_cov_c extends beer_c {
event randomized;

coverage_group beer_cov {
sample_event = sync(ANY, randomized) async; // <-- ASYNC sample
sample brand {
at_least = 10;
coverage_goal = 100; // goal for sample
}
coverage_goal = 100; // goal for group
}
}

Bottoms Up!

- Chris Spear, Synopsys
Back to top
View user's profile
Display posts from previous:   
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Verification Guild Forum Index -> Coverage All times are GMT - 5 Hours
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Verification Guild ? 2006 Janick Bergeron
Web site engine's code is Copyright © 2003 by PHP-Nuke. All Rights Reserved. PHP-Nuke is Free Software released under the GNU/GPL license.
Page Generation: 0.142 Seconds