Verification Martial Arts: A Verification Methodology Blog

Archive for the 'VMM 1.2' Category

Blocking/Non-blocking Transport Adaption in VMM 1.2

Posted by John Aynsley on 1st September 2010

John Aynsley, CTO, Doulos

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

vmm_connect serves four distinct purposes in VMM 1.2.

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

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

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

class initiator extends vmm_xactor;
`vmm_typename(initiator)

vmm_tlm_b_transport_port  #(initiator, vmm_tlm_generic_payload) m_b_port;

virtual task run_ph;
forever begin: loop
vmm_tlm_generic_payload tx;

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

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

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

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


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

class target extends vmm_xactor;
`vmm_typename(target)

vmm_tlm_nb_transport_export #(target, vmm_tlm_generic_payload) m_nb_export;

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

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

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

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

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

virtual function void connect_ph;

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

endfunction: connect_ph

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

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

TLM and VMM – What is it all for?

Posted by John Aynsley on 18th August 2010

John Aynsley, CTO, Doulos

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

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

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

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

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

TLM Sockets in VMM 1.2

Posted by John Aynsley on 12th August 2010

John Aynsley, CTO, Doulos

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

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

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

Let’s look at an example:

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

begin

m_socket.b_transport(tx, delay);

end

begin

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

end

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

endfunction : nb_transport_bw

endclass: initiator


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

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


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

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

endtask : b_transport

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

endfunction : nb_transport_fw


begin

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

end

endclass: target


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

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

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

virtual function void connect_ph;

m_initiator.m_socket.tlm_bind( m_target.m_socket );

endfunction: connect_ph

endclass: tb_env


That’s all there is to it!

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

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

Learn about VMM adoption from customers – straight from SNUG India 2010

Posted by Srinivasan Venkataramanan on 1st July 2010

…Reflections form core engineering team of CVC, fresh from SNUG India 2010

Jijo PS, Thirumalai Prabhu, Kumar Shivam, Avit Kori, Praveen & Nikhil – TeamCVC www.cvcblr.com

Here is a quick collection of various VMM updates from SNUG India 2010 – as seen by TeamCVC. Expect to hear more on VMM1.2 soon from us as now I have a young team all charged up with VMM 1.2 (thanks to Amit @SNPS). All the papers and presentations can be accessed through: http://www.synopsys.com/Community/SNUG/India/Pages/IndiaConferenceataGlance.aspx

TI’s usage of VMM 1.2 & RAL

In one of the well received papers, TI Bangalore talked about “Pragmatic Approach for Reuse with VMM1.2 and RAL “. The design is a complex digital display subsystem involving numerous register configurations. Not only handling the register configurations is a challenge, but also the ability to reuse of block level subenvs at system level with ease, and with minimal rework and reduced verification time. The author presented their success with VMM 1.2 & RAL to address these challenges.

Key elements touched up on advanced VMM are:

· TLM 2.0 communication mechanism

· VMM Multi-Stream Scenario gen (MSS)

· VMM RAL

Automated Coverage Closure with ECHO

