Verification Martial Arts: A Verification Methodology Blog

Archive for the 'Coding Style' Category

Avoiding Redundant Simulation Cycles with your UVM VIP based simulation with a Simple Save-Restore Strategy

Posted by paragg on 6th March 2014

In many verification environments, you reuse the same configuration cycles across different testcases. These cycles might involve writing and reading from different configuration and status registers, loading program memories, and other similar tasks to set up a DUT for the targeted stimulus. In many of these environments, the time taken during this configuration cycles are very long. Also, there is a lot of redundancy as the verification engineers have to run the same set of verified configuration cycles for different testcases leading to a loss in productivity. This is especially true for complex verification environments with multiple interfaces which require different components to be configured.

The Verilog language provides an option of saving the state of the design and the testbench at a particular point in time. You can restore the simulation to the same state and continue from there. This can be done by adding appropriate built in system calls from the Verilog code. VCS provides the same options from the Unified Command line Interpreter (UCLI).

However, it is not enough for you to restore simulation from the saved state. For different simulations, you may want to apply different random stimulus to the DUT. In the context of UVM, you would want to run different sequences from a saved state as show below.

In the above example apart from the last step which varies to large extent, the rest of the steps once established need no iterations.

Here we explain how to achieve the above strategy with the simple existing UBUS example available in the standard UVM installation. Simple changes are made in the environment to show what needs to be done to bring in this additional capability. Within the existing set of tests, the two namely, “test_read_modify_write” and “test_r8_w8_r4_w4”, differs only w.r.t the master sequence being executed – i.e. “read_modify_write_seq” and “r8_w8_r4_w4_seq” respectively.

Let’s say that we have a scenario where we would want to save a simulation once the reset_phase is done and then start executing different sequences post the reset_phase the restored simulations. To demonstrate a similar scenario through the UBUS tests, we introduced a delay in the reset_phase of the base test (in a real test, this may correspond to the PLL lock, DDR Initialization, Basic DUT Configuration).

The following snippet shows how the existing tests are modified to bring in the capability of running different tests in different ‘restored’ simulations.

As evident in the code we made two major modifications.

  • Shifted the setting of the phase default_sequence from the build phase to the start of the main phase.
  • Get the name of the sequence as an argument from the command-line and process the string appropriately in the code to execute the sequence on the relevant sequencer.

As you can see, the changes are kept to a minimum. With this, the above generic framework is ready to be simulated.  In VCS, one of the different ways, the save/restore flow can be enabled as follows.

Thus above strategy helps in optimal utilization of the compute resources with simple changes in your verification flow. Hope this was useful and you manage to easily make the changes in your verification environment to adopt this flow and avoid redundant simulation cycles.

Posted in Automation, Coding Style, Configuration, Creating tests, Customization, Optimization/Performance, Organization, Reuse, Stimulus Generation, SystemVerilog, Tutorial, UVM, Uncategorized, Verification Planning & Management | 1 Comment »

SNUG-2012 Verification Round Up – Miscellaneous Topics

Posted by paragg on 29th March 2013

In my final installment of the series of blogs summing up the various SNUG verification papers of 2012, I try to cover the user papers on the Design IP/Verification IP and SystemC and SystemVerilog co-simulation. Please find my earlier blogs on the other domains here: System Verilog Language, Methodologies & VCS technologies

DesignWare core USB3.0 Controller (DWC_usb3) can be configured as a USB3.0 Device Controller. When verifying a system that comprises a DWC_usb3 Device Controller, the verification environment is responsible for bringing up the DWC_usb3 Device Controller to its proper operation mode to communicate with the USB3.0 Host. The paper Integrating DesignWare USB3.0 Device Controller In a UVM-based Testbench from Ning Guo of Paradigm Works describes the process of configuring and driving the DWC_usb3 Device Controller in a UVM based verification environment using the Discovery USB 3.0 Verification IP. This paper describes how the verification environment needs to be created so that it’s highly configurable and reusable.

