TLM Interfaces, Ports, and Exports

The OVM TLM library defines several abstract, transaction-level interfaces and the ports and exports that facilitate their use.  Each TLM interface consists of one or more methods used to transport data, typically whole transactions (objects) at a time.  Component designs that use TLM ports and exports to communicate are inherently more reusable, interoperable, and modular.

Interface Overview

The TLM standard specifies the required behavior (semantic) of each interface method.  Classes (components) that implement a TLM interface must meet the specified semantic.

Each TLM interface is either blocking, non-blocking, or a combination of these two.

blockingA blocking interface conveys transactions in blocking fashion; its methods do not return until the transaction has been successfully sent or retrieved.  Because delivery may consume time to complete, the methods in such an interface are declared as tasks.
non-blockingA non-blocking interface attempts to convey a transaction without consuming simulation time.  Its methods are declared as functions.  Because delivery may fail (e.g. the target component is busy and can not accept the request), the methods may return with failed status.
combinationA combination interface contains both the blocking and non-blocking variants.  In SystemC, combination interfaces are defined through multiple inheritance.  Because SystemVerilog does not support multiple inheritance, the OVM emulates hierarchical interfaces via a common base class and interface mask.

Like their SystemC counterparts, the OVM’s TLM port and export implementations allow connections between ports whose interfaces are not an exact match.  For example, an ovm_blocking_get_port can be connected to any port, export or imp port that provides at the least an implementation of the blocking_get interface, which includes the ovm_get_* ports and exports, ovm_blocking_get_peek_* ports and exports, and ovm_get_peek_* ports and exports.

The sections below provide and overview of the unidirectional and bidirectional TLM interfaces, ports, and exports.

Summary
TLM Interfaces, Ports, and Exports
The OVM TLM library defines several abstract, transaction-level interfaces and the ports and exports that facilitate their use.
Unidirectional Interfaces & PortsThe unidirectional TLM interfaces consist of blocking, non-blocking, and combined blocking and non-blocking variants of the put, get and peek interfaces, plus a non-blocking analysis interface.
PutThe put interfaces are used to send, or put, transactions to other components.
Get and PeekThe get interfaces are used to retrieve transactions from other components.
AnalysisThe analysis interface is used to perform non-blocking broadcasts of transactions to connected components.
Ports, Exports, and ImpsThe OVM provides unidirectional ports, exports, and implementation ports for connecting your components via the TLM interfaces.
Bidirectional Interfaces & PortsThe bidirectional interfaces consist of blocking, non-blocking, and combined blocking and non-blocking variants of the transport, master, and slave interfaces.
TransportThe transport interface sends a request transaction and returns a response transaction in a single task call, thereby enforcing an in-order execution semantic.
Master and SlaveThe primitive, unidirectional put, get, and peek interfaces are combined to form bidirectional master and slave interfaces.
Ports, Exports, and ImpsThe OVM provides bidirectional ports, exports, and implementation ports for connecting your components via the TLM interfaces.
UsageWe provide an example to illustrate basic TLM connectivity using the blocking put inteface.

Unidirectional Interfaces & Ports

The unidirectional TLM interfaces consist of blocking, non-blocking, and combined blocking and non-blocking variants of the put, get and peek interfaces, plus a non-blocking analysis interface.

Put

The put interfaces are used to send, or put, transactions to other components.  Successful completion of a put guarantees its delivery, not execution.

Get and Peek

The get interfaces are used to retrieve transactions from other components.  The peek interfaces are used for the same purpose, except the retrieved transaction is not consumed; successive calls to peek will return the same object.  Combined get_peek interfaces are also defined.

Analysis

The analysis interface is used to perform non-blocking broadcasts of transactions to connected components.  It is typically used by such components as monitors to publish transactions observed on a bus to its subscribers, which are typically scoreboards and response/coverage collectors.

Ports, Exports, and Imps

The OVM provides unidirectional ports, exports, and implementation ports for connecting your components via the TLM interfaces.

Portsinstantiated in components that require, or use, the associate interface to initiate transaction requests.
Exportsinstantiated by components that forward an implementation of the methods defined in the associated interface.  The implementation is typically provided by an imp port in a child component.
Impsinstantiated by components that provide or implement an implementation of the methods defined in the associated interface.

A summary of port, export, and imp declarations are

class ovm_*_export #(type T=int)
  extends ovm_port_base #(tlm_if_base #(T,T));

class ovm_*_port #(type T=int)
  extends ovm_port_base #(tlm_if_base #(T,T));

class ovm_*_imp #(type T=int)
  extends ovm_port_base #(tlm_if_base #(T,T));

where the asterisk can be any of

blocking_put
nonblocking_put
put

blocking_get
nonblocking_get
get

blocking_peek
nonblocking_peek
peek

blocking_get_peek
nonblocking_get_peek
get_peek

analysis

Bidirectional Interfaces & Ports

The bidirectional interfaces consist of blocking, non-blocking, and combined blocking and non-blocking variants of the transport, master, and slave interfaces.