It is real and live – automated coverage closure is slowly becoming reality atleast in select designs & projects. Having been attempted by various vendors for a while (see: http://www.cvcblr.com/blog/?tag=acc) VCS has added this under ECHO technology. At SNUG, TI presented their experience with ECHO & VMM-RAL. In their paper titled “Automating Functional Coverage Convergence and Avoiding Coverage Triage with ECHO Technology” TI described how an ECHO based methodology in a VMM RAL based environment, can in an automated manner close the feedback loop in targeting coverage groups involving register configuration combinations resulting in significant reduction in verification time.

WRED Verification with VMM

In her paper on “WRED verification with VMM”, Puja shared her usage of advanced VMM capabilities for a challenging verification task. Specifically she touched upon:

· VMM Multi-Stream Scenario gen

· VMM Datastream Scoreboard with its powerful “with_loss” predictor engine

· VMM RAL to access direct & indirect RAMs & registers

What we really liked is to see real application of some of these advanced VMM features – we were taught all of these during our regular CVC trainings and we even tried many of them on our own designs. It feels great to hear form peers on similar usage and to appreciate the value we derive out of VMM @CVC and the vibrant ecosystem that CVC creates around the same.

System-Level verification with VMM

Ashok Chandran, of Analog Devices presented their use of specialized VMM components in a system-level verification project. Specifically he touched upon specialized VMM base classes like vmm_broadcast and vmm_scheduler

At the end the audience learnt what are some of the unique challenges a SoC verification project can present. Even more interesting was the fact that the ever growing VMM seems to have solution for a wide variety of such problems, well thought-out upfront – Kudos to the VMM developers!

Ashok also briefed on his team’s usage of relatively new features in VMM such as vmm_record and vmm_playback and how it helps us to quickly replay streams.

On the tool side, a very useful feature for regressions is the usage of separate compile option in VCS.

VMM 1.2 for VMM users

Amit from SNPS gave a very useful and upto-the-point update on VMM 1.2 for long time VMM users. It was rejuvenating to listen to the VMM 1.2 run_tests feature and the implicit phasing techniques. Though look like little “magic” these features are bound to improve our productivity as there are lesser things to code-debug and move-on..

Amit also touched upon the use of TLM 2.0 ports and how they can be nicely used for integrating functional coverage, instead of using the vmm_callbacks.

The hierarchical component creation and configurations in VMM 1.2 puts us on track for the emerging UVM and is very pleasing to see how the industry keeps moving to more-n-more automation.

A truly vibrant ecosystem enabled by CVC -VMM Catalyst member

A significant addition to this year’s SNUG India was the DCE – Designer Community Expo – a genuine initiative by Synopsys to bring in partners to serve the larger customer base better all under one roof. CVC (www.cvcblr.com) being the most active VMM catalyst member in this region was invited to setup a booth showcasing its offerings. We gave away several books including our popular VMM adoption book http://systemverilog.us/?p=14 and all the new SVA Handbook 2nd edition http://systemverilog.us/?p=16 .

Here is a snapshot of CVC’s booth with our VMM and other offerings.

clip_image002

Posted in Coverage, Metrics, Register Abstraction Model with RAL, VMM, VMM 1.2 | 1 Comment »

Verification in the trenches: Setting options with VMM1.2

Posted by Ambar Sarkar on 28th June 2010

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

Every parameter in a verification environment should be easy to access and tweak. We often use the term “knob” or “switch” when referring to these parameters. “Ease of access and tweak” here means:

  1. You can declare knobs as needed conveniently
  2. You can locate the knob easily
  3. You can configure multiple knobs to the same value in a single command
  4. You can list all the knobs defined, with documentation
  5. You can override values set elsewhere easily

In case you have not already guessed it, I am talking about the vmm_opts class, available with VMM1.2. The user guide introduces the class as:

The vmm_opts object allows to define and set configuration options. Options can be set from the simulator command line, file or within the code itself. These options can be set on a per-instance basis or globally by using regular expressions.

Let us consider how this class stacks up against the various criteria set earlier for “ease of access and tweak”.

  1. a. You can declare knobs as needed conveniently

    Say you want to declare a switch, “do_checks”, to turn on/off checking in a monitor.

    Intitutively, you would expect to perform the following steps to declare a variable “do_checks” as a knob(or switch):

          1. Declare the knob and set its default

            task my_monitor::run_ph();
            do_checks = vmm_opts::get_bit( “do_checks”, “Turn on checks”);

            In the line above, vmm_opts::get_bit() returns a 1 if the option is set to 1 elsewhere or in the command line. Otherwise, it returns 0.

          2. Set the knob overrides
            This can be done from the command line, from a command file, or even from within your code. For example, from command line, you can type:

            ./simv +vmm_opts+do_checks

          3. Use the knob in your code

            Step 1 above also shows how you typically can access the value.

This use model seems straightforward so far.

If you are dealing with structural configuration parameters, you can even forego the steps 1 and 3 above if you use the vmm_unit_config_xx macros:


class my_monitor extends vmm_xactor;

bit do_checks;

`vmm_unit_config_begin(my_monitor)

`vmm_unit_config_boolean(do_checks, “Turn checking on/off”, 1, DO_ALL)
`vmm_unit_config_end(my_driver)

endclass

By declaring it in this manner, the underlying VMM implementation makes sure that during the configure_ph() phase, do_checks is set to the desired value. In this example, do_checks will be a value of 1 by default, unless configured otherwise elsewhere.

  1. b. You can locate the knob easily.
  2. c. You can configure multiple knobs to the same value in a single command.

As long as you know the name of the knob, you can easily get to it by either specifying the hierarchical path to the object or even use regular expressions. For example, you can use the following command to set do_check as follows:

vmm_opts::set_bit(“%*:do_checks”, 0);

This code ensures that all objects derived from the vmm_unit class that declared the do_checks as a configuration parameter will have that variable set to false. Note that a single command succeeded in setting all “do_checks” to 0.

  1. d. You can list all the knobs defined, with documentation

This will print out the documentation of all the parameters defined as configurable objects in the vmm_unit derived classes:

vmm_opts::get_help()

  1. e. You can override values set elsewhere easily

    These parameters can be accessed and tweaked in order of increasing priority as follows:

    • within the code itself using vmm_opts::set_*() methods,
    • external option files, and
    • command-line options.

IMHO, the examples above demonstrate how vmm_opts makes it easy to create, maintain, and document various knobs you may require in your environment. Note that vmm_opts is powerful and provides many more bells and whistles, including tracking file-name and line-number where some option was being overridden. If you want to get very fancy with your option processing, this class can indeed support it and I recommend going into the details within the VMM User Guide.

In summary, even if you already have implemented your own switching mechanism for your environment, do consider “switching” over to vmm_opts, pun intended.

This article is the 8th 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 Tutorial, VMM 1.2 | 3 Comments »

Non-blocking Transport Communication in VMM 1.2

Posted by John Aynsley on 24th June 2010

John Aynsley, CTO, Doulos

When discussing the TLM-2.0 transport interfaces my posts on this blog have referred to the blocking transport interface alone. Now it is time to take a brief look at the non-blocking transport interface of the TLM-2.0 standard, which offers the possibility of much greater timing accuracy.

The blocking transport interface is restricted to exactly two so-called timing points per transaction, marked by the call to and the return from the b_transport method, and by convention corresponding to the start of the transaction and the arrival of the response. The non-blocking transport interface, on the other hand, allows a transaction to be modeled with any number of timing points so it becomes possible to distinguish between the start and end of an arbitration phase, address phase, write data phase, read data phase, response phase, and so forth.

image

As shown in the diagram above, b_transport is only called in one direction from initiator to target, and the entire transaction is completed in a single method call. nb_transport, on the other hand, comes in two flavors: nb_transport_fw, called by the initiator on the so-called forward path, and nb_transport_bw, called by the target on the backward path. Whereas b_transport is blocking, meaning that it may execute a SystemVerilog event control, nb_transport is non-blocking, meaning that it must return control immediately to the caller. A single transaction may be associated with multiple calls to nb_transport in both directions, the actual number of calls (or phases) being determined by the protocol being modeled.

With just one call per transaction, b_transport is the simplest to use. nb_transport allows more timing accuracy, with multiple method calls in both directions per transaction, but is considerably more complicated to use. b_transport is fast, simple, but inaccurate. nb_transport is more accurate, supporting multiple pipelined transactions, but slower and more complicated to use.

In VMM the role of the TLM-2.0 non-blocking transport interface is usually played by vmm_channel, which allows multiple timing points per transaction to be implemented using the notifications embedded within the channel and the vmm_data transaction object. The VMM user guide still recommends vmm_channel for this purpose. nb_transport is provided in VMM for interoperability with SystemC models that use the TLM-2.0 standard.

Let’s take a quick look at a call to nb_transport, just so we can get a feel for some of the complexities of using the non-blocking transport interface:


class initiator extends vmm_xactor;
`vmm_typename(initiator)
vmm_tlm_nb_transport_port #(initiator, vmm_tlm_generic_payload) m_nb_port;

begin: loop
vmm_tlm_generic_payload tx;
int                     delay;
vmm_tlm::phase_e        phase;
vmm_tlm::sync_e         status;

phase  = vmm_tlm::BEGIN_REQ;

status = m_nb_port.nb_transport_fw(tx, phase, delay);
if (status == vmm_tlm::TLM_UPDATED)

else if (status == vmm_tlm::TLM_COMPLETED)

From the above, you will notice some immediate differences with b_transport. The call to nb_transport_fw takes a phase argument to distinguish between the various phases of an individual transaction and returns a status flag which signals how the values of the arguments are to be interpreted following the return from the method call. A status value of TLM_ACCEPTED indicates that the transaction, phase, and delay were unchanged by the call, TLM_UPDATED indicates that the return from the method call corresponds to an additional timing point and so the values of the arguments will have changed, and TLM_COMPLETED indicates that the transaction has jumped to its final phase.

You are not recommended to use nb_transport except when interfacing to a SystemC model because the rules are considerably more complicated than those for either b_transport or vmm_channel.

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

Generic Payload Extensions in VMM 1.2

Posted by John Aynsley on 22nd June 2010

John Aynsley, CTO, Doulos

In a previous post I described the TLM-2 generic payload as implemented in VMM 1.2. In this post I focus on the generic payload extension mechanism, which allows any number of user-defined attributes to be added to a generic payload transaction without any need to change its data type.

Like all things TLM-2, the motivation for the TLM-2.0 extension mechanism arose in the world of virtual platform modeling in SystemC. There were two requirements for generic payload extensions: firstly, to enable a transaction to carry secondary attributes (or meta-data) without having to introduce new transaction types, and secondly, to allow specific protocols to be modeled using the generic payload. In the first case, introducing new transaction types would have required the insertion of adapter components between sockets of different types, whereas extensions permit meta-data to be transported through components written to deal with the generic payload alone. In the second case, extensions enable specific protocols to be modeled on top of the generic payload, which makes it possible to create very fast, efficient bridges between different protocols.

Let us have a look at an example that adds a timestamp to a VMM generic payload transaction using the extension mechanism. The first task is to define a new class to represent the user-defined extension:

class my_extension extends vmm_tlm_extension #(my_extension);

int timestamp;

`vmm_data_new(my_extension)
`vmm_data_member_begin(my_extension)
`vmm_data_member_scalar(timestamp, DO_ALL)
`vmm_data_member_end(my_extension)

endclass


The user-defined extension class extends vmm_tlm_extension, which should be parameterized with the name of the user-defined extension class itself, as shown. The extension can contain any number of user-defined class properties; this example contains just one, the timestamp.

The initiator of the transaction will create a new extension object, set the value of the extension, and add the extension to the transaction before sending the transaction out through a port:


class initiator extends vmm_xactor;
`vmm_typename(initiator)

vmm_tlm_b_transport_port #(initiator, vmm_tlm_generic_payload) m_port;

vmm_tlm_generic_payload randomized_tx;

begin: loop
my_extension ext = new;

$cast(tx, randomized_tx.copy());
ext.timestamp = $time;
tx.set_extension(my_extension::ID, ext);
m_port.b_transport(tx, delay);


Note the use of the extension ID in the call to the method set_extension: each extension class has its own unique ID, which is used as an index into an array-of-extensions within the generic payload transaction.

Any component that receives the transaction can test for the presence of a given extension type and then retrieve the extension object, as shown here:


class target extends vmm_xactor;
`vmm_typename(target)

vmm_tlm_b_transport_export #(target, vmm_tlm_generic_payload) m_export;

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

my_extension ext;

$cast(ext, trans.get_extension(my_extension::ID));

if (ext)
$display(“Target received transaction with timestamp = %0d”, ext.timestamp);


Note that once again the extension type is identified by using its ID in the call to method get_extension. If the given extension object does not exist, get_extension will return a null object handle. If the extension is present, the target can retrieve the value of timestamp and, in this example, print it out.

The neat thing about the extension mechanism is that a transaction can carry extensions of many types simultaneously, and those transactions can be passed to or through transactors that may not know of the existence of particular extensions.

image

In the diagram above, the initiator sets an extension that is passed through an interconnect, where the interconnect knows nothing of that extension. The interconnect adds a second extension to the transaction that is only known to the interconnect itself and is ignored by the other transactors.

And the point of all this? The generic payload extension mechanism in VMM will permit transactions to be passed to SystemC virtual platform models, where the TLM-2.0 extension mechanism is heavily used.

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

VMM 1.2.1 is now available

Posted by Janick Bergeron on 15th June 2010

We, in the VMM team, have been so busy working on improving VMM that we only recently noticed that it has been almost a full year since we released an Open Source distribution of VMM. With the release of VCS 2010.06, we took the opportunity to release an updated Open Source distribution that contains all of the new features and capability in VMM now available in the VCS distribution.

I am not going to repeat in details what changed (you can refer to the RELEASE.txt file for that), but I will point two of the most important highlights…

First, this version supports the VMM/UVM interoperability package (also available from download). This interoperability package will allow you to use UVM verification assets in your VMM verification environment (and vice-versa). Note that the VMM/UVM interoperability package is also included in the VCS distribution (along with the UVM-1.0EA release) in VCS2010.06.

Second, many new features were added to the VMM Register Abstraction Layer (RAL). For example, RAL now supports automatic mirroring of registers by passively observing read/write transaction or by actively monitoring changes in the RTL code itself. Another important addition is the ability to perform sub-register accesses when fields are located in individual byte lanes.

The Open Source distribution is the exact same source code as the VMM distribution included in VCS. Therefore, you can trust its robustness acquired in the field through many hundreds of successful verification projects.

Posted in Announcements, Interoperability, Register Abstraction Model with RAL, VMM 1.2 | 1 Comment »

Diagnosing Transaction-Level Connections in VMM 1.2

Posted by John Aynsley on 7th June 2010

John Aynsley, CTO, Doulos

In my previous post on this blog I discussed hierarchical transaction-level connections in VMM 1.2. In this post I show how to remove connections, and also discuss the various diagnostic methods that can help when debugging connection issues.

Actually, removing transaction-level connections is very straightforward. Let’s start with a consumer having an export that permits multiple bindings:

class consumer extends vmm_xactor;

vmm_tlm_b_transport_export #(consumer, vmm_tlm_generic_payload) m_export;

function new (string inst, vmm_object parent = null);
super.new(get_typename(), inst, -1, parent);
m_export = new(this, “m_export”, 4);
endfunction: new

Passing the value 4 as the third argument to the constructor permits up to four different ports to be bound to this one export. These ports can be distinguished using their peer id, as described in a previous blog post.

function void start_of_sim_ph;
vmm_tlm_port_base #(vmm_tlm_generic_payload) q[$];
m_export.get_peers(q);
m_export.tlm_unbind(q[0]);

TLM ports have a method get_peer (singular) that returns the one-and-only export bound to that particular port. TLM exports have a similar method get_peers (plural) that returns a SystemVerilog queue containing all the ports bound to that particular export. The method tlm_unbind can then be called to remove a particular binding, as shown above.

There are several other methods that can be helpful when diagnosing connection problems. For example, the method get_n_peers returns the number of ports bound to a given export:

$display(“get_n_peers() = %0d”, m_export.get_n_peers());

There are also methods for getting a peer id from a peer, and vice-versa, as shown in the following code which loops through the entire queue of peers returned from get_peers:

m_export.get_peers(q);
foreach(q[i])
begin: blk
int id;
id = m_export.get_peer_id(q[i]);
$write(“id = %0d”, id);
$display(“, peer = %s”, m_export.get_peer(id).get_object_hiername());
end

In addition to these low-level methods that allow you to interrogate the bindings of individual ports and exports, there are also methods to print and check the bindings for an entire transactor. The methods are print_bindings, check_bindings and report_unbound, which can be called as follows:

class tb_env extends vmm_group;

virtual function void start_of_sim_ph;
$display(“\n——– print_bindings ——–”);
vmm_tlm::print_bindings(this);
$display(“——– check_bindings ——–”);
vmm_tlm::check_bindings(this);
$display(“——– report_unbound ——–”);
vmm_tlm::report_unbound(this);
$display(“——————————–”);
endfunction: start_of_sim_ph

print_bindings prints information concerning the binding of every port and export below the given transactor. check_bindings checks that every port and export has been bound at least the specified minimum number of times. report_unbound generates warnings for any unbound ports or exports, regardless of the specified minimum.

In summary, VMM 1.2 allows you easily to check whether all ports and exports have been bound, whether a given port or export has been bound, to find the objects to which a given port or export has been bound, and even to remove the bindings when necessary.

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

Implicit vs. Explicit Phasing

Posted by JL Gray on 3rd June 2010

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

Last week I discussed the concepts of phases and threads. This week, I’ll continue that theme by focusing on the difference between implicit and explicit phasing.

Quick quiz: If you had to remember just one thing about the benefits of implicit phasing, what would that one thing be?

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

Verification in the trenches: TLM2.0 or vmm_channel? Communicating with other components using VMM1.2

Posted by Ambar Sarkar on 1st June 2010

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

Did life get easier with the availability of TLM2.0 style communication in VMM1.2?

Or the other way around? Are you asking: Should I use a vmm_tlm port or just stick to the tried and trusted vmm_channel as my communication method within the verification environment? You are not alone.

As you are aware, VMM1.2 provides the following interfaces for exchanging transactions between components:

  1. vmm_channel (Pre VMM1.2)
  2. vmm_tlm based blocking and non-blocking interfaces (TLM based)
  3. vmm_analysis_port (TLM based)
  4. vmm_callback (Pre VMM1.2)

Which option is the best for communicating between components? Under what circumstances?

There are two real requirements here,

  1. Maintain the ability to work with existing VMM (pre VMM 1.2) components.
  2. Be forward compatible with components created with TLM based ports, as is expected as the industry moves toward UVM (Yes, the early adopter version was released recently!).

TLM2.0 based communication mechanism offers a flexible, sufficient, efficient, and clear and well-defined semantics for communication between two components, and the industry as a whole is moving towards a TLM based approach. For these reasons, I recommend going forward that any new VIP using only the TLM2.0 based communication.

Since VMM1.2 provides complete interoperability between vmm_channel and tlm 2.0 style ports, the user is guaranteed that the created component will keep on working with vmm_channel based counterparts:

class subenv extends vmm_group;
initiator i0;
target t0;

virtual function void connect_ph();
vmm_connect #(.D(my_trans))::tlm_bind(
t0.in_chan, // Channel
i0.b_port, // TLM port
vmm_tlm::TLM_BLOCKING_EXPORT);
endfunction: connect_ph