The AMBA 4 ACE specification enables system level cache coherency across clusters of multicore processors, such as the ARM Cortex-A15 and Cortex-A7 MPCore™ processors .This ensures optimum performance and power efficiency of complex SoC designs. However, the design complexity associated with these capabilies is also higher.  And it throws up new verification challenges.  In the paper, Creating AMBA4 ACE Test Environment With Discovery VIP”, Whitney Huang, Sean Chou, MediaTek Inc, demonstrates how to tackle complex verification challenges increase their verification productivity by using Synopsys Discovery AMBA ACE VIP.

The paper, Verification Methodology of Dual NIC SOC Using VIPs by A.V. Anil Kumar, Mrinal Sarmah, Sunita Jain of Xilinx India Technology Services Pvt. Ltd, talks about how various features of Synopsys PCIe and Ethernet Verification IPs can be exploited to help in the efficient verification of the DUT across various traffic configurations. The paper explores how the VIP Application Programming Interface (API)s can leveraged in the  tests cases to reach high  functional coverage numbers  in a very short duration. They also show how a dual NIC verification environment can effectively use Ethernet VIP APIs to test various Media Access Control (MAC) features. Finally conclude how of the implementation can be used across future revisions of their design.

The ability to analyze the performance of the SoC at the early stage of the design can make a significant different to the end product.  This can lead to more accurate and an earlier estimate of the desired performance that is expected.  Dayananda Yaraganalu Sadashivappa, Igal Mariasin, Jayaprakash Naradasi of SanDisk India Device Design Centre Pvt. Ltd., in the paperGeneric MLM environment for SoC Performance Enhancement”, outlines the solution that was found by using the Synopsys VIP models. The VIPs were used in conjunction with interconnect, which in this case is a Multi-Layer-Matrix (MLM). The environment was built leveraging the VMM base classes. The VMM multiple stream scenario (vmm_ms_scenario) base class was used to create the traffic across the matrix, and the performance meters were constructed using the base classes. The callbacks were leverage appropriately help in collating the statistics. Multiple knobs were used to make the environment generic and configurable. The approach helped in finding multiple performance bugs which could not have been easily found using conventional verification.

In the paper, “User Experience Verifying Ethernet IP Core”, Puneet Rattia of Altera Corporation, presents his experience with verifying the Altera® 40-100Gbps Ethernet IP core utilizing VMM environment while integrating the Ethernet VIP from Synopsys. He explains how he created a full suite of system and blocks level regression tests and then goes on to show how he  utilizes the coverage mapping capabilities of VCS to merge the results across these various testbenches and produce meaningful reports. Besides showing how to reuse the verification infrastructure at the SoC level, the paper also demonstrates how they went in for horizontal reuse by integrating the reference SystemC based models developed and prototyped in the early phase of the project.

UVM 1.x includes support for the communication interfaces defined by the SystemC TLM-2.0 standard. This enables integration of SystemC TLM-2.0 IP into a SystemVerilog UVM verification environment. Dr David Long, John Aynsley, Doug Smith, Doulos in the paper A Beginner’s Guide to Using SystemC TLM-2.0 IP with UVMdescribes how this is done best. They talk about the fact that the connection between SystemC and SystemVerilog currently requires a tool specific interface such as Synopsys Transaction Level Interface (TLI). This paper begins with a brief overview of TLM-2.0 aimed at novice users. It then discusses the steps required to add a SystemC TLM-2.0 model into a SystemVerilog UVM environment and simulate it with VCS. At each step, issues that users will face are explored and suggestions made for practical fixes, showing the relevant pieces of code. Finally, the paper gives a summary of areas where the UVM implementation of TLM-2.0 differs from the SystemC standard and proposes workarounds to ensure correct communication between the SystemVerilog and SystemC domains.

