Verification Martial Arts: A Verification Methodology Blog

Archive for 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), VMM 1.2 | 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, VMM 1.2 | 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, VMM 1.2 | 1 Comment »

Communication Options in VMM 1.2

Posted by John Aynsley on 30th November 2010

 

John Aynsley, CTO, Doulos

Having given an example of transaction-level communication in VMM in my previous post, I am now going to say a little about each of the communication options available in VMM 1.2. The VMM user is presented with an array of communication options from which to chose, but which-is-which and which is best?

Blocking Transport

Blocking transport is used to send a single payload representing a transaction from an initiator to a target where very little timing information is required. Each transaction can have a begin time and an end time, but there is no more structure or detail to the lifetime of the transaction than that. It is possible to annotate timing information onto the b_transport call to offset the begin and end times of the transaction from the actual times as which the function is called and returned, but not to add further timing points or events during the lifetime of the transaction. The b_transport function is called with a transaction object as an argument, and returns only when the processing of that transaction at the target is complete. Blocking transport is the cleanest and simplest way to send a payload from A to B.

Non-blocking Transport

Like blocking transport, non-blocking transport is used to send a single payload representing a transaction from an initiator to a target. Unlike blocking transport, non-blocking transport allows significant events within the lifetime of the transaction to be modeled to whatever degree of timing granularity you like, and also allows multiple pipelined transactions to be in-flight at the same time. This is achieved by allowing multiple calls to nb_transport_fw and nb_transport_bw, in the forward and backward directions respectively, associated with a single transaction. The ability to model multiple timing points and pipelining comes at a cost in terms of the complexity of the interface, however. In the context of VMM, non-blocking transport is only of interest if you need to communicate with an existing SystemC TLM-2.0 model that uses this same interface.

Analysis Interface

The analysis interface is the third import to VMM from the TLM-2.0 standard. The analysis interface is used to broadcast a single payload to passive subscribers (aka observers or listeners). The defining characteristics of the analysis interface are firstly that there can be any number of subscribers to a single call and secondly that the subscribers are not permitted to modify the transaction object. Hence the analysis interface is ideal for distributing transactions to passive components such as checkers, scoreboards, and coverage collectors within a verification environment. The analysis interface is non-blocking and does not have any associated timing or status information.

Channels

Channels are the classic way to send payloads around a VMM verification environment. Unlike the three transaction-level interfaces described above, a channel is more than a function call. A channel is a FIFO buffer that can store multiple outstanding transactions. A transaction can be accessed by both producer and consumer transactors while it remains in the so-called active slot of the channel, and it is possible to model multiple timing points at that stage by having VMM notifications associated with the channel or with the transaction object. Channels are still the preferred mechanism used to model the timing and synchronization details of specific protocols within VMM, while blocking transport is preferred where a more abstract model is adequate.

Callbacks

In some respects callbacks play the same role as the analysis interface described above, while in other respects there are fundamentally different. Callbacks are like the analysis interface in the sense that they distribute function calls to any subscriber that has register itself with the transactor making the calls. They are different in that a callback is allowed to modify transactions passed as arguments to the function call whereas the analysis interface does not allow transactions to be modified. Also, callbacks are more flexible in the sense that both the callback function names and their argument lists are user-defined, which is not true of the analysis interface. In VMM, callbacks are the preferred mechanism where the behavior of a transactor needs to be modified by tweaking the contents of a transaction.

Notifications

VMM notifications are preferred for data-less synchronization where transactors need to synchronize without passing a payload. The analysis interface is preferred when passing around payloads. VMM notifications are also built into the transaction and channel base classes, so can be used to introduce additional timing points during the lifetime of a transaction when using VMM channels for communication as described above.

So, those are the six main choices for communication available to you in VMM. You can read more details in the VMM 1.2 Standard Library User Guide.

Posted in Communication, Transaction Level Modeling (TLM), VMM 1.2 | 1 Comment »

Planning for Functional Coverage

Posted by JL Gray on 23rd November 2010

JL Gray, Vice President, Verilab, Austin, Texas, and Author of Cool Verification

Functional coverage cures cancer and the common cold. It helps kittens down from trees and old ladies up the stairs. Functional coverage is the greatest thing since sliced bread, right? So all we need to do is put some in our verification environments and our projects will be a success.

Well… perhaps not.

Many teams I’ve worked with have struggled with various aspects of functional coverage. The area I’d like to address today is the part most engineers spend the least amount of time on – planning (as opposed to implementing) your functional coverage model. One of the critically important uses for functional coverage is to help us (engineers and project managers alike) know when we are done with verification. We all know it’s impossible to 100% verify a complex chip. But it is possible to define what “done” means for our individual teams and projects. The functional coverage model gives us a way to correlate what we’ve accomplished in the testbench with some definition of “done” in our verification plan.

What that means is that the verification plan itself is the key to understanding where we are relative to where we need to be to finish a project. It is the document that managers, designers, verification engineers and others can all interact and agree on what constitutes “good enough.” Verification plans often contain lists of design features and statements about what types of tests should be run to verify these features. Unfortunately, that is almost never sufficient to give stakeholders insight into what should be covered.

Verification plans need to contain a sufficient level of detail to meet the following goals:

  • 1. Allow key stakeholders who may not know anything about SystemVerilog to see what will be covered and then provide a platform for them to give feedback to the verification engineer.
  • 2. Allow this discussion to take place before any SystemVerilog code has been written.
  • 3. Allow an engineer other than the author of the document to understand and implement the coverage model.
  • 4. Allow the project manager to understand what coverage goals should be reached for the module to be considered verified.

Point 3 is important in ways some engineers may not realize. During my verification planning seminars I often ask students if they’ve ever heard of a project’s “truck factor”. Basically, a project’s truck factor is the number of people on the project who could get hit by a truck before the project is in trouble. A verification plan should specify coverage goals in enough detail that someone besides you (this could include the you 6 months from now who’s forgotten all relevant details about the block in question) can understand what the original intent of the document was. For example, some plans I’ve seen make comments like “test all valid packet lengths”. What are the valid lengths? They should be specified in the plan. Are there certain corner cases you think are important to cover? Make sure you call out this information with sufficient detail so that the project manager can tell if you’ve actually accomplished your goals!

Adding this extra level of detail to your verification plan takes time. But without the detail, how can you easily share with your colleagues what you plan to do? And how can you know which features your testbench needs to support in order to meet your coverage goals?

As always, questions and comments welcome.

Posted in Coverage, Metrics, Verification Planning & Management | 2 Comments »

You can also “C” with RAL.

Posted by S. Varun on 18th November 2010

The RAL C interface provides a convenient way for verification engineers to develop firmware code which can be debugged on a RTL simulation of the design. This interface provides a rich set of C API’s using which one can access fields, registers and memories included within the RAL model. The developed firmware code can be used to interface with the RAL model running on a SystemVerilog simulator using DPI (Direct Programming Interface) and can also be re-used as application-level code to be compiled on the target processor in the final application as illustrated in the figure below.

image

The “-gen_c” option available with “ralgen“ has to be used for this and that would cause the generator to generate the necessary files containing the API’s to interface to the C domain. The generated API’s can be used in one of two forms.

1. To interface to the System Verilog RAL model running on a System Verilog simulator using DPI and

2. As a pure standalone-C code designed to compile on the target processor.

Typically firmware runs a set of pre-defined sequences of writes/reads to the registers on a device performing functions for boot-up, servicing interrupts etc. You generally have these functions coded in C, and these would need to access the registers in the DUT. Using the RAL C model, these functions can be generated so that the firmware can now perform the register access through the SystemVerilog RAL model. Thus, this allows firmware and application-level code to be developed and debugged on a simulation of the design and the same functions can later be used as part of the device drivers to perform the same tasks on the hardware.

In the first scenario, when executing the C code within a simulation, it is necessary for the C code to be called by the simulation to be executed and hence the application software’s main() routine must be replaced by one or more entry points known to the simulation. All entry points must take at least one argument that will receive the base address of the RAL model to be used by the C code. The C-side reference is then used by the RAL C API to access required fields, registers or memories.

image

Consider a sequence of register accesses performed at the boot time of a router as defined in function system_bootup() shown below,

File : router_test.c

#ifdef VMM_RAL_PURE_C_MODEL

/*when used as a pure C model for the target processor */

int main() {

void *blk = calloc(410024,1);

system_bootup((void *) (((size_t)blk)>>2)+1);

}

#endif

void system_bootup (unsigned int blk){

unsigned int dev_id = 0xABCD;

unsigned int ver_num = 0×1234;

/* Invoking the generated RAL C APIs */

ral_write_DEV_ID_in_ROUTER_BLK(blk, &dev_id);

ral_write_VER_NUM_in_ROUTER_BLK(blk, &ver_num);

}

In the above example “ral_write_DEV_ID_in_ROUTER_BLK()” & “ral_write_VER_NUM_in_ROUTER_BLK()” are C API’s generated by “ralgen” to access registers named “DEV_ID” & “VER_NUM” located in a block named “ROUTER_BLK”. In general registers can be accessed using “ral_read_<reg>_in_<blk>” & “ral_write_<reg>_in_<blk>” macros present within the RAL-C interface.

The above C function can also be called from within a System Verilog testbench via DPI

File : ral_boot_seq_test.sv

import “DPI-C” context task system_bootup(int unsigned ral_model_ID);

program boot_seq_test();

initial

begin

router_blk_env env = new();

// Configuring the DUT

env.cfg_dut();

// Calling the C function using DPI