endclass: subenv

Similarly, any vmm_notify based callback events can be communicated using tlm_analysis ports. For details, see example 5-48 in the Advanced Usage section in the user guide.

By creating components that solely depend on TLM style communication schemes will greatly facilitate interoperability and migration of VIP implementations to currently evolving statndard of UVM.

The following summarizes my recommendations:

Type of interaction

Recommended mechanism

Issuing and receiving transactions vmm_tlm based blocking interface
Issuing notification to passive observers vmm_analysis_port

For issuing transactions to other components on a point-to-point manner, typically seen in master-slave based communications, use the vmm_tlm based blocking port interfaces.

For slave-like transactors which expect to receive transactions from other transactors, use vmm_tlm based blocking export interface.

For reactive transactors, define additional vmm_tlm based blocking export interface.

For broadcast transactions to be communicated to multiple consumers such scoreboards, functional coverage models, etc, use vmm_analysis_port.

So, did life get easier with TLM? I believe so. Especially if you want to be forward compatible with the new and upcoming methodologies.

This article is the 7th 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 Communication, Transaction Level Modeling (TLM), Tutorial, VMM 1.2 | No Comments »

Phases and Threads

Posted by JL Gray on 27th May 2010

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

Back in the fall, I wrote about the differences between Phases and Threads, and how that relates to implicit and explicit phasing in the VMM.  In this video, I’ve taken a step back to describe phases, threads, and timelines using a real-world analogy based on the seasons of the year.

As you’re watching this video, one thing to keep in mind is that threads are dealt with by vmm_xactor-based objects, and phases are handled by vmm-timeline and vmm_group-based objects.

Posted in Phasing, Tutorial, VMM 1.2 | 1 Comment »

Hierarchical Transaction-Level Connections in VMM 1.2

Posted by John Aynsley on 19th May 2010

John Aynsley, CTO, Doulos

In a previous blog post I described ports and exports in VMM 1.2, and explored the issue of binding a port of one transactor to the export of another transactor, where the two transactors are peers. Now let us look at how we can make hierarchical connections between ports and exports in the case where one transactor is nested within another.

