Any component can send a transaction to another component through a TLM put port. The receiving component should define an implementation of the put port. The implementation gives receiver the chance to define what has to be done with the incoming packet.

The port can be either blocking or nonblocking in nature, which will decide whether the put method will block execution in the sender until the receiver accepts the object. The example shown below is a TLM blocking put port in one component connected to its implementation port in another component.

UVM TLM Port Example

A class called Packet is defined below to act as the data item that will be transferred from one component to another. This class object will have two random variables that can be randomized before sending.

 
// Create a class data object that can be sent from one 
// component to another
class Packet extends uvm_object;
  rand bit[7:0] addr;
  rand bit[7:0] data;
 
  `uvm_object_utils_begin(Packet)
    `uvm_field_int(addr, UVM_ALL_ON)
    `uvm_field_int(data, UVM_ALL_ON)
  `uvm_object_utils_end
 
  function new(string name = "Packet");
    super.new(name);
  endfunction
endclass
 
1. Create sender class with a port of type uvm_blocking_put_port

A class called componentA is created which has a uvm_blocking_put_port parameterized to accept a data object of type Packet. The port has to be instantiated with the new() method preferably in the build_phase of the same component.

In this example, a class object of type Packet is created, randomized and sent via the put_port handle by calling the put() method. Many such packets can be sent using a simple loop controlled by a configurable variable.

 
class componentA extends uvm_component;
   `uvm_component_utils (componentA)
 
  // Create a blocking TLM put port which can send an object
  // of type 'Packet'
  uvm_blocking_put_port #(Packet) m_put_port;
  int m_num_tx;
 
   function new (string name = "componentA", uvm_component parent= null);
      super.new (name, parent);
   endfunction
 
   // Remember that TLM put_port is a class object and it will have to be 
   // created with new ()
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
     m_put_port = new ("m_put_port", this);
   endfunction
 
  // Create a packet, randomize it and send it through the port
  // Note that put() is a method defined by the receiving component
  // Repeat these steps N times to send N packets
   virtual task run_phase (uvm_phase phase);
     phase.raise_objection(this);
     repeat (m_num_tx) begin
         Packet pkt = Packet::type_id::create ("pkt");
         assert(pkt.randomize ()); 
 
          // Print the packet to be displayed in log
         `uvm_info ("COMPA", "Packet sent to CompB", UVM_LOW)
         pkt.print (uvm_default_line_printer);
 
         // Call the TLM put() method of put_port class and pass packet as argument
         m_put_port.put (pkt);
      end
      phase.drop_objection(this);
   endtask   
endclass
 
3. Create receiver class that implements the put method

The receiver class needs to define an implementation port using uvm_blocking_put_imp. Since the port is blocking in nature, the put() implementation is a task which has to be defined by this component.

 
class componentB extends uvm_component;
   `uvm_component_utils (componentB)
 
   // Declare a put implementation port to accept transactions
   uvm_blocking_put_imp #(Packet, componentB) m_put_imp;
 
   function new (string name = "componentB", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
     m_put_imp = new ("m_put_imp", this);
   endfunction
 
    // Implementation of the 'put()' method in this case simply prints it.
    virtual task put (Packet pkt);
      `uvm_info ("COMPB", "Packet received from CompA", UVM_LOW)
      pkt.print(uvm_default_line_printer);
   endtask
endclass
 
tlm-testbench-structure 4. Connect port and its implementation at a higher level

