Verification Martial Arts: A Verification Methodology Blog

Archive for December, 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 | 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 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) | 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 | 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) | 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 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, VMM | No Comments »