Let’s start with ports. Suppose we have a producer that sends transactions out through a port and is nested inside another transactor:

class producer extends vmm_xactor;
vmm_tlm_b_transport_port #(producer, vmm_tlm_generic_payload) m_port;

virtual task run_ph;

vmm_tlm_generic_payload tx;

m_port.b_transport(tx, delay);

This transactor calls the b_transport method to send a transaction out through a port. So far, so good. Now let’s look at the parent transactor:

class producer_parent extends vmm_xactor;

vmm_tlm_b_transport_port #(producer_parent, vmm_tlm_generic_payload) m_port;

producer  m_producer;

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

The producer’s parent also has a port, through which it wishes to send the transaction out into its environment. The port on the producer must be bound to the port on the parent. This is done in the connect phase:

virtual function void connect_ph;
m_producer.m_port.tlm_import( this.m_port );
endfunction: connect_ph

Note the use of the method tlm_import in place of tlm_bind to perform the child-to-parent port binding. Why is this particular method named tlm_import? I am tempted to say “Don’t ask!” Whatever name had been selected, somebody would have found it confusing. Of course, the idea is that something is being imported. In this case it is actually the address of the b_transport method that is effectively being imported from the parent (this.m_port) to the child (m_producer.m_port). tlm_import is being called in the sense CHILD.tlm_import( PARENT), which makes sense to me, anyway.

So much for the producer. On the consumer side the situation is very similar so I will cut to the chase:

class consumer_parent extends vmm_xactor;

vmm_tlm_b_transport_export #(consumer_parent, vmm_tlm_generic_payload) m_export;

consumer  m_consumer;

virtual function void connect_ph;
m_consumer.m_export.tlm_import( this.m_export );
endfunction: connect_ph

In this case, the address of the b_transport method is effectively being passed up from the child (m_consumer.m_export) to the parent (this.m_export). In other words, b_transport is being exported rather than imported, but note that it is still the tlm_import method that is being used to perform the binding in the direction CHILD.tlm_import( PARENT ).

Now for the top-level environment, where we instantiate the parent transactors:

class tb_env extends vmm_group;

producer_parent  m_producer_1;
producer_parent  m_producer_2;
consumer_parent  m_consumer;

virtual function void build_ph;
m_producer_1 = new( “m_producer_1″, this );
m_producer_2 = new( “m_producer_2″, this );
m_consumer   = new( “m_consumer”,   this );
endfunction: build_ph

virtual function void connect_ph;
m_producer_1.m_port.tlm_bind( m_consumer.m_export, 0 );
m_producer_2.m_port.tlm_bind( m_consumer.m_export, 1 );
endfunction: connect_ph

endclass: tb_env

This is straightforward. We just use the tlm_bind method to perform peer-to-peer binding between a port and an export at the top level. Note that we are binding two distinct ports to a single export; as explained in a previous blog post, a VMM transactor can accept incoming transactions from multiple sources, distinguished using the value of the second argument to the tlm_bind method.

So, in summary, it is possible to bind TL- ports and exports up, down, and across the transactor hierarchy. Use tlm_bind for peer-to-peer bindings, and tlm_import for child-to-parent bindings.

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

Required and Provided Interfaces in VMM 1.2

Posted by John Aynsley on 14th May 2010

John Aynsley, CTO, Doulos

Before diving into more technical detail concerning VMM 1.2, let’s take some time to review a basic concept of transaction-level communication that often causes confusion, particularly for people more familiar with HDLs like Verilog and VHDL than with object-oriented software programming. This is the idea of the transaction-level interface.

A transaction-level interface is a software interface that permits software components to communicate using a specific set of function calls (also known as method calls). In the case of VMM, the software components in question are VMM transactors, and the function calls are the VMM TLM methods such as b_transport, introduced in previous posts on this blog. Such transaction-level interfaces are often depicted diagrammatically as shown here:

clip_image002

Ports and exports are depicted as if they were pins on the periphery of a component, which is accurate in a metaphorical sense, but misleading if taken too literally. A port is a structured way of representing the fact that the Producer transactor above makes a call to a specific function, and thus requires an implementation of that function in order to compile and run. On the other side, an export is a structured way of representing the fact that the Consumer transactor provides an implementation of a specific function. So although the diagram may appear to show two components with a structural connection between them, it actually shows the Producer making a call to a function implemented by the Consumer. What may appear to be a hardware connection turns out to be an object-oriented software dependency between Producer and Consumer.

When it comes to combining multiple transactors, the types of the transaction-level interfaces have to be respected. The declarations of ports and exports are each parameterized with the type of the transaction object to be passed as a function argument:

vmm_tlm_b_transport_port #(Producer, transaction) port;

vmm_tlm_b_transport_export #(Consumer, transaction) export;

The port, which requires a transaction-level interface of a given type, must be bound to an export that provides an interface of the same type. The type in question is provided by the second parameter transaction. The tlm_bind method effectively seals a contract between the transactor that requires the interface and the provider of the interface:

producer.port.tlm_bind( consumer.export );

One benefit of transaction-level interfaces is that this connection is strongly typed, so the SystemVerilog compiler will catch any mismatch between the types of the port and the export.

As well as binding a port to an export peer-to-peer, it is also possible to bind chains of ports or exports going up or down the component hierarchy, as shown diagrammatically below:

clip_image004

Child-to-parent port bindings carry the function call up through the component hierarchy to the left, while parent-to-child export bindings carry the function call down through the component hierarchy to the right. A port-to-export binding is only permitted at the top level.

At run-time, a method call to the appropriate function is made through the child port:

port.b_transport(tx, delay);

This will result in the corresponding function implementation being called directly, with no intervening channel to store the transaction en route. Transaction-level interfaces are fast, robust, and simple to use, which is why they have been incorporated into VMM.

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

Verification in the trenches: Configuring your environment using VMM1.2

Posted by Ambar Sarkar on 7th May 2010

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

Let’s start with a quick and easy quiz.
Imagine you are verifying a NxN switch where N is configurable, and it supports any configuration from 1×1 to NxN. Assume that for each port, you instantiate one set of verification components such as monitors, bfms, scoreboards. For example, if this was a PCIe switch with 2 ingress and 3 egress ports, you would instantiate 2 ingress and 3 egress instances of PCIe verification components. So, for N = 5, how many environments you should actually write code for and maintain?

The answer better be 1 J™. Maintaining NxN = 25 distinct environments will be quixotic at best. What you want is to write code that creates the desired number and configurations of component instances based on some runtime parameters. This is an example of what is known as “structural” configuration, where you are configuring the structure of your environment.

In VMM1.2 parlance, this means that you want to make sure you can communicate to your build_ph() phase the required number of ingress and egress verification component instances. The build phase can then construct the configured number of components. This way, you get to reuse your verification environment code in multiple topologies, and reduce the number of unique environments that need to be separately created and maintained.

Hopefully, this establishes why structural configuration is important. So the questions that arise next are:

How do you declare the structural configuration parameters?

How do you specify the structural configuration values?

How do you use the structural configuration values in your code?

Declaring the configurable parameters

Structural configuration declarations should sit in a class that derives from vmm_unit. A set of convenient macros are provided, with the prefix `vmm_unit_config_xxx where xxx stands for the data type of the parameter. For now, xxx can be int, boolean, or string.

For example, for integer parameters, you have:

`vmm_unit_config_int    (
<name of the int data member>,
<describe the purpose of this parameter>,
<default value>,
<name of the enclosing class derived from vmm unit>
);

If you want to declare the number of ports as configurable, you can declare a variable num_ports as shown below:class switch_env extends vmm_group;
`vmm_typename(switch_env)?
int     num_ports; // Will be configured at runtime
vip bfm[$]; // Will need to create num_port number of instances

function new(string inst, vmm_unit parent = null);
super.new(get_typename(), inst, parent); // NEVER FORGET THIS!!
endfunction

// Declare the number of ingress ports. Default is 1.
`vmm_unit_config_int(num_ports,”Number of ingress ports”, 1, switch_env);

endclass

Specifying the configuration values

There are two main options:

1.  From code using vmm_opts.

The basic rule is that you need to specify it *before* the build phase gets called, where the construction of the components take place.  A good place to do so is in vmm_test::set_config().
function my_test::set_config();
….
// Override default with 4. my_env is an instance of switch_env class.
vmm_opts::set_int(“my_env:num_ports”, 4);
endfunction

