Skip to main content

Using Blocking and Non-blocking Put/Get Ports in UVM

· loading · loading · ·
Education UVM Verification UVM Verification SystemVerilog Blocking Port Non-Blocking Port TLM
Education UVM Verification
Axolot Logic
Author
Axolot Logic
Digital Design Engineer
Table of Contents
UVM Series - This article is part of a series.
Part 27: This Article

🚀 Introduction
#

This page covers the use of blocking and non-blocking put/get ports in UVM TLM and the port-export-imp topology. It also highlights which methods need to be implemented by the sender (driver) and the receiver (monitor) for each port type.


1️⃣ Blocking Put Usage
#

📌 Description
#

  • A blocking put port blocks the sender while sending data until the receiver completes the operation.
  • The put() method is called by the sender and implemented by the receiver.

📌 Sender
#

  • Uses uvm_blocking_put_port.
  • Calls the put() method.

📌 Receiver
#

  • Uses uvm_blocking_put_imp.
  • Implements the put() method.

📦 Example
#

import uvm_pkg::*;
`include "uvm_macros.svh"

// Transaction Class
class my_transaction extends uvm_sequence_item;
  rand int data;
  `uvm_object_utils(my_transaction)
  function new(string name = "my_transaction");
    super.new(name);
  endfunction
  function string convert2string();
    return $sformatf("data = %0d", data);
  endfunction
endclass

// Driver
class my_driver extends uvm_driver #(my_transaction);
  `uvm_component_utils(my_driver)
  uvm_blocking_put_port #(my_transaction) put_port;
  function new(string name, uvm_component parent);
    super.new(name, parent);
    put_port = new("put_port", this);
  endfunction
  task run_phase(uvm_phase phase);
    my_transaction tr;
    phase.raise_objection(this);
    tr = my_transaction::type_id::create("tr");
    assert(tr.randomize() with { data inside {[1:100]}; });
    `uvm_info("DRV", $sformatf("Sending: %s", tr.convert2string()), UVM_LOW)
    put_port.put(tr);
    phase.drop_objection(this);
  endtask
endclass

// Monitor
class my_monitor extends uvm_component;
  `uvm_component_utils(my_monitor)
  uvm_blocking_put_imp #(my_transaction, my_monitor) put_imp;
  function new(string name, uvm_component parent);
    super.new(name, parent);
    put_imp = new("put_imp", this);
  endfunction
  task put(my_transaction tr);
    `uvm_info("MON", $sformatf("Received: %s", tr.convert2string()), UVM_LOW)
  endtask
endclass

2️⃣ Non-blocking Put Usage
#

📌 Description
#

  • A non-blocking put port checks whether the receiver is ready before sending data and immediately returns a response.
  • try_put() and can_put() methods are used.

📌 Sender
#

  • Uses uvm_nonblocking_put_port.
  • Calls the try_put() method.

📌 Receiver
#

  • Uses uvm_nonblocking_put_imp.
  • Implements the try_put() and can_put() methods.

📦 Example
#

package my_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;

// Transaction Class
class my_transaction extends uvm_sequence_item;
  rand int data;
  `uvm_object_utils(my_transaction)
  function new(string name = "my_transaction");
    super.new(name);
  endfunction
endclass

// Consumer
class my_consumer extends uvm_component;
  uvm_nonblocking_put_imp #(my_transaction, my_consumer) put_imp;
  `uvm_component_utils(my_consumer)

  function new(string name = "my_consumer", uvm_component parent = null);
    super.new(name, parent);
    put_imp = new("put_imp", this);
  endfunction

  function bit try_put(my_transaction tr);
    if (can_put()) begin
      `uvm_info("CONSUMER", $sformatf("Received data: %0d", tr.data), UVM_LOW)
      return 1;
    end
    return 0;
  endfunction

  function bit can_put();
    return 1;
  endfunction
endclass

// Producer
class my_producer extends uvm_component;
  uvm_nonblocking_put_port #(my_transaction) put_port;
  `uvm_component_utils(my_producer)

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

  task send_data(int data);
    my_transaction tr;
    tr = my_transaction::type_id::create("tr");
    tr.data = data;

    if (put_port.try_put(tr)) begin
      `uvm_info("PRODUCER", $sformatf("Sent data: %0d", data), UVM_LOW)
    end else begin
      `uvm_warning("PRODUCER", "Failed to send data")
    end
  endtask
endclass

3️⃣ Blocking Get Usage
#

📌 Description
#

  • A blocking get port is called by the component that wants to receive data and is implemented by the sender.
  • The get() method is called by the receiver and implemented by the sender.

📌 Sender
#

  • Uses uvm_blocking_get_imp.
  • Implements the get() method.

📌 Receiver
#

  • Uses uvm_blocking_get_port.
  • Calls the get() method.

📦 Example
#

package my_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;