system_bootup(env.ral_model.get_block_ID()); // ‘ral_model ‘is the instance of the generated SV model in the env class

end

endprogram

In the example above, system_bootup is the service entry point which is called from the SV simulation and is passed the RAL Model reference. The ‘C’ code that is executed and simulation freezes till one of the registers accesses is made which then shifts the execution to the SV side.

image

The entire execution in ‘C’ is in ‘0’ time in the simulation timeline. The RAL C API hides the physical addresses of registers and the position and size of fields. The hiding is performed by functions and macros rather than an object-oriented structure like the native RAL model in SystemVerilog. This eliminates the need to compile a complete object-oriented model in an embedded processor object code with a limited amount of memory

Thus by using the RAL C interface one can develop a compact and efficient firmware code, while preserving as much as possible of the abstraction offered by RAL. I hope this was useful and do let me know your thoughts on the same if you plan to use this flow to meet some of your requirements of having your firmware and application-level code to be developed and debugged on a simulation of the design.

Please refer to the RAL userguide for more information. You can also refer to the example present within VCS installation located at $VCS_HOME/doc/examples/vmm/applications/vmm_ral/C_api_ex.

Posted in Register Abstraction Model with RAL, SystemC/C/C++, SystemVerilog, VMM | No Comments »

Reusing Your Block Level Testbench

Posted by JL Gray on 12th November 2010

JL Gray, Vice President, Verilab, Austin, Texas, and Author of Cool Verification

Building reusable testbench components is a desirable, but not always achievable goal on most verification projects. Engineers love the idea of not having to rewrite the same code over and over again, and managers love the idea that they can get more work out of their existing team by simply reusing code within a project and/or between projects. It is interesting, then, that I frequently encounter code written by engineers that cannot work in a reusable way.

Consider the following scenario. You’ve just coded up a new block level testbench to verify an OCP to AHB bridge. As any good engineer knows, your environment must be self-checking, so you create a VMM scenario that does the following:

class my_dma_scenario extends vmm_scenario;

  rand ocp_trans ocp;
  ahb_trans ahb;

  // ... 

  virtual task execute(ref int n);
    vmm_channel ocp_chan = this.get_channel("OCP");
    vmm_channel ahb_chan = this.get_channel("AHB");
    this.ocp.randomize();
    ocp_chan.put(this.ocp.copy());

    // Wait for the transaction to complete...

    ahb_chan.get(ahb) 

    // Compare actual and expected values
    // in the stimulus instead of the scoreboard.
    my_compare_function(ocp, ahb);

  endtask

endclass

Or, perhaps you decide to get fancy and update and create a situation where you add the expected transaction from the scenario directly to a scoreboard, which compares the results with transactions on the AHB interface as observed by a monitor. Either way, you go merrily about your business verifying the bridge. You get excellent coverage numbers, and eventually, with the help of the block’s designer, you feel you’ve fully verified the module at the block level. However, things are not going well in the full chip environment. Full chip tests are failing in unexpected ways, and other engineers debugging the issue feel it must be a bug in the brigde! They start assigning bugs to you and the block designer. If only you had some checkers operating at the full chip level you could prove your module was operating correctly…

Unfortunately, you have a problem. In your block level testbench, your checkers only work in the presence of stimulus, and in the full chip environment the stimulus components cannot be used. And at this stage of the project, there is no time to go back and create the additional infrastructure (monitor(s) and possibly a new scoreboard) required to run your self-checking module-level testbench at the full chip level.

Fortunately, there is another way to build a module level testbench so that it will be guaranteed to work at the full chip. Follow these simple rules:

  1. Always create a monitor, driver, and scenario generator for every testbench component.
  2. Never populate a scoreboard from a driver or scenario. Always pull in this information from a passive monitor.

In general, when writing your testbench components always assume they will need to run in the absence of testbench stimulus. Yes – this means additional up-front work. Writing checkers that work with passive monitors instead of the information you have available to you in the driver can be time consuming. Often, though, the amount of additional work required is much less than the time you will spend debugging issues without checkers at the full chip level. That being said, sometimes it is too difficult to implement purely passive checkers. You will have a good idea of whether or not this is the case based on the upfront work you did writing a comprehensive testplan. You do have a testplan, right?

Posted in Reuse, VMM, Verification Planning & Management | 3 Comments »

Modeling ISRs with VMM RAL

Posted by Amit Sharma on 4th November 2010

In a verification environment, different components may be trying to access the DUT registers and memories. For example, the BFM might be programming some registers while the bus monitor might be sampling the values of these registers. In specific cases, there may be an interrupt monitor which triggers an Interrupt Service Routine (ISR) whenever it sees an Interrupt pin toggling in the interface. The ISR might end up having to read the Interrupt registers and end up clearing the Interrupt bit/s through a front door access.

To ensure that different components in a verification environment can access the DUT registers at any given point in time, the RAL model instantiated in the environment can be passed to different VMM components. These different components whose methods are executing in separate parallel threads can now access the same set of registers in the DUT through the RAL model. A question many folks ask is: when there are multiple parallel register accesses, how do they get scheduled through the RAL layer?  Here is an explanation of how threads are scheduled in RAL:

A Register read/write from different threads is comparable to an ‘atomic’ channel.put() from  different threads. Hence it gets scheduled in the order of pipelining of the threads.

A write/read would basically consist of the following atomic operations:

- A generic vmm_rw_access transaction with its fields (addr, data, kind etc) being populated and posted onto the execute_single()   task of the Translate Transactor

- The transaction being translated in the execute_single() task and pushed into the input channel of the user BFM

- The transaction being retrieved through get/activate in the User BFM main thread and then subsequently driven to the DUT interface

Thus ‘posting’ of RAL accesses whenever a Read/Write/Mirror/Update is invoked is in the same order they are issued. Subsequently, the execute_single() task just translates the generic RW RAL transaction to a User BFM comprehensible transaction, and doesn’t change the order.

Now, how do we handle a scenario when specific register accesses like those coming from an ISR need to be given a higher priority than accesses coming from other threads in a verification environment?  VMM and RAL methods give us specific hooks to achievethis requirement. Here is one of the options on how this can be done.

If we look at the vmm_ral_reg::write method, we have the given signature:

virtual task write( output vmm_rw::status_e status,

    input bit [63:0] value,

    input vmm_ral::path_e path = vmm_ral::DEFAULT,

    input string domain = "",

    input int data_id = -1,

    input int scenario_id = -1,

    input int stream_id = -1)

Now, a generic RAL transaction that gets created through any Register access has the same data_id, scenario_id, stream_id arguments which get passed on from the read/write call. These arguments help us tag and track the transactions if that is so desired. Now, these arguments can be made use of in the execute_single() task to ensure that accesses from ISRs have the highest priority. But, first, if we go back to the earlier post by Varun, Issuing concurrent READ/WRITE accesses to the same register on two physical interfaces using RAL, Issuing concurrent accesses to the same registers on two physical interfaces using RAL , we note that if the RAL model is processing a ‘register access’ , it will not initiate the next one until the earlier one is completed. So, this is what we need to get done.We first use the RAL Proxy transactor to schedule the ISR register access simultaneously. After that, we flush out any existing accesses and prevent any new register access through RAL until access from the ISR is completed

This is how it, will be done:

image

For a normal register access, a read/write method will be invoked as follows:

          ral_model.<reg_name>.write(stat,wdata, “AHB”); //’wdata’ is the value to be driven, “AHB” is the domain /physical interface

For a register access in an ISR modeled in a MS Scenario, we have:

         env.bfm.to_ahb.grab(this); //grabs the channel

         env.ral.read(status, env.ral_model.<reg_name>.get_address_in_system("AHB"), data, 32, "AHB",,,1); // The last argument is again the “stream_id” argument

         env.bfm.to_ahb.ungrab(this); //allows the channel to be accessed from other threads once the ISR is completed

Once, this is done, the execute_single() task inside the translate Transcator will know if an access is through an ISR and can ensure that ISRs are processed on priority through a combination of functionality provided through the VMM Channel methods in combination with a semaphore:

virtual task execute_single(vmm_rw_access tr);

        AHB_tr cyc;

        AHB_tr cyc_active; //to keep any transactions residing on the active slot

        semaphore sem = new(1); //semaphore with a single key to prevent new accesses when a reg access from an ISR is processed

       // Translate the generic RW into a simple RW

       cyc = new;

       {cyc.dev, cyc.addr} = tr.addr;

       if (tr.kind == vmm_rw::WRITE) begin

         cyc.cycle = simple_tr::WRITE;

         cyc.data = tr.data;

         …

       end

      else begin

         cyc.cycle = simple_tr::READ;

         …

      end

      if (tr.stream_id != 1) sem.get(1); // regular register access gets blocked here when a reg access from an ISR is processed

      else begin

         if(this.bfm.to_ahb.is_full()) begin

           this.bfm.to_ahb.activate(cyc_active); //removes any existing transactions in the channel’s active slot so that the current access can be pushed through

           this.in_chan.start();

           this.in_chan.complete();

           this.in_chan.remove();

           this.bfm.to_ahb.put(cyc);

           this.bfm.to_ahb.put(cyc_active); //restores the original transaction back into the active slot

           cyc = cyc_active;

           sem.put(1); //ths ISR access puts back the key for normal accesses to resume

        end

        else this.bfm.to_ahb.put(cyc);

      end

      // Send the result back to the RAL

      if (tr.kind == vmm_rw::READ) begin

        tr.data = cyc.data;

      end

      endtask: execute_single

Posted in Register Abstraction Model with RAL, VMM infrastructure, vmm_channel | No Comments »