2.  From command line or external option file. Here is how the number of ingress ports could be set to 5.
./simv +vmm_opts+num_ports=5@my_env

The command line supersedes option set within code as shown in 1.
Do note that one can specify these options for specific instances or hierarchically using regular expressions.

Using the configuration values

This is the easy part. As long as you declare the parameter using `vmm_unit_config_xxx , you are all set. It will be set to the correct value when the containing object derived from vmm_unit is created.

function switch_env::build_ph();   ?
// Just use the value of num_ports
for (i = 0; i< num_ports; i++) begin
bfm[i] = new ($psprintf(“bfm%0d”, i), this);
end
endfunction

This article is the 6th 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 Configuration, Tutorial, VMM 1.2 | No Comments »

Sharing RTL Configuration with the Testbench

Posted by JL Gray on 3rd May 2010

by Jason Sprott, CTO, Verilab

Often times we find ourselves with some configurable RTL to verify. The amount of configuration can vary from a few bus width parameters, or a configurable IP block with optional features and performance related control parameters, to a whole chip with optional interfaces. This can make our life as verification engineers that bit more complicated. We not only have to verify the multitude of scenarios for a specific implementation, we have to somehow handle building and running a testbench for the various RTL configurations. Especially in the case of standalone IP, it’s often the case that different configurations are included in our verification space.

Configurations may affect the physical interface between the testbench and RTL, for example:
•    Bus widths
•    Number of interrupts
•    Number of instances of an external interfaces such as USB or Ethernet

Or, the configurations might control internal behavior, which doesn’t affect the physical interface, but does affect the way the test bench has to interact with the DUT functionally. Examples might include:
•    FIFO Depth – which may affect data pattern generation to hit corner cases, or performance related water marks
•    QoS algorithms – where different algorithms implementations are selected depending on requirements, which could affect traffic generation, checking, or functional coverage

The VMM has a new RTL configuration feature which can make life a bit easier. RTL configurations can now be encapsulated in a testbench object that can be used to generate an output file. This output file can be shared between the RTL and testbench. The steps in the process go something like this:

STEP 1: Compile the RTL (with some default configuration) and testbench. This is needed to run the configuration file generation only. Tests will not be run in the simulation.

STEP 2: Run simulation to generate configuration file

./simv +vmm_rtl_config=<PREFIX> +vmm_gen_rtl_config …

The format of the output file can be customized by extending a companion format class. This determines the output format written and also the parsing of the file back into the testbench. The (simple) format that comes out of the box looks like this:

num_of_mems : 4
has_buffer  : 1

This obviously isn’t RTL code, so if we want our Verilog to understand it, for example converting it to assign parameter values, we have to perform the next step.

STEP 3: Convert output configuration file to verilog params file (or format of your choice, such as IP-XACT)

A script is provided as a demonstration, in the memsys_cntrlr std_lib VMM example.

./cfg2param <config_file>

STEP 4: Re-compile the RTL using the new parameters generated by the configuration, e.g. using -parameters switch in VCS.

STEP 5: Run the simulation executing tests, passing the configuration into the testbench

./simv +vmm_rtl_config=<PREFIX> …

The code encapsulating the configuration parameters in the testbench is encapsulated using the vmm_rtl_config class. The code that implements output to the configuration file for each variable is done using macros. The example below shows implementation for class member variables.

class my_cfg extends vmm_rtl_config;
rand int num_of_mems;
rand bit has_buffer;
constraint valid_c {
num_of_mems inside {1,2,4,8};
}

`vmm_rtl_config_begin(my_cfg)
`vmm_rtl_config_int(num_of_mems,num_of_mems)
`vmm_rtl_config_boolean(has_buffer,has_buffer)
`vmm_rtl_config_end(my_cfg)

endclass:my_cfg

On the testbench side the configuration is set using the vmm_opts::set_object() in the initial block of the program block of your testbench and can be retrieved through the vmm_opts::get_object().

The beauty of using this method for RTL configuration is that constrained randomization and functional coverage collection can be used for RTL configurations. In projects where highly configurable IP has to be verified, it’s a convenient way to control and observe progress of verification across, not only the modes of operation, but also those modes relating to specific RTL configurations.

Posted in Automation, Configuration, Reuse, VMM 1.2 | No Comments »

Why do the SystemC guys use TLM-2.0?

Posted by John Aynsley on 29th April 2010

JohnAynsley John Aynsley, CTO, Doulos

Since this is the Verification Martial Arts blog, I have focused so far on features of VMM 1.2 itself. But some of you may be wondering why all the fuss about TLM-2.0 anyway? Why is TLM-2.0 used in the SystemC domain?

I guess I should first give a quick summary of how and why SystemC is used. That’s easy. SystemC is a C++ class library with an open-source implementation, and it is used as “glue” to stick together component models when building system-level simulations or software virtual platforms (explained below). SystemC has Verilog-like features such as modules, ports, processes, events, time, and concurrency, so it is conceivable that SystemC could be used in place of an HDL. Indeed, hardware synthesis from SystemC is a fast-growing area. However, the primary use case for SystemC today is to create wrappers for existing behavioral models, which could be plain C/C++, in order to bring them into a concurrent simulation environment.

A software virtual platform is a software model of a hardware platform used for application software development. Today, such platforms typically include multiple processor cores, on-chip busses, memories, and a range of digital and analog hardware peripherals. The virtual platform would typically include an instruction set simulator for each processor core, and transaction-level models for the various busses, memories and peripherals, many of which will be intellectual property (IP) reused from previous projects or bought in from an external supplier.

The SystemC TLM-2.0 standard is targeted at the integration of transaction-level component models around an on-chip communication mechanism, specifically a memory-mapped bus. When you gather component models from multiple sources you need them to play together, but at the transaction level, using SystemC alone is insufficient to ensure interoperability. There are just too many degrees of freedom when writing a SystemC communication wrapper to ensure that two models will talk to each other off-the-shelf. TLM-2.0 provides a standardized modeling interface between transaction-level models of components that communicate over a memory-mapped bus, such that any two TLM-2.0-compliant models can be made to talk to each other.

In order to fulfil its purpose, the primary focus of the SystemC TLM-2.0 standard is on speed and interoperability. Speed means being able to execute application software at as close to full speed as possible and TLM-2.0 sets very aggressive simulation performance goals in this respect. Interoperability means being able to integrate models from different sources with a minimum of engineering effort, and in the case of integrating models that use different bus protocols, to do so without sacrificing any simulation speed.

So finally back to VMM. It turns out that the specific features of TLM-2.0 used to achieve speed and interoperability do not exactly translate into the SystemVerilog verification environment, where the speed goals are less aggressive and there is not such a singular focus on memory-mapped bus modeling. But, as I described in a previous post on this blog, there are still significant benefits to be gained from using a standard transaction-level interface within VMM, both for its intrinsic benefits and in particular when it comes to interacting with virtual platforms that exploit the TLM-2.0 standard.

Posted in Interoperability, Optimization/Performance, SystemC/C/C++, Transaction Level Modeling (TLM), VMM 1.2 | 2 Comments »

Verification in the trenches: Chaining tests using VMM1.2

Posted by Ambar Sarkar on 26th April 2010

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

Have you been frustrated by how long it takes to run the initial phases of a test before it gets to those that really matter? And it gets worse when you need to run a number of these tests. I often see projects where about 80% of the test execution time is used up in these initial phases.

While you do need to execute these initial phases, repeating them for each test does not contribute to any additional verification. Ideally, you would like to run the initial phases for a group of related tests just once, just to a point where you are ready to send real traffic or transactions. The phases that follow should be executed once per test, with the environment rolled back to an appropriate state before the next test starts executing.

The diagram below shows how you may want chain two testsTEST1 and TEST2, where you want to use the same configuration for both.


VMM1.2 supports this model of test execution for implicitly phased environments.

First the steps.

Step 1. Tag a test as concatenable and specify the rewind-to phase.?

If you are using implicit phasing of your environment, this is done simply by using the macro that announces the tests as concatenable, along with the phase to roll_back to.

class TEST1 extends vmm_test;
//Macro to indicate which phase to rollback to
`vmm_test_concatenate(configure_test)

endclass

