Verification Martial Arts: A Verification Methodology Blog

Archive for 2009

Verification in the trenches: Creating your verification components using VMM1.2

Posted by Ambar Sarkar on 25th December 2009

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

Ever wonder why it is hard to mix and match verification components from different sources and have them play nicely with the one you created? You want all of these components to execute in sync with each other through the phases of their construction, configuration, shutdown, etc. For example, if the AXI slave transactor is executing its reset phase while the PCIe stimulus generator is sending in DMA read requests to the AXI interface, you have a problem. Often, you end up adding dedicated code or using explicit synchronization objects such as events to get the right coordination. Wouldn’t it be nice if this synchronization came automatically?

This is where the vmm_unit base class introduced in VMM1.2 comes in. The basic idea is to derive your verification component from this predefined class and you are guaranteed that the verification environment will automatically synchronize its execution with the others. While there is much offered by the vmm_unit class, the following statement summarizes its real benefit:

vmm_unit class comes with a rich set of built in synchronization points.

These synchronization points are represented as predefined tasks or functions called phases. The verification engineer provides the actual implementation of these phases. The environment makes sure that all the objects derived from the vmm_unit class get their phases called in a well defined order, so that once an object moves into a phase, it is guaranteed that all its siblings have completed the previous phase. For example, once a component enters reset, you know every other associated component is either being reset or about to enter the reset state.

Couple of things to note, however. First, you do not have to provide implementation for each and every phase. If you do not define them, the default action is that this object will wait for the others to finish this phase before moving to the next one. Second, you can override, replace, or even add your own phases to introduce a finer or different synchronization scheme altogether.

So how does the implementation end up looking like? Here is a snippet from something that I coded recently for a reusable module-level verification environment. Note that I only defined a few of the phases of my own and used the default implementation for the others.

Predefined phase Sample Code Snippet
build_ph()
// Create various functional components of this environment
pwr_hi = new(“subenv”, “PWR_HI”, this);
pwr_pi = new(“subenv”,”_PWR_PI”, this);
// Instantiate a consensus manager
cm = new(this, {this.get_object_name(), “_CM”}, pwr_port);
….
configure_ph() // If someone built me as a sub-environmnet, take appropriate action
if (is_subenv) begin
// Disable the host interface driver
connect_ph() // Connect the components as needed
pwr_hi.chk.ana_port.tlm_bind(sb.pwr_hi_sb_chk_ap);
pwr_pi.has_generator) pwr_pi.gen.ana_port.tlm_bind(sb.pwr_pi_sb_post_ap);
start_of_sim_ph() // Put a diagnostic message, otherwise leave empty
`vmm_verbose(log, “Starting simulation”);
reset_ph() // Power_cycle
pwr_port.dck.reset <= 0;
@(pwr_port.dck);
pwr_port.dck.reset <= 1;
repeat (10) @(pwr_port.dck);
pwr_port.dck.reset <= 0;
repeat (2) @(pwr_port.dck);
training_ph() // Leave as default
config_dut_ph() // Sw initialization sequence
// …
start_ph() // Leave as default
start_of_test_ph() // Leave as default
run() // All you need is to wait for the consensus manager to agree to shut down
cm.wait_for_end_t();
shutdown_ph() // Leave as default
cleanup() // Leave as default
report() // Dump the final scoreboard status
sb.report();
destruct() // Leave as default

The key is to make sure that your code is partitioned into the appropriate phases, as shown above. Also note that a bunch of the phases were left alone to their default implementation.

Okay, one minor detail. You do not directly derive from the vmm_unit base class. Instead, two classes, vmm_xactor and vmm_group have been provided. Both are derived from vmm_unit, so you have all the support for synchronization. vmm_xactor should be used as the base class for defining your individual transactors, whereas vmm_group should be used as the base class for components that put together several others into a single entity such as the top-level environment or a top-level interface vip.

Of course, the correctness of your synchronization  will still depend on what you end up implementing in the body of the pre-defined phases. The names of the predefined phases give a hint. So you are still subject to what the other components implemented in their corresponding phases. Do follow the spirit of what each phase is supposed to do. Do not connect your components in build_ph()  phase even if the test passes. Do so in connect_ph(). A small price to pay for most cases, IMHO.

Do note that there often are scenarios where some components need to be synchronized separately from others. For example, in a PCIe based SOC, what if you need the OCP interface to be up and running before you bring your PCIe interface out of reset? In this case, you definitely do not want the OCP and the PCIe VIPs to run their configure and reset phases in lock-step with each other. This is where advanced synchronization features such as vmm_timelines come to play, but that’s a topic for the next post. Stay tuned.

This article is the 3rd 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 Phasing, Structural Components, VMM 1.2 | No Comments »

Viewing VMM log details in waveforms

Posted by Avinash Agrawal on 17th December 2009


Avinash Agrawal, Corporate Applications, Synopsys

Often engineers need a combination of logfile outputs and waveforms, to look at their simulations. And they wonder if it is possible to look at waveforms and get information on the number of simulation errors that might have occurred in a simulation upto any particular point of simulation time.

The good news is that the VMM log service helps a user track the source of different messages to different verification components.

When VMM macros such as `vmm_error, `vmm_note etc are used at different places in the verification environment, the user is able to view the corresponding information in the simulation output. This information includes the time at which the message was logged, the verification component and the specified instance of which the message was issued from. However, it can be very useful if the timing of the errors or warnings in the simulation output can be correlated with waveforms in simulation. For example, if there is a protocol violation message issued from one of the testbench monitors, the user can map the time when the message was issued to the actual signals in the waveform. That way user can quickly uncover the relevant problem in the DUT.

The VMM message service vmm_log consists of the vmm_log_format object to control the format of the messages. The vmm_log_format object also gets the information of the type/severity of the messsages. The vmm_log class for each component uses the default implementation of all these methods. The user can easily extend the vmm_log_format class and add in his modifications. Modifications can be either to trigger an assertion or incrememt a variable which can be dumped into the waveform window. This way, the  engineer can correlate the errors with the change in the variable or an assertion in the waveform window.

The following code shows how this can be done:

module top();
  int error_count;

initial begin
 $vcdpluson();
end

endmodule

program P;

class env_format extends vmm_log_format;  //extending vmm_log_format
                                          // for adding in user modifications
      virtual function string format_msg(string name,
                                         string inst,
                                         string msg_typ,
                                         string severity,
                                         ref string lines[$]);
         if (msg_typ == "FAILURE" && severity == "!ERROR!") begin
            top.error_count++;  //incrementing error count for warnings and errors
         end
//or trigger an assertion which can also be seen in the waveform
        assert (~(msg_typ == "FAILURE" && severity == "!ERROR!"));
          format_msg = super.format_msg(name, inst, msg_typ, severity, lines);
      endfunction
endclass

class xactor extends vmm_xactor;

  int id;

  function new(int id, string instance);
    super.new("xactor", instance);
    this.id = id;
  endfunction

  virtual task main();
    super.main();
    `vmm_note(log, "This is a note message");
    #5;
    `vmm_error(log, "This is an error message");
    if (id == 0)
      #10 `vmm_error(log, "This is an error message");
    else
      #30 `vmm_error(log, "This is an error message");
  endtask
endclass

class env extends vmm_env;
  xactor x1;
  xactor x2;
  vmm_log log;

  function new();
    env_format fmt;
    log = new("env", "class");
    fmt = new();
    log.set_format(fmt);
  endfunction

  virtual function void build();
    super.build();
    x1 = new(0, "x1");
    x2 = new(1, "x2");
  endfunction

  virtual task start();
    super.start();
    x1.start_xactor();
    x2.start_xactor();
  endtask

  virtual task wait_for_end();
    super.wait_for_end();
    #1000;
  endtask

  virtual task stop();
    super.stop();
    x1.stop_xactor();
    x2.stop_xactor();
  endtask

endclass

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

endprogram

Posted in Debug, Messaging | No Comments »

Just in time for the holidays: VMM 1.2!

Posted by Janick Bergeron on 15th December 2009

Janick Bergeron
Synopsys Fellow

I am pleased to see that the OpenSource version of VMM 1.2 is finally released. It is the culmination of six months of hard work by the entire VMM teams and the hundreds of customers who have provided inputs on its requirements and the dozens of teams who have contributed their feedback during the beta period.

What is new in VMM 1.2 is a “secret de Polichinelle“. Ever since the start of the beta period, several VMM users and Synopsys engineers have published tutorials, seminar presentations and blog articles on many of its powerful aspects. Nonetheless, I would like to take this opportunity to give you the highlights and pointers to where you can find more information.

A new User’s Guide

One of the most important aspect of this release—and one that has not been mentioned so far—is the completely revamped and expanded User’s Guide. We have integrated the content of the VMM for SystemVerilog book, the book’s errata, and the previous User’s Guide into a single User Guide that completely documents all of the features of the class library. Furthermore, the body of this new User’s Guide has been expanded to present the methodology in a style that will be easier to learn, with many examples. Speaking of examples, this latest distribution contains a lot more examples (in $VMM_HOME/sv/examples), illustrating the many applications domains of the VMM and all of its new features.

Implicit Hierarchical Phasing

The original VMM used explicit phasing exclusively. With 1.2, VMM now supports implicit hierarchical phasing. With implicit phasing, transactors and environments need not be responsible for the phasing of the components they instantiate: that is taken care of automatically by the new vmm_timeline object. The implicit phasing is also hierarchical, meaning that an environment may contain more than one vmm_timeline instances. Sub-timelines limit the scope and interaction of user-defined phases when block-level environments are reused in a system context. Sub-timelines may also be rolled back if their portion of verification environment needs to be stalled or restarted, for example because its corresponding functionality in the DUT has been powered down. Furthermore, VMM allows implicit and explicit phasing to be arbitrarily mixed: instead of insisting that it be in control of every aspect of a verification environment, it can import portions of an environment described using an alternative phasing methodology and have it be explicitly phased using a different mechanism by encapsulating in a vmm_subenv instance. Similarly, any VMM environment can be subjugated to another phasing methodology by allowing vmm_timeline instances to be explicitly phased.

TLM 2.0