There is an inherent need to enable the horizontal reuse of components created during the architecture and exploration stage. Subhra S Bandyopadhyay, Pavan N M, Intel Technology India Pvt. Ltd, in Integrating SystemC OSCI TLM 2.0 Models to OVM based System Verilog Verification Environments talks about how  theur architecture team creates SystemC models  for early performance analysis and accelerated software development. In OVM-based verification environment, the objective was to reuse this model as a reference model and thus helped in reducing the overall environment bring-up time. The challenge was not only to integrate the SystemC model in the OVM-based verification environment but also to be able to efficiently send transactions from SV to SystemC and vice versa. This paper explores the successful integration of SystemC TLM2 components in OVM based verification environments and also highlight how the VCS TLI (Transaction Level Interface) adapters help TLM2.0 sockets in SystemC to communicate with those in SV and vice versa.

Truly, I feel overwhelmed by the numbers of papers and the interesting use of technology across a variety of domains on which user share their experiences across the various SNUG conferences. As we speak, the SNUG events for 2013 have started, and the stage is all set for a new set of very informative and interesting sessions. I am sure most of you would be attending the SNUIG conferences in your area. . You can find the detailed schedule of those here.

Posted in Announcements, Automation, Callbacks, Coding Style, Communication, Reuse, Structural Components, SystemC/C/C++, SystemVerilog, Transaction Level Modeling (TLM), Tutorial, UVM, VMM | Comments Off

Coverage coding: simple tips and gotchas

Posted by paragg on 28th March 2013

Author - Bhushan Safi (E-Infochips)

Functional coverage has been the most widely accepted way by which we track the completeness of any constrained random testbench. However, does achieving 100% functional coverage means that the DUV is bug free? Certainly not , but it boosts the confidence of the verification engineer and management team.

Based on my experience of defining functional covergroups for different projects, I realized that coverage constructs and options in the SystemVerilog language have their own nuances for which one needs to keep an eye out. These “gotchas” have to be understood so that coverage can be used optimally to achieve appropriate usage results in correct alignment with the intent desired. Let me talk about some of these finer aspects of coverage so that you can use the constructs more productively.

Usage of ignore_bins

The ‘ignore_bins’ construct is meant to exclude a collection of bins from coverage.  While using this particular construct, you might end up with multiple ‘shapes’ issues (By ‘shapes’ I mean “Guard_OFF” and “Guard_ON”, which appears in the report whenever ‘ignore_bins’ is used). Lets look at a simple usage of ignore_bins is as shown in figure 1.

Looking at the code in figure 1, we would assume that since we have set “cfg.disable = 1” the bin with value 1 would be ignored from the generated coverage report.  Here we use the ‘iff’ condition to try to match our intent of not creating a bin for the variable under the said condition.  However in simulations, where the sample_event is not triggered, we see that we end up having an instance of our covergroup which still expects both the bins to be hit. (See the generated report in figure 1). Why does this happen? If you dig deep into the semantics, you will understand that the “iff” condition will come into action only when the event sample_event is triggered. So if we are writing  ‘ignore_bins’ for a covergroup which may/may not be sampled on each run then we need to look for an alternative.  Indeed there is a way to address this requirement and that is through the usage of the multi-mastered intelligent ternary operator. Look at the code in figure 2 to see how the ternary operator is used to model the same intent.

Now the report is as you expect!!!

Using the above mentioned coding style we make sure that the bin which is not desired in specific conditions is ignored irrespective of the condition of whether or not the covergroup is being sampled.  Also, we use the value “2’b11” to make sure that we don’t end up in ignoring a valid value for the variable concerned.

Using detect_overlap

The coverage option called “detect_overlap” helps in issuing a warning if there is an overlap between the range list (or transition list) of two bins of a coverpoint. Whenever we have plenty of ranges to be covered, and there is a possibility of overlap, it is important to use this option.

Why is it important and how can you be impacted if you don’t use it? You might actually end up with incorrect and unwanted coverage results!

Let’s look at an example. In the above scenario, if a value of 25 is generated, the coverage scores reported would be 50% when the desired outcome would ideally have been 25%. This is because the value ‘25’ contributes to two bins out of four bins when that was probably not wanted. The usage of ‘detect_overlap’ would have warned you about this and you could have fixed the bins to make sure that such a scenario doesn’t occur.