Accelerating Design Verification with Virtual CPU models

Posted by Srivatsa Vasudevan on 29th October 2010

Srivatsa Vasudevan, Sr Staff CAE, Synopsys Inc & Filip Thoen, Product Architect, Synopsys Inc.

In today’s SOC architectures, one or more on-chip core(s) or CPU(s), like ARM or MIPS, are often used to provide flexibility in the system, through the use of embedded software. Besides executing software functionality, they program the hardware peripherals and perform I/O with them. Here, from a low-level hardware perspective the CPU is typically performing control and data plane functions.

virtual_models

Let’s look at this from a SoC verification perspective: in such cases, besides being able to drive the appropriate stimuli for the tests, the RTL simulation of the core(s)/CPU(s) doesn’t provide any additional value to a verification engineer. These core(s)/CPU(s) are typically pre-validated by their IP supplier, and hence don’t need to be validated again.

Running a performance profile on simulations of such SoCs reveals that a majority of the events simulated are typically within the CPU, which is often not a portion of the DUT being verified. Consequently, valuable verification time is lost in debug, and no additional information is gained from the simulation of these cores/CPUs. Often, verification teams use embedded software tests running on these core(s)/CPU(s) to validation individual IPs and/or to perform system integration validation. These software tests will program the different peripheral(s) / module(s) being tested, exercise them and observe and validate the result for correctness. Other software scenarios include the testing of the SoC’s boot sequence, which is typically performed by boot code executed by these core(s)/CPU(s).

(Note: Click on the figure if it appears too smal in your browser, Thanks!)

In situations like this, it often makes sense to replace the event-heavy CPU RTL model with a transaction –level model (TLM), typically called an instruction-set simulator (ISS) in case of a CPU or core. These models are written in C/C++ or SystemC, and depending on the abstraction level used, can run up to 100+ MIPS standalone. Such a model will execute the actual embedded software program binaries and issue the appropriate (bus) transactions to the peripheral(s) / module(s) being verified. As a result, the events originating from the simulating the CPU are now removed from the RTL simulation, and the net effect observed is a significant simulation acceleration while obtaining the required results. Moreover, non-interesting events, like the fetching of instructions from off- or on-chip ROM/RAM, requiring a high number of events in the bus-memory controller-memory chain, can be avoided in the RTL simulation by shadowing these memories in the virtual CPU model.

image

Several system level models for popular cores/CPUs are now available as part of the Synopsys Designware® System-Level Library product, including ARM, MIPS and PowerPC architectures. Combined with a pin level transactor, which matches the transaction level interface of the TLM model to the pin-level interface of the CPU bus, a virtual model of the core/CPU present in the SoC can be set up. Integration & deployment of this is straightforward: the virtual CPU model is a direct pin-level replacement of the RTL CPU, simply requiring a SystemC integration as historically supported by VCS.

There are several ways of implementing this keeping in mind re-use requirements, configurations and the stage at which verification is being carried out. In the figure above, the CPU has been replaced by a virtual model along with a pin level transactor. The CPU still runs the same embedded directed software test code as before, but with a significant simulation speed-up, ranging between 3-20x depending on the design specific parameter like RTL size, speed & number of other (high-speed) modules, testbench overhead, etc. Another significant benefit is the gain in debugability, and the associated increase in productivity. These TLM models allow commercial embedded software debuggers to be connected to the simulation, allowing verification (and software) engineers to (interactively) debug the running verification or boot software program. With the RTL of the CPU in place, this is typically not realizable.

What do you think of this concept? Interesting? How do you handle such situations? Drop me a line.

-Srivatsa

Posted in Transaction Level Modeling (TLM) | 1 Comment »

Using VMM template Generator to ramp up your testbench development

Posted by Amit Sharma on 25th October 2010

Amit Sharma, Synopsys
‘vmmgen’, the template generator for creating robust, extensible VMM compliant environments, has been available for a long time with VMM and it was upgraded significantly with VMM1.2. Though the primary functionality of ‘vmmgen’ is to help minimize VIP and environment development cycle by providing detailed templates for developing VMM Compliant verification environments, a lot of folks also use it to quickly understand how different VMM base classes can be used in different contexts. This is done as the templates uses a rich set of the latest VMM features to ensure the appropriate base classes and their features are picked up optimally.

Given that it has a wide user interaction mechanism which provides available features and options to the user, the user can pick up the modes which are most relevant to his or her requirement. It also provides them the option to provide their own templates thus providing a rich layer of customization. Based on the need, one can generate individual templates of different verification components or they can generate a complete verification environment which comes with a ’Makefile’ and an intuitive directory structure, thus propelling them on their way to catch the first set of bugs in their DUTs. I am sure all of you know where to pick up ‘vmmgen’ form. It available in the <VMM_HOME>/Shared/bin area or in $VCS_HOME/bin

Some of the rich set of features available now includes:

• Template Options:

– Complete environment generation

– Individual templates generation

• Options to create Explicitly phased environments or Implicitly phased environment or to mix Implicitly phased components and Explicitly phased components

• Usage of VMM Shorthand macros

• Creating RAL based environment, and providing Multiplexed domain support if required

• Hooking up VMM Performance Analyzer at the appropriate interfaces

• Hooking up the DS Scoreboard at the relevant interfaces (with options to chose from a range of integration options, e.g. : through callbacks, through TLM2.0 analysis ports, connect directly through to transactors, channels or notifications)

• Ability to hook up different generators (atomic, scenario, Multistream generators) at different interfaces

• Creating a scenario library and Multistream scenario creation

• Multi-driver generator support for different kinds of transactions in the same environment

• Factory support for transactions, scenarios and multi stream scenarios. Sample factory testcase which can explain the usage of transaction override from a testcase.

• ‘RTL config’ support for drivers and receivers.

• Various types of unidirectional and bi-directional TLM connections between generator and driver.

• Analysis ports/exports OR parameterized notify observers to broadcast the information from monitor to scoreboard and coverage collectors.

• Multi test concatenation support and management to run the tests

• Creating portable Interface wrapper object, and setting up interface connections to the testbench components using vmm_opts::set_object/get_object_obj

• Creating a Generic slave component

• Option to use default names or user provided names for different components

As you can see the above list itself is quite comprehensive and let me tell you that that it is not exhaustive as there are many more features in vmmgen.

With respect to the usage as well, there are multiple flavors. In the default mode, the user is taken through multiple detailed choices/options as he is creating/connecting different components in his verification environment. However, some folks might want to use ‘vmmgen’ within their own wrapper script/environment and for them there are options to generate the environments by providing all required options in the command line or through a configuration file… Some of these switches include

-SE [y/n] : Generates a complete environment with sub-environments

-RAL [y/n] : Create RAL based verification environments

-RTL [y/n] : Generates RTL configuration for the environment

-ENV <name>, -TR <name> : Provide the name for the environment class and transaction classes. names for multiple transaction class names can be provide as well:

vmmgen –l sv –TR tr1+tr2

-cfg_file <file_name> : Option to provide a configuration file for the options

There is an option to generate an environment quickly by taking the user through the minimum number of questions (-q).

Additionally, the user can provide his or her own templates through the –L <template directory> option.

As far as individual template generation goes, you have the complete list. Here, I am outlining this down for reference:

 

 

image

I am sure a lot of you have already been using ‘vmmgen’. For those, who haven’t, I encourage you to try out the different options with it. I am sure you will find this immensely useful and it will not only help you create verification components and environments quickly but will also make sure they are optimal and appropriate based on your requirements.

Posted in Automation, Coding Style, Customization, Modeling Transactions, Organization, Performance Analyzer, Scoreboarding, Tools & 3rd Party interfaces, Uncategorized, VMM infrastructure | No Comments »

Setting data members for factory-created objects

Posted by Avinash Agrawal on 11th October 2010

Avinash Agrawal, Corporate Applications, Synopsys

One question asked by verification engineers is that given that we are using the VMM factory, how do we assign the data members of VMM class factory service while calling the create_instance method ?

For example, the create_instance() method of class factory service always calls
the class constructor with default arguments. Therefore, it would appear that extra arguments
cannot be passed to the class instance class. i.e., there is no way to pass
additional arguments for class members with the following statements:

  tr = ahb_trans::create_instance(this, "Ahb_Tr0", `__FILE__, `__LINE__);

The answer is that while you cannot use the create_instance() function to initialize   ?
data members, you can use any of the following methods to deal with this assignment:
1. By using virtual function/task set_* in the base class and derived class, and the
   arguments are passed from the arguments list of set_* function/task. But there
   is a limitation with this as the set_* function/task arguments for the base
   class and the derived class must be the same.

2. By using vmm_opts::set_* and vmm_opts::get_* combination to set the value of
   properties of class. 

Examples:

1. By using set_* to pass arguments as described in point 1:

////////////////////
//Source code
////////////////////
`define VMM_12
program P;
`include "vmm.sv"

// Define base class
class ahb_trans extends vmm_data;
  `vmm_typename(ahb_trans00)
  rand int addr;
  static vmm_log log = new("ahb_trans", "object");

  `vmm_data_member_begin(ahb_trans)
    `vmm_data_member_scalar(addr,	DO_ALL)
  `vmm_data_member_end(ahb_trans)

  virtual function void set_data(int addr=0, int data=0);	// <-- Two arguments
    this.addr = addr;
  endfunction

  virtual function void display_data();
    `vmm_note(log, $psprintf("ahb_trans.addr=='h%0h", this.addr));
  endfunction

  `vmm_class_factory(ahb_trans)
endclass

