Login with a Cadence account.
Not a member yet?
Create a permanent login account to make interactions with Cadence more conveniennt.

Register | Membership benefits
Get email delivery of the System Design and Verification blog (individual posts).


* Required Fields

Recipients email * (separate multiple addresses with commas)

Your name *

Your email *

Message *

Contact Us

* Required Fields
First Name *

Last Name *

Email *

Company / Institution *

Comments: *

Connecting OVM Testbench and SystemC TLM2 IP

Comments(5)Filed under: System Design and Verification, OVM, SystemC, testbench, TLM2 IP1. Introduction

With TLM2 enabling more sophisticated SystemC IP interoperability, most of the new TLM IP models will come with TLM2 interfaces. Along the way there will continue to be some IP that use TLM1. For example, the recently contributed SystemC portion of OVM-ML uses TLM1 to be compatible with IP that is available today. This experiment (and I hope useful guide) to help you bridge the gap when mixing TLM1 and TLM2 models.

For the sake of simplicity, this experiment is based on a simple SystemC TLM2 un-timed description of a dummy IP having only one TLM2 target socket connected to a simple OVM testbench. The same methodology can be applied to any real IP models having TLM2 sockets.

2. The experiment

The goal of this experiment is to verify a TLM2 IP using an OVM environment. The problem here is that the OVM library (Multi-Language OVM library implemented by Cadence) only supports TLM1 interfaces with a payload deriving from ovm_object. I have therefore implemented an adapter, called “OVM bridge” to convert from an OVM port to a TLM2 socket. I will first describe the OVM environment (a simple env with an OVM agent and a sequencer sending random addresses on its initiator port and monitoring read values), then detail the OVM bridge and last show how to assemble together the OVM test, the bridge and the TLM2 IP.

Creating the TLM2 IP

I assume you have already created your TLM2 IP (i.e. a SystemC IP with TLM2 sockets). Here I use a simplistic TLM2 model of a slave IP: a sc_module class with a simple_target_socket implementing a blocking read. It models a fake register bank (10 registers of 32bits) accessible from address 0 to 9. If a read is attempted at an address larger than 9, an error is returned. The read returns the address value plus 10. A sample of the TLM2 slave is given here after:


class tlm2_slave : public sc_module {


  simple_target_socket<tlm2_slave> target; // socket

  tlm2_slave(sc_module_name module_name) :