In addition to the vmm_channel, VMM 1.2 now offers an alternative transaction-level interface mechanism inspired by OSCI’s Transaction-Level Modeling standard version 2.0. I say “inspired” because it is not a direct translation of the SystemC TLM standard, as the SystemVerilog language does not support multi-inheritance used in the SystemC implementation. The TLM2 standard is radically different from TLM1 because the latter did not live up to its promises of model interoperability and simulation performance. In addition to specifying an interface and transport mechanism, TLM2 specifies clear transaction progress and completion models through phases and the Base Protocol. VMM has always provided similarly well-defined transport mechanism (vmm_channel) and completion models (see pp176-195 of the original VMM book). With the addition of TLM2 sockets, VMM can also be used to implement high-performance virtual prototyping models in SystemVerilog. Of course, we’ve made sure that you can attach a vmm_channel to an initiator or target blocking or nonblocking socket interface for maximum flexibility.

Object Hierarchy

Whereas modules form a strict hierarchy in SystemVerilog, class instances (also known as objects) do not – at least from a language standpoint. However, it is a common mental model even though it is not enforced by the language. VMM 1.2 has the ability to define parent-child relationships between any instances of the vmm_object class. And because that class is the base class for all other VMM classes, any instance of a VMM class or user-defined extensions thereof can have a parent and any number of children. This creates a user-defined hierarchy of objects. And because each object has a name, it implicitly creates a hierarchical naming structure. Furthermore, because this hierarchical and the name of its component is entirely user-defined, VMM 1.2 provides the concept of namespaces to create alternative object hierarchies and names, making it easy to create hierarchical registries or to map an object hierarchy to another one. Objects can easily be found by name or by traversing the hierarchy from parent to child or vice-versa.

Factory API

VMM always had the concept of class factories (see p217 in the original VMM book). It used the factory pattern in all of its pre-defined generators and recommended that it be used whenever transaction objects were created or randomized (see Rules 4-115 and 5-6 in the original VMM book). It simply did not provide any pre-defined utility to ease the implementation or overriding of class factory instances. VMM 1.2 remedies this situation by introducing a class factory API that makes it easier to replace class factory instances, as well as to build class factories. Furthermore, it provides two factory override mechanism: a fast one that creates class instances with default values, and a slower one that creates exact copies. And, being strongly typed, the new factory API will detect at compile time if you are attempting to replace a factory instance with an incompatible type.

And many more!

VMM 1.2 provides many more additional features, like hierarchical options, RTL configuration support, and test concatenation.

Learning more

You can download the OpenSource distribution here. You will also find VMM 1.2 in your VCS 2009.12-1 distribution (use the +define+VMM_12 compile-time command-line option to enable it!).

Visit this blog often, as many industry leaders and Synopsys engineers will continue to provide insights on the new features included in VMM 1.2

Also, stay tuned for a series of one-day VMM 1.2 seminars and workshops that will be touring the major semiconductor centers around the globe.

Posted in Announcements, Debug, Phasing, Structural Components, Transaction Level Modeling (TLM), VMM 1.2, VMM infrastructure | 2 Comments »

Transaction Level Modeling – Value add in different languages.

Posted by Nasib Naser on 14th December 2009

Nasib_NaserNasib Naser, PhD

Sr. Staff Corporate Applications Engineer – Synopsys

One of the driving factors of creating SystemVerilog is to raise the design verification abstraction level. The reason for such a move is described in the SV LRM Abstract. It says: “A set of extensions to the IEEE 1364-2001 Verilog Hardware Description Language to aid in the creation and verification of abstract architectural level models.” So why and how? The “Why” is obvious. A number of reasons come to mind:

  1. Faster development and simulation to enable reaching design and verification goals sooner than later.
  2. Achieve early closure on architectural decisions without early commitment to implementation details.
  3. Enable running “some” software within the complete system context.
  4. Create an environment in which verification methodologies could be developed that creates true verification IP re-usability and models interoperability.

This blog will discuss the methodology behind the “How.” The technology will be explained in subsequent blogs. Up until vmm 1.2 was released Transaction based models were created by utilizing VMM constructs such as vmm_channel(), vmm_xactor(), and vmm_data() . With all VMM strengths this use model succeeded only on in-house designs and didn’t gain wide modeling adoption. The TLM methodology built into VMM lacked common practices to enable interoperability – emphasis on common practice.

Meanwhile, driven by users, the Open SystemC Initiative aka OSCI TLM community managed to create a standard on which models could be developed for re-use and interoperability. Without dwelling in the past I’d like to give a brief history on the evolution that led to the TLM standard. In the beginning there was C, then C++. For obvious reasons these widely known software languages were used to develop in-house system level models for doing high level performance and architectural analysis. The use model was very limited as the simulation behavior was very far from the actual hardware. It lacks concurrency and timing. C++ extensions augmented with a “proof of concept” simulator was created and called SystemC that enable modeling these hardware behaviors in C++, and more. That worked very well. Failing to replace Verilog and vhdl for design and verification SystemC found its niche use model in the architectural modeling domain. At that point architects using SystemC started to demand a standard that enables interoperability for fast platform composition, ease of use, and re-usability. Hence, the OSCI SystemC Transaction Level Modeling 1.0 and later 2.0 standards were created.

So why re-invent the wheel when it comes to SystemVerilog TLM? And why not adopt a powerful and robust verification methodology such as VMM standard to enable seamless integration between SystemC and SystemVerilog? That’s why features described in the OSCI TLM standard found its way into the SystemVerilog/VMM world. These features are available in the newly released VMM 1.2. Subsequent blogs I will explain these features and the value add they bring into a true IP re-use and interoperable verification methodologies.

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

Hitting the “Playback” button on VMM transactions

Posted by Avinash Agrawal on 11th December 2009

Avinash Agrawal

Avinash Agrawal, Corporate Applications, Synopsys




Often verification engineers face the challenge of recording transactions in one simulation, and wanting to replay the same set of transactions in the same sequence of transactions in a different simulation, and try different ways to make this happen.

Well, there is some good news !!!

VMM provides a facility where you can record the transactions going through a VMM channel and save it into a file. This can be done through the record method of the VMM channel. Later, for replay, disconnect the producer of the channel (may be generator/transactor, etc which sends transactions to the channel) and use playback method to load the channel with the transactions from the file in the same order.

Here the saved transaction file acts as a virtual producer. This way random stability is guaranteed. Note that byte_pack() or save() method of transaction (vmm_data) class must be implemented to use record mechanism and byte_unpack() or load() method of transaction (vmm_data) class must be implemented to use playback mechanism. Since playback avoids randomization of the transaction/corresponding scenarios, performance can be improved in case of complex transaction/scenario constraints. Also generation is not scheduling-dependent and will work with different versions of the simulator as well as with different simulators.

This record/replay mechanism can also be used to go through known states at one interface while stressing another interface with random scenarios within the same simulation itself.

The code below snippet shows how to use VMM record/playback.

task start();

`ifdef RECORD_MODE
    fork
      chan.record("Chan.dat"); //call record method of the channel
                               //with a filename
    join_none
    gen.start_xactor();       //start the generator (producer) of the  channel
                              // if it is record mode.
 `endif
 `ifdef PLAYBACK_MODE    // In playback mode, make sure that the
                         // generator (producer) is not started.
     fork begin
         bit success;
         trans tr = new;
         chan.playback(success, "Chan.dat", tr);  // call playback method of
                                               // the channel with the same file
         if (!success) begin
            `vmm_error(log, "Playback mode failed for channel");
         end
     end join_none
`endif

endtask

Posted in Optimization/Performance, Reuse | No Comments »

Using Explicitly-Phased Components in an Implicitly-Phased Testbench

Posted by JL Gray on 11th December 2009

In my last post, I described the new VMM 1.2 implicit phasing capabilities.  I also recommended developing any new code based off of implicit phasing.  Obviously, though, companies that have been using the VMM for quite some time will have developed all of their existing testbench components using explicit phasing.  It is relatively straightforward (and in some sense almost trivial) to use an explicitly phased component in an implicitly phased testbench.

Remember that the whole point of explicit phasing is that users cycle components through the desired phases by manually calling functions and tasks within the component itself. vmm_env contains the following methods:

  • gen_cfg
  • build
  • reset
  • config_dut
  • start
  • wait_for_end
  • stop
  • cleanup
  • report

vmm_subenv contains the following relevant methods:

  • new
  • configure
  • start
  • stop
  • cleanup
  • report

In an explicitly-phased environment, subenv methods are called manually by integrators, usually from the equivalent method in vmm_env. There are two approaches for instantiating a vmm_subenv-based component in an implicitly-phased testbench. The default approach is to simply allow the implicit phasing mechanism to call these explicit phases for you. Explicitly phased components are identified by the implicit phasing mechanism, and methods are called using a standard (and not entirely unexpected) mapping:

Implicit Phase Explicit Phase Called
build_ph vmm_subenv::new[1]
configure_ph vmm_subenv::configure
start_ph vmm_subenv::start
stop_ph vmm_subenv::stop
cleanup_ph vmm_subenv::cleanup
report_ph vmm_subenv::report

[1] Users must call vmm_subenv::new manually.

Now, you might want to phase your vmm_subenv in a non-standard way. If that’s the case, the first thing you’ll need to do is disable the automatic phasing. Here’s how. First, instantiate a null phase:

vmm_null_phase_def null_ph = new();

Next, override the phases you don’t want to start automatically. For example:

my_group.override_phase(“start”, null_ph);
my_group.override_phase(“stop”, null_ph);

Finally, call the explicit phases from the parent object’s implicit phases.  A complete example is shown below.

class testbench_top extends vmm_group;
bus_master_subenv bus_master;
vmm_null_phase_def null_ph = new();

function void build_ph();
bus_master = bus_master_subenv::create_instance(this, “bus_master”);
bus_master.override_phase(“start”, null_ph);
bus_master.override_phase(“stop”, null_ph);
endfunction: build_ph

task reset_ph();
bus_master.start();
// wait 1000 clocks…
bus_master.stop();
endtask: reset_ph

endclass: testbench_top

Posted in Communication, Modeling, Phasing, Reuse, VMM, VMM 1.2 | No Comments »

Blocking Transport Communication in VMM 1.2

Posted by John Aynsley on 10th December 2009

image John Aynsley, CTO, Doulos

One of the new features in VMM 1.2 is the blocking transport interface, borrowed from the SystemC TLM-2.0 standard. This interface provides an alternative to the using the put and get methods of the vmm_channel class when communicating between a producer and consumer. There are a couple of reasons why you might consider using the blocking transport interface in VMM: perhaps you are trying to interface to a SystemC reference model, or perhaps you want to write a transactor that has very clean semantics when it comes to determining the start and end of a transaction. In either case, the new blocking transport interface can help.

A blocking transport call is a method call that carries with it a transaction object and is expected not to return until the transaction is complete. The status of the transaction, that is, whether the transaction succeeded or failed, is expected to be carried within the transaction object itself. You call blocking transport as follows:

class my_tx extends vmm_data; // A user-defined transaction class

enum {OKAY, FAIL} status; // Status flag embedded in the transaction itself
endclass

my_tx tx;
int delay;

$cast(tx, randomized_tx.copy()); // Create and randomize a transaction object

m_port.b_transport(tx, delay);

if( tx.m_status != OKAY ) // Check the response status
`vmm_warning(log, “Transaction failed”);