// Define derived class
class my_ahb_trans extends ahb_trans;
  `vmm_typename(my_ahb_trans)
  rand int data;

  static vmm_log log = new("my_ahb_trans", "object");

  `vmm_data_member_begin(my_ahb_trans)
    `vmm_data_member_scalar(data,		DO_ALL)
  `vmm_data_member_end(my_ahb_trans)

  virtual function void set_data(int addr=0, int data=0);	// <-- Two arguments
    this.addr = addr;
    this.data = data;
  endfunction

  virtual function void display_data();
    `vmm_note(log, $psprintf("my_ahb_trans.addr=='h%0h, my_ahb_trans.data=='h%0h", this.addr, this.data));
  endfunction

  `vmm_class_factory(my_ahb_trans)
endclass

class env extends vmm_env;

   ahb_trans tr;

   function new;
      super.new("ENV");
   endfunction;

   function void build;
      super.build;

     ahb_trans::override_with_new("@%*", my_ahb_trans::this_type, log, `__FILE__, `__LINE__);
     tr = ahb_trans::create_instance(this, "Ahb_Tr0", `__FILE__, `__LINE__); 

     if(!(tr.get_typename == "class P.my_ahb_trans"))
        `vmm_error(log,"ERROR");

     tr.set_data(32'h5555_5555, 32'haaaa_aaaa);	// <-- Call set_* after create_instance
     tr.display_data();

   endfunction

endclass

initial begin
  env env = new();
  env.run;
end

endprogram

////////////////////
//Simulation Result
////////////////////
Normal[NOTE] on my_ahb_trans(object) at                    0:
    my_ahb_trans.addr=='h55555555, my_ahb_trans.data=='haaaaaaaa
Simulation PASSED on /./ (/./) at                    0 (0 warnings, 0 demoted errors & 0 demoted warnings)

2. By using vmm_opts::set_* and vmm_opts::get_* combination as described in point 2:

////////////////////
//Source code
////////////////////
`define VMM_12
program P;
`include "vmm.sv"

// Define base class
class ahb_trans extends vmm_data;
  `vmm_typename(ahb_trans00)
  rand int addr;
  static vmm_log log = new("ahb_trans", "object");

  `vmm_data_member_begin(ahb_trans)
    `vmm_data_member_scalar(addr,	DO_ALL)
  `vmm_data_member_end(ahb_trans)

  virtual function void set_data();
    this.addr = vmm_opts::get_int ("ADDR", 0, "Value set for addr");
  endfunction

  virtual function void display_data();	//<-- no arguments passed, so there is no limitation on number of arguments
    `vmm_note(log, $psprintf("ahb_trans.addr=='h%0h", this.addr));
  endfunction

  `vmm_class_factory(ahb_trans)
endclass

// Define derived class
class my_ahb_trans extends ahb_trans;
  `vmm_typename(my_ahb_trans)
  rand int data;

  static vmm_log log = new("my_ahb_trans", "object");

  `vmm_data_member_begin(my_ahb_trans)
    `vmm_data_member_scalar(data,		DO_ALL)
  `vmm_data_member_end(my_ahb_trans)

  virtual function void set_data();	//<-- no arguments passed, so there is no limitation on number of arguments
    this.addr = vmm_opts::get_int ("ADDR", 0, "Value set for addr");
    this.data = vmm_opts::get_int ("DATA", 0, "Value set for data");
  endfunction

  virtual function void display_data();
    `vmm_note(log, $psprintf("my_ahb_trans.addr=='h%0h, my_ahb_trans.data=='h%0h", this.addr, this.data));
  endfunction

  `vmm_class_factory(my_ahb_trans)
endclass

class env extends vmm_env;

   ahb_trans tr;

   function new;
      super.new("ENV");
   endfunction;

   function void build;
      super.build;

     vmm_opts::set_int("ADDR", 32'h55,null);	//	<-- Call vmm_opts::set_*
     vmm_opts::set_int("DATA", 32'haa,null);

     ahb_trans::override_with_new("@%*", my_ahb_trans::this_type, log, `__FILE__, `__LINE__);
     tr = ahb_trans::create_instance(this, "Ahb_Tr0", `__FILE__, `__LINE__); 

     if(!(tr.get_typename == "class P.my_ahb_trans"))
        `vmm_error(log,"ERROR");

     tr.set_data();
     tr.display_data();

   endfunction

endclass

initial begin
  env env = new();
  env.run;
end

endprogram

////////////////////
//Simulation Result
////////////////////
Normal[NOTE] on my_ahb_trans(object) at                    0:
    my_ahb_trans.addr=='h55, my_ahb_trans.data=='haa
Simulation PASSED on /./ (/./) at                    0 (0 warnings, 0 demoted errors & 0 demoted warnings)

Posted in vmm_log | No Comments »

The Blind Leading the Deaf

Posted by Andrew Piziali on 28th September 2010

The Wall of Separation Between Design and Verification

by Andrew Piziali, Independent Consultant

I remember a time in my zealous past, leading a large microprocessor verification team, where one my junior engineers related how they had forcefully resisted examining the floating point unit RTL, explaining to the designer that they did not want to become tainted by the design! The engineer insisted on a dialog with the designer rather than reviewing the RTL. Their position was mine: we must maintain some semblance of separation between the design and verification engineers.

There has been an age old debate between whether or not there ought to be a “wall” between the design and verification teams. “Wall” in this context refers to an information barrier between the teams that minimizes the verification engineer’s familiarity with the details of the implementation (but not the specification!) of the design component being verified. Similarly, the wall minimizes the designer’s familiarity with the verification environment for their implementation.

Re-convergence Model

Re-Convergence Model

The intent of the wall is to allow two pairs of eyes (and ears!)—those of the designer and those of the verification engineer—to independently interpret the specification for a common component and then compare their results. The hypothesis is that if they reach the same conclusion, they are likely to have correctly interpreted the specification. If they do not, one or both are in error. This process is an example of the re-convergence model[1], where a design transformation is verified by performing a second parallel transformation and then comparing the two results. What are the pros and cons of the wall?

The argument in favor of the wall depends upon what we might call original impressions, the fresh insight provided by a person unfamiliar with a concept upon initial exposure. In this context, the verification engineer reading the specification will acquire an understanding of the design intent, independent of the designer, but only if study of its implementation is postponed. Why? Because nearly any implementation will be a plausible interpretation of the specification. The objective is to acquire two independent interpretations for comparison. Hence, influencing a second understanding with an initial implementation would defeat the purpose. What is the opposite position?

The argument against the wall is that a verification engineer and designer, working closely together, are more likely to gain a more precise understanding of the specification than either one working alone. The interactive exploration of possible specification interpretations, each implementing their understanding—the designer the RTL and software, the verification engineer the properties, stimulus, checking and coverage aspects of their verification environment—is argued to lead to convergence more quickly than each party working alone. Well, what should it be? Should verification engineers and designers scrupulously avoid one another, should they collaborate or should they find some intermediate interaction?

Pondering the answer brings to mind the metaphor of the blind leading the deaf, where each of two parties is crippled in a different way such that neither is able to grasp the whole picture. Nevertheless, working together they are able to progress further than working alone. Are the verification engineer and designer the blind leading the deaf? Before I weigh in with my opinion, I’d like to read yours. Type in the “Leave a Reply” box below to respond. Thanks!

—————————
[1] Writing Testbenches Using SystemVerilog, Janick Bergeron, 2006, Springer Science+Business Media, Inc.

Posted in Coverage, Metrics, Verification Planning & Management | 4 Comments »

Performance verification of a complex bus arbiter using the VMM Performance Analyzer

Posted by Shankar Hemmady on 20th September 2010

Performance verification of system bus fabrics is an increasingly complex problem. An article in EE Times by Kelly Larson, John Dickol and Kari O’Brien of MediaTek Wireless describes how they used the VMM Performance Analyzer to complete performance validation for an AXI bus arbiter:

http://www.eetimes.com/design/eda-design/4207764/Performance-verification-of-a-complex-bus-arbiter-using-the-VMM-Performance-Analyzer

Posted in Performance Analyzer, VMM infrastructure | No Comments »

Shared Register Access in RAL though multiple physical interfaces

Posted by Amit Sharma on 19th September 2010

Amit Sharma, Synopsys

Usually, designs have a single interface but some designs may have more than one physical interface, each with accessible registers or memories. RAL supports designs with multiple physical interfaces, as well as registers and memories shared across multiple interfaces.

In RAL, a physical interface is called a domain. Only blocks and systems can have domains. Domains can contain registers and memories.

image

 

Now, how do you enable a register or a memory to be shared across multiple physical interfaces or ‘domains’ in RAL? This is done by declaring the register/memory as ‘shared’ in the RALF file and instantiating it in more than one domain. Once this is done, the functionality will be enabled in the generated RAL model.

Let us take an example. Suppose the design block has two domains or interfaces (say pci/ahb) which can have a shared access to the register STATUS. This can be specified through the ‘shared’ keyword in the register description in RALF file, generating a RAL block model, as follows.

image

In the example above, the text box on the left shows a snippet of the generated code. You can see that ‘domain’s do not create an additional level of hierarchy. The different registers belonging to the two domains in the block are available in the flat namespace in the block and can be accessed directly without specifying the domain name in the access hierarchy.

Once a register is defined as ‘shared’, I can go ahead and access that register through different domains. A typical usage scenario for a ‘shared’ register is that it can be read from one interface and written from another interface. The two different physical BFMs are mapped to the corresponding domains easily in the SV environment.

The following snippet of code how this is done:

