|HOME |ABOUT |ARTICLES |ACK |FEEDBACK |TOC |LINKS |BLOG |JOBS |


Tutorials



UVM TLM 1

Before going into the TLM interface concepts, lets see why we need TLM interface

Port Based Data Transfer:

Following is a simple verification environment.  


Components generator and driver are implemented as modules.  These modules are connected using module ports or SV interfaces.  The advantage of this methodology is, the two above mentioned components are independent. Instead of consumer module, any other component which can understand producer interface can be connected, which gives a great reusability.

The disadvantage of this methodology is , data transfer is done at lower lever abstraction.

Task Based Data Transfer:



In the above environment, methods are used to transfer the data between components. So, this gives a better control and data transfer is done at high level. The disadvantage is, components are using hierarchal paths which do not allow the reusability.


TLM interfaces:

UVM has TLM interfaces which provide the advantages which we saw in the above two data transfer styles.
Data is transferred at high level.  Transactions which are developed by extending the uvm_sequence_item can be transferred between components using method calls. These methods are not hierarchal fixed, so that components can be reused.

The advantages of TLM interfaces are
1) Higher level abstraction
2) Reusable. Plug and play connections.
3) Maintainability
4) Less code.
5) Easy to implement.
6) Faster simulation.
7) Connect to Systemc.
8) Can be used for reference model development.


Operation Supported By Tlm Interface:

Putting:
Producer transfers a value to Consumer.
Getting:
Consumer requires a data value from producer.
Peeking:
Copies data from a producer without consuming the data.
Broadcasting:
Transaction is broadcasted to none or one or multiple consumers.


Methods

BLOCKING:
virtual task put(input T1 t)
virtual task get(output T2 t)
virtual task peek(output T2 t)

NON-BLOCKIN:
virtual function bit try_put(input T1 t)
virtual function bit can_put()
virtual function bit try_get(output T2 t)
virtual function bit can_get()
virtual function bit try_peek(output T2 t)
virtual function bit can_peek()

BLOCKING TRANSPORT:
virtual task transport(input T1 req,output T2 rsp)

NON-BLOCKING TRANSPORT:
virtual function bit nb_transport(input T1 req,output T2 rsp)

ANALYSIS:
virtual function void write(input T1 t)


Tlm Terminology :

Producer:
A component which generates a transaction.
Consumer:
A component which consumes the transaction.
Initiator:
A component which initiates process.
Target:
A component which responded to initiator.


Tlm Interface Compilation Models:

Blocking:
A blocking interface conveys transactions in blocking fashion; its methods do not return until the transaction has been successfully sent or retrieved.  Its methods are defined as tasks.
Non-blocking:
A 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.
Combined:

A combination interface contains both the blocking and non-blocking variants.  


Interfaces:

The UVM provides ports, exports and implementation and analysis ports for connecting your components via the TLM interfaces. Port, Export, implementation terminology applies to control flow not to data flow.

Port:
Interface that requires an implementation is port.
Import:
Interface that provides an implementation is import ot implementation port.
Export:
Interface used to route transaction interfaces to other layers of the hierarchy.
Analysis:
Interface used to distribute transactions to passive components.


Direction:

Unidirectional:
Data transfer is done in a single direction and flow of control is in either or both direction.
Bidirectional:
Data transfer is done in both directions and flow of control is in either or both directions.

Examples:
A read  operation is a bidirectional.
A write operation is unidirectional.

Lets look at a example:
In this example, we will use *_put_* interface.


There are 2 components, producer and consumer.
Producer generates the transaction and Consumers consumes it.
In this example, producer calls the put() method to send transaction to consumer i.e producer is initiator and consumer is target.
When the put() method in the producer is called, it actually executes the put() method which is defined in consumer component.

Transaction

We will use the below transaction in this example.