Coverage coding for crosses and assigning weight

What does the LRM (Table 19-1—Instance-specific coverage options) say about the ’weight’ attribute?  

If set at the covergroup syntactic level, it specifies the weight of this covergroup instance for computing the overall instance coverage of the simulation. If set at the coverpoint (or cross) syntactic level, it specifies the weight of a coverpoint (or cross) for computing the instance coverage of the enclosing covergroup. The specified weight shall be a non-negative integral value.”

What kinds of surprises can a combination of cross and option.weight create?

The SystemVerilog LRM shows a very simple way of writing a cross. Let’s look at the code below.

The expectation here is that for a single simulation (expecting one of the bins to be hit), we will end up with 25 % coverage as we have specified the weight of the individual coverpoints to zero. However, what essentially happens is the following, 2 internal coverpoints for check_4_a and check_4_b are generated, which are used to compute the coverage score of the ‘crossed’ coverpoint here. So you’ll end up having a total of four coverpoints, two of which have option.weight specified to 0 (i.e. CHECK_A and CHECK_B) and two of which are coverpoints with option.weigh as 1 (i.e. check_4_a and check_4_b). Thus for a single simulation, you will not get the 25% coverage desired.

Now with this report we see the following issues:

  • => We see four coverpoints while expectation is only two coverpoints
  • => The weights of the individual coverpoints is set to be expected to zero as option.weight is set to ‘0’
  • => The overall coverage numbers are undesired.

In order to avoid above disastrous results we need to take care of following aspects:

  • => Use the type_option.weight = 0, instead of option.weight = 0.
  • => Use the coverpoint labels instead of coverpoint names to specify the cross.

Hope my findings will be useful for you and you will use these options/attributes appropriately to get the best value out of your coverage metrics (without losing any sleep or debug cycles to figure out why they didn’t behave as you expected them to)!

Posted in Coding Style, Coverage, Metrics, Customization, Reuse, SystemVerilog, Tutorial | 9 Comments »

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:


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, VMM infrastructure | Comments Off

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()
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);

task wait_for_end();

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.




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.




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


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



Posted in Coding Style, VMM | 2 Comments »

Using vmm_opts to create a configurable environment

Posted by S. Prashanth on 19th July 2010

S. Prashanth, Verification & Design Engineer, LSI Logic

To accommodate changing specifications and to support different clusters/subsystems which would have multiple processors/memory connected through a bridge), I am building a reusable environment which can support any number of masters and slaves of any standard bus protocols.  The environment requires high level of configurability since it should work for different DUTs. So, I decided to use vmm_opts not just to set the switches globally/hierarchically, but also to specify the ranges (like setting address ranges in scenarios) from the test cases/command line.

I created a list of controls/switches that need to be provided as global options (like simulation timeout, scoreboard/coverage enable, etc)  and hierarchical options  to control instance specific behaviors (like number of transactions to be generated ,  burst enables, transaction speed, address ranges to be generated by a specific instance, set of instances, etc).  Let’s see how these options can be used and what all things are required for it through examples.

Global Options

Step 1:
Declare an integer, say simulation_timeout in the environment class or wherever it is required and use vmm_opts::get_int(..) method as shown below. Specify a default value as well just in case, if the variable is not set anywhere.

simulation_timeout = vmm_opts::get_int(“TIMEOUT”, 10000);

Step 2:
Then either at the test case or/and at the command line, I can override the default value using vmm_opts::set_int  or +vmm_opts+ runtime option.

Override from the test case.

vmm_opts::set_int(“%*:TIMEOUT”, 50000);

Override from the command line.

./simv +vmm_opts+TIMEOUT=80000

Options can also be overridden from a command file, if it is difficult to specify all options in the command line. Also, options can be a string or Boolean as well.

Hierarchical Options

In order to use hierarchical options, I am building the environment with parent/child hierarchy which is a very useful feature of vmm_object. This is required since the hierarchy specified externally (from test case/command line) will be used to map the hierarchy in the environment.