Since this is SystemVerilog, the transaction object itself is passed by reference. The delay argument instructs the recipient of the transaction to process the transaction after the given delay has elapsed, and can be set by both the caller of and the implementation of the b_transport method.

The blocking transport method needs to be implemented by the transactor that will receive or consume the transaction:

task b_transport(int id = -1, my_tx tx, ref int delay);

// Process transaction

// Pause for given delay and then reset the delay argument
#(delay);
delay = 0;

// Set the response status in the transaction object
tx.m_status = OKAY;
endtask : b_transport

By definition, blocking transport “blocks” until the transaction has been fully processed. It is coded as a SystemVerilog task so that it is allowed to execute timing controls such as the #(delay) in this example. The task is not obliged to suspend for the given delay in this manner; it could have simply returned leaving the delay without modification, or even have increased the value of the delay, leaving it for the caller to realize the delay later. The task is obliged to set the response status flag within the transaction object before returning, and the caller must check the response status on return.

One significant thing that is happening here is that communication between a producer and a consumer (or in TLM-2.0 jargon, an initiator and a target) is being accomplished without having any intervening channel to buffer the transactions. The b_transport method is called by the producer, provided by the consumer, and only returns when the transaction is complete. The advantage of not having an intervening channel is that it can help increase execution speed in the context of a very fast simulation model. The disadvantage is that it is not possible to have multiple transactions in progress given only a single execution thread in the initiator; therefore, it is a good idea to add a semaphore in the implementation of the b_transport method in the target transactor

class target_xactor extends vmm_xactor;

local semaphore m_sem = new(1);

task b_transport(int id = -1, my_tx tx, ref int delay);
m_sem.get(1);
// Process transaction

m_sem.put(1);
endtask : b_transport

This is not the whole story, because we also need to explain how to connect the producer to the consumer. Look out for future blog posts.

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

Developing transactors using VMM 1.2

Posted by Vidyashankar Ramaswamy on 8th December 2009

There are many ways to design and develop a transcator. The following is the way I visualize it. Typical transactor components are shown in the following figure. Based on the functionality, transactor can be grouped into up-stream, down-stream, pass through or a passive monitor types. I shall explain in brief what I mean by these. Up-stream can be a stimulus generator and down-stream can be a bus function master/slave model. A pass through can exists in the test bench to connect a master and the bus function model. A passive monitor simply monitors the bus interface on to which it is connected and broadcasts the packet information whenever it is available. The dotted line in the figure partitions the transactor based on functionality and shows different port connections.

The right side of the dotted line in the above figure represents the upstream. This can be a producer (master or stimulus generator), in which case it can have only output port. This output port is designed as a TLM transport port.

The left side of the dotted line represents the downstream: this can be a slave transactor in which case the receiving port will be a VMM channel. If the downstream transactor is connected to the DUT, then you need to declare a virtual interface and bind it through a port object to the physical interface. This is done from the enclosing environment. This makes the BFM component reusable across test benches. In my next article I shall show you an example about the port object.

If you are designing a pass through transactor, then you need to have both VMM channel for receiving the transaction from the producer and the TLM transport port for sending the transaction to the consumer. Analysis port can be used if any observers are hooked up to this transactor. Also note that you need not have any virtual port connection for a pass through transactor.

A monitor component will have only analysis port along with the physical interface connection.

You might be wondering why the analysis port and the callback interface are centered between up-stream and the down-stream. If you have guessed it, yes you’re right. Both master and slave need to broadcast the information/packet which is passing through them to the observers. The observer can be a scoreboard, coverage collector or a simple file write for debug purposes.

To make the master/slave transactor re-usable, callback methods are used. A callback method allows the user to extend the behavior of a transactor without having to modify the transactor itself. VMM 1.2 supports factory service to replace a transactor. I favor callbacks for transactor extensions. So which one should you use ? I shall leave that up to you to decide and this could be a topic on its own.

Please feel free to comment and share your opinion. For more information please refer to the VMM 1.2 user guide.

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

Paved With Good Intentions: Examining Lost Design Intent

Posted by Adiel Khan on 7th December 2009

image-thumb.pngandys-picture_2

Adiel Khan, Synopsys CAE

Andrew Piziali, Independant Consultant

Remember the kick-off of your last new project, when the road to tape-out was paved with good intentions? Architects were brainstorming novel solutions to customer requirements? Designers were kicking around better implementation solutions? Software engineers were building fresh new SCM repositories? And you, the verification engineer, were excitedly studying the new design and planning its verification? Throughout all of this early excitement, all sorts of good intentions were revealed. Addressing the life story of each intention would make a good short story, or even a novel! Since we don’t have room for that, let’s just focus on the design intentions.

Design intent, how the architect intended the DUV (design under verification) to behave, originates in the mind’s eye of the architect. It is the planned behavior of the final implementation of the DUV. Between original intent and implementation the DUV progresses through a number of representations, typically referred to as models, wherein intent is unfortunately lost. However, intent’s first physical representation, following conception, is its natural language specification.

We may represent the space of design behaviors as the following Venn diagram:1

Each circle—Design Intent (AEGH), Specification (BEFH) and Implementation (CFGH)—represents a set of behaviors. AEGH represents the set of design requirements, as conveyed by the customer. BEFH represents the intent captured in the specification(s). CFGH represents intent implemented in the final design. The region outside the three sets (D) represents unintended, unspecified and unimplemented behavior. The design team’s objective is to bring the three circular sets into coincidence, leaving just two regions: H (intended, specified and implemented) and D. By following a single design intention from set Design Intent to Specification to Implementation, we learn a great deal about how design intent is lost.

An idea originally conceived appears in set AEGH (Design Intent) and, if successfully captured in the specification, is recorded in set EH. However, if the intent is miscommunicated or not even recorded and lost, it remains in set A. There is the possibility that a designer learns of this intent, even though it not recorded in the specification, and recaptures it in the design. In that case we find it in set G: intended, implemented, but unspecified.

Specified intent is recorded in set BEFH. Once the intent is captured in the specification it must be read, comprehended and implemented by the designer. If successful, it makes it to the center of our diagram, set H: intended, specified and implemented. Success! Unfortunately some specified requirements are missed or misinterpreted and remain unimplemented, absent from the design as represented by set E: intended, specified but unimplemented. Sometimes intent is introduced into the specification that was never desired by the customer and (fortunately) never implemented, such as set B: unintended, unimplemented, yet specified. Unfortunately, there is also unintended behavior that is specified and implemented as in set F. This is often the result of gratuitous embellishment or feature creep.

Finally, implemented intent is represented by set CFGH, all behaviors exhibited by the design. Those in set G arrived as originally intended but were never specified. Those in set H arrived as intended and specified. Those in set F were introduced into the specification, although unintended, and implemented. Behaviors in set C were implemented, although never intended nor specified! In order to illustrate the utility of this diagram, let’s consider a specific example of lost design intent.

We can think of each part of the development process as building a model. Teams write documentation as a specification model, such as a Microsoft Word document. System architects build an abstract algorithmic system model that captures the specification model requirements, using SystemC or C++. Designers build a synthesizable RTL model in Verilog or VHDL. Verification engineers build an abstract problem space functional model in SystemVerilog, SVA and/or e.

If any member of the team fails to implement an element of the upstream, more abstract model correctly (or at all), design intent is lost. The verification engineer can recover this lost design intent by working with all members of the team and giving the team observability into all models.

Consider an example where the system model (ex. C++) uses a finite state machine (FSM) to control the data path of the CPU whereas the specification model (ex. MS Word) implies how the data path should be controlled. This could be a specification ambiguity that the designer ignores, implementing the data path controller in an alternate manner, which he considers quite efficient.

Some time later the system architect may tell the software engineers that they do not need to implement exclusive locking because the data path FSM will handle concurrent writes to same address (WRITE0, WRITE1). However, the designer’s implementation is not based on the system model FSM but rather the specification model. Therefore, exclusive locking is required to prevent data corruption during concurrent writes. We need to ask: How can the verification engineer recover this lost design intent by observing all models?

Synopsys illustrates a complete solution to the problem in a free verification planning seminar that dives deep into this topic. However, for the purposes of this blog we offer a simplified example, using the design and implementation of a coverage model:

  1. Analyze the specification model along with the system model
  2. Identify the particular feature (ex. mutex FSM) and write its semantic description
  3. Determine what attributes contribute to the feature behavior
  4. Identify the attribute values required for the feature
  5. Determine when the feature is active and when the attributes need to be sampled and correlated

This top-level design leads to:

Feature CPU_datapath_Ctrl
Description Record the state transitions of the CPU data path controller
Attribute Data path controller state variable
Attribute Values IDLE, START, WRITE0, WRITE1
Sample Whenever the state variable is written

The verification engineer can now implement a very simple coverage model to explicitly observe the system model, ensuring entry to all states:

enum logic [1:0] {IDLE, START, WRITE0, WRITE1} st;

covergroup cntlr_cov (string m_name) with function sample (st m_state);

option.per_instance = 1;

option.name = m_name;

model_state: coverpoint m_state {

bins t0 = (IDLE   => IDLE);

bins t1 = (IDLE   => START);

bins t2 = (START  => IDLE);

bins t3 = (START  => WRITE0);

bins t4 = (WRITE0 => WRITE1);

bins t5 = (WRITE1 => IDLE);

bins bad_trans = default;

}