class instruction extends uvm_sequence_item;
  typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_t; 
  rand inst_t inst;

  `uvm_object_utils_begin(instruction)
    `uvm_field_enum(inst_t,inst, UVM_ALL_ON)
  `uvm_object_utils_end

  function new (string name = "instruction");
    super.new(name);
  endfunction 
endclass 

Producer:  

1) Define producer component by extending uvm_component.

 class producer extends uvm_component;

 endclass : producer

2) Declare uvm_blocking_put_port port.

   uvm_blocking_put_port#(instruction) put_port;

3) In the constructor, construct the port.

    function new(string name, uvm_component p = null);
      super.new(name,p);
      put_port = new("put_port", this);
    endfunction

4) Define the run() method. In this method, randomize the transaction.
   Then call the put() of the put_port and pass the randomized transaction.

    task run;
      for(int i = 0; i < 10; i++)
        begin
          instruction ints;
          #10;
          ints = new();
          if(ints.randomize()) begin
          `uvm_info("producer", $sformatf("sending   %s",ints.inst.name()), UVM_MEDIUM)
          put_port.put(ints);
          end
        end
    endtask

Producer source code
  class producer extends uvm_component;

    uvm_blocking_put_port#(instruction) put_port;
    
    function new(string name, uvm_component p = null);
      super.new(name,p);
      put_port = new("put_port", this);
    endfunction
    
    task run;
      for(int i = 0; i < 10; i++)
        begin
          instruction ints;
          #10;
          ints = new();
          if(ints.randomize()) begin
          `uvm_info("producer", $sformatf("sending   %s",ints.inst.name()), UVM_MEDIUM)
          put_port.put(ints);
          end
        end
    endtask
    
  endclass : producer

Consumer:

1) Define a consumer component by extending uvm_component.

  class consumer extends uvm_component;

  endclass : consumer

2) Declare  uvm_blocking_put_imp import. The parameters to this port are transaction and the consumer component itself.

 uvm_blocking_put_imp#(instruction,consumer) put_port;


3) In the construct , construct the port.

    function new(string name, uvm_component p = null);
      super.new(name,p);
      put_port = new("put_port", this);
    endfunction

4) Define put() method. When the producer calls "put_port.put(ints);", then  this method will be called. Arguments to this method is transaction type "instruction".
   In this method, we will just print the transaction.

    task put(instruction t);
          `uvm_info("consumer", $sformatf("receiving %s",t.inst.name()), UVM_MEDIUM) 
    endtask