this.ahb = new(…); // AHB BFM instantiation

this.ral.add_xactor(this.ahb, "ahb"); //tying the AHB BFM to the “ahb” domain

this.pci = new(…); // PCI BFM instantiation

this.ral.add_xactor(this.pci, "pci"); //tying the PCI BFM to the “pci” domain

Once the mapping is done, the way you access a specific ‘shared’ register is quite simple. You need to additionally specify the ‘domain’ name in your read/write task

image

When you make the writes/reads as described above, the accesses would be routed through the different BFMs corresponding to the different domains as specified through the ‘domain’ argument  in the read/write access tasks as seen in the code specified in the textbox above.

In the test environment, if simulation is run with ‘trace’ or ‘debug’ verbosity , details of the access to the shared register through different domains can be observed.

The following lines shows a snippet of messages going to STDOUT

//———

Trace[DEBUG] on RVM RAL Access(Main) at 135:

Writing ‘h00000000000000aa at ‘h0000000000000A004 via domain "pci"…

Trace[DEBUG] on RAL(register) at 245:

Wrote register "ral_model.INT_MASK" via frontdoor with: ‘h00000000000000aa

Trace[DEBUG] on RVM RAL Access(Main) at 355:

Read ‘h00000000000000aa from ‘h0000000000000002 via domain "ahb"…

Trace[DEBUG] on RAL(register) at 355:

Read register "ral_model.INT_MASK" via frontdoor: ‘h00000000000000aa

//———

By the way, one of the pre-defined tests that ship with the VMM RAL is the "shared_access" test. It exercises all shared registers and memories using the following process for each domain (requires at least one domain with READ capability or backdoor access).

- Write a random value via one domain

- Check the content via all other domains.

Hence, without writing a single line of test code, you can verify the basic functionality of all shared registers/memories of your DUT through this test.


Posted in Register Abstraction Model with RAL, Uncategorized, vmm_xactor | 1 Comment »

Using the Factory Infrastructure in VMM

Posted by Avinash Agrawal on 15th September 2010

Avinash Agrawal, Corporate Applications, Synopsys

As a well-known Object-Oriented technique, the factory concept has been applied in VMM ever since inception.

For example, if you assign different blueprints to randomized_obj and scenario_set[]
in the VMM atomic and scenario generators, these generators can generate transactions
with user specified patterns. By using the class factory pattern, you can create an
instance with a pre-defined method such as allocate() or copy() instead of the
constructor.

VMM now simplifies the application of the class factory pattern within the complete
verification environment that helps you to easily replace any kind of object,
transaction, scenario and transactor by a similar object. 

The following simple procedure helps you to apply the class factory pattern within
the verification environment:

1. Define "new", "allocate" and "copy" methods for a class and use the factory macros
   to create the necessary infrastructure for factory replacement.

class vehicle_c extends vmm_object;
`vmm_typename(vehicle_c);
//defines the new function. each argument should have default values
   function new(string name="",vmm_object parent=null);
      super.new(parent,name);
   endfunction

//defines allocate and copy methods, you can use the VMM Data shorthand macros for
the same if classes are extended from vmm_data.

   virtual function vehicle_c allocate();
      vehicle_c it;
      it = new(this.get_object_name,get_parent_object());
      allocate = it;
   endfunction

   virtual function vehicle_c copy();
      vehicle_c it;
      it = new this;
      copy = it;
   endfunction

//`vmm_typename and `vmm_class_factory will  define necessary infrastructure
 `vmm_class_factory(vehicle_c);
endclass

2. Create an instance using the pre-defined "create_instance()" method. To use the
   class factory, the class instance should be created with the pre-defined
   create_instance() method instead of the constructor. 

For example,

class driver_c extends vmm_xactor;
   vehicle_c myvehicle;
   function new(string name="",vmm_object parent=null);
      super.new(parent,name);
   endfunction

   task drive();
   //create an instance from create_instance method
      myvehicle = vehicle_c::create_instance(this,"myvehicle");
      $display("%s is driving %s(%s)", this.get_object_name(),
       myvehicle.get_object_name(),myvehicle.get_typename());
   endtask
endclass

program p1;
      driver_c Tom=new("Tom",null);
      initial begin
         tom.drive();
     end
endprogram

The output for the example will be,
Tom is driving myvehicle(class $unit::vehicle_c)

3.  Create a new class which should replace the existing one as shown in the
    following example:

class sedan_c extends vehicle_c;
   `vmm_typename(sedan_c);
    function new(string name="",vmm_object parent=null);
       super.new(name,parent);
    endfunction

    virtual function vehicle_c allocate();
       sedan_c it;
       it = new(this.get_object_name,get_parent_object());
       allocate = it;
    endfunction

    virtual function vehicle_c copy();
       sedan_c it;
       it = new this;
      copy = it;
    endfunction
    `vmm_class_factory(sedan_c);
endclass

You are now required to create a 'myvehicle' instance from this new class without
modifying the driver_c class.

4. Override the original instance or type with the new class. VMM provides two
   methods for you to override the original instances or type.

- override_with_new(string name, new_class factory, vmm_log   log,string
  fname="",int lineno=0);
  With this method, when create_instance() is called, a new instance of new_class
  will be created through facory.allocate() and returned.

- override_with_copy(string name, new_class factory,vmm_log log, string
  fname="", int lineno=0);
  With this method, when create_instance() is called, a new instanced of new_class
  will be created through factory.copy() and returned.

For both the methods, the first argument is the instance name, as specified in
the create_instance() method, which you would want to override with the type of
new_class. You can use the powerful pattern matching mechanism defined in VMM to
specify the override happens on specified instances or all the instances of one
class in the whole verification environment.

The code below will override all vehicle_c instances with sedan_c type in the
environment:

program p1;
   driver_c Tom=new("Tom",null);
   vmm_log log;
   initial begin
      //override all vehicle_c instances with type of sedan_c
      vehicle_c::override_with_new("@%*",sedan_c::this_type,log);
      Tom.drive();
    end
endprogram

And the output of the above code is:

  Tom is driving myvehicle(class $unit::sedan_c)

If you only want to override one dedicated instance with a copy of another
instance, you can call override_with_copy by using the following code:

vehicle_c::override_with_copy("@%Tom:myvehicle",another_sedan_c_instance,log);

As the above example shows, with the class factory, match patterns and shorthand
macros provided with VMM, you can easily use class factories patterns to replace
transactors, transactions and other verification components without modifying the
testbench code.

Posted in VMM | 4 Comments »

RAL: Using TCL to conditionally generate registers

Posted by Vidyashankar Ramaswamy on 12th September 2010

 

Let us say you have a DMA RTL block and its registers are specified using RALF format. Now you want to instantiate it 4 times at the system level. The most common approach is to use array notation. What if you want to include one (or many) extra registers only for one of the instances ? The trivial approach is to manually create two block instances with appropriate registers. What if I want to create 5 different flavors with a very small addition or deletion of a register ? Do I have to create 5 of them by duplicating the same content ? No! This can be done automatically using the ralgen tool and its built-in tcl interpreter. Let us take a look at the following simple example.

# File: dma_registers.ralf

# Register definitions

register device_id {

   bytes 4;
 
 field device_id {
      
bits   32;
 
     access   ro;
 
     soft_reset   32′h0;
   
}
}

register command_status {

   field cmd_status {
      bits 31;
     
access rw;
      reset 32′h0;
   }
}

# DMA Block code generation

for {set i 0} {$i < 2} {incr i} {
   block dma_$i {
      bytes 4;
      register device_id@’h100;?
      if {$i == 1} {
         register command_status @’h124;
      }
   }
}

# System top level

system SYSTEM_TOP {

   bytes 4;
   block dma_0 @’h0000 ;
   block dma_1[3] @’h1000 + ‘h1000;
}

This example shows a DMA block which contains 2 registers. Say I want to create two different DMA blocks out of which one instance has the command status register. Let us call them dma_0 and dma_1. This can be easily achieved using a for loop construct to conditionally include the register as shown above. The system top instantiates one dma_0 type and three dma_1 type blocks. The following is the output from the ralgen tool.

class ral_sys_SYSTEM_TOP extends vmm_ral_sys;

   rand ral_block_dma_0 dma_0;
   rand ral_block_dma_1 dma_1[3];
   . . .

endclass : ral_sys_SYSTEM_TOP

Can I use the loop construct even at the system level ? Yes, you can replace the array notation with the loop construct. Though this is bit more complex, you will have better control over the instantiation names generated by the tool. Following code demonstrates the concept.

 system SYSTEM_TOP {

   bytes 4;

   for {set i 0} {$i < 2} {incr i} {
      set OFFSET [expr $i * 2048];
      if {$i == 1} {
        for {set j 0} {$j < 3} {incr j} {
           set OFFSET_1 [expr $OFFSET + ($j+1) * 2048];
           block dma_$i=dma_$i$j @$OFFSET_1;
        }
      }
      else {
            block dma_$i @$OFFSET;
      }
   }
}

 

The tool output is shown below.

 class ral_sys_SYSTEM_TOP extends vmm_ral_sys;

   rand ral_block_dma_0 dma_0;
   rand ral_block_dma_1 dma_10;
   rand ral_block_dma_1 dma_11;
   rand ral_block_dma_1 dma_12;
   . . .

endclass : ral_sys_SYSTEM_TOP

 Please consider creating separate files for block and systems which will help in block to top reuse of RALF.

 

 

Posted in Register Abstraction Model with RAL | No Comments »

Example of Transaction-Level Communication in VMM 1.2