    target("TLM2_TARGET_PORT") {

    target.register_b_transport(this, &tlm2_slave::b_read);


  // simple blocking read: set data value to address + 10

  virtual void b_read( tlm_generic_payload &payload,

                   sc_core::sc_time &tdelay) {

    if (payload.get_address() < 10) {

      unsigned char* data = payload.get_data_ptr();

      *data = payload.get_address() + 10;


    } else {





Creating the OVM testbench This testbench is written in SystemVerilog. It includes an OVM active agent, an OVM monitor, an OVM sequencer and an OVM scoreboard. The agent drives the TLM interface (i.e. sends read transactions and prints the data read). The agent also records transactions sent to the scoreboard. The monitor listens to this TLM interface and sends the transactions read to the scoreboard for comparison with the ones recorded. This is illustrated in the following figure.






The active tlm_agent includes a driver (BFM) and a monitor. The driver generates OVM sequence items as illustrated in the following ovm payload class. This simple SystemVerilog implementation includes an address, a data and a message defined as OVM fields.


class ovm_payload extends ovm_sequence_item;

  // data members

  int addr;

  int data;

  string msg = "OVM";

  // enable automation of the payload's fields  


    `ovm_field_int(addr, OVM_ALL_ON)

    `ovm_field_int(data, OVM_ALL_ON)

    `ovm_field_string(msg, OVM_ALL_ON|OVM_NOCOMPARE)


  // This is required for the registration of the payload

  function new(string name = "ovm_payload");





As illustrated in figure1, the tlm_env class instantiates the four OVM components: agent, monitor, virtual sequencer and scoreboard and connect them together. It also defines two transactional interfaces: the monitor port (tlm_mon_port) and the agent driver’s port (tlm_drv_port) which will transport the ovm_payload. An implementation of the tlm_env class is given here after.


class tlm_env extends ovm_env;

   tlm_agent       t_agent;

   tlm_monitor    t_mon;

   virt_seq       v_seq;

   tlm_scbd       r_scbd;

   // register this class

   function new(string name, ovm_component parent);

      super.new(name, parent);

   endfunction : new


   // instantiate the OVM components

   // (should have been defined earlier)

   function void build();


     t_agent = tlm_agent::type_id::create("tlm_agent", this);

     t_mon   = tlm_monitor::type_id::create("tlm_monitor", this);

     v_seq   = virt_seq::type_id::create("v_sequencer", this);

     r_scbd  = tlm_scbd::type_id::create("tlm_scbd", this);

   endfunction : build

   // connect

   function void connect();

     v_seq.tlm_seqr = t_agent.seq;



     // define external interfaces

     ml_ovm::external_if(t_mon.tlm_mon_port, "ovm_payload");



     // connect to the SystemC TLM2 socket of the design IP





   endfunction : connect

endclass : tlm_env


The tricky part of this OVM implementation is the connection to the SystemC interfaces. In the connect function of the tlm_env class, the driver port of the agent is connected like this:





The first argument defines the hierarchical path to the driver port name using SystemVerilog instances. The second argument defines the hierarchical path to the target SystemC port name, but here the SC path is constructed with the names of the instances given to the SC modules constructor. For example, “OVM_bridge” is the name of the instance “bridge” of the module “ovm_bridge”. Note that in this experiment, only the agent port is connected to the uart; the monitor port is left dangling.

Creating the OVM Bridge

The OVM Bridge is made of two serial processes that are defined as two separate modules: first converting a TLM1 initiator port with an ovm payload into a TLM1 initiator port with a tlm simple payload, then converting this TLM1 initiator port to a TLM2 simple initiator socket with a tlm_generic_payload. To simplify the connection to the external world, these two serial converters are grouped inside a hierarchical module (the OVM bridge). This is illustrated in the following figure.






The payloads are defined as template arguments to the converter classes, hence making the bridge more generic. A sample of the ovm_bridge top module is given here after:


template <typename OVM_PAYLOAD, typename PAYLOAD>

class ovm_bridge : public sc_module {


  sc_export<tlm_transport_if<OVM_PAYLOAD,OVM_PAYLOAD> > target;

  // the last template arg (2) is to limit the binding to

  // the sub module initiator socket and to an external slave

  // target soket


    32,tlm::tlm_base_protocol_types,2>                  initor;

  ovm_bridge(sc_module_name name) : sc_module(name),

                            totlm1 ("ovm_to_tlm1"),

                            totlm2 ("tlm1_to_tlm2"),


                            initor("TLM2_INITOR_PORT")  {







  bridge_ovmtlm1<OVM_PAYLOAD,PAYLOAD>  totlm1;

  bridge_tlm1tlm2<PAYLOAD>             totlm2;



The tricky part is the hierarchical connection of the socket. To isolate the testbench and the slave TLM2 IP from the bridge content, I have created a TLM1 export and a TLM2 socket on the bridge. This TLM2 socket will therefore connect internally to the socket of the TLM1 to TLM2 converter, and it will connect externally to the slave TLM2 IP. This can not be achieved with a simple_initiator_socket but instead with a multi_passthrough_initiator_socket. The ovm to tlm1 bridge just converts an ovm payload into its equivalent tlm payload and calls transport from ints initiator port. The tlm1 to tlm2 bridge converts a tlm1 payload into a tlm_generic_payload and calls the b_transport from its initiator socket. Both payload conversions his is done by copying the data from one to the other. Note that to keep the bridges implementations independent from the payload content; I have defined the bridge as a template of both the payload and the ovm payload. Moreover, the tlm1 payload used is a derived class of a pure virtual class defining two methods to copy from an ovm payload and to a tlm generic payload.

Assembling all pieces together

To ease the connection to the OVM testbench I have created a simple sub-system only containing the tlm2 slave and the bridge. This sub-system has no port/socket; it only serves the multi-language OVM-SystemC communication. Therefore the OVM test connection has to directly bind to the bridge target port (sc_export).





Note that the payloads (ovm_payload, simple_payload) are only instantiated in this sub_system class all sub modules use templates. Note also that the ovm_bridge multi passthrough initiator socket connects directly to the tlm2_slave simple target socket. A sample of this sub_system class is given here after:


using namespace ml_ovm;

class sub_system : public sc_module {


  ovm_bridge<ovm_payload,simple_payload> bridge;

  // constructor

  sub_system(sc_module_name name) :


    bridge ("OVM_bridge"),

    slave  ("tlm2_slave") {


    // register the external ports for mixed-language OVM

    // communication



  // phase callback that is used for connection

  void end_of_construction() {

    // ml ovm binding

    // Note that the 2nd argument defines the path with class

    // names and not class objects (i.e. instance names) !





  // can't acces slave ports directly

  tlm2_slave slave;




This sub-system also has the advantage of localizing the multi-language (ml_ovm) code in one place: – register the external ports for mixed-language OVM communication in the constructor – do the ml ovm binding at the end of construction You’re now ready to run the simulation in Incisive. First edit the irun command file (run.f) to include: the OVM top module and the SC top module (i.e. -ovmtop SV:tlm_test –sctop sub_system). Then run the command:

% irun -f ovm_run.f

A sample of the generated output is given here after:





3. Conclusion & future posts

You have now the basic bricks for defining a simple OVM testbench and connecting to a TLM2 IP with simple target sockets. More experiments – stay tuned! – shall include usage of extended tlm generic payload and creation of a systemC OVM testbench.


By Jason on May 19, 2009
Very interesting post. Can I get a copy of the files, save me copy and pasting?

By Waqas on June 19, 2009
It's an interesting post and really usefull in my work. May i get the files?  

By Shane on November 23, 2009
Great overview.  I'm new to both OVM and SystemC TLM modeling and recognize that the mixed language bridge strategy is very important.  Would it be possible to get the files so I can experiment further.  Also, if you 've made any updates since this post, could you point me to them.  Thanks!

By Online Shopping on June 25, 2011
The information written in the blog is unique in its own way, as it has been presented in such concise and interesting manner. I am inquisitive to visit and read such information further.

By Wholesale Gold Jewellery on June 25, 2011
It seems that high amount of effort has been taken to write it down. I have nothing much to say, but simply great work!

Leave a Comment

E-mail (will not be published)
 I have read and agree to the Terms of use and Community Guidelines.
Community Guidelines
The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.