Verification Martial Arts: A Verification Methodology Blog

Archive for December, 2010

Verification in the trenches: A SystemC implementation of VMM1.2

Posted by Ambar Sarkar on 16th December 2010

Dr. Ambar Sarkar, Chief Verification Technologist, Paradigm Works Inc.

VMM1.2 class library is now also implemented in SystemC(VMM-SC).

Will it help your project? Please take a few minutes to consider this, especially if you have been using or thinking about C/C++/SystemC models in your environment.

Following are the some use cases that I have come across or can anticipate among our clients. With each use case, I have put down some thoughts on why VMM-SC may(or may not) be of any benefit. Do you agree?

Use Case 1. Using SystemC as the primary verification language for unit level verification

Many teams today use SystemC as their primary verification language for all unit and system-level tests. However, with the increasing popularity of SystemVerilog, they often adopt a hybrid model. They do use C++/SystemC as the primary language for testbench creation, but complement it using SystemVerilog language features for assertions, functional coverage analysis, and constrained random generation.

Such teams often consider moving to SystemVerilog based environments, but are concerned about the reuse of their existing code, and cannot justify the ROI against the resources needed to make the transition to SystemVerilog completely. Ideally, they would like to have a multi-language solution that can mix and match SystemVerilog and SystemC components.

For this class of users, VMM-SystemC can ease their adoption of a multi-language solution. First, they can create newer environments in VMM-SC, and encapsulate preexisting VIP within VMM-SC components using thin wrappers. Once transitioned to VMM-SC, they can easily interoperate with other VMM-SV based environments and components. As an added benefit of the SystemVerilog-SystemC interoperability, the VMM-SV applications such as RAL/Scoreboarding etc. can now be made available to the SystemC side.

Use Case 2. Using SystemC as the primary verification language for system-level verification

Some teams decide to write their system level verification environments primarily in C/C++, as it is often used by the SW team or in the lab. Depending on the sophistication of these teams, these environments may range from being simple directed C testbenches to those that are highly sophisticated and use SystemC. Adopting VMM-SC as the system-level methodology can make it easier to cleanly pull in various environments and components by encapsulating them as VMM objects and making them work together taking advantage of VMM-SC support for phasing, TLM ports etc.

For this class of users, adopting VMM-SC as the top-level glue environment will be a big win.

Use Case 3. Planning to move to SystemVerilog and adopt industry standard practices.

Often, teams are willing to start from scratch for new projects as they realize their existing environments have become outdated and simply cannot scale. With the maturity of SystemVerilog, they would likely adopt it as the primary HVL language of choice. However, they would still like to preserve a number of previously developed components.

This is an ideal application for using VMM-SC. Pre-existing transactors can be converted to VMM-SC components by deriving them from vmm_xactor/vmm_unit classes(see 4.3 below). The new environment can now be written in VMM-SV, which can pull in the legacy components using VMM-SC/SV interoperability solution. All VMM features, such as cross language option setting, communicating transactions in a language agnostic way between source and target components using TLM, coordinated phasing etc. make the SystemVerilog adoption process much easier.

Use Case 4.  Using SystemC to develop ESL environments and reference models.

There are users who are primarily interested in creating models at various levels of abstraction. These models can be used in multiple applications, such as performance modeling and architectural exploration. In some cases, such models may not be of sufficient detail to use in functional verification.

At the minimum, such models should be created using TLM2.0 ports to enable easy plug and play in ESL contexts. Adopting VMM-SC as the primary methodology for developing such components can be of benefit, since it offers a rich set of features that can help in controlling and coordinating the components, while maintaining strong interoperability with verification environments.

 

So, to which one of these use cases do you belong? Any other use case that I missed?

This article is the 9th in the Verification in the trenches series. Hope you found this article useful. If you would like to hear about any other related topic, please comment or drop me a line at ambar.sarkar@paradigm-works.com. Also, if you are starting out fresh, please check out the free VMM1.2 environment generator at http://resourceworks.paradigm-works.com/svftg/vmm .

Posted in SystemC/C/C++, Transaction Level Modeling (TLM) | 1 Comment »

How you can figure how you configure (a VMM testbench): Part 2

Posted by JL Gray on 13th December 2010

Jonathan Bromley, Verilab Inc, Austin, Tx

Part 2: An interesting way to use VMM 1.2’s configuration features

Part 1

examined the use of descriptor objects to pass configuration information around the testbench.  In VMM 1.2, though, the available toolkit has grown dramatically, inviting us to reconsider our approach.
 

 

Configuration in VMM 1.2

VMM1.2 provides powerful additional configuration features.  Other articles in this series, Verification in the trenches: Configuring your environment using VMM1.2 and Sharing RTL Configuration with the Testbench