Posted by John Aynsley on 9th September 2010

John Aynsley, CTO, Doulos

Having introduced many of the technical details of transaction-level communication in VMM 1.2 in previous posts on this blog, we will now look at an example highlighting the various transaction-level communication mechanisms available in VMM 1.2 so that we can get a feel for the roles they perform and how they work together.
Let’s start with a producer that generates a stream of transactions:

 

class  my_gen  extends  vmm_xactor;

  vmm_tlm_b_transport_port #(my_gen, my_tx)  m_port;
  vmm_tlm_analysis_port    #(my_gen, my_tx)  m_ap;
  …

  begin: loop
    assert( randomized_tx.randomize() ) …
    $cast(tx, randomized_tx.copy());

    `vmm_callback(my_gen_callbacks,  pre_trans(this, tx));

    m_port.b_transport(tx, delay);

    `vmm_callback(my_gen_callbacks,  post_trans(this, tx));
    m_ap.write(tx);
  end
  …

The example above makes use of a blocking transport port, an analysis port, and callbacks. After randomizing and copying the next transaction in the stream, the producer offers the transaction to the pre_trans callback, giving a test the chance to modify the transaction before it is sent downstream. The transaction is then sent through the blocking transport port, with the advantage that the simple completion semantics of b_transport makes the producer code very clean. b_transport only returns when the transaction is finished, at which point the transaction is offered to another callback post_trans, which gets the chance to modify the transaction once more. Finally the transaction is sent out through the analysis port for broadcast to passive verification components for checking and coverage collection. The analysis port may be connected to zero, one, or many such passive components.
Now for the consumer that receives the transaction:

 


class  my_bfm  extends  vmm_xactor;
  my_channel  m_chan;
  vmm_tlm_analysis_port  #(my_bfm, my_tx)  m_ap;
  …
  begin: loop
    `vmm_callback(my_bfm_callbacks, pre_trans(this, tx));

    m_chan.activate(tx);
    m_chan.start();
    @(i_f.bus_cb); …
    m_chan.complete();
    m_chan.remove();

    `vmm_callback(my_bfm_callbacks, post_trans(this, tx));
    m_ap.write(tx);
  end


The consumer transactor makes use of callbacks and an analysis port in exactly the same way as the consumer, but does not use b_transport. Rather, it receives the incoming transaction using a vmm_channel, which gives more flexibility in interacting with the transaction during its lifetime. VMM recommends the use of vmm_channel when modeling slave-like transactors. The start and complete methods each cause notifications within the transaction, which may be significant for transaction recording or debug. The remove method removes the transaction from the channel, which in this example will have the effect of unblocking the call to b_transport from the producer.
Finally, the producer and consumer are connected together in the environment:

 


class  tb_env  extends  vmm_group;

  virtual function void  build_ph;
    m_tx_chan  = new( “my_channel”, “m_tx_chan” );
    m_tx_chan.reconfigure(1);
    m_gen      = new( “m_gen”, this );
    m_bfm      = new( “m_bfm”, this );
  endfunction

  function void  connect_ph();
    vmm_connect #(.D(my_tx))::tlm_bind(m_tx_chan, m_gen.m_port,
                              vmm_tlm::TLM_BLOCKING_EXPORT );
    m_bfm.m_chan = m_tx_chan;
  endfunction
  …


It is important to note that the channel is reconfigured with a full level of exactly 1 so that the blocking transport call will indeed block until the one-and-only transaction is removed from the channel by the consumer. If the full level were greater than 1, b_transport would return immediately and the communication scheme would be broken.
In order to bind the blocking transport port of the producer to the input channel of the consumer, it is necessary to use the tlm_bind method of the vmm_connect utility, as discussed in previous posts.

So, we have seen how the transaction-level ports, analysis ports, and callbacks each have their own role to play in constructing a VMM transactor.

 

 

Posted in Communication, Transaction Level Modeling (TLM), VMM 1.2 | No Comments »

Blocking/Non-blocking Transport Adaption in VMM 1.2

Posted by John Aynsley on 1st September 2010

John Aynsley, CTO, Doulos

One neat feature of the SystemC TLM-2.0 standard is the ability provided by the so-called simple target socket to perform automatic adaption between the blocking and non-blocking transport calls; an initiator that calls b_transport can be connected to a target that implements nb_transport, and vice-versa. VMM 1.2 provides similar functionality using the vmm_connect utility.

vmm_connect serves four distinct purposes in VMM 1.2.

•    Connecting one channel port to another channel port, taking account of whether each channel port is null or actually refers to an existing channel object
•    Connecting a notify observer to a notification
•    Binding channels to transaction-level ports and exports
•    Binding a transaction-level port to a transaction-level export where one uses the blocking
transport interface and the other the non-blocking transport interface. This is the case we are considering here.

When making transaction-level connections I think of vmm_connect as covering the “funny cases”.

To illustrate, suppose we have a transactor that acts as an initiator and calls b_transport. The following code fragment also serves as a reminder of how to use the blocking transport interface in VMM:

class initiator extends vmm_xactor;
`vmm_typename(initiator)

vmm_tlm_b_transport_port  #(initiator, vmm_tlm_generic_payload) m_b_port;

virtual task run_ph;
forever begin: loop
vmm_tlm_generic_payload tx;

// Create valid generic payload
assert( randomized_tx.randomize() with {…} )
else `vmm_error(log, “tx.randomize() failed”);

$cast(tx, randomized_tx.copy());

// Send copy through port
m_b_port.b_transport(tx, delay);

// Check response status
assert( tx.m_response_status == vmm_tlm_generic_payload::TLM_OK_RESPONSE );


Further, suppose we have another transactor that acts as a target for nb_transport calls, and which therefore implements nb_transport_fw to receive the request and subsequently calls nb_transport_bw to send the response. The code fragment below is just an outline, but it does show the main idea that non-blocking transport allows timing points to be modeled by having multiple method calls in both directions (as opposed to a single method call in one direction for b_transport):

class target extends vmm_xactor;
`vmm_typename(target)

vmm_tlm_nb_transport_export #(target, vmm_tlm_generic_payload) m_nb_export;

// Implementation of nb_transport method
virtual function vmm_tlm::sync_e nb_transport_fw(
int id=-1, vmm_tlm_generic_payload trans,
ref vmm_tlm::phase_e ph, ref int delay);

-> ev;
return vmm_tlm::TLM_ACCEPTED;
endfunction : nb_transport_fw

// Process to send response by calling nb_transport on backward path
virtual task run_ph;
forever  begin: loop
vmm_tlm::phase_e phase = vmm_tlm::BEGIN_RESP;
@(ev);
tx.m_response_status = vmm_tlm_generic_payload::TLM_OK_RESPONSE;
status = m_nb_export.nb_transport_bw(tx, phase, delay);
end
endtask: run_ph

Now for the main point. We can use tlm_connect to bind the two components together, despite the fact that one uses blocking calls and the other non-blocking calls:

virtual function void build_ph;
m_initiator = new( “m_initiator”, this );
m_target    = new( “m_target”,    this );
endfunction: build_ph

virtual function void connect_ph;

vmm_connect #(.D(vmm_tlm_generic_payload))::tlm_transport_interconnect(
m_initiator.m_b_port, m_target.m_nb_export, vmm_tlm::TLM_NONBLOCKING_EXPORT);

endfunction: connect_ph

That’s all there is to it! Notice that tlm_transport_interconnect is a static method of vmm_connect so its name is prefixed by the scope resolution operator and an instantiation of the vmm_connect parameterized class with the appropriate transaction type, namely the generic payload. The first argument is the port, the second argument the export, and the third argument indicates that it is the export that is non-blocking.
It is also possible to connect a non-blocking initiator to a blocking target: you would simply replace TLM_NONBLOCKING_EXPORT with TLM_NONBLOCKING_PORT. The objective of this post has been to show that VMM has all the bases covered when it comes to connecting together transaction-level ports.

Posted in Interoperability, Reuse, SystemC/C/C++, Transaction Level Modeling (TLM), VMM 1.2 | No Comments »

Vaporware, Slideware or Software?

Posted by Andrew Piziali on 27th August 2010

The Role of the Technical Marketing Engineer in Verification

by Andrew Piziali, Independent Consultant

In our previous blog posts on the subject of verification for designers we addressed the role of the architect, software engineer and system level designer. We now turn our attention to perhaps the least understood—and oftentimes most vilified—member of the design team, the technical marketing engineer. But, before we explain why, what is the role of the technical marketing engineer? After all, not all companies have such a position.

The technical marketing engineer is responsible for determining customer product requirements and ensuring that these requirements are satisfied in the delivered product. They typically build product prototypes—initially slideware and later rudimentary functional code—that are evaluated by the customer while refining requirements. With each iteration the customer and engineer come closer to understanding precisely what the product must do and the constraints under which it must operate. This role differs from other traditional marketing functions such as inbound and outbound communication. Given that such a position exists, how does the technical marketing engineer contribute to the functional verification of a design?

Functional Verification Process

Although there are many definitions of functional verification, my favorite is one I recorded at Convex Computer Corporation some twenty years ago: “Demonstrate that the intent of a design is preserved in its implementation.” It is short, colloquial and simple:

  • • Demonstrate — Illustrate but not necessarily prove
  • • Intent — What behaviors are desired?
  • • Design — The various representations of the product before it is ultimately realized and shipped
  • • Preserve — Prevent corruption, scrambling and omission
  • • Implementation — Final realization of the product for the customer

The technical marketing engineer plays a key role in this process as demonstrated by one personal experience of mine in this role.

One company I worked for offered a product that assisted in producing clean, efficient, bug-free code through pre-compilation analysis. In this environment one could navigate through your code within its module (file) structure, as well as within its object structure. However, the programming language supported not only objects—language elements that are defined, inherited and instantiated–but also aspects—language elements that group object extensions into common concerns. I proposed adding an aspect browser to the product as a natural extension to its existing navigation facilities. The challenge was inferring the aspect structure of a program from its files and object structure because an aspect is not explicitly identified as such in the program.

Slide Presentation Model Use

I put together a slide presentation that illustrated the two existing navigation paradigms, as well as the proposed aspect navigation. The feature looked great from a user interface perspective, but could it be implemented? Since heuristics could be employed in identifying each aspect, I also illustrated each heuristic and its application by way of sample code and its structure. This animated slide presentation served as the first prototype demonstration of the aspect browser for the product. When reviewed with existing users, they were able to provide valuable feedback about the new feature and its utility and limitations. When subsequently referenced by the programmer implementing the feature, it served as a rough executable specification.

Returning to the vilified technical marketing engineer, why are some poor souls subject to this criticism? More often than not, the marketing engineer promised more capability, performance or features than could be delivered by the developers. It is easy to “Powerpoint” a feature that cannot be implemented so the marketing engineer must walk far enough down the implementation path to understand what is feasible. If they do, they will likely avoid this charge and remain a perceived asset by the design team. Moreover, their employer will retain a reputation for delivering quality “product-ware,” not vaporware or slideware!

Posted in Organization, Reuse, Verification Planning & Management | No Comments »

Using VMM Consensus to end your test

Posted by JL Gray on 23rd August 2010

Asif Jafri, Verification Consultant, Verilab

One topic that is often overlooked is how does one end a test. One common approach has been to use pound delays or count the number of transactions generated. While this worked well for directed test environments this approach is not well suited for use with constrained random testbenches. Usually there are several threads running in parallel and to be able to intelligently tell whether all test criterions are met, we need a more centralized approach to manage and decide test completion. vmm_group now has a mechanism to centrally manage test completion with the use of VMM Consensus.To better explain the usage let’s try to build an example:

1.    Instantiate a vmm_voter class to indicate consensus or oppose end of test.
vmm_voter     end_voter;
2.    Identify the participants that will need to consent before the test ends. These participants can be in the form of transactors which will consent when idle, channels consent when empty, notifications and vmm_consensus.  Add the voters in the vmm_group::build_ph()
vmm_consensus::register_*
3.    Add vmm_consensus::wait_for _consensus() to vmm_group::wait_for_end() method. Once all participants consent, the test will complete.
 

 

 


class tb_top extends vmm_group;
`vmm_typename(tb_top)vmm_voter    end_voter;
function void build_ph();
    transaction_channel     master_chan;
    slave_transactor          slave_xactor;
    end_voter =  end_vote.register_channel(master_chan);
    end_voter = end_vote.register_xactor(slave_xactor);