class TEST2 extends vmm_test;
//Macro to indicate which phase to rollback to
`vmm_test_concatenate(configure_test)

endclass

Step 2. Invoke it.

This is done by simply specifying the command line parameters:

//Command line arguments to run the example
./simv +vmm_test=TEST1+TEST2

You also have the option of specifying all tests in a file or just chain all the tests.

So what are the gotchas?

The underlying VMM implementation (and you can read the gory details of how this is done using the three timelines: pre, top, and post in the VMM user guide) takes care of synchronizing all the test phases so that the tests can be effectively chained. However, the user still has to implement any application specific cleanup in between tests to restore the environment. This seems fair enough, and the cleanup phase is the perfect phase to implement that.

However, there is one limitation when considering chained tests. You cannot use vmm_test::set_config() in concatenated tests, which is the place to perform class-factory overrides for the environment components. This is not a limitation of the implementation however, but just how class factory overrides work. Note that you can still use class factory to override your traffic scenarios, just not the environment components.

First, a test can only run after the environment is created. Second, a class factory override for the environment can work only if it is called before the environment is created. This means that after the first test is executed, you cannot override the environment setting using class factory override, unless you are willing to rebuild the environment from scratch in between. Clearly, this rebuild per test run will negate any benefits of chaining. Since vmm_test::set_config is explicitly used to reconfigure environments using the class factory method, it is not allowed when tests are chained.

This article is the 5th 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 Creating tests, VMM 1.2 | No Comments »

Combining TLM Ports with VMM Channels in VMM 1.2

Posted by John Aynsley on 23rd April 2010

JohnAynsley

John Aynsley, CTO, Doulos

In previous posts I described the TLM ports and exports introduced with VMM 1.2. Of course, the vmm_channel still remains one of the primary communication mechanisms in VMM. In this article I explore how TLM ports can be used with VMM channels.

TLM ports and exports support a style of communication between transactors in which a b_transport method made from a producer is implemented within a consumer, thereby allowing a transaction to be passed directly from producer to consumer without any intervening channel. On the other hand, a vmm_channel sits as a buffer between a producer and a consumer, with the producer putting transactions into the tail of the channel and the consumer getting transactions from the head of the channel. The channel deliberately isolates the producer from the consumer.

Each style of communication has its advantages. The b_transport style of communication is fast, direct, and the end-of-life of the transaction is handled independently from the contents and behavior of the transaction object. On the other hand, vmm_channel provides a lot more functionality and flexibility, including the ability to process transactions while they remain in the channel and to support a range of synchronization models between producer and consumer.
In VMM 1.2, support for TLM ports and exports is now built into the vmm_channel class. It is possible to bind a TLM port to a VMM channel such that the channel provides the implementation of b_transport. The goal is to get the best of both worlds: the clean semantics of a b_transport call in the producer, and the convenience of using the active slot in the vmm_channel in the consumer.
An example is shown below. First we need a transaction class and a corresponding channel type:

class my_tx extends vmm_data;   // User-defined transaction

typedef vmm_channel_typed #(my_tx) my_channel;

The producer sends transactions using a b_transport call, knowing that by the time the call returns, the transaction will have been completed:

class producer extends vmm_xactor;

vmm_tlm_b_transport_port #(producer, my_tx) m_port;

my_tx tx;

m_port.b_transport(tx, delay);

The consumer manipulates the transaction while leaving it in the active slot of a vmm_channel and executing the standard notifications:

class consumer extends vmm_xactor;

my_channel m_chan;

my_tx tx;

m_chan.activate(tx);   // Peek at the transaction in the channel
m_chan.start();        // Notification vmm_data::STARTED

m_chan.complete();     // Notification vmm_data::ENDED
m_chan.remove();       // Unblock the producer

The channel is instantiated and connected in the surrounding environment:

class tb_env extends vmm_group;

my_channel  m_tx_chan;
producer    m_producer;
consumer    m_consumer;

virtual function void build_ph;

m_producer = new( “m_producer”, this );
m_consumer = new( “m_consumer”, this );

m_tx_chan  = new( “my_channel”, “m_tx_chan” );
m_tx_chan.reconfigure(1);

endfunction

function void connect_ph();

vmm_connect #(.D(my_tx))::tlm_bind( m_tx_chan, m_producer.m_port,
vmm_tlm::TLM_BLOCKING_EXPORT );
m_consumer.m_chan = m_tx_chan;

There are two key points to note in the above. Firstly, the channel is reconfigured to have a full level of 1. This ensures that the blocking transport call does indeed block. If the full level is greater than 1, the first call to b_transport will return immediately before the transaction has completed, which would defeat the purpose.

Secondly, the transport port and the vmm_channel are bound together using the vmm_connect utility. This connect utility must be used when binding VMM TLM objects to channels, and can also used in order to bind TLM ports and exports of differing interface types (e.g. a blocking port to a non-blocking export). The third argument to tlm_bind indicates that the connection is being made from the port in the producer to a blocking export within the channel. I will discuss other uses for this method in later posts.

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

Changing Functionality: The Factory Service #3

Posted by JL Gray on 21st April 2010

by Jason Sprott

Jason Sprott is CTO at Verilab.

In Factory Service #1 and in Factory Service #2 we discussed what a Factory is and what it can do for us. In this post we’ll take a look at the two general types of objects we might want to replace using the Factory. Typically objects in testbenches fall into two categories:

  • Dynamic: These are objects that get created and garbage collected spuriously during normal operation. An example might be a randomized transaction generated by a scenario, or even the scenario itself.
  • Structural: These objects get created once at the beginning and live throughout the simulation. An example might be vmm_xactor or vmm_group.

The location within the VMM Phasing scheme where the factory override performed is different between the two.  A factory override has to be done before any of the objects affected by the override are instantiated (using <class>::create_instance() ). Structural components in the testbench are typically instantiated earlier than dynamic ones.

In the case of overriding dynamic objects, we need to perform the override before the test starts running the simulation. A good place to perform dynamic object overrides is in the vmm_test::configure_test_ph()method. This is executed in the test phase (see “Constructing and Controlling Environments > Composing Implicitly Phased Environments/Sub-Environments” section of the VMM User Guide), before test execution is started by the vmm_test::start_of_sim_ph() method. So no dynamic objects are likely to have been created. The following example shows where we override a transaction in a testcase:

class my_dynamic_factory_test extends vmm_test;

virtual function void configure_test_ph();

my_trans::override_with_new(“@%*”,
custom_trans::this_type(),
log, `__FILE__, `__LINE__);
endfunction:configure_test_ph

endclass

Structural object overrides on the other hand potentially give us a bit of a problem, because these objects are instantiated in the pre-pest “build” phase. Performing the factory override in vmm_test::configure_test() would be too late, as it happens after the objects have already been instantiated. Instead structural object factory overrides must be performed in vmm_test::set_config(), which is executed before the Pre-Test timeline, as long as test concatenation is not being done. The following is an example where we override a VIP (encapsulated in a vmm_group) in the testcase:

class my_structural_factory_test extends vmm_test;

virtual function void set_config();

my_vip::override_with_new(“@%*”,
my_extended_vip::this_type(),
log, `__FILE__, `__LINE__);
endfunction:set_config

endclass

The figure below illustrates the phases where overrides should be implemented, with respect to the associated objects types:

image

Another thing to be aware of when using a Factory is test concatenation. In general, if you want to be able to concatenate a test, it’s not a good idea to change components in the environment using factory overrides. Reprogramming the environment, either structural or dynamic objects, will affect subsequent concatenated tests. If the changes can be undone, in the cleanup phase of the test for example, it may be OK, but changes to structural objects are difficult to undo. Any change to the environment may affect the validity of a test. For this reason the VMM will not execute vmm_test::set_config() if test concatenation is being performed.  That might not be enough though. For reuse, considering test concatenation is an important point test developers and users of the tests need to be aware of. Factory overrides incompatible with test concatenation will not necessarily cause a noticeable side affect, or failure, so misuse could go unnoticed. This is not a problem of the Factory; it’s just a consideration when using factory overrides with concatenation of tests.

Posted in Configuration, Reuse, VMM 1.2, VMM infrastructure | No Comments »

Changing Functionality: The Factory Service #2

Posted by JL Gray on 19th April 2010

by Jason Sprott

Jason Sprott is CTO at Verilab

In a previous post we took a look at what the VMM Factory is and what it could do for us. In this post, we take a look at the problem the Factory solves. In order to understand when we should use a Factory, we really need to understand a bit more about the problem it solves, so we can spot situations where a Factory might be appropriate. Let’s start by looking at how we might do things in SystemVerilog without using VMM Factories. Typically we might instantiate a class of type Foo like this:

Foo my_foo;

my_foo = new(…);

The problem with that is my_foo will always create an object of type Foo, no matter what.  We can’t change that. We might use this class Foo in lots of places around our testbench code. If we want to replace Foo with a new class that maybe adds more variables, or replaces a method, we could have a problem. We’d have to go around our testbench looking for everywhere Foo is used, to see if there was a way it could be replaced. Anywhere Foo is constructed using new(), it’s is highly likely to involve modifying the original code. This is very undesirable, especially if the code may be part of a tested IP library. In general, modifying the original source is a Bad Thing.

In VMM we can avoid this problem using a different way to create an instance of the class with the factory. It would look something like this:

Foo my foo;

my_foo = Foo::create_instance(…);

Although this looks similar to an instantiation with new(), something much more sophisticated  is at play. If we factory enable the class Foo, we never call new()to instantiate it again. We now call the Factory’s static method for generating instances of the object create_instance(). Since the method is static, it can be called without having an instance of Foo, therefore it can be used to create itself. What’s the point?

Now that we’ve encapsulated the task of creating an instance in a method, we can change what that method does. We can tell create_instance() to return something different. This might be a new type (with some modifications), derived from Foo, or the same type populated with different values for the variables. What’s more, we can do this easily anywhere Foo is used in the code. We can pick specific instances to be replaced, or replace multiple instances globally.

The two methods used to reprogram the factory are:
•    create_with_new() – tells the factory, when creating new instances, to return a brand new instance of the type specified.
•    create_with_copy() – tells the factory, when creating new instances,  to return a copy of instance specified.

This replacement can be done anywhere in the code, as long as it happens before the instances you want to replace are created. Let’s say a test needs to replace our type Foo, with a new class, FooWithAttitude, derived from Foo. Here’s what that might look like:

class my_factory_test extends vmm_test;

virtual function void configure_test_ph();
Foo::override_with_new(         // Foo’s factory is being overridden
“@%*”,                        // instances matching this pattern will be replaced
FooWithAttitude::this_type(), // they will be replaced with this type of class
log, `__FILE__, `__LINE__);   // some generic log and debug stuff
endfunction:configure_test_ph

endclass:my_factory_test

As can be seen, the Factory override uses regular expression pattern matching to specify which instances will be targeted. The syntax is expressive enough to indentify single instances, multiple instances (e.g. by hierarchy), or all instances (as in this example). More information on the pattern matching syntax can be found in the “Common Infrastructure and Services > Simple Match Patterns” section of the VMM User Guide.

This next example shows how the factory can be programmed to return a copy of the class we’ve modified with some values.

class test_read_back2back extends vmm_test;

virtual function void configure_test_ph();
FooBusTrans tr = new();        // create a template for the override copy
tr.address = ‘habcd_1234;      // special value we want to set up for override
tr.address.rand_mode(0);       // you might want to protect value during randomization
FooBusTrans::override_with_copy(
“@top:foobus0:*”,           // instances matching this pattern will be replaced
tr,                         // they will be replaced by a copy of this instance
log, `__FILE__, `__LINE__); // some generic log and debug stuff
endfunction

endclass

The above example  shows a Factory override in the configure test phase of the simulation timeline. Exactly when a Factory replacement is done is quite important. As previously mentioned, the replacement has to be done before any instances of the class have been created. This depends on the type of class being replaced. For example, dynamic objects (such as transactions), created many times during normal operation of the testbench, are likely to be created after the test has started. However, structural objects (such as instances of a VIP), are likely to be created once when the testbench is built. The details of where to put Factory overrides is covered in another post.

Posted in Configuration, Reuse, VMM 1.2, VMM infrastructure | No Comments »

Changing Functionality: The Factory Service #1

Posted by JL Gray on 16th April 2010

by Jason Sprott

Jason Sprott is CTO at Verilab.

Building a testbench using SystemVerilog, an object-oriented testbench language, does not automatically make the end solution reusable, or easily extensible. In SystemVerilog we can certainly implement the kind of object-oriented principles and design patterns that enable reuse, but this requires significant programming skills and experience in understanding the requirements of building substantial reusable software solutions. Also, when users inevitably need to change the original functionality, the mechanisms in place to allow those changes would have to be well documented and understood.

Two common changes we might need to make are:
•    Swap one type of class with another (which may replace or add new constraints, variables and methods).
•    Add new functionality to an existing method without replacing it

Fortunately the VMM provides some standard solutions for handling these types of changes. This post takes a look at how the Factory Service helps with the first of the two requirements, swapping classes.

There are many cases where we might want to swap one class for another in a testbench. For example, a typical requirement in constrained random testbenches is to replace one randomizable class with derived version, adding or modifying constraints. We might also have various derived versions of VIP, supporting slightly different versions of a protocol, or injecting different types of errors. When we first develop some VIP, or a testbench, it’s almost impossible to predict what people will want and need from our solution in the future. We can however provide users with a standard way to replace our implementation with something slightly different. This could be a derived version (extension with additions or modification), of what we originally implemented, or a copy with modified values.

Although this sounds quite trivial, testbenches that do not have this capability can be very hard to change without modifying the original source. Changing an original implementation to add new functionality is undesirable and sometimes impossible. Access to original source code is quite often restricted, or at least controlled. The original code may be well tested and proven, and changing the original source could affect other users of the code. Testbench components implementing the Factory are easily replaced at runtime without modifying the original source. Such modification can even be done on a per test basis if required.

However, we don’t get a Factory for free; there’s a bit of up-front thought required. We have to care enough about this capability to implement a Factory in the first place, obviously. This seems obvious, but in fact many times I come across a class that was in dire need of a factory that wasn’t there, the only reason was that the original developer didn’t think to put one in. I try not to judge too harshly, because we’ve all developed code that didn’t quite meet down-steam requirements at some point in time. Theres’s also a bit of effort (a small amount of additional coding) required. So why should we bother?

The VMM Factory Service (Factory) is a standard mechanism for changing functionality by replacing classes. VMM has always recommended the use of Factories, but The Factory Service was introduced in VMM 1.2 to ease implementation. The Factory provides the following API and utilities:
•    Short-hand macros to make implementing a Factory simple. The macros implement the Factory API methods for a given class.
•    API method to create instances of the class – replacing the use of the new()constructor method.
•    API method to change the instance generated by the Factory to a new derived type.
•    API method to change the instance generated by the Factory to a copy of a class of the same type
•    Specific and regular expression based selection of component factories to override

As an example, imagine a Bus VIP. The VIP has quite a lot going on inside. It has a master, a slave, some monitors, coverage and checking. All use a particular transaction type. So if we wanted to replace that transaction with a derived type, to maybe inject some errors, it would affect multiple components in the VIP. If any of the components using the transaction create an instance using new(), there’s a pretty good chance we cannot replace the transaction type without modifying that source. This may not be an option, or at the very least not desirable.

If we use the Factory, we can not only do the replacement, we can choose which instances of the VIP in a testbench we want to perform the replacement on. Maybe not all nodes on the bus have to inject errors. In our test, or new testbench environment, we might decide to say something like:
•    When creating new classes of type Foo, instead instantiate my new class FooWithAttitude. Using the Factory in VMM that might look like:
Foo::override_with_new(         // Foo’s factory is being overridden
“@%*”,                        // match all instances
FooWithAttitude::this_type(), // they will be replaced with this type of class
log, `__FILE__, `__LINE__);   // some generic log and debug stuff

Or, maybe we want to make sure some variables in a configuration object are replaced with something specific. We can make sure a copy of a class is instantiated, with some specific values set. We might decide to say something like:
•    When creating new classes of type Foo,  instead insatiate my copy of that class (in this case we have created our own instance, tr), with some values set and randomization turned off:

FooBusTrans::override_with_copy(
“@top:foobus0:*”,           // instances matching this pattern will be replaced
tr,                         // they will be replaced by a copy of this instance
log, `__FILE__, `__LINE__); // some generic log and debug stuff

To understand when we should use a Factory, we really need to understand a bit more about the problem it solves. We’ll discuss this in another post.

Posted in Configuration, Reuse, VMM 1.2, VMM infrastructure | No Comments »

Analysis Ports in VMM 1.2

Posted by John Aynsley on 3rd March 2010

JohnAynsley

John Aynsley, CTO, Doulos

Analysis ports are another feature from the SystemC TLM-2.0 standard that has been incorporated into VMM 1.2. Analysis ports provide a mechanism for distributing transactions to passive components in a verification environment, such as checkers and scoreboards.

Analysis ports and exports are a variant on the TLM ports and exports that I have discussed in previous blog posts. The main difference between analysis ports and regular ports is that a single analysis port can be bound to multiple exports, in which case the same transaction is sent to each and every export or “subscriber” or “observer” connected to the analysis port. The terms subscriber and observer are used interchangeably in the VMM documentation.

Let us take a look at an example:

class my_tx extends vmm_data;  // User-defined transaction class

class transactor extends vmm_xactor;
vmm_tlm_analysis_port #(transactor, my_tx) m_ap;   // The analysis port

virtual task main;
my_tx tx;

m_ap.write(tx);

The transactor above sends a transaction tx out through an analysis port m_ap.

The type of the analysis port is parameterized with the type of the transactor and of the transaction my_tx. The call to write sends the transaction to any object that has registered itself with the analysis port. There could be zero, one, or many such observers registered with the analysis port.

To continue the example, let us look at one observer:

class observer extends vmm_object;
vmm_tlm_analysis_export #(observer, my_tx) m_export;
function new (string inst, vmm_object parent = null);

m_export = new(this, “m_export”);

function void write(int id, my_tx tx);

The observer has an instance of an analysis export and must implement the write method that the export will provide to the transactors. Note that the observer extends vmm_object. Since an observer is passive, it need not extend vmm_xactor.

The analysis port may be bound to any number of observers in the surrounding environment:

class tb_env extends vmm_group;
transactor  m_transactor;
observer    m_observer_1;
another     m_observer_2;
yet_another m_observer_3;
virtual function void build_ph;
m_transactor = new( “m_transactor”, this );
m_observer   = new( “m_observer”,   this );

endfunction
virtual function void connect_ph;
m_transactor.m_ap.tlm_bind( m_observer_1.m_export );
m_transactor.m_ap.tlm_bind( m_observer_2.m_export );
m_transactor.m_ap.tlm_bind( m_observer_3.m_export );

Note the use of the predefined phase methods from VMM 1.2. Transactors are created during the build phase, and ports are connected during the connect phase.

Finally, let us compare analysis ports with VMM callbacks:

m_ap.write(tx);
versus
`vmm_callback(callback_facade, write(tx));