, offer a great overview of the new mechanisms and how to use them.  The vmm_opts facilities make it easy to control configuration values deep in the test environment from top-level code, or even from scripts thanks to the built-in command-line and file reading features.  Within any vmm_unit you can use the vmm_unit_config macros to grab configuration values that were set up from the top level, picking up default values if the values were not set.  Within any vmm_object you can use vmm_opts::get_object_***() function calls to discover whether a given option has been explicitly set, and set a variable appropriately.
Given this new ability to set and grab arbitrary configuration values by name, is there still a role for the trusty configuration object?  I believe there is, for at least two reasons.

 

Legacy components

First, you will surely have some traditional transactors and subenvs in your VMM1.2 environments.  They will use config objects.  The two mechanisms can happily coexist, using VMM1.2 options to populate fields of the config objects before passing them to the transactors that need them:
class MagicToEth_gp extends vmm_group;
  `vmm_typename(MagicToEth_gp)
  MagicPacketXactor magic_xactor;
  EthernetXactor eth_xactor;
  bit has_voodoo;
  …
  function void build_ph();
    …
    MagicPacketXactor_cfg magic_cfg = new;
    …
    magic_cfg.SupportsVoodooMode = has_voodoo;
    magic_xactor = new(…., magic_cfg);
  endfunction
  …
  `vmm_unit_config_boolean(has_voodoo,
         “turn this on to enable MagicVoodoo”,
         0, MagicToEth_gp)
endclass

 

Configure just the variables you care about

Configuration using the new mechanisms is wonderfully flexible, but it needs to be thought through.  Used carelessly it can turn your testbench configuration into a grab-bag of hundreds of individual configuration values with no obvious relationships among them.  Using objects to group together a bunch of related configuration values allows you to use constrained randomization to enforce relationships among the values that were not specified from above, so that you can specify just those values you want to nail down and allow the remaining values to be randomized.  Making that work requires just a little bit of extra effort, and just a tiny bit of guru-level SystemVerilog randomization.
 

 

Selective configuration and randomization

For example, let’s go back to the configuration of our MagicPacket transactor.  We already have a configuration object for it – but now we must take care to derive from vmm_object so that the hierarchical configuration mechanism knows about it:
class MagicPacketXactor_cfg extends vmm_object;
  rand shortint unsigned MaxLength;
  rand bit SupportsVoodooMode;
    constraint c_valid_packet {
      if (SupportsVoodooMode) { MaxLength inside {[64:16384]}; }
      else                    { MaxLength == 1024; }
    }

Now we can play some interesting games with VMM configuration and SystemVerilog constrained randomization to make the configuration as flexible as possible:

function new(string name, vmm_object parent = null);
  bit is_set;
  super.new(parent, name);  // as usual for any vmm_object
  SupportsVoodooMode = vmm_opts::get_object_int(
    is_set,   // reports whether the option was set from above
    this,     // we’re interested in options applying to this object
    “SupportsVoodooMode”,  // identifying name of the option
    0,        // default value
    “some helpful documentation text” );
  if (is_set)  // This variable was explicitly configured.
               // Disable randomization of this variable.
    SupportsVoodooMode.rand_mode(0);
  MaxLength = vmm_opts::get_object_int(
    is_set,   // reports whether the option was set from above
    this,     // we’re interested in options applying to this object
    “MaxLength”,  // identifying name of the option
    1024,     // default value
    “some helpful documentation text” );
  if (is_set)  // This variable was explicitly configured.
               // Disable randomization of this variable.
    MaxLength.rand_mode(0);
endfunction

Why all this complexity?  Because of the huge flexibility it gives us.  The key is in understanding what happens later, when we randomize() this object.  If neither of the options was set, then rand_mode is true (its default) on both variables and randomization proceeds as normal.  If just one of the variables was configured, that variable now has rand_mode(0) and so it will not be altered by randomization – you have fixed it from the options.  The other variable is randomized, but respecting all constraints imposed by the variable that is already configured.  For example, suppose we configure MaxLength=5000 from the command line.  Thanks to our ingenious constructor code, that variable is fixed and won’t be randomized.  Other variables in the object are randomized, though, so the SystemVerilog constraint solver tries to find a value for SupportsVoodooMode that will satisfy the constraints.  Of course, that value must be true because of the if…else constraint – a false value is possible only if MaxLength is equal to 1024.

Finally, suppose we configure both variables from option values.  Now, any attempt to randomize() the configuration object will have no effect on the variables, because randomization has been disabled for both.  But randomization is still useful to us, because it will give a constraint violation error if the two options have been given contradictory values.

Getting the balance right is tricky – if you try to enforce too tight a structure on your collections of configuration data it will prove inflexible as verification requirements change, but leaving the configuration as a bunch of scattered, unrelated variables will soon become a maintenance nightmare.  Creative use of get_object_* options methods and randomization can offer a useful new set of compromises between flexibility and ease of deployment, by allowing you to specify just a few of an object’s configuration values and allowing randomization to choose a consistent set of values for the remaining variables.