Consumer source code
  class consumer extends uvm_component;

    uvm_blocking_put_imp#(instruction,consumer) put_port;
    
    function new(string name, uvm_component p = null);
      super.new(name,p);
      put_port = new("put_port", this);
    endfunction
    
    task put(instruction t);
         `uvm_info("consumer", $sformatf("receiving %s",t.inst.name()), UVM_MEDIUM) 
    //push the transaction into queue or array
    //or drive the transaction to next level
    //or drive to interface
    endtask
    
  endclass : consumer

Connecting producer and consumer

In the env class, take the instance of producer and consumer components.
In the connect method, connect the producer put_port to consumer put_port using  
  p.put_port.connect(c.put_port);

Env Source code
  class env extends uvm_env;
    producer p;
    consumer c;
    
    function new(string name = "env");
      super.new(name);
      p = new("producer", this);
      c = new("consumer", this);
    endfunction
    
    function void connect();
      p.put_port.connect(c.put_port);
    endfunction
    
    task run();
      #1000;
       global_stop_request();
    endtask
    
  endclass
  
Testcase
module test;
  env e;
  
  initial begin
    e = new();
    run_test();
  end

endmodule 

Download the example

uvm_tlm_1.tar
Browse the code in uvm_tlm_1.tar

Command to sun the simulation

VCS Users : make vcs
Questa Users: make questa


Log

UVM_INFO producer.sv(26) @ 10:
  env.producer [producer] sending   PUSH_A
UVM_INFO consumer.sv(20) @ 10:
  env.consumer [consumer] receiving PUSH_A
UVM_INFO producer.sv(26) @ 20:
  env.producer [producer] sending   PUSH_B
UVM_INFO consumer.sv(20) @ 20:
  env.consumer [consumer] receiving PUSH_B



One more example using  *_get_* interface as per the below  topology.



Download the example

uvm_tlm_2.tar
Browse the code in uvm_tlm_2.tar

Command to sun the simulation

VCS Users : make vcs
Questa Users: make questa


All Interfaces In Uvm:


uvm_blocking_put_port #(T)
uvm_nonblocking_put_port #(T)
uvm_put_port #(T)
uvm_blocking_get_port #(T)
uvm_nonblocking_get_port #(T)
uvm_get_port #(T)
uvm_blocking_peek_port #(T)
uvm_nonblocking_peek_port #(T)
uvm_peek_port #(T)
uvm_blocking_get_peek_port #(T)
uvm_nonblocking_get_peek_port #(T)
uvm_get_peek_port #(T)
uvm_analysis_port #(T)
uvm_transport_port #(REQ,RSP)
uvm_blocking_transport_port #(REQ,RSP)
uvm_nonblocking_transport_port #(REQ,RSP)
uvm_master_port #(REQ,RSP)
uvm_blocking_master_port #(REQ,RSP)
uvm_nonblocking_master_port #(REQ,RSP)
uvm_slave_port #(REQ,RSP)
uvm_blocking_slave_port #(REQ,RSP)
uvm_nonblocking_slave_port #(REQ,RSP)
uvm_put_export #(T)
uvm_blocking_put_export #(T)
uvm_nonblocking_put_export #(T)
uvm_get_export #(T)
uvm_blocking_get_export #(T)
uvm_nonblocking_get_export #(T)
uvm_peek_export #(T)
uvm_blocking_peek_export #(T)
uvm_nonblocking_peek_export #(T)
uvm_get_peek_export #(T)
uvm_blocking_get_peek_export #(T)
uvm_nonblocking_get_peek_export #(T)
uvm_analysis_export #(T)
uvm_transport_export #(REQ,RSP)
uvm_nonblocking_transport_export #(REQ,RSP)
uvm_master_export #(REQ,RSP)
uvm_blocking_master_export #(REQ,RSP)
uvm_nonblocking_master_export #(REQ,RSP)
uvm_slave_export #(REQ,RSP)
uvm_blocking_slave_export #(REQ,RSP)
uvm_nonblocking_slave_export #(REQ,RSP)
uvm_put_imp #(T,IMP)
uvm_blocking_put_imp #(T,IMP)
uvm_nonblocking_put_imp #(T,IMP)
uvm_get_imp #(T,IMP)
uvm_blocking_get_imp #(T,IMP)
uvm_nonblocking_get_imp #(T,IMP)
uvm_peek_imp #(T,IMP)
uvm_blocking_peek_imp #(T,IMP)
uvm_nonblocking_peek_imp #(T,IMP)
uvm_get_peek_imp #(T,IMP)
uvm_blocking_get_peek_imp #(T,IMP)
uvm_nonblocking_get_peek_imp #(T,IMP)
uvm_analysis_imp #(T,IMP)
uvm_transport_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_blocking_transport_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_nonblocking_transport_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_master_imp #(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_blocking_master_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_nonblocking_master_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_slave_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_blocking_slave_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_nonblocking_slave_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)


Index
Introduction
Uvm Testbench
Uvm Reporting
Uvm Transaction
Uvm Configuration
Uvm Factory
Uvm Sequence 1
Uvm Sequence 2
Uvm Sequence 3
Uvm Sequence 4
Uvm Sequence 5
Uvm Sequence 6
Uvm Tlm 1
Uvm Tlm 2
Uvm Callback

Report a Bug or Comment on This section - Your input is what keeps Testbench.in improving with time!





<< PREVIOUS PAGE

TOP

NEXT PAGE >>

copyright © 2007-2017 :: all rights reserved www.testbench.in::Disclaimer