endgroup

planner

The verification engineer can link the feature “CPU_datapath_Ctrl” in his verification plan to the cntlr_cov covergroup. Running the system model with the verification environment and RTL implementation will reveal that bin “t4″ is never visited, hence state transition WRITE0 to WRITE1 is never observed. The team can review the verification plan to determine if the intended FSM controller should be improved in the design to conform to all design intent.

Although there are many other subsets of the design intent diagram we could examine, it is clear that a design intention may be lost through many recording and translation processes. By understanding this diagram and its application, we become aware of where intent may be lost or corrupted and ensure that our good intentions are ultimately realized.


1The design intent diagram is more fully examined in the context of a coverage-driven verification flow in chapter two of the book Functional Verification Coverage Measurement and Analysis (Piziali, 2004, Springer, ISBN 978-0-387-73992-2).

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

VMM scenario generators and dependent scenarios

Posted by Avinash Agrawal on 4th December 2009

Avinash Agrawal

Avinash Agrawal, Corporate Applications, Synopsys

Often folks wonder if it possible to have a VMM scenario generator, where one scenario is dependent on another scenario.

The answer is “Yes.”

Consider the testcase below. You can define two scenarios, scn_a and scn_b, both of which have their own set of constraints. The variables generated in scn_b are a multiple of the values that were set when scn_a was generated previously, in this case by variable “ratio”. For more details on how VMM scenario generators work, refer to the VMM user guide.

Systemverilog testcase:

——————————————————————————–

class packet extends vmm_data;

rand int sa;

rand int da;

`vmm_data_member_begin(packet)

`vmm_data_member_scalar(sa, DO_ALL)

`vmm_data_member_scalar(da, DO_ALL)

`vmm_data_member_end(packet)

endclass

`vmm_channel(packet)

`vmm_scenario_gen(packet, “packet”)

class a_scenario extends packet_scenario;

int unsigned scn_a;

rand int ratio;

function new();

scn_a = define_scenario(“scn_a”, 5);

endfunction

constraint cst_a {

$void(scenario_kind) == scn_a ->  {

foreach(items[i]) {

this.items[i].sa inside {[0:100]};

this.items[i].da inside {[0:100]};

}

ratio inside {[1:5]};

}

}

endclass

class b_scenario extends packet_scenario;

int unsigned scn_b;

function new();

scn_b = define_scenario(“scn_b”, 10);

endfunction

constraint cst_b {

$void(scenario_kind) == scn_b -> {

foreach(items[i]) {

this.items[i].sa inside {[100:300]};

this.items[i].da inside {[100:300]};

}

}

}

endclass

class hier_scenario extends packet_scenario;

rand a_scenario scn_a;

rand b_scenario scn_b;

function new();

this.scn_a = new();

this.scn_b = new();

this.scn_a.set_parent_scenario(this);

this.scn_b.set_parent_scenario(this);

endfunction

virtual task apply(packet_channel channel, ref int unsigned n_insts);

this.scn_a.apply(channel, n_insts);

this.scn_b.apply(channel, n_insts);

endtask

constraint cst_hier {

foreach(scn_b.items[i]) {

// Create a scenario ‘scn_b’ depending upon ‘scn_a’

scn_b.items[i].sa inside {[scn_a.ratio*100:scn_a.ratio*800]};

scn_b.items[i].da inside {[scn_a.ratio*100:scn_a.ratio*800]};

}

}

endclass

program automatic test;

packet_scenario_gen scn_gen;

packet_channel      pkt_chan;

packet pkt;

hier_scenario scn_hier;

initial begin

scn_hier = new();

scn_gen = new(“scn_gen”, -1, pkt_chan);

scn_gen.register_scenario(“scn_hier”, scn_hier);

scn_gen.unregister_scenario_by_name(“Atomic”);

$display(“Size of the scn is %0d”, scn_gen.scenario_set.size());

scn_gen.stop_after_n_insts = 15;

scn_gen.start_xactor();

while(1) begin

#10 scn_gen.out_chan.get(pkt);

$display(“id is %0d , %0d %0d”, pkt.data_id, pkt.sa, pkt.da);

end

end

endprogram

——————————————————————————–

Posted in Reuse, Stimulus Generation, Support, VMM | No Comments »

Verification in the trenches: Traversing object hierarchies using VMM1.2

Posted by Shankar Hemmady on 25th November 2009

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

Ever get stuck trying to configure an object deep inside a verification environment? More likely than not, someone else created the environment, changed it drastically over time, and did not leave a description of the object hierarchy. It is quite time-consuming to unravel the whole hierarchical path from the root to that object. The phrase “needle in a haystack” comes to mind.

I will share some tips on how the vmm_object class, recently added to VMM1.2, can help.

As you are most likely aware, a typical verification environment today contains hundreds of classes defined by the user. Many of these are related to each other in parent/child relationships, creating several hierarchies.

As you are most likely aware, a typical verification environment today contains hundreds of classes defined by the user. Many of these are related to each other in parent/child relationships, creating several hierarchies.

Ambar_Sarkar_blog2_fig1

So how does the vmm_object class help in traversing these hierarchies? In a nutshell, it is the base class for all classes defined in the VMM library. This means that for every object derived from a class in the VMM library, you can use the same API for:

  • Finding its type
  • Finding its ancestors and children
  • Avoiding name clashes ( a new feature called namespace and we will save this topic for another post)
  • Displaying the hierarchy
  • Iterate over the objects
  • Find object by a regular expression search

The last two bullets above are what I feel help me most, because:
a. I do not have to write separate code for traversing the hierarchy of different types of objects.
b. I can locate an object easily without having to remember or figure out the exact path to it.

Here is an example. I wanted to access all the atomic generators in my environment and they were all named with the suffix GEN.

There are at least two ways to accomplish this:

1. Use the `foreach_vmm_object macro:

// Configuration phasefor test

virtual function void configure_ph();

string pattern = “@%*GEN”; // regular expression used to search for generator name

// Just iterate over all `VMM_ATOMIC_GEN objects that end with “GEN”
`foreach_vmm_object(`VMM_ATOMIC_GEN, pattern, env)
begin
// Do something with this generator
`vmm_note(log, $psprintf(“Found %s\n”, obj.get_object_name()));
end

When applicable, this approach seems like a quite concise way to get things done. Do note that the implementation of `foreach_vmm_object macro requires it to be the first statement following the declarations in a method or following a begin keyword due to the way it is implemented.

Of course, you can avoid the macro if you are so inclined as shown below. Sometimes I do this to get better visibility while debugging.

2. Use iteration class vmm_object_iter provided with this release:


vmm_object_iter iter; // iterator object
vmm_object obj;
`VMM_ATOMIC_GEN at_gen;
string pattern = “@%*GEN”; // regular expression used to search for generator name

// Just iterate over all `VMM_ATOMIC_GEN objects that end with “GEN”
iter = new(env, pattern); // Get a pointer to the iterator

obj = iter.first(); // Get the first in the list
while (obj != null) begin
// Always check type!
if (!$cast(at_gen, obj))
`vmm_error(log, $psprintf(“Unexpected type for %s. \n”, obj.get_object_name()));

// Do something with this generator
`vmm_note(log, $psprintf(“Found %s\n”, obj.get_object_name()));

obj = iter.next(); // Get the next one

end

Pretty much similar to what I expected as a use model for an iterator class.

Of course, your mileage/opinion may differ from mine. Please share how you plan on using the common vmm_object base class in your daily verification tasks.

This article is the 2nd 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 Debug, Phasing, Reuse, VMM 1.2, VMM infrastructure | No Comments »

Implicit and Explicit Phasing

Posted by JL Gray on 20th November 2009

JLGray JL Gray, Verilab, Inc.

In my last post I discussed the difference between phases and threads in the VMM. Phases and threads are conventions used to simplify testbench development. The classic VMM approach has been to use the vmm_env and vmm_subenv classes to manage phases of testbench components, leaving vmm_xactor to deal with thread management. Phases were managed “explicitly”, that is to say, users had complete control over when to step the environment and its subcomponents through individual phases via function and task calls.

The VMM 1.2 introduces the concept of “implicit” phasing. Both implicit and explicit phasing can be used to accomplish many of the same goals, albeit in different ways. In an implicitly-phased testbench, functions and tasks representing phases are called automatically at the appropriate times. A global controller (vmm_simulation) works in conjunction with specially configured schedulers (vmm_timeline) to walk all testbench components through relevant phases. vmm_simulation acts like a conductor, keeping all of the various testbench components in sync during pre-test, test, and post-test portions of a typical simulation.

A natural question to ask is, “Are there any benefits to implicit phasing over and above the explicit phasing techniques I’m using today?” When testbench components are walked through phases automatically, there are a few interesting possibilities that arise. For starters, it becomes possible to add and remove phases. Let’s imagine a simulation that has the following phases1:

  • reset_ph
  • training_ph
  • config_dut_ph
  • start_ph
  • shutdown_ph

What happens if I need to use a piece of verification IP that has implemented a training phase that is not applicable in my test environment? In an explicitly phased environment, I’d need to have control of the code that called the sub-component’s training phase in order to ensure the task in question was not called. In an implicitly-phased environment, I can simply delete the phase from being executed on that component:

my_enet_component.override_phase(“training”, vmm_null_phase_def);