Step 1:
Declare options, say burst_enable and num_of_trans in a subenv class and use vmm_opts::get_object_bit(..) and vmm_opts::get_object_int(..) to retrieve appropriate values passing current hierarchy and default values as arguments. Also, for setting ranges, declare min_addr and max_addr, and use get_object_range().

class master_subenv extends vmm_subenv;
bit burst_enable;
int num_of_trans;
int min_addr;
int max_addr;

function void configure();
bit is_set; //to determine if the default value is overridden
burst_enable = vmm_opts::get_object_bit(is_set, this, “BURST_ENABLE”);
num_of_trans = vmm_opts::get_object_int(is_set, this, “NUM_TRANS”, 100);
vmm_opts::get_object_range(is_set, this, “ADDR_RANGE”, min_addr, max_addr, 0, 32’hFFFF_FFFF);


Step 2:
Build the environment with parent/child hierarchy either by passing the parent handle through the constructor to every child, or using vmm_object::set_parent_object() method. This is easy as almost all base classes are extended from vmm_object by default.

class  dut_env extends vmm_env;
virtual function build();
mst0 = new(“MST0”, ….);
mst1 = new(“MST1”, …);

Step 3:
From the test case, I can override the default value using vmm_opts::set_bit or vmm_opts::set_int(..) specifying the hierarchy. I can use pattern matching as well to avoid specifying to every instance

vmm_opts::set_int(“%*:MST0:NUM_TRANS”, 50);
vmm_opts::set_int(“%*:MST1:NUM_TRANS”, 100);
vmm_opts::set_range(“%*:MST1:ADDR_RANGE”, 32’h1000_0000, 32’h1000_FFFF);

I can also override the default value from the command line as well.
./simv +vmm_opts+NUM_TRANS=50@%*:MST0+NUM_TRANS=100@%*:MST1+burst_enable@%*

In summary, vmm_opts provides a powerful and user friendly way of configuring the environment from the command line or the test case. User can provide explanation of each of the options while calling get_object_*() and get_*() methods which will be displayed when vmm_opts::get_help() is called.

Posted in Coding Style, Configuration, Tutorial | Comments Off

VMM data macros are cool, but how do I customize the constructor?

Posted by Shankar Hemmady on 27th August 2009

Srinivasan VenkataramanPawan BellamkondaSrinivasan Venkataramanan, CVC

Pawan Bellamkonda, Brocade

During our recent VMM training at CVC, we learned about VMM data member macros, and our engineers liked it. Some of our teams at Brocade have started adopting it in their projects right away! We see that we can avoid much of the lengthy code and increase readability with these new macros. We will surely avoid making silly mistakes which might be hard to debug later.

However as with any built-in automation, there are always scenarios where-in user level customization of some or all of the methods is required. VMM provides this flexibility for overriding the default behavior of virtual methods of vmm_data class. In one of our blocks we needed to tweak the constructor of the transaction. One question that perplexed us was:

“I have built a transaction class extending from vmm_data. We have used the short hand macro `vmm_data_member…..  to get all the functions automatically. But while creating an object of this transaction, we want to pass a configuration class object as argument in the new function. How should we override the new() function alone when we use the short hand macros? When we tried using do_new() (like overriding other functions), it did not work.”

As we explored a bit, we found another macro specifically meant for this:


This macro should be used before the beginning of data-member macros. This lets the succeeding macros do all the work except the “new” function implementation.

class s2p_xactn extend vmm_data;
rand bit [7:0] pkt_len, pkt_pld;

function new(int my_own_arg = 2);
`vmm_note (log, $psprintf (“my val is %0d”, my_own_arg));
endfunction : new

`vmm_data_member_scalar(pkt_len, DO_ALL)
`vmm_data_member_scalar(pkt_pld, DO_ALL)
endclass : s2p_xactn

As with traditional martial arts, functional verification too has some slightly different styles/requirements that makes each project interesting and unique. To its credit, we feel that VMM is as proven as traditional martial arts: it can be tailored to different requirements while providing a standardized means of combat.