endfunction

task wait_for_end();
     super.wait_for_end();
     end_vote.wait_for_consensus();
endtask

endclass: tb_top 

The code above shows how we can instantiate various voters that will participate in the test completion.

The figure below shows how a participant opposes test completion, while all other participants have given consent.
 

 

 

image

One of the simplest forms of usage is to oppose completion using the command above before a completing some given task like programming registers or pulling reset and then giving consent by using the command:  this.consent(“Programming Completed”); There is often a need to force consensus to end a test if one of the opposing blocks is not releasing. This can be achieved by using the forced command.
 

 

 

image

You can choose to use the consensus_force_thru command to pass to propagate the force up.

image

Using these techniques to end your test will make your testbench scalable and reusable over various projects.

 

 

Posted in Coding Style, VMM | No Comments »

TLM and VMM – What is it all for?

Posted by John Aynsley on 18th August 2010

John Aynsley, CTO, Doulos

Having discussed some of the technical details of the implementation of the TLM-2 standard in VMM 1.2 in previous posts, it is time to stand back and ask what it is all for, especially considering that VMM must now co-exist alongside UVM in some projects. Fortunately, using the OSCI TLM standard as a basis for communication takes us a long way down the road toward interoperability, both between VMM and UVM and between VMM and SystemC.

We have seen that VMM 1.2 now supports the concept of ports and exports borrowed from SystemC and the closely related concept of sockets from the TLM-2 standard. Armed with these new concepts, VMM now offers two alternative communication mechanisms: communication between a producer and a consumer through an intermediate channel (the vmm_channel), and communication using direct function calls from producer to consumer (and vice versa) through ports and sockets. So what? Well, the benefits of direct communication were reviewed in an earlier post, but in summary, the simplicity of direct communication can speed up simulation by removing the number of context switches between processes and can provide an easy-to-follow completion model for knowing when a transaction is over. VMM users now have the choice between channel-based communication and direct communication. Channel-based communication works just fine, and may be preferred in native VMM environments. Direct communication is closer to the communication model used in UVM and in SystemC TLM-2.0, so may be preferred when working in a mixed environment, such as when integrating VMM and UVM components or when driving SystemC models from a VMM test bench.