In addition to adding and removing phases, you can also alias one phase to another so that the two phases overlap. For example, let’s say I need the reset phase of one portion of my design to overlap with the training phase of another. I could reconfigure the phasing of the components so they occurred in parallel instead of serially. Here are the steps:

  • Disable the reset phase for the group 1

    group1.override_phase(“reset”, vmm_null_phase_def);

  • Create a new user-defined phase for group 1 called “reset_during_training”

    class reset_during_training_phase_def extends
    vmm_fork_task_phase_def #(group1);
    `vmm_typename(reset_during_training_phase_def)

    virtual task do_task_phase(group1 obj);
    if(obj.is_unit_enabled())
    obj.reset_ph();
    endtask:do_task_phase

    endclass:reset_during_training_phase_def

  • Alias the new user-defined phase to the “training” phase

    vmm_timeline tl = vmm_simulation::get_top_timeline();
    tl.add_phase(“training”, reset_during_training_phase_def);

Another benefit of implicit phasing is that vmm_xactor, which normally manages threads, is also phased. Because of this, your transactors are now aware of the stage of the simulation we are in at any given moment. They can then change their behavior based on this knowledge. Because of this, VIP developer could configure a transactor to automatically start during the reset phase, stop during training, and continue during the start phase. Users could easily reconfigure the transactor to start and stop at other times as needed.

One thing to note is that implicitly phased components can be phased explicitly if desired by the user. This means that implicit phasing provides a superset of the explicit functionality provided by vmm_env and vmm_subenv.

The addition of implicit phasing means developers now have a choice to make when building a verification environment. Should you build it based on implicitly or explicitly-phased components? Luckily, it is possible to use implicitly-phased components in an explicitly-phased environment, and vice versa. My recommendation is for users to create all new testbench code using implicit phasing. If you are used to explicit phasing, the new development style can seem perplexing. However, as I mentioned, implicit phasing is effectively a superset of explicit phasing in its capabilities. Adopting the new methodology across your entire team will ensure the additional capabilities discussed above are available in your testbench in the future without users having to make changes down the road.

1 The listed phases are a subset of the actual pre-defined implicit phases available

Posted in Modeling, Phasing, VMM 1.2 | No Comments »

What Has TLM-2.0 Got To Do With It?

Posted by John Aynsley on 17th November 2009

JohnAynsley

John Aynsley, CTO, Doulos

You may have noticed that the public release of VMM 1.2 is just around the corner, and with this new version of VMM comes the introduction of features inspired by the SystemC TLM-2.0 standard.

Excuse me! TLM-2.0? What? Why do we need features from SystemC in VMM?

I will set out to answer that question fully in a series of blog posts over the coming months. But first off I will remark that the idea is not so strange. After all, VMM has always been transaction-level (with a small ‘t’ and ‘I’). Communication within a VMM verification environment exploits transaction-level modeling for speed and productivity, because “TLM” is about abstracting the model of communication used in a simulation. If we can adopt a common standard for transaction-level modeling across both SystemC and SystemVerilog, that has to be a good thing for everyone. It is evident that the design and verification community demands more than one language standard (witness VHDL, SystemVerilog, C/C++, and SystemC). Each individual language standards has progressed over time by borrowing the best features from the others. Having VMM borrow features from SystemC makes it easier to learn and work with both standards.

The other natural link between VMM and SystemC is that mixed-language simulation environments and C/C++ reference models are not unusual. Virtual platform models, as used for software development and architectural exploration, are growing in importance, and the SystemC TLM-2.0 standard is used to achieve interoperability between the components of a virtual platform model. If a constrained random VMM environment is to be used with a reference model that consists of a virtual platform adhering to the SystemC TLM-2.0 standard, then having TLM-2.0 support within VMM promises to make life easier for the VMM programmer.

Besides interoperability, the other main objective of the SystemC TLM-2.0 standard is simulation speed. The combination of speed and interoperability is achieved by the technical details of the ways in which transactions are passed between components. Fortunately, those technical details are a good fit with the way communication has always worked in VMM. In particular, both VMM and TLM-2.0 support the idea that each transaction has a finite lifetime with a well-defined time-of-birth and time-of-death.

The SystemC TLM-2.0 standard is based on C++. Unfortunately, not all C++ coding idioms translate naturally into SystemVerilog, so the transaction-level communication in VMM 1.2 is “inspired by” the TLM-2.0 standard rather than being a literal rendition of it.

So what are these new features? If you are already a SystemC user you may recognize ports and exports, borrowed directly from the SystemC standard, and analysis ports, transport interfaces, sockets and the generic payload, borrowed from the TLM-2.0 standard. I will explain how VMM is able to exploit each of these features in future blog posts, so watch this space…

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

Finding out which vmm callbacks are registered with a particular vmm_xactor instance

Posted by Avinash Agrawal on 13th November 2009

Avinash Agrawal Avinash Agrawal, Corporate Applications, Synopsys

Is it possible to find out which VMM callbacks are registered with a particular vmm_xactor instance?

The answer is yes. Here’s how:

The vmm_xactor base class in VMM instantiates a queue of vmm_xactor_callbacks, callbacks[$]. So, it is possible to use the queue methods to find out details on the callbacks associated with an instance of the vmm_xactor class. To find out the number of callbacks associated with a vmm_xactor instance, we can use the size() function on the callbacks queue. And to list the names of the callbacks associated with the vmm_xactor instance, we can add a new string member (that would carry the name associated with the callback) to the classes derived from vmm_xactor_callbacks, and display this string variable as needed.

Here is an example. Assume that we have extended the vmm_xactor_callbacks class as follows. We also add a string name, that would carry the name associated
with the callback.

class atm_driver_callbacks extends vmm_xactor_callbacks ;
string name;
// Called before a transaction is executed
virtual task pre_trans_t(atm_driver master, atm_cell tr, ref bit drop); endtask
// Called after a transaction has been executed
virtual task post_trans_t(atm_driver master, atm_cell tr);endtask
endclass

The vmm_xactor instance can have a task as follows that displays the callbacks associated with that vmm_xactor instance, as the following example code shows:

task atm_driver::displaycallbacks;
begin
atm_driver_callbacks mycb = new();
$display(“2LOG : number of callbacks is %d\n”, callbacks.size());
$cast(mycb,callbacks[0]);
$display(“2LOG : callback[0] is %s\n”, mycb.name);
$cast(mycb,callbacks[1]);
$display(“2LOG : callback[1] %s\n”, mycb.name);
end
endtask

And, in the build() method of the environment, we have the following code:

//Instantiate the callback objects
atm_sb_callbacks atm_sb_cb = new();
atm_cov_callbacks atm_cov_cb = new();
atm_driver_callbacks mycb = new();
atm_cov_cb.name = “CBNAME1″;
atm_sb_cb.name =”CBNAME2″;

//Register the callbacks to the driver instance drv
this.drv.append_callback(atm_cov_cb);
this.drv.append_callback(atm_sb_cb);
//Call the xactor instance class method that displays the callbacks as follows:
this.drv.displaycallbacks;

This will produce the following output:
LOG : number of callbacks is 2
LOG : callback[0] is CBNAME1
LOG : callback[1] CBNAME2

As seen in the output pasted above, the names and the number of VMM callbacks are registered with the particular vmm_xactor instance, are displayed.

Posted in Callbacks, Debug, Structural Components | No Comments »

Verification in the trenches: an end user’s viewpoint on VMM1.2

Posted by Shankar Hemmady on 11th November 2009

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

As a soup-to-nuts functional verification consultant, I always find myself as an integral member of my client’s verification team, be it during the project planning stage or during the mad rush end of the tape-out. The roles include everything; being an individual test writer, a verification architect, or even as the verification lead for a globally dispersed large verification team. And yes, the schedules are invariably aggressive, and the budgets tight.

So how does having a sound verification methodology such as VMM help? Broadly speaking, it offers a framework within which the verification engineer can get the job done efficiently. Instead of spending time on environment and methodology issues such as wondering about how to configure all the verification components to the same setting, he or she can focus more on identifying the application specific scenarios and easily configuring the environment to generate those. The challenge, of course, is being able to understand exactly how the methodology helps within the context of a given project.

Given this challenge, I often find myself explaining to teams, in terms of their existing verification environments, how a methodology or a feature can help and the corresponding trade-offs involved. Of course, not every feature is applicable to the needs of a given team, so I pay extra attention in explaining how a feature helps, its pros-and-cons, and how to best integrate it with the current verification environment.

In this series of blog posts, I will share my opinions on how the features newly introduced with the VMM1.2 release can help or could have helped the projects I have been directly involved with so far. Of course, I will not share the gory details, but I hope to share enough so that anyone looking at these new features can evaluate them from an end-user’s perspective.

So what’s new with VMM1.2? And how does it help? Check out the table below where it identifies some of the key features, and a “one-line” description for each. This table reflects how I see these features potentially benefit the projects I have worked with; your mileage may vary.

Feature

Description

vmm_object

A base class for all object types, making it easy to traverse hierarchies and locate objects by name

vmm_unit

A base class for all structural elements such as generators, transactors etc, making it easier to synchronize their actions when executing the phases of a test

vmm_timeline

Allows users to coordinate and even define custom phases

vmm_test

Adds support for multi-test management

`vmm_unit_config_xxx

Macros to configure and build verification environment structure in a top-down manner

vmm_opts

Flexible options handling, command-line or otherwise

vmm_rtl_config

Facilitates covering cases where a number of structural configurations for the RTL exist

vmm_tlm

Making sure your components can really connect to each other and foreign objects in a “plug and play” manner

regular expressions

A very convenient way to access objects by name and set specific properties to them