The connection between a port and its implementation has to be done at a higher hierarchical level. Since both components are instantiated directly within the test class in this example, the connection between them can be done during the connect_phase of the test. If these two components were instantiated in another component or environment, they have to be connected during the connect_phase of that component or environment.

 
class my_test extends uvm_test;
  `uvm_component_utils (my_test)
 
   componentA compA;
   componentB compB;
 
  function new (string name = "my_test", uvm_component parent = null);
      super.new (name, parent);
   endfunction
 
   // Create objects of both components, set number of transfers
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      compA = componentA::type_id::create ("compA", this);
      compB = componentB::type_id::create ("compB", this);
      compA.m_num_tx = 2;
   endfunction
 
   // Connection between componentA and componentB is done here
   // Note that the "put_port" is connected to its implementation "put_imp"
   virtual function void connect_phase (uvm_phase phase);
     compA.m_put_port.connect (compB.m_put_imp);  
   endfunction
endclass
 
Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO testbench.sv(81) @ 0: uvm_test_top.compB [COMPB] Packet received from CompA
pkt: ([email protected]) { addr: 'h2d  data: 'hd1  } 
UVM_INFO testbench.sv(57) @ 0: uvm_test_top.compA [COMPA] Packet sent to CompB
pkt: ([email protected]) { addr: 'h2d  data: 'hd1  } 
UVM_INFO testbench.sv(81) @ 0: uvm_test_top.compB [COMPB] Packet received from CompA
pkt: ([email protected]) { addr: 'h42  data: 'h62  } 
UVM_INFO testbench.sv(57) @ 0: uvm_test_top.compA [COMPA] Packet sent to CompB
pkt: ([email protected]) { addr: 'h42  data: 'h62  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

Click to try this example in a simulator!   

Watch the example !

Put Port Blocking Behavior

The example shown above used a blocking put port declared of the type uvm_blocking_put_port which would stall the sender from resuming until the put() task returns. This blocking behavior can be observed if there is a delay inside the implementation of the put method within componentB. The code shown below inserts a 20ns delay inside the put task.

 
// Implementation of the 'put()' method in this case simply prints it.
  virtual task put (Packet pkt);      
 
  // Lets assume the receiver takes some time to process the packet after 
  // which this task will return. The "put" method in the sender should be
  // stalled there until this task returns.
  `uvm_info("COMPB", $sformatf("Processing packet"), UVM_LOW)
  #20;
  `uvm_info("COMPB", $sformatf("Processing packet finished ..."), UVM_LOW)
 
  `uvm_info ("COMPB", "Packet received from CompA", UVM_LOW)
  pkt.print(uvm_default_line_printer);
endtask
 

Note that time is consumed by componentB inside put task which blocks the sender from sending the next packet. At 20ns, componentB finishes the task and componentA proceeds to send the next packet.

Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(579) @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------------------
Name            Type                   Size  Value
--------------------------------------------------
uvm_test_top    my_test                -     @1837
  compA         componentA             -     @1906
    m_put_port  uvm_blocking_put_port  -     @1972
  compB         componentB             -     @1937
    m_put_imp   uvm_blocking_put_imp   -     @2011
--------------------------------------------------

UVM_INFO testbench.sv(63) @ 0: uvm_test_top.compA [COMPA] Packet sent to CompB
pkt: ([email protected]) { addr: 'ha1  data: 'h64  } 
UVM_INFO testbench.sv(97) @ 0: uvm_test_top.compB [COMPB] Processing packet
UVM_INFO testbench.sv(99) @ 20: uvm_test_top.compB [COMPB] Processing packet finished ...
UVM_INFO testbench.sv(102) @ 20: uvm_test_top.compB [COMPB] Packet received from CompA
pkt: ([email protected]) { addr: 'ha1  data: 'h64  } 
UVM_INFO testbench.sv(63) @ 20: uvm_test_top.compA [COMPA] Packet sent to CompB
pkt: ([email protected]) { addr: 'hc1  data: 'hb9  } 
UVM_INFO testbench.sv(97) @ 20: uvm_test_top.compB [COMPB] Processing packet
UVM_INFO testbench.sv(99) @ 40: uvm_test_top.compB [COMPB] Processing packet finished ...
UVM_INFO testbench.sv(102) @ 40: uvm_test_top.compB [COMPB] Packet received from CompA
pkt: ([email protected]) { addr: 'hc1  data: 'hb9  } 
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 40: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 40: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

You may also like:

You consent to our cookies if you continue to use our website. To know more about cookies, see our privacy policy. I accept cookies from this site.

Agree