Bidirectional interfaces involve both a transaction request and response.

Transport

The transport interface sends a request transaction and returns a response transaction in a single task call, thereby enforcing an in-order execution semantic.  The request and response transactions can be different types.

Master and Slave

The primitive, unidirectional put, get, and peek interfaces are combined to form bidirectional master and slave interfaces.  The master puts requests and gets or peeks responses.  The slave gets or peeks requests and puts responses.  Because the put and the get come from different function interface methods, the requests and responses are not coupled as they are with the transport interface.

Ports, Exports, and Imps

The OVM provides bidirectional ports, exports, and implementation ports for connecting your components via the TLM interfaces.

Portsinstantiated in components that require, or use, the associate interface to initiate transaction requests.
Exportsinstantiated by components that forward an implementation of the methods defined in the associated interface.  The implementation is typically provided by an imp port in a child component.
Impsinstantiated by components that provide or implement an implementation of the methods defined in the associated interface.

A summary of port, export, and imp declarations are

class ovm_*_port #(type REQ=int, RSP=int)
  extends ovm_port_base #(tlm_if_base #(REQ, RSP));

class ovm_*_export #(type REQ=int, RSP=int)
  extends ovm_port_base #(tlm_if_base #(REQ, RSP));

class ovm_*_imp #(type REQ=int, RSP=int)
  extends ovm_port_base #(tlm_if_base #(REQ, RSP));

where the asterisk can be any of

transport
blocking_transport
nonblocking_transport

blocking_master
nonblocking_master
master

blocking_slave
nonblocking_slave
slave

Usage

We provide an example to illustrate basic TLM connectivity using the blocking put inteface.

port-to-portleaf1’s out port is connected to its parent’s (comp1) out port
port-to-exportcomp1’s out port is connected to comp2’s in export
export-to-exportcomp2’s in export is connected to its child’s (subcomp2) in export
export-to-impsubcomp2’s in export is connected leaf2’s in imp port.
imp-to-implementationleaf2’s in imp port is connected to its implementation, leaf2

Hierarchical port connections are resolved and optimized just before the ovm_component::end_of_elaboration phase.  After optimization, calling any port’s interface method (e.g. leaf1.out.put(trans)) incurs a single hop to get to the implementation (e.g. leaf2’s put task), no matter how far up and down the hierarchy the implementation resides.

`include "ovm_pkg.sv"
import ovm_pkg::*;

class trans extends ovm_transaction;
  rand int addr;
  rand int data;
  rand bit write;
endclass

class leaf1 extends ovm_component;

  `ovm_component_utils(leaf1)

  ovm_blocking_put_port #(trans) out;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
    out = new("out",this);
  endfunction

  virtual task run();
    trans t;
    t = new;
    t.randomize();
    out.put(t);
  endtask

endclass


class comp1 extends ovm_component;

  `ovm_component_utils(comp1)

  ovm_blocking_put_port #(trans) out;

  leaf1 leaf;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
  endfunction

  virtual function void build();
    out = new("out",this);
    leaf = new("leaf1",this);
  endfunction

  // connect port to port
  virtual function void connect();
    leaf.out.connect(out);
  endfunction

endclass


class leaf2 extends ovm_component;

  `ovm_component_utils(leaf2)

  ovm_blocking_put_imp #(trans,leaf2) in;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
    // connect imp to implementation (this)
    in = new("in",this);
  endfunction

  virtual task put(trans t);
    $display("Got trans: addr=%0d, data=%0d, write=%0d",
        t.addr, t.data, t.write);
  endtask

endclass


class subcomp2 extends ovm_component;

  `ovm_component_utils(subcomp2)

  ovm_blocking_put_export #(trans) in;

  leaf2 leaf;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
  endfunction

  virtual function void build();
    in = new("in",this);
    leaf = new("leaf2",this);
  endfunction

  // connect export to imp
  virtual function void connect();
    in.connect(leaf.in);
  endfunction

endclass


class comp2 extends ovm_component;

  `ovm_component_utils(comp2)

  ovm_blocking_put_export #(trans) in;

  subcomp2 subcomp;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
  endfunction

  virtual function void build();
    in = new("in",this);
    subcomp = new("subcomp2",this);
  endfunction

  // connect export to export
  virtual function void connect();
    in.connect(subcomp.in);
  endfunction

endclass


class env extends ovm_component;

  `ovm_component_utils(comp1)

  comp1 comp1_i;
  comp2 comp2_i;

  function new(string name, ovm_component parent=null);
    super.new(name,parent);
  endfunction

  virtual function void build();
    comp1_i = new("comp1",this);
    comp2_i = new("comp2",this);
  endfunction

  // connect port to export
  virtual function void connect();
    comp1_i.out.connect(comp2_i.in);
  endfunction

endclass


module top;
  env e = new("env");
  initial run_test();
  initial #10 ovm_top.stop_request();
endmodule
virtual function void end_of_elaboration ()
The end_of_elaboration phase callback is one of several methods automatically called during the course of simulation.