`vmm_class_factory

Macros to replace and extend object functionality anywhere in the code hierarchy conveniently, and support top-down build process


Table 1. VMM 1.2 Features

While the “one-liner”s above help get an overall idea about the feature, the Verification in the trenches series of blog posts will describe each in further detail starting with their motivation, pros and cons, and how to incorporate them quickly. The hope is that you will be able to judge for yourself how some of the features described can help your current and future project needs.

Feel free to comment/share your opinions and experiences. I will be very interested in hearing from folks in the trenches. How are these features working out for you? Are you getting the benefits as you had hoped? Which one of these features you can use today? Drop me a line! Share!

Posted in Phasing, Structural Components, Transaction Level Modeling (TLM), VMM infrastructure | No Comments »

SV-AOE: your friendly debug-companion!

Posted by Shankar Hemmady on 9th November 2009

CVC_Ajeetha_KumariCVC_srinivasan_VenkataramanCisco_Ramanathan_S Ajeetha Kumari & Srinivasan Venkataramanan, Contemporary Verification Consultants (CVC),

Ramanathan Sambamurthy, Cisco Systems

As verification specialists, like some of our readers, we thrive on solving challenging problems. Specifically Debug is an area that fascinates us a lot. Here is one of them, using VCS’s Aspect Oriented Extensions (AOE) to SystemVerilog to debug a Memory blow-up in SV-TB code. The problem statement was elaborated in our previous entry: http://www.vmmcentral.org/vmartialarts/?p=385

We brainstormed on potential improvements in order to shorten debug cycles in the future. One option we explored was the use of SIZED mailboxes, when applicable:

function new();

// parameter MBOX_SIZE = 100;

this.out_mbx = new(MBOX_SIZE);

endfunction : new

A better, scalable option is to use a wrapper class around plain SV Mailboxes. That would then allow AOE to aid in debug around the “put”. For instance, consider a simple class-wrapper around a SV Mailbox:

class mbox_c;

mailbox mbox;

function new(string name);

this.name = name;

this mbox = new();

endfunction : new

virtual task put (my_xactn x0);

this.mobx.put(x0);

endtask : put

// Similarly get(), new() etc.

endclass : mbox_c

Now, instead of a plain mailbox, we have a wrapper around it called mbox_c. Assuming that this wrapper was used all across the environment instead of the mailboxes, one can leverage the VCS’s Aspect Oriented Extensions to SystemVerilog to the rescue. In a separate file one can write:

extends chan_dbg(mbox_c);

before task put(my_xactn x0);

$display (“%m AOE: Mailbox %s size is %0d”, this. name,

this.num());

endtask : put

endextends

In contrast to standard OOP, AOE allows “in-place extensions” and hence adds to existing class code without creating hierarchy new class type. This means no factory is needed to swap the base class with a derived class and other associated extra code. Instead, just an additional file with few lines and you are set to go! While some of us may debate whether this is a good coding style for developing reusable environments, for throw away code like debug this is very handy as it requires no intervention to existing code. For instance, with the above code, a typical VCS run (with the dbg.sv code included in command line) produces the following output:

mbox_c::put_before AOE: Channel S2P_BFM_IN_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_SER_MON_OUT_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_PAR_MON_OUT_CHANNEL level is 1

mbox_c::put_before AOE: Channel S2P_BFM_IN_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_SER_MON_OUT_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_PAR_MON_OUT_CHANNEL level is 2

mbox_c::put_before AOE: Channel S2P_BFM_IN_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_SER_MON_OUT_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_PAR_MON_OUT_CHANNEL level is 3

mbox_c::put_before AOE: Channel S2P_BFM_IN_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_SER_MON_OUT_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_PAR_MON_OUT_CHANNEL level is 4

mbox_c::put_before AOE: Channel S2P_BFM_IN_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_SER_MON_OUT_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_PAR_MON_OUT_CHANNEL level is 5

mbox_c::put_before AOE: Channel S2P_SER_MON_OUT_CHANNEL level is 0

mbox_c::put_before AOE: Channel S2P_PAR_MON_OUT_CHANNEL level is 6

Of course, there are few safe-guards for such common pit-falls. We address this topic in our VMM adoption book (http://www.systemverilog.us/vmm_adoption) in Chapter-8, Advanced Topics (Section 8.3).

· A vmm_channel (equivalent of a Mailbox in SV) is blocking and has a default size of 1 (can be increased, reconfigured at run time as well)

· It includes built-in debug messages that can be turned on via command line

· It provides a vmm_channel::sink() routine – can be useful if the consumer is not available for integration

· It allows similar AOE debug extension, a code snippet is shown below:

extends chan_dbg(vmm_channel);

before task put(vmm_data obj, int offset = -1,

`VMM_SCENARIO grabber = null);

$display (“%m AOE: Channel %s level is %0d”,

this.log.get_name(),

this.size());

endtask : put

endextends

Posted in Debug | No Comments »

Life After Verification

Posted by Janick Bergeron on 8th November 2009

Janick Bergeron, Synopsys

Two years ago, Francoise left her job as a VMM/verification CAE with Synopsys. Her husband Pierre similarly quit his job at a large semi-conductor company. They embarked on a career change, the magnitude of which few of us undertake voluntarily: they traded high-tech careers and their Porsches for a tractor and ATV and took over Francoise’s father’s vineyard in south-western France

I had the pleasure of working with Francoise for several years before and, upon hearing the news, the wine-enthusiast in me quickly promised to help her with her harvest. This year, I made good on my promise. On October 27th, at 12:30pm, my girlfriend and I landed at the Pau airport, full of energy and anticipation at the prospect of experiencing a tiny sliver of a winemaking tradition that goes back 400 years.

P1010257

North Americans have a very distorted sense of time. One hundred years is “old” to us. Very few can trace their ancestry on this continent beyond a few generations.

Francoise’s winery, Domaine Guirardel, is housed in the original home and barn. They were built at the same time Samuel de Champlain was busy establishing Quebec City. The “new” house is 300-years old. Both are nestled on a south-facing hill with an incredible view of the Pyrenees. The domaine has never been sold: it has always been passed down through generations.

 

P1010396The postcard atmosphere of the place immediately reminded me of the movie “A Good Year”. A hammock is hung between two palm trees. Jean, Francoise’s father, is at work in an immense garden that is the source of many of the vegetables we will be eating throughout our stay. Lunch is often served on the front patio, under the protective shade of a huge oleander tree.

The vineyard is small: 5 hectares planted with Petit Manseng and Gros Manseng, two of the five varietals that make up the Jurancon appelation. The harvest is done entirely by hand by uncles, aunts, in-laws, cousins and friends, most of whom have been helping with the harvest for many years. Of the 20 or so harvesters, it is a first experience for only four of us.

P1010317 The work is simple: armed with shears, one cuts the bunches of golden grapes from the vines into plastic buckets. The content of full buckets is then transferred to a large bin behind the tractor.

P1010302

The day starts at 9am. We break for lunch at 1pm, then resume at 3pm until 5pm. It is intense but not hard work. Despite the physical nature of the work, shuffling along the vines shifting buckets up the hill and untangling twisted bunches of grapes, I was surprised to discover that it is accomplished in a very social and jovial atmosphere. Everyone is chatting and updating one another on common acquaintances.

 

 

 

P1010301 Lunch is reminiscent of a Thanksgiving dinner in the U.S. Everyone gathers in the dinning room, in front of a fireplace large enough to roast an entire cow, and share in a feast of delicacies from various French regions: home-made peach wine, raw oysters from Brittany, sausages from Corsica, wine from Bordeaux, Bearn pork bellies and many others.

Going back to the fields filled with so much great food is not easy! But the grapes cannot wait and we must make the best of the ideal weather conditions. Francoise and her father have been working in the chais since 7am, busily cleaning the press to receive the freshly-picked grape. And they will continue to work after dinner to complete the last pressing of the day.

P1010312P1010314 At the end of the day, under Francoise’s watchful eyes, everyone gathers again in the Tasting Room for a glass of the proprietor’s wine. It gives us a glimpse of what our (and her) labor will yield in two years.

 

 

 

P1010291P1010294Trading a keyboard, mouse and a well-paying secure job for a mechanical press, half a dozen stainless steel vats, oak barrels and the uncertainties of climate vagaries requires more courage than I possess! They are bringing their technical and marketing savoir-faire to this artisanal enterprise in the hopes of ensuring the future of their two children. For example, they are continuing the production of a late-harvest version of their wine, an experiment that was started back in 2005 and repeated only once in 2007. They are also planning to reclaim vines currently leased to another producer and, under the aegis of being a “young farmer”, plant additional vines in a new parcel of land.

She produces three wines: Tradition, Bi de Prat and Vendanges Tardives. They are very fruity, syrupy white wines similar to Sauterne, excellent with foie gras. Unlike most white wines, hers will improve with age, up to 20 years. A few years ago, I had the great fortune of tasting the 1967 vintage: it was the color of maple syrup and was totally sublime. Unfortunately, her wines are not available in North America (which makes the dozen or so bottles I have in my cellar even more precious!).

The French have a poor reputation when it comes to visitors. But it has never been my experience – nor mustn’t it be for the majority of visitors that continue to make France the single-most visited country in the world. And it most definitely has not been the case for our 6-day stay with Francoise and Pierre. We were welcomed in their home and immediately treated like family. It has been a wonderful experience that I hope I’ll be able to repeat next year.

For further reading (in french): http://www.sudouest.com/accueil/actualite/vin/article/757483/mil/5310416.html

Posted in Uncategorized | 1 Comment »

Phases vs. Threads

Posted by JL Gray on 4th November 2009

JLGray JL Gray, Consultant, Verilab, Austin, Texas, and Author of Cool Verification

Building a testbench for personal use is easy. Building a testbench that can be used by others is much more difficult. To make it easier for verification IP written by different people to interoperate, modern verification methodologies support the concept of standardized “phases” during a simulation run. Phases are a way to help verification engineers communicate in a standard language about what is meant to be taking place at any given time during a simulation.  For example, an explicitly phased VMM testbench built using vmm_env contains the following phases of execution:

· gen_cfg

· build

· reset

· config_dut

· start

· wait_for_end

· stop

· cleanup

· report

Ideally, each of these phases serves a clear purpose. If I want to reset the DUT, a good way to do it is to instrument the reset phase with the appropriate reset logic.  Similarly, the bulk of the simulation activity will likely occur during the wait_for_end phase.  The VMM now has support for implicit phasing. In an implicitly-phased system, components in the verification environment are stepped through each phase automatically by a global controller called vmm_simulation (and its associated “timelines”).  I will discuss timelines in a separate post.  In both the explicit and implicitly phased cases, the phases serve as guides through the simulation. However, most of the real work of the testbench will be accomplished by threads spawned off from these phases.

It is easy to spawn threads using a simple fork/join, but the VMM provides tools to make managing threads easier. In the VMM, the vmm_xactor base class provides support for managing threads and is at the same time phase-aware.  How does it do this?  For starters, vmm_xactor is now implicitly phased by the top level vmm_simulation controller.  However, users maintain full control over the ability to start and stop the transactor, just as they did in earlier versions of the VMM.  That means that a user could start a transactor during any VMM phase, and stop the transactor during the same or a later phase. The transactor could then query the current phase and change its behavior depending on the state of the simulation. Users can also modify their behavior via the use of callbacks.

Here is a diagram demonstrating the interaction between threads and an example subset of the new VMM implicit phases.

clip_image002

The diagram demonstrates activities that take place during specific phases of the testbench. It also shows that threads may start in one phase (such as the host generator starting in the reset phase) and stop in another (in this case, the shutdown phase).  The astute reader will note that I didn’t really need standardized phases at all to handle this. I could have done all of the activities described above in the “run” phase. In fact, that’s what many people do, even today where other phases are available.  The issue, as I stated at the beginning of the article, is that by standardizing when we do specific types of activities, our verification IP will be easier to reuse in other compatible environments.