// Transaction Class
class my_transaction extends uvm_sequence_item;
  rand int data;
  `uvm_object_utils(my_transaction)
  function new(string name = "my_transaction");
    super.new(name);
  endfunction
endclass

// Sender
class my_sender extends uvm_component;
  uvm_blocking_get_imp #(my_transaction, my_sender) get_imp;
  `uvm_component_utils(my_sender)

  function new(string name = "my_sender", uvm_component parent = null);
    super.new(name, parent);
    get_imp = new("get_imp", this);
  endfunction

  virtual task get(output my_transaction tr);
    tr = my_transaction::type_id::create("tr");
    assert(tr.randomize());
    `uvm_info("SENDER", $sformatf("Sent data: %0d", tr.data), UVM_LOW)
  endtask
endclass

// Receiver
class my_receiver extends uvm_component;
  uvm_blocking_get_port #(my_transaction) get_port;
  `uvm_component_utils(my_receiver)

  function new(string name = "my_receiver", uvm_component parent = null);
    super.new(name, parent);
    get_port = new("get_port", this);
  endfunction

  task receive_data();
    my_transaction tr;
    get_port.get(tr);
    `uvm_info("RECEIVER", $sformatf("Received data: %0d", tr.data), UVM_LOW)
  endtask
endclass

4️⃣ Non-blocking Get Usage
#

📌 Description
#

  • A non-blocking get port is called by the component that wants to receive data using try_get() or can_get().
  • The sender implements the try_get() and can_get() methods.

📌 Sender
#

  • Uses uvm_nonblocking_get_imp.
  • Implements the try_get() and can_get() methods.

📌 Receiver
#

  • Uses uvm_nonblocking_get_port.
  • Calls the try_get() or can_get() methods.

📦 Example
#

package my_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;

// Transaction Class
class my_transaction extends uvm_sequence_item;
  rand int data;
  `uvm_object_utils(my_transaction)
  function new(string name = "my_transaction");
    super.new(name);
  endfunction
endclass

// Sender
class my_sender extends uvm_component;
  uvm_nonblocking_get_imp #(my_transaction, my_sender) get_imp;
  my_transaction available_tr;
  `uvm_component_utils(my_sender)

  function new(string name = "my_sender", uvm_component parent = null);
    super.new(name, parent);
    get_imp = new("get_imp", this);
  endfunction

  function bit try_get(output my_transaction tr);
    if (can_get()) begin
      tr = available_tr;
      available_tr = null;
      `uvm_info("SENDER", $sformatf("Sent data: %0d", tr.data), UVM_LOW)
      return 1;
    end
    return 0;
  endfunction

  function bit can_get();
    return (available_tr != null);
  endfunction

  task prepare_transaction();
    available_tr = my_transaction::type_id::create("tr");
    assert(available_tr.randomize());
    `uvm_info("SENDER", $sformatf("Prepared data: %0d", available_tr.data), UVM_LOW)
  endtask
endclass

// Receiver
class my_receiver extends uvm_component;
  uvm_nonblocking_get_port #(my_transaction) get_port;
  `uvm_component_utils(my_receiver)

  function new(string name = "my_receiver", uvm_component parent = null);
    super.new(name, parent);
    get_port = new("get_port", this);
  endfunction

  task receive_data();
    my_transaction tr;
    if (get_port.can_get()) begin
      if (get_port.try_get(tr)) begin
        `uvm_info("RECEIVER", $sformatf("Received data: %0d", tr.data), UVM_LOW)
      end else begin
        `uvm_warning("RECEIVER", "Failed to get data")
      end
    end else begin
      `uvm_info("RECEIVER", "No data available", UVM_LOW)
    end
  endtask
endclass

5️⃣ Port-Export-Imp Topology
#

📌 Description
#

  • Port: Declared in the component initiating the transaction.
  • Export: Bridges the port to an imp or another export, useful in hierarchical structures.
  • Imp: Implemented in the component that processes the transaction.

📦 Example
#

// Connection topology
driver.put_port.connect(env.put_export);
env.put_export.connect(monitor.put_imp);

📌 Tips
#

  • A port can be connected directly to an imp.
  • Export is used to bridge port connections in hierarchical designs.
  • The imp is always implemented by the receiving component.

UVM Series - This article is part of a series.
Part 27: This Article

Related

What is Transaction-Level Modeling (TLM) in UVM?
· loading · loading
Education UVM Verification UVM Verification SystemVerilog TLM Data Communication
Education UVM Verification
UVM Agent Usage and Adder Example
· loading · loading
Education UVM Verification UVM Verification SystemVerilog Uvm_agent Testbench Structure
Education UVM Verification
UVM Base Classes
· loading · loading
Education UVM Verification UVM Verification SystemVerilog Class Hierarchy
Education UVM Verification
UVM Driver Usage and Adder Example
· loading · loading
Education UVM Verification UVM Verification SystemVerilog Uvm_driver Stimulus Driving
Education UVM Verification
UVM Environment Usage and adder_env Example
· loading · loading
Education UVM Verification UVM Verification SystemVerilog Uvm_env Testbench Structure
Education UVM Verification
UVM Monitor Usage and Adder Example
· loading · loading
Education UVM Verification UVM Verification SystemVerilog Uvm_monitor Coverage
Education UVM Verification