Posted in Automation, Coding Style, Customization, Modeling | 2 Comments »

VMM VIP’s on multiple buses

Posted by Adiel Khan on 27th May 2009


Adiel Khan, Synopsys CAE

Increasingly, more design-oriented engineers are writing VMM code. Some are trying to map typically good design architecture practices to verification development.

A dangerous mapping is parameterization, from modules to classes.

In my old Verilog testbenches I would develop reusable modules and use #parameters extensively to control the settings of the modules I was instantiating. (It was a sad day when I heard IEEE was deprecating my friend the defparam).

1. module vip #(parameter int data_width = 16,

2. parameter int addr_width = 16)

3. (addr, data);

4. output [addr_width-1:0] addr;

5. inout [data_width-1:0] data;


7. endmodule

This would allow me to instantiate this VIP for many bus variants.

8. vip #(64, 32) vip_inst1(…);

9. vip #(32, 128) vip_inst2(…);

Mapping the approach from modules to classes, I could end up with:

1. class pkt_c #( parameter int data_size=16,

2. parameter int addr_size=16)

3. extends vmm_data;

4. rand bit [addr_size-1:0] addr;

5. rand bit [data_size-1:0] data;


7. endclass

8. //specialized class with 64 & 32 sizes

9. pkt_c #(64, 32) pkt1=new();

10. //specialized class with 32 & 128 sizes

11. pkt_c #(32, 128) pkt2=new();

Be warned, in the SystemVerilog testbench centric view of VIP reusability, parameterization of classes leads to a dead-end path. Moving one layer of abstraction up, I really don’t care if it is a 32/64/128 bits wide interfaces. What I want to do is use pkt_c around the verification environment. The simplest case is creating a reusable driver using pkt_c to drive any bus-width interface.

However, if I try to use a generic class instantiation, I will get a specialization with parameters = 16&16. I cannot perform the $cast() to put the right pkt_c type onto the bus.

1. class pkt_driver_c extends vmm_xactor;

2. virtual protected task main();

3. forever begin : GET_OBJ_TO_SEND

4. pkt_c pkt_to_send; //default class instance

5. pkt_c #(64, 32) pkt_created;

6. randomize(pkt_created);// generator code

7. $cast(pkt_to_send, pkt_created); //FAILS !!!!!

If you are using VMM channels, they must similarly be specialized and cannot carry generic parameterized classes:

8. `vmm_channel(pkt_c)

9. class pkt_driver_c extends vmm_xactor;

10. pkt_c_channel in_chan; //Can only carry pkt_c#(16,16)!!!

Or you must upfront select which specialization you want for use with a parameterized channel.

8. class pkt_driver_c extends vmm_xactor;

9. vmm_channel_typed #(pkt_c#(64, 32)) in_chan;

Hence, for the driver to operate on the correct object type, I need to instantiate the exact specialization throughout my entire environment and make the driver itself parameterized. Now you can clearly see instantiating a specific specialization in the driver (or monitor, scoreboard etc) stops the code from being really reusable for other bus_widths.

1. pkt_c #(64, 32) pkt_to_send;

2. pkt_driver_c #(64, 32) driver;

A better approach is one that was described by Janick in the “Size Does Matter” blog of using `define. Let’s expand on this and see how it works for reusable VIPs. Well, the first thing that comes to my mind is that a `define is a global namespace macro with a single value, whereas I am using my VIP with 2 different bus architectures. Therefore, the `define alone is not enough: you also need a local constant to be able to exclude unwanted bits when you have a VIP instantiated for various bus widths.

1. //default define values

2. `define MAX_DATA_SIZE 16

3. `define MAX_ADDR_SIZE 16

4. class pkt_c extends vmm_data;

5. static vmm_log log = new(“Pkt”, “class”);

6. //instance constant to control actual bus sizes

7. const int addr_size;