Posted in Communication, Modeling, Phasing, VMM 1.2 | 1 Comment »

Say What? Another Look At Specification Analysis

Posted by Shankar Hemmady on 26th October 2009

Andy's Picture_2 Andrew Piziali, Independent Consultant

Have you ever been reviewing a specification and asked yourself “Say what?!” Then you’re not alone! One of the most challenging tasks we face as verification engineers is understanding design specifications. What does the architect mean when she writes “The conflabulator remains inoperative until triggered by a neural vortex?” Answering that question is part of specification analysis, the first step in planning the verification of a design, the subsequent steps being coverage model design, verification environment implementation, and verification process execution.

The specifications for a design—DUV, or “design-under-verification” for our purposes—typically include a functional specification and a design specification. The functional specification captures top level, opaque box, and implementation- independent requirements. Conversely, the design specification captures internal, clear box, implementation dependent behaviors. Each is responsible for conveying the architect’s design intent at a particular abstraction level to the design and verification teams. Our job is to ultimately comprehend these specifications in order to understand and quantify the scope of the verification problem and specify its solution. This comprehension comes through analyzing the specifications.

In order to understand the scope of the verification problem, the features of the DUV and their relationships must be identified. Hence, specification analysis is sometimes referred to as feature extraction. The features are described in the specifications, ready to be mined through our analysis efforts. Once extracted and organized in the verification plan, we are able to proceed to quantifying the scope and complexity of each by designing its associated coverage model. How do we tackle the analysis of specifications ranging from tens to hundreds of pages? The answer depends upon the size of the specification and availability of machine-guided analysis tools. For relatively small specifications, less than a hundred pages or so, bottom-up analysis ought to be employed. Specifications ranging from a hundred pages and beyond require top-down analysis.

Bottom-up analysis is the process of walking through each page of a specification: section-by-section, paragraph-by-paragraph, and sentence-by-sentence. As we examine the text, tables and figures, we ask ourselves what particular function of the DUV is addressed? What behavioral requirements are imposed? What verification requirements are implied? Is this feature amenable to formal verification, constrained random, or a hybrid technology? If formal is applicable, how might I formulate a declarative statement of the required behavior? What input, output and I/O coverage is needed? If this feature is more amenable to constrained random simulation, what are the stimulus, checking and coverage requirements?

Each behavioral requirement is a feature to be placed in the verification plan, in either the functional or design requirements sections, as illustrated below:

1 Introduction ………………………………… what does this document contain?

2 Functional Requirements …………….. opaque box design behaviors

2.1 Functional Interfaces ……………. external interface behaviors

2.2 Core Features ………………………. external design-independent behaviors

3 Design Requirements ………………….. clear box design behaviors

3.1 Design Interfaces …………………. internal interface behaviors

3.2 Design Cores ……………………….. internal block requirements

4 Verification Views ……………………….. time-based or functional feature groups

5 Verification Environment Design …. functional specification of the verification environment

5.1 Coverage …………………………….. coverage aspect functional specification

5.2 Checkers …………………………….. checking aspect functional specification

5.3 Stimuli ………………………………… stimulus aspect functional specification

5.4 Monitors ……………………………… data monitor functional specifications

5.5 Properties ……………………………. property functional specifications

Bottom-up analysis is amenable to machine-guided analysis, wherein an application presents a specification before the user. For each section of the spec, perhaps for each sentence, the tool asks if this describes a feature, what are its property, stimulus, checking and coverage requirements, and records this information so that it may be linked to the corresponding section of the verification plan. This facilitates keeping the specifications and the verification plan synchronized. The verification plan is incrementally constructed within a verification plan integrated development environment (IDE).

The alternative to bottom-up analysis is analyzing a specification from the top down, required for large specifications. Your objective here is to bridge the intent abstraction gap between the detail of the specification and the more abstract, incrementally written verification plan. Behavioral requirements are distilled into concise feature descriptions, quantified in their associated coverage models. Top-down analysis is conducted in brainstorming sessions wherein representatives from all stakeholders in the DUV contribute. These include the systems engineer, verification manager, verification engineer, hardware designer and software engineer. After the verification planning methodology is explained to all participants, each engineer contributing design intent explains their part of the design. The design is explored through a question-and-answer process, using a whiteboard for illustration. In order to facilitate a fresh examination of the design component, no pre-written materials should be used.

Whether bottom-up or top-down analysis is used, each design feature should be a design behavioral requirement, stating the intended behavior of the DUV. Both the data and temporal behaviors of each feature should be recorded. In addition to recording the name of each feature, the behavior should be summarized in a sentence or two semantic description. Optionally, design and verification responsibilities, technical references, schedule information and verification labor estimates may be recorded. If the verification plan is written in Microsoft Word, Excel or in HVP1 plain text, it may drive the downstream verification flow, serving as a design-specific verification user interface.

The next time you ask “Say what?!,” make sure you are methodically analyzing the specification using either of the above approaches and don’t hesitate to contact the author directly. Many bugs discovered during these exchanges are the least expensive of all!

1Hierarchical Verification Planning language

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

Simple Mailbox usage: woes, solution, and debug-automation thoughts

Posted by Shankar Hemmady on 5th October 2009

Cisco_Ramanathan_SCVC_Ajeetha_Kumarisrinivasan_venkataramanRamanathan Sambamurthy, Cisco Systems

Ajeetha Kumari & Srinivasan Venkataramanan, Contemporary Verification Consultants

We recently experienced a memory blow-up when an environment used for typical constrained random verification was used to apply a stress test. A quick background on the environment: it had a complex mix of C reference models, SystemVerilog generator, monitor and some checkers. The usual Memory Leak suspects of C /HDL boundary and dangling pointers were suggested. We tried using DVE-CBug (C-debugger). Not so lucky this time

Then we tried using the dynamic memory profiler within VCS, it provided vital clues pointing to the SV-TestBench code. After some more debug, we finally narrowed it down to a few Mailboxes on the monitor side. Two had recently been added for enhanced checking purposes with their consumers (i.e. checkers) still maturing. While the typical random tests didn’t show the issue, with stress tests with large number of packets, unsunk mailboxes created a memory leak!

The Mailboxes were constructed in default fashion as:

class new_mon;

mailbox out_mbx;

function new();

this.out_mbx = new();

endfunction : new

// ..

endclass : new_mon

Spot anything wrong above? The Mailbox is UNSIZED and hence never blocked. With no “consumer”, the monitor pushed packets into this Mailbox tirelessly leading to an eventual memory blow-up.

