🚀 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.