8. logic [`MAX_ADDR_SIZE:0] addr;

9. logic [`MAX_DATA_SIZE:0] data;

10. // pass a_size as arg to coverage

11. // ensuring valid coverage ranges.

12. covergroup cg (int a_size);

13. coverpoint addr

14. {bins ad_bin[] = {[0:a_size]};}

15. endgroup

16. // sizes specialized at construction for pkts

17. // on buses less than MAX bus widths

18. function new(int a_s=`MAX_ADDR_SIZE);

19. addr_size = a_s;

20. cg = new(addr_size);

21. `vmm_note(log, $psprintf(“\nADDR_TYPE: “,$typename(addr),

22. “\nDATA_TYPE: “,$typename(data),

23. “\nMAX_BUS_SIZE: “, addr_size));

24. endfunction

The code above allows for a default implementation and all the user needs to do is set the `MAX_ADDR_SIZE and `MAX_DATA_SIZE symbols and all the code will be fully reusable across drivers, monitors, subenv, SoC etc.

For situations where two VIP’s of differing bus architectures are used, the compiler symbols need to be set to the biggest bus architecture in the system; smaller bus-widths are set using addr_size. It is not necessary for addr_size variable to be an instance constant or set during construction. By using instance constants, this ensures the bus-widths are not changed at runtime by users. Having the value of addr_size set during construction gives the users the flexibility to setup the object as they want. For pseudo-static objects such as drivers, monitors, subenvs, masters, slaves, scoreboards etc you should check the construction of verification modules for your particular design architecture during the vmm_env::start phase.

N.B not shown above, but assumed, is that the addr_size variable would be used to ensure correct masking occurs when performing do_pack(), do_unpack() compare() etc.

Just to wrap up some loose ends…

I’m not totally discounting the merits of parameterized classes just insuring people look at all the options. For instance you could parameterize everything and then set the SIZE at the vmm_subenv level and map the SIZE parameters to all other objects. At some point you will want to monitor or scoreboard across different bus-widths and then the parameterized class casting will bite you, reducing you to manually mapping the members within the comparison objects. There is a time and place for everything, so probably need another blog showing merits and where to use parameterized classes.

The vmm_data class is not the only place you might need to know the size of the bus, the same `define & instance constant technique can be used throughout your VIP classes.

This blog does not discuss the pros and cons of putting coverage groups in your data object class. I merely used the covergroup in the data-object as a vehicle to demonstrate how you can make your classes more reusable. I think a separate blog about where best to put coverage will clarify the usage models.

All the code snippets can be run with VCS-2009.06 & VMM1.1. Contact me for more complete code examples and bugs or issues you find.

Posted in Coding Style, Configuration, Register Abstraction Model with RAL, Reuse, Structural Components, VMM | 8 Comments »

Size does matter

Posted by Janick Bergeron on 6th July 2008

The VMM Register Abstract Abstraction layer is documented with a 64-bit data value system. For example, the write() method in the vmm_ral_field class is documented as:

task vmm_ral_field::write(output vmm_rw::status_e status,
                          input  bit [63:0]       value,

However, to be able to handle fields (and registers and virtual registers) that are wider than 64 bits, the implementation uses the VMM_RAL_DATA_WIDTH macro to define the maximum size of a field (and register and virtual register):

class vmm_ral_field;
   local bit [`VMM_RAL_DATA_WIDTH-1:0] value;
   task vmm_ral_field::write(output vmm_rw::status_e              status,
                             input  bit [`VMM_RAL_DATA_WIDTH-1:0] value,

Of course, the macro is defined by default to “64″, unless you define it otherwise:

   `define VMM_RAL_DATA_WIDTH 64

Normally, you don’t really need to worry about the value of this macro, as SystemVerilog will silently extend or trim the value assigned to the value argument (or returned via that same argument in the read() method). That works fine if you know a priori the size of the field and use an appropriately sized expression or variable for it.

However, when writing generic code that needs to work with unknown field sizes, you must be careful to declare any temporary or working variable using:

bit [`VMM_RAL_DATA_WIDTH-1:0] tmp;

to avoid inadvertently losing data. You can get the actual size of the field by using the vmm_ral_field::get_n_bits() method. See the pre-defined register tests in $VMM_HOME/sv/RAL/tests for examples.

All of the above is documented in Chapter 16 “Maximum Data Size” of the RAL User’s Guide.

But why not make the size of the field a parameter and do away with the macro altogether?

You mean beside the fact that VCS did not support parameterized class at the time RAL was created? :-)

There are a few reasons…

One could be the potential memory saving by making the data members holding the mirrored field value match the actual size of the field. However, experiments on a customer RAL model with hundreds of thousands of fields (with a VMM_RAL_DATA_WIDTH defined to 512) did not show a significant memory saving (at least with VCS).

The other is that the notion of an absolute maximum field size would still be required to be able to write generic code, independent of their respective sizes. It is impossible to operate on a field through a parameterized class unless its size is known a priori because you must specialize1 a parameterized class whenever you use it. It would thus still be necessary to create a size-generic field base class that can deal with any size of fields (so a function such as vmm_ral_reg::get_fields() can be provided). As a user, you would still need to know about and use the VMM_RAL_DATA_WIDTH macro.

Finally, the automatic value resizing only works for input, output and inout arguments. A ref argument (such as the ones use in the field pre/post_read/write callback methods) require that a variable of the exact same size be used. Again, this would require knowing, a priori, how large a field is before you could write code for it.

So my current line of thinking is that class parameters are fine for generic types, but not that useful for generic sizes. Or am I all wet?

1and that specialization must be defined at compile-time because you can’t use a run-time expression to specialize a class. For example, you could not call get_n_bits() to obtain the size of the field then use the result to specialize a parameterized class to access it.

Posted in Coding Style, Register Abstraction Model with RAL, Reuse, SystemVerilog | 4 Comments »

Do they diss this “this”?

Posted by Janick Bergeron on 9th June 2008

One of the issues with open-sourcing, is that everyone gets to see the code you write.

So now that you have had the chance to look at the VMM source code, you probably have noticed what a former co-worker once described as “a compulsive use of “this” in method implementations“. Why is it that I indeed compulsively use “this” whenever I refer to a data member in a class? As with every coding guideline, there are pros and cons. This one is no exception.

First of all, a bit of background, for those of you who may not be familiar with “this”… Those of you who are, can skip forward…

When referring to a variable or function/task that is declared as a member of a class from within that same class, you can prefix the reference with “this.”. For example:

class packet;
bit [47:0] da;
function new(bit [47:0] da);
this.da = da;
function void display();
$write("DA = %h\n", this.da);

“this” is an implicitly defined handle to the object instance that is executing the procedural code. In the constructor of the above example, “this.da” is an explicit reference to the “packet::da” variable contained in the class. Using the “this.” prefix makes it clear that I am not referring to the “da” argument. In this case, the use of “this.” is not optional as it is needed to differentiate between the constructor argument and the data member. It also avoids having to come up with different names for the constructor argument and data member when in fact they both represent the same thing.

I choose to use the “this.” prefix even in cases where it is clearly optional. For example, in the “packet::display()” method, because there are no other variables named “da” in the scope of the function, it would be clear that a simple reference of “da” would refer to the packet::da variable.

I always use the “this.” prefix to document that the variable or method referred to is a member of the class and not some variable/task/function inherited from some larger scope. In the simple example above, it is easy to see that the “da” variable is a class data member. But what if the procedural code making use of the “packet::da” variable and the declaration of that variable where separated by several dozens — or hundreds — of lines? What if they were in different files? Consider the code below. Using “this.”, despite being option, clearly document the nature of the “stream_id” and “fcs” variables.

class bad_frames_in_stream_0 extends eth_frame;
rand bit is_bad;

constraint some_bad_frames {
if (this.stream_id == 0 && this.is_bad) this.fcs != 0;


Posted in Coding Style, Modeling Transactions, VMM | 4 Comments »