We have also seen that VMM 1.2 supports the generic payload and extension mechanism from the SystemC TLM-2.0 standard. Frankly, this addition is unlikely to be of much interest in native VMM environments, but could be critical when it comes to including SystemC reference models in a VMM test bench. Say you have an existing SystemC reference model written to the TLM-2.0 standard, and you want to drive it from a VMM test bench. The new features in VMM 1.2 would allow you to construct a transaction within the VMM environment that has precisely the attributes expected by the TLM-2.0 standard generic payload, and then to pass that transaction over the fence from SystemVerilog to SystemC. It is possible to code the SystemVerilog-to-SystemC interface yourself using the DPI (for details see http://www.doulos.com/knowhow/sysverilog/DVCon10_dpi_paper) or to use the TLI (Transaction Level Interface) provided by Synopsys. I will say more about the TLI in a later post.

I have heard some VMM users say that they like the idea of being able to use the industry standard blocking transaction-level interface in VMM. Indeed, given the relatively small size of the overall hardware verification community and the cost of maintaining multiple standards, the sharing of ideas between standards in this way has to be a good thing. The early adopter release of UVM, the Universal Verification Methodology from Accellera, uses communication based on the SystemC TLM-1 standard. Even as I write this post, Accellera are considering the best way to incorporate the TLM-2-style interfaces as used in VMM into UVM, which should help make transaction-level communication between the two methodologies a little easier.

Posted in Reuse, SystemC/C/C++, Transaction Level Modeling (TLM), VMM 1.2 | No Comments »

TLM Sockets in VMM 1.2

Posted by John Aynsley on 12th August 2010

John Aynsley, CTO, Doulos

Having introduced both the blocking and the non-blocking transport interfaces in previous blog posts, it’s now time to look at TLM sockets in VMM 1.2.

In the SystemC TLM-2.0 standard, sockets are a mechanism that simplifies the instantiation and binding of multiple ports and exports for the various standard transaction-level interfaces by encapsulating several interfaces in a single object, the so-called socket. One initiator socket is bound to one target socket using one bind operator, and it is then possible to call all of the standard TLM-2.0 interface methods through that one pair of sockets. Moreover the socket has a BUSWIDTH parameter which represents the width of the data word transferred over the bus on each beat and which prevents sockets of different widths being inadvertently connected together.

Because VMM supports neither the full set of interfaces from the TLM-2.0 standard, nor the functionality of specified bus widths, nor the interoperability achieved by mandating the use of standard socket types, the value of sockets is considerably reduced in VMM compared to the SystemC TLM-2.0 standard. Nonetheless, the TLM sockets in VMM do allow both blocking and non-blocking transport method calls to be made through a single pair of sockets.

Let’s look at an example:

class initiator extends vmm_xactor;
`vmm_typename(initiator)
vmm_tlm_initiator_socket #(initiator, vmm_tlm_generic_payload) m_socket;

begin

m_socket.b_transport(tx, delay);

end

begin

status = m_socket.nb_transport_fw(tx, phase, delay);

end

virtual function vmm_tlm::sync_e nb_transport_bw( int id=-1,
vmm_tlm_generic_payload trans, ref vmm_tlm::phase_e ph, ref int delay);

endfunction : nb_transport_bw

endclass: initiator


As you can see above, the vmm_tlm_initiator_socket allows both b_transport and nb_transport_fw calls to be made through a single socket, as well as being able to accept incoming nb_transport_bw calls.

On the target side, the vmm_tlm_target_socket accepts incoming calls to both b_transport and nb_transport_fw as well as allowing outgoing calls to nb_transport_bw:


class target extends vmm_xactor;
`vmm_typename(target)
vmm_tlm_target_socket #(target, vmm_tlm_generic_payload) m_socket;

task b_transport(int id = -1, vmm_tlm_generic_payload trans, ref int delay);

endtask : b_transport

virtual function vmm_tlm::sync_e nb_transport_fw( int id=-1,
vmm_tlm_generic_payload trans, ref vmm_tlm::phase_e ph, ref int delay);

endfunction : nb_transport_fw


begin

status = m_socket.nb_transport_bw(tx, phase, delay);

end

endclass: target


Finally, at the top level, there is just a single pair of sockets to bind:

class tb_env extends vmm_group;
`vmm_typename(tb_env)
initiator  m_initiator;
target     m_target;

virtual function void build_ph;
m_initiator = new( “m_initiator”, this );
m_target    = new( “m_target”,    this );
endfunction: build_ph

virtual function void connect_ph;

m_initiator.m_socket.tlm_bind( m_target.m_socket );

endfunction: connect_ph

endclass: tb_env


That’s all there is to it!

The SystemC TLM-2.0 standard provides so-called convenient sockets, some of which can automatically convert incoming calls from b_transport to nb_transport and vice-versa. The VMM implementation achieves a similar result using the vmm_connect utility, which I will describe in a future post.

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

Issuing concurrent READ/WRITE accesses to the same register on two physical interfaces using RAL.

Posted by S. Varun on 9th August 2010

Currently, RAL does not schedule multiple concurrent READ/WRITE accesses to the same register at the same time. RAL
assumes all physical transactions to be atomic, i.e. a transaction is completed before the next cycle is started.
Even if we try to do a parallel READ, RAL will block one of the two accesses and schedule it sequentially. This is
to ensure that the accesses are not corrupted and also because of the fact that the main idea behind RAL is in
abstracting accesses to the registers.

However, in specific cases, there might be a verification requirement to create specific corner cases on the buses.

A typical corner case to check parallel access to a shared register would be as shown in the code snippet below,

Sample code :

     fork
       /* READ the CONTROL_REG via the APB interface */
       env.ral_model.blk_inst.CONTROL_REG.read(status, data , , "APB");

       /* Read the CONTROL_REG via the AHB interface */
       env.ral_model.blk_inst.CONTROL_REG.read(status, data , , "AHB");
     join

Our intention here is to query the control register from two different domains "APB" & "AHB" in parallel or write
through one interface and do a read access through another. But as already mentioned, a controlling semaphore
mechanism tied to each register will sequentially schedule the above two accesses. To create such corner cases we
can use the RAL proxy transactor with "vmm_ral_access::read/write()" which is not going be blocked as the semaphore
resides in the register. The code below illustrates how we can do this.

Sample code :

     fork
       /* READ the CONTROL_REG via the APB interface */
       env.ral.read(status, env.ral_model.blk_inst.CONTROL_REG.get_address_in_system("APB"), data, 32, "APB");

       /* READ the CONTROL_REG via the AHB interface */
       env.ral.read(status, env.ral_model.blk_inst.CONTROL_REG.get_address_in_system("AHB"), data, 32, "AHB");
     join

The "vmm_ral_reg::get_address_in_system()" method can be used here to generate the correct address corresponding
to the domain of interest and the same can be passed with the "vmm_ral_access::read()/write()" task. 

We can also do the following:

1)	Read CONTROL_REG through one of the interfaces using "env.ral_model.blk_inst.CONTROL_REG.read()" &
2)	Read the same register via vmm_ral_access::read() through the other interface as illustrated in the
        sample code below.

Sample code :

     fork
       /* READ the CONTROL_REG via the APB interface */
       env.ral_model.blk_inst.CONTROL_REG.read(status, data , , "APB");

       /* READ the CONTROL_REG via the AHB interface */
       env.ral.read(status, env.ral_model.blk_inst.CONTROL_REG.get_address_in_system("AHB"), data, 32, "AHB");
     join

Because the semaphore is in the register, the access through the proxy transactor is not going to be blocked. 

Also, if the DUT and physical transactor supports pipelined read/write accesses, the generic transactions can
also be executed concurrently. You must first enable this capability by defining the compile time macro,
VMM_RAL_PIPELINED_ACCESS.

Posted in Register Abstraction Model with RAL | No Comments »

Accessing Virtual Registers in RAL

Posted by Amit Sharma on 5th August 2010

Amit Sharma, Synopsys

In one of my previous posts on  Virtual Registers I talked about how you use RAL to model Virtual Registers or fields which are an efficient means of implementing large number of registers in  memory or RAM instead of individual flip-flops.  I also mentioned that they are  implemented as arrays associated with a memory.

In this post, I will talk about how you access these registers through RAL. Normal registers can be accessed using  the hierarchical name in RAL. For these, RAL  would generate the offsets and addresses required.  However, for virtual registers, along with  the virtual register name you need to provide the index ID to refer to them for read and write operations. Accordingly, RAL  generates the offset address based on the index ID of the virtual register.

Consider the following example:

image

The above RALF specification would translate into the following SV classes in the RAL model

 

image

‘DMA_BFRS’  which is the instance of the Virtual Registers class ‘ral_vreg_dut_DMA_BFRS’ is not an array in the RAL Model and is thus different from a typical register array modeled in RAL.

Now, how do you access the individual registers mapped to the memory?  You have to specify the index as the argument to the read/write methods. For example, To access the 4th index of this array, the access would like:

blk.DMA_BFRS.write(4, status, ‘hFF);

RAL would ultimately access the RAM to enable this access. Hence, the following are functionally equivalent:

blk.DMA_BFRS.write(4, status, ‘hFF);
and
blk.ram.write(4 * 0×0004 + 0×1000, status, ‘hFF);

The following illustration explains this point:

image

You can see that you now have an option to access these registers in different modes, but both will eventually go through the RAM. You can leverage the Virtual register Callbacks or the RAL memory callbacks for additional customizations and extensibility, in addition to other capabilities that you get when you are using VMM RAL.

Hope, this was useful.. do comment on any other specific functionality that you might look at when modeling these kind of registers.

Posted in Memory Management, Register Abstraction Model with RAL | No Comments »

Low Power Verification for telecom designs with VMM-LP

Posted by Paul Kaunds on 2nd August 2010

Paul Kaunds, founder, Kacper Technologies

Today power has become a dominant factor for most applications including hand-held portable devices, consumer electronics, communications and computing devices. Market requirements are shifting focus from traditional constraints like area, cost, performance and reliability to power consumption.

For efficient power management, power intent has to be reviewed at each phase along the design flow — starting from RTL, during synthesis, and in place and route in the physical design. Power specifications of the design will be common throughout the flow. In order to implement uniform power flow, we need a common format which is easily understood by all tools used along the flow with reusability and that can be declared independent of the RTL. The creation of the UPF (unified power format) as the IEEE standard has been the best solution for specifying power intent for design implementation as well as verification. UPF sits beside design and verification, and develops relationship between the design specifications and low power specifications.

Understanding the driving factors, designers have opted for advanced low power design techniques like Power Gating, Multi Supply Multi Voltage (MSMV), Power-Retention, Power-Isolation etc. Such low power constraints have increased design complexity, which have a direct impact on verification. This makes the verification engineers’ job more challenging as they have to verify the power intent bugs along with functionality.

Some of the low-power design verification challenges are:

  • · Power switch off/on duration
  • · Transitions between different power modes and states
  • · Interface between power domains
  • · Missing of level shifters, isolation and retention
  • · Legal and illegal transitions
  • · Clock enabling/toggling
  • · Verifying retention registers and isolation cells and level shifter Strategies

To tackle these challenges, we need a structured and reusable verification environment which is power aware and encapsulates the best practices when verifying such complex low power designs. To address the low power requirements of one of the complex telecom designs, we made use of (Verification Methodology Manual for Low Power (VMM-LP) ) for SONET/SDH verification. (http://www.vmmcentral.org/vmmlp/vmmlp.html)

We developed a framework for accelerating the verification of low power designs. Our verification environment included VMM along with UPF, RAL, and a power state manager controlled by a power management unit.


Generation of Power control signals

Using extensive features provided by VMM, we developed a VMM based power generator to enable the user to generate the power control signals as random, constrained random and directed to verify power management blocks, and implement low power strategies like Retention Registers and Isolation Cells. This power generator was designed with a creed that it can be hooked into any VMM based environment. It provides the user the ability to generate power signals without any hassles in generating the complex power control sequencing of such signals.

 

Power Scenario generation

Power scenario generation eased the verification of power aware designs. To generate power signals, a user can simply enter the ranges in a scenario file depending on power specifications. The same generator can be used along with any other VMM environment without any modifications for conventional verification. Some of the capabilities that can be made configurable in this scenario include: varying retention, isolation and power widths.  Additionally, other key capabilities were enabled:

  • · Adheres to standard power sequence as specified in VMM_LP
  • · All the power signals and power domains are parameterizable
  • · Power Generator can be hooked to any VMM environment
  • · Gives a well defined structure to define power sequences
  • · Avoids overlapping of control signals
  • · Allows multiple save, restore in each sequence

The Power state manager takes care of state transition operations by defining power domains, states, modes and their dependencies. It is the key holder for all power transitions that can monitor all transitions dynamically.


Automated Power Aware Assertion Library

Another mechanism to address the challenges in low power verification is the usage of powerful LP assertions. Low power assertions are dealt in conjunction with design’s logic data checkers. VMM-LP assertion rules and coverage techniques helped us to achieve a comprehensive low power verification solution. It suggested some handy recommendations to ensure a consistent and portable implementation of the methodology.

To increase our efficiency of Low Power Telecom design verification, we also developed an automated Power Aware Assertion Library which is generic to any domain and can be hooked to any design. Assertion library is built on top of VMM_LP rules and guidelines with standard power sequencing. Key features of our Assertion library include:

  • · Assertions to verify access of software addressable registers in on/off conditions
  • · Clock toggling during power on/off
  • · Reset during power off
  • · Power control signals sequencing etc.


More details on our flow and usage can be found online in the paper we recently presented at the Synopsys User Group meeting (SNUG) in Bangalore:

https://www.synopsys.com/news/pubs/snug/india2010/TA3.1_Kaunds_Paper.pdf
https://www.synopsys.com/news/pubs/snug/india2010/TA3.1_kaunds_pres.pdf

Posted in Low Power, Register Abstraction Model with RAL | 3 Comments »