Posted in Configuration | No Comments »

How you can figure how you configure (a VMM testbench): Part 1

Posted by JL Gray on 7th December 2010

Jonathan Bromley, Verilab Inc, Austin, Tx

Part 1: Using configuration objects


Each VMM transactor class should have a companion config class, where you create data members for all critical “vital statistics” of the transactor.  Just before constructing the transactor object, an enclosing subenv would create one of these config objects and then pass it to the transactor as a constructor argument.  Every transactor should also provide a reconfigure method allowing other parts of the environment to set up a new configuration for it at any time.

 

Objects to control objects
Here’s an imaginary example of a transactor configuration class:

class MagicPacketXactor_cfg;
  rand shortint unsigned MaxLength;
  rand bit SupportsVoodooMode;
    constraint c_valid_packet {
      if (SupportsVoodooMode) { MaxLength inside {[64:16384]}; }
      else { MaxLength == 1024; }
    }
endclass

Note how the class contains rand data members for all the transactor’s configurable attributes, and also has constraints so that it will yield a meaningful and interesting set of configuration values when randomized.  Of course, further constraints can easily be added to enforce specific configurations.

The corresponding transactor code might be something like this:

class MagicPacketXactor extends vmm_xactor;
  …
  local MagicPacketXactor_cfg cfg;
  function new( … // usual VMM constructor arguments, and then…
                MagicPacketXactor_cfg cfg = null);
    …
    if (cfg == null) begin
      cfg = new;
      cfg.randomize();
    end
    this.cfg = cfg;
    …
  endfunction
  function void reconfigure(MagicPacketXactor_cfg cfg);
    this.cfg = cfg;
  endfunction

In the transactor’s main() task, or elsewhere, we can now do things like

if (this.cfg.SupportsVoodooMode)
  castSpell(…);

It’s really important to note that the transactor takes no responsibility for setting up its own configuration.  It merely assumes that a configuration object has been supplied from outside, and then makes use of the values within that configuration object.  If the enclosing environment fails to provide a configuration object, as a last resort the transactor constructs its own randomized configuration.

This is a good example of the notion of encapsulating configuration in a single object.  By gathering all important attributes of a component into a class:
•    we can pass the entire configuration around as a single object or reference;
•    it becomes easy to write randomization constraints that establish relationships among configuration attributes;
but most important of all:
•    it is straightforward to assemble several configuration objects into a single, larger configuration object suitable for use at the next level up the hierarchy– typically at the vmm_subenv level.

Environment configuration
A verification environment for a MagicPacket-to-Ethernet bridge will of course contain both MagicPacket and Ethernet transactors.  The environment or subenv gets its own configuration class, containing references to configuration objects for both its transactors:

class MagicToEthernet_subenv_cfg;
  rand MagicPacketXactor_cfg magic_cfg;
  rand Ethernet_cfg eth_cfg;
endclass

Configuration proceeds in much the same way as for the transactors.  The environment’s constructor takes one of these objects as an argument, and then passes on its inner configuration objects to the corresponding transactors as it constructs them.  Reconfiguring the environment is equally straightforward:

class MagicToEthernet_subenv extends vmm_subenv;
  MagicPacketXactor magic_xactor;
  EthernetXactor eth_xactor;
  …
  function void reconfigure(MagicToEthernet_subenv_cfg cfg);
    magic_xactor.reconfigure(cfg.magic_cfg);
    eth_xactor.reconfigure(cfg.eth_cfg);
  endfunction

Finally, the code that launches your VMM test can create and populate all the necessary configuration objects, assemble them into environment-wide configuration objects, and pass them into constructors as needed.  It is also very easy to configure multiple transactors to match a common configuration simply by passing the same configuration object to all of them.

Testbench configuration vs. test configuration
The configuration mechanism we’ve explored is neat, powerful and straightforward.  However, it doesn’t offer very much help in separating the two rather different concerns of configuring the test environment and configuring the test.

Test environment configuration gets you the right structure – correct number of transactors, correct choice of active vs. passive transactors, choice of reference model or scoreboard – to match the chosen device-under-test.  Using that test environment, though, you probably wish to run a large and ever-growing battery of testcases as you develop new stimulus to hit elusive coverage objectives.  From a programming point of view, configuring the test is not very different from configuring the testbench –a matter of passing appropriate configuration objects to the constructors of various components.  From an organizational point of view, though, there are big differences.   Testbench configuration generally needs to be at least partly driven by parameters of the DUT and test harness, whereas testcase configuration is likely to be much more flexible and dynamic, and will vary from run to run.

When using configuration objects in this style, I’ve learnt the value of keeping a strict separation between these two kinds of configuration to simplify those organizational concerns.

In the second part of this article we’ll look at the impact of the extensive new configuration facilities in VMM 1.2, and explore a novel way to manage groups of inter-related configuration options.

 

Posted in Configuration | 1 Comment »