The effect is very similar, but there are differences. Unlike VMM callbacks, the name of the method called through an analysis port is fixed at write. A VMM callback method is permitted to modify the transaction object, whereas a transaction sent through an analysis port cannot be modified. When multiple callbacks are registered, the prepend_callback and append_callback methods allow you to determine the order in which the callbacks are made, whereas you have no control over the order in which write is called for multiple observers bound to an analysis port. Because of these differences, only VMM callbacks are appropriate for modifying the behavior of transactors. Analysis ports are only appropriate for sending transactions to passive components that will not attempt to modify the transaction object. On the other hand, that in itself is the feature and strength of analysis ports; they are only for analysis.

It can make sense to combine a VMM callback with an analysis port in the same transactor, using the callback to inject an error and the analysis port to send the modified transaction to a scoreboard, for example:

`vmm_callback(callback_facade, inject_error(tx));
m_ap.write(tx);

In this situation, the VMM recommendation is to make the analysis call after the callback, as shown here.

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

Handling Incoming Transactions from Multiple Sources in VMM 1.2

Posted by John Aynsley on 16th February 2010

JohnAynsley John Aynsley, CTO, Doulos

In the previous post I described TLM ports and exports from VMM 1.2. In this post, we will look at how to handle incoming transactions from multiple sources, that is, multiple producers connected to a single consumer. VMM 1.2 provides two separate mechanisms to handle this situation: peer ids, and shorthand macros. We will explore what these mechanisms have in common, and also the differences between them.

We are discussing the following situation, where two separate producer instances send transactions to a single consumer:

class producer extends vmm_xactor;

vmm_tlm_b_transport_port #(producer, my_tx) m_port;

m_port.b_transport(tx, delay);

class consumer extends vmm_xactor;

vmm_tlm_b_transport_export #(consumer, my_tx) m_export;

function new (string inst, vmm_object parent = null);
super.new(get_typename(), inst, -1, parent);
m_export = new(this, “m_export”, 2); // 3rd argument = max # bindings
endfunction

function void start_of_sim_ph;
vmm_note(log, $psprintf(“Number of peers = %d”, m_export.get_n_peers()));
endfunction

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

class my_env extends vmm_group;

producer m_producer_1;
producer m_producer_2;
consumer m_consumer;

virtual function void connect_ph;
m_producer_1.m_port.tlm_bind( m_consumer.m_export, 0 ); // 2nd argument = id
m_producer_2.m_port.tlm_bind( m_consumer.m_export, 1 );
endfunction

The first thing to notice is the connect_ph method of the environment, which binds two separate ports to the same export. The tlm_bind method takes a second argument, the peer id, which allows transactions from the two ports to be distinguished.

The second thing to notice is that when the export is instantiated, the constructor new takes a third argument that specifies the maximum number of bindings to this export. The default value of 1 would be inadequate in this case, since the export is bound twice.

Thirdly, the method get_n_peers called from start_of_sim_ph returns the number of peers, which would be 2 in this case.

Finally, the first argument to the b_transport method implemented in the consumer is the peer id passed to the tlm_bind method. The implementation of b_transport can now use the peer id to distinguish between transactions from the two producers.

So much for peer ids. Now let us take a look at the alternative, that is, shorthand macros. Instead of binding two ports to a single export, we could have used the shorthand macros to create two separate exports:

class consumer extends vmm_xactor;

`vmm_tlm_b_transport_export(_1) // Argument is suffix to name
`vmm_tlm_b_transport_export(_2)
vmm_tlm_b_transport_export_1 #(consumer, my_tx) m_export_1;
vmm_tlm_b_transport_export_2 #(consumer, my_tx) m_export_2;

task b_transport_1(int id = -1,
my_tx trans, ref int delay);

task b_transport_2(int id = -1,
my_tx trans, ref int delay);

The argument passed to the macro is used as the suffix for a new type name and a new method name. Those new types are then used to create two separate exports, and the consumer contains two separate and differently named implementations of the b_transport method, one for each export. It is good practice to use the same suffix when naming the export members themselves (e.g. m_export_1), though this is not strictly necessary. Since peer ids are not being used, the id argument to b_transport will have the value 0 for both methods.

As usual, ports are bound to exports in the surrounding environment, but this time using separate exports rather than peer ids:

class my_env extends vmm_group;

producer m_producer_1;
producer m_producer_2;
consumer m_consumer;

virtual function void connect_ph;
m_producer_1.m_port.tlm_bind( m_consumer.m_export_1 );
m_producer_2.m_port.tlm_bind( m_consumer.m_export_2 );
endfunction

In conclusion, we have seen peer ids and shorthand macros used to accomplish the same thing, that is, multiple producers sending transactions to a single consumer. With peer ids we instantiate a single export and provide a single b_transport method, distinguishing between the incoming transactions using the peer id argument. With shorthand macros we instantiate two exports and provide two implementations of b_transport, distinguished by the suffix to their names.

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

Connecting Multiple Analysis Ports to a Single Analysis Export

Posted by JL Gray on 9th February 2010

Today’s post was written by my colleague Asif Jafri. Enjoy! JL

by Asif Jafri

Asif Jafri is a verification engineer at Verilab.

This post introduces the VMM implementation of the Transaction Level Modeling (TLM) 2.0 specification of how you can connect multiple broadcasting ports to the same receiving export using peer ID’s. Figure 1 shows multiple initiators communicating with the same target. The initiators can be monitors on either side of your DUT passing transaction to a single scoreboard which keeps track of the transactions and does various checks. In TLM 2.0 message broadcast is accomplished through write function calls from the initiator which are then implemented in the target. image

Figure 1: Connecting using ID

Read the rest of this entry »

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