Interestingly, we addressed this exact topic in our VMM adoption book (http://www.systemverilog.us/vmm_adoption) in Chapter-8 Advanced Topics (Section 8.3). Let’s do some reality check – first and foremost, this is bad code, let us admit it to ourselves. It is obvious in hindsight. We should have done possibly one of the following:

· Used sized mailboxes

· Used a wrapper class around plain SV Mailbox to aid in debug around the “put”

· Even better, used a VMM_Channel, with tons of built-in features to aid in such cases (will be able to debug if not do it correct by construction). Refer to Chapter-2 http://www.systemverilog.us/vmm_adoption/vmm_isbn_0970539495.pdf

· Added an “artificial sink” for every such monitor (Something that we at CVC recommend during our regular SV/VMM trainings & consulting).

Also, with VMM 1.2, there are enhanced features built-in to navigate around the whole environment, locate all channels, and probe them at will – all without touching the existing code! It will be fun to debug such a scenario again in near future with such sophistication.

Posted in Communication, Debug | No Comments »

Exclusive Access of VMM Channel

Posted by Shankar Hemmady on 4th September 2009

rahul_shah1 Rahul V. Shah (bio)
Director of Customer Solutions, eInfochips


It is always challenging when it comes to controlled randomization. Constraints may be an easier way to think about it, but at chip level we are often interested in generating a few scenarios which are controlled in specific sequences. However, we still don’t want to develop scenarios that are very directed.

Let’s consider an example: we have an AHB bus interface with different master. We have a DMA controller on the bus along with few other masters. The chip level stress scenario might include multiple master performing data transfer to the memory interface on the bus. The transactions can be completely random. To make the scenario more interesting, we may want to add random reads from the status register, random read of some read only registers along with other data xfer.

One of the scenarios can include handling an error/exception scenario where we want read the status register, followed by the interrupt register followed by a write transfer to clear the interrupt. In the normal scenario, we can generate such scenario in directed fashion. But that will take away the random behavior.

Earlier, such scenarios were implemented in a lot more complex fashion as it was difficult to create a random scenario while getting exclusive access to randomness whenever required. Here I describe a mechanism to get exclusive access to a channel when required, while utilizing the benefits of randomness.

Scenario generators are used to generate a sequence of transactions. Multiple scenario generators may be connected to the same output channel but such a connection does not prevent other generators to concurrently inject transactions to that channel. A scenario is thus not guaranteed the exclusive access to an output channel. Multiple threads in the same multi-stream scenario, or multiple single stream scenarios, or any transactor may inject transactions in the same channel.

If the requirement is to generate a sequence of transactions without any interruption from other generator or from any transactor, an exclusive access of channel can be obtained and later released when exclusive access is no longer required. Let’s consider the scenario below:

01. class my_scenario extends vmm_ms_scenario;
02. rand atm_cell atm_cell_inst ;
03. atm_cell_channel atm_out_chan;
04. int MSC = this.define_scenario(“MY SCENARIO”, 0);
05. local bit [7:0] id;
06.
07. function new();
08. super.new(null);
09. atm_cell_inst = new;
10. endfunction: new
11.
12. task execute(ref int n);
13. $cast(atm_out_chan, this.get_channel(“ATM_SCENARIO_CHANNEL”));
14. atm_out_chan.grab(this);
15. repeat (10) begin
16. atm_out_chan.put(atm_scenario,.grabber(this));
17. repeat (10) @ (posedge clk) ;
18. end
19. atm_out_chan.ungrab(this);
20. endtask: execute
21. endclass: my_scenario

If a scenario requires exclusive access to a channel to ensure the uninterrupted execution of the sequence of transactions, it can grab the channel as shown in line 16, atm_out_chan.grab(this). This will grab the atm_out_chan channel and once grabbed, access of this channel will not be provided to any other scenario until it is explicitly ungrabbed. As shown in lines 15-18, the scenario sends 10 sequential transactions with a delay of 10 clock cycless after grabbing the channel. To inject transactions in the grabbed channel, a reference to the scenario currently injecting the transaction must be provided to the put method as shown in line 16 atm_out_chan.put(atm_scenario,.grabber(this)). After completion of the sequence of transactions, the channel is ungrabbed at line 19 atm_out_chan.ungrab(this).

When the channel is grabbed by one scenario and other scenarios try to put transactions in the same channel, the put method is blocked until the channel is ungrabbed by the scenario who has previously grabbed the channel. To prevent the blocking that would occur as a result of the grabbed channel, we can check the status of the channel using the vmm_channel::is_grabbed() function. This function will return “1”, if the channel is grabbed.

Posted in Communication, Stimulus Generation, Transaction Level Modeling (TLM), Tutorial | No Comments »

Make Your Coverage Count!

Posted by Shankar Hemmady on 31st August 2009

Andy Piziali Andrew Piziali, Independent Consultant

You are using coverage, along with other metrics, to measure verification progress as part of your verification methodology.1 2 Yet, lurking in the flow are the seeds of a bug escape that will blindside you. How so?

Imagine you are responsible for verifying an in-order, three-way x86 superscalar processor in the last millennium, before the Constrained Random Generation. Since your management wouldn’t spring for an instruction set architecture (ISA) test generator, you hired a team of new college grads to write thousands of assembly language tests. Within the allocated development time, the tests were written, they were functionally graded and achieved 100% coverage, and they all finally passed. Yeah! But, not so fast …

When first silicon was returned and Windows was booted on the processor, it crashed. The diagnosis revealed a variant of one of the branch instructions was misbehaving. (This sounds better than “It had a bug escape.”) How could this be? We reviewed our branch instruction coverage models and confirmed they were complete. Since all of the branch tests passed, how could this bug slip through?

Further analysis revealed this branch instruction was absent from the set of branch tests yet used in one of the floating point tests. Since the floating point test was aimed at verifying the floating point operation of the processor, we were not surprised to find it was insensitive to a failure of this branch instruction. In other words, as long as the floating point operations verified by the test behaved properly, the test passed, independent of the behavior of the branch instruction. From a coverage aspect, the complete ISA test suite was functionally graded rather than each sub-suite graded according to its functional requirements. Hence, we recorded full coverage.

The problem was now clear: the checking and coverage aspects of each test were not coupled, conditioning coverage recording on passing checked behavior. If we had either (1) functionally graded each test suite only for the functionality it was verifying or (2) conditionally recorded each coverage point based upon a corresponding check passing, this bug would not have slipped through. Using either approach, we would have discovered this particular branch variant was absent from the branch test suite. In the first case, that coverage point would have remained empty for the branch test suite. Likewise, in the second case we would not have recorded the coverage point because no branch instruction check would have been activated and passed.

Returning to the 21st century, the lesson we can take away from this experience is that coverage—functional, code and assertion—is suspect unless, during analysis, you confirm that for each coverage point a corresponding checker was active and passed. From the perspective of implementing your constrained random verification environment, each checker should emit an event (or some other notification), synchronous with the coverage recording operation, indicating it was active and the functional behavior was correct. The coverage code should condition recording each coverage point on that event. If you are using a tool like VMM Planner to analyze coverage, you may use its “-feature” switch to restrict the annotation of feature-specific parts of your verification plan to the coverage database(s) of that feature’s test suite.

You might ask if functional qualification3 would address this problem. Functional qualification answers the question “Will my verification environment detect, propagate and report each functional bug?” As such, it provides insight into how well your environment detects bugs but says nothing about the quality of the coverage aspect of the environment. I will address this topic in a future post if there is sufficient interest.

Remember, make your coverage count by coupling checking with coverage!

1Metric Driven Verification, 2008, Hamilton Carter and Shankar Hemmady, Springer

2Functional Verification Coverage Measurement and Analysis, 2008, Andrew Piziali, Springer

3“Functional Qualification,” “EDA Design Line,” June 2007, Mark Hampton

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

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:

`vmm_data_new(<class_name>)

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;

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

`vmm_data_member_begin(s2p_xactn)
`vmm_data_member_scalar(pkt_len, DO_ALL)
`vmm_data_member_scalar(pkt_pld, DO_ALL)
`vmm_data_member_end(s2p_xactn)
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 »

class factory

Posted by Wei-Hua Han on 26th August 2009

Weihua Han, CAE, Synopsys

As a well-known Object-Oriented technique, class factory has actually been applied in VMM since inception. For instance, in the vmm atomic and scenario generators, by assigning different blueprints to randomized_obj and scenario_set[] properties, these generators can generate transactions with user specified patterns. Using the class factory pattern, users create an instance with a pre-defined method (such as allocate() or copy()) instead of the constructor. This pre-defined method will create an instance from the factory not just the type of the variable being assigned.

VMM1.2 now simplifies the application of the class factory pattern within the whole verification environment so that users can easily replace any kind of object, transaction, scenario and transactor by a similar object. Users can easily follow the steps below to apply the class factory pattern within the verification environment.

1. define “new”, “allocate”, “copy” methods for a class and create the factory for the class.

class vehicle_c extends vmm_object;

//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

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

//these two macros will define necessary methods for class factory and create factory for the class

`vmm_typename(vehicle_c);

`vmm_class_factory(vehicle_c);

endclass

`vmm_typename, `vmm_class_factory will implement the necessary methods to support the class factory pattern, like get_typename(), create_instance(), override_with_new(), override_with_copy(), etc.

Users can also use `vmm_data_member_begin and `vmm_data_member_end to implement the “new”, “copy”, “allocate” methods conveniently.

2. create an instance using the pre-defined “create_instance()” method

To use the class factory, the class instance should be created with pre-defined create_instance() method instead of the constructor. For example:

class driver_c extends vmm_object;

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

For this example, the output is:

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

3.  define a new class

Let’s now define the following new class which is derived from the original class vehicle_c:

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

And we would like to create myvehicle instance from this new class without modifying driver_c class.

4. override the original instance or type with the new class

VMM1.2 provides two methods for users 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 methods, the first argument is the instance name, as specified in the create_instance() method, which users hope to override with the type of new_class. Users can use powerful name matching mechanism defined in VMM to specify the override happens on dedicated instance 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 users only want to override one dedicated instance with a copy of another instance, users can call override_with_copy 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 pattern short-hand macros provided with VMM1.2, users can easily use class factories patterns to replace transactors, transactions and other verification components without modifying the testbench code. I find this very useful for increasing the reusability of verification components.

Posted in Configuration, Modeling, SystemVerilog, Tutorial, VMM, VMM 1.2, VMM infrastructure | 1 Comment »

How to connect your SystemC Reference Models to your verification VMM based framework

Posted by Shankar Hemmady on 17th August 2009

Nasib_Naser

Nasib Naser, Phd, CAE, Synopsys

In this blog I will discuss the Use Model demonstrated in Figure 1 where a VMM layered testbench is used to verify an RTL DUT against a SystemC transaction level model. SystemVerilog allows for the creation of a reusable layered testbench architectures. The VMM methodology provides the basis for such a layered architecture. With a layered approach, transaction-level reference models can be easily integrated at the appropriate level to provide self-checking functions. In this Use Model the VMM Function layer is communicating with SystemC model using the TLI mechanism to perform read/write transactions, and using the same testbench scenarios the VMM command layer is driving the DUT at pin level.

clip_image002

Figure 1 – VMM driving TLM and RTL with checking

Synopsys’ VCS functional verification solution addresses the challenge of this use model with its SystemC-SystemVerilog Transaction-Level Interface (TLI). Using TLI SystemC interface methods can be invoked in SystemVerilog and vice versa. The **value add** for using TLI is that the SystemVerilog DPI based communication code that synchronizes both domains is automatically generated.

Let’s take a look at the various code components in SystemC and SystemVerilog based on the VMM methodology that enables such a verification use model. In the following example we define the read and write transactions as SC Interface methods.

class Buf_if: virtual public sc_interface {
public:

// do the pure virtual function read()/write() declarations here
virtual void read(unsigned int addr, unsigned int* data) = 0;

virtual void write(unsigned int addr, unsigned int data) = 0;
};

Following code shows VMM Transactor invoking SystemC transactions read and write at function layer. VMM transactor tb_mast is communicating to SystemC TLM using vmm channel tb_mast_out_ch1 and with the RTL model using the vmm channel tb_mast_out_ch2 channel, as shown in the following code:

class tb_master extends vmm_xactor;
virtual tb_if.master ifc;
tb_data_channel tb_master_in_ch;
tb_data_channel
tb_master_out_ch1;
tb_data_channel
tb_master_out_ch2;

extern virtual task main();
endclass: tb_master

task tb_master::main();
tb_data tr, tr_out;
super.main();
forever begin
.
..
// Send the Instruction to SC Reference Model
tb_master_out_ch2.put(tr_out);
// Send the Instruction to RTL
tb_mast_out_ch1.put(tr_out);
end
endtask: main

The following code shows VMM reference Transactors calling the SystemC transaction functions via the adaptor alu_tl_if_adpt_vlog which was automatically generated by VCS TLI.

class tb_ref extends vmm_xactor;

tb_data_channel     tb_ref_in_ch;

alu_tl_if_adpt_vlog alu_tl_if_adpt_vlog_inst0;


extern function new (string instance,
integer stream_id = -1,

tb_data_channel tb_ref_in_ch = null);

extern virtual task main();

endclass: tb_ref

task tb_ref::main();

super.main();

forever begin

case(tr_out.tb_data_type)

SA_SB_OP_GO : begin

alu_tl_if_adpt_vlog_inst0.write(addrA,a);

alu_tl_if_adpt_vlog_inst0.write(addrB,b);

alu_tl_if_adpt_vlog_inst0.write(addrOP,op);

alu_tl_if_adpt_vlog_inst0.read(addrOut,d);

tr_out.data_out = d;
end

endcase

end

endtask

This VCS TLI use model provides a complete and easy way to integrating blocking and non-blocking SystemC reference models into a VMM based multi-layer verification environment.

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