📄 Using the _decl
Macro#
📌 Introduction#
In UVM, when modeling transaction-level data transfer using TLM (Transaction-Level Modeling) interfaces, we often connect a single source (e.g. uvm_analysis_port
) to a single target (uvm_analysis_imp
). However, sometimes we might want to process data from the same source (for example, a producer) in different functions within the subscriber.
This is where the uvm_*_imp_decl()
macro comes in. This macro allows us to define multiple “imp” implementations within interfaces, each of which can direct data to a different function.
🔎 Purpose of _decl
Macro#
- To define multiple port implementations inside a single subscriber.
- To direct incoming data to different functions.
- To easily separate data from different components or even from the same component in a testbench.
📌 How to Use#
1️⃣ First, declare the _decl
macro:
`uvm_analysis_imp_decl(_1)
`uvm_analysis_imp_decl(_2)
2️⃣ Inside the subscriber:
uvm_analysis_imp_1 #(my_transaction, my_subscriber) analysis_imp1;
uvm_analysis_imp_2 #(my_transaction, my_subscriber) analysis_imp2;
3️⃣ Write different write_*()
functions:
virtual function void write_1(my_transaction t);
...
endfunction
virtual function void write_2(my_transaction t);
...
endfunction
4️⃣ Connect the producer port to these subscriber ports:
producer.analysis_port.connect(subscriber.analysis_imp1);
producer.analysis_port.connect(subscriber.analysis_imp2);
🚀 Example Code#
Below is a complete example showing _decl
usage:
package my_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;
`uvm_analysis_imp_decl(_1)
`uvm_analysis_imp_decl(_2)
// Transaction
class my_transaction extends uvm_sequence_item;
int data;
`uvm_object_utils(my_transaction)
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
// Producer
class my_producer extends uvm_component;
uvm_analysis_port #(my_transaction) analysis_port;
`uvm_component_utils(my_producer)
function new(string name = "my_producer", uvm_component parent = null);
super.new(name, parent);
analysis_port = new("analysis_port", this);
endfunction
task run_phase(uvm_phase phase);
my_transaction tr;
phase.raise_objection(this);
for (int i = 0; i < 5; i++) begin
tr = my_transaction::type_id::create("tr");
tr.data = i;
`uvm_info("PRODUCER", $sformatf("Sending data: %0d", tr.data), UVM_LOW)
analysis_port.write(tr);
#100;
end
phase.drop_objection(this);
endtask
endclass
// Subscriber
class my_subscriber extends uvm_component;
`uvm_component_utils(my_subscriber)
uvm_analysis_imp_1 #(my_transaction, my_subscriber) analysis_imp1;
uvm_analysis_imp_2 #(my_transaction, my_subscriber) analysis_imp2;
function new(string name = "my_subscriber", uvm_component parent = null);
super.new(name, parent);
analysis_imp1 = new("analysis_imp1", this);
analysis_imp2 = new("analysis_imp2", this);
endfunction
virtual function void write_1(my_transaction t);
`uvm_info("SUBSCRIBER", $sformatf("write_1: Received data: %0d", t.data), UVM_LOW)
endfunction
virtual function void write_2(my_transaction t);
`uvm_info("SUBSCRIBER", $sformatf("write_2: Received data: %0d", t.data), UVM_LOW)
endfunction
endclass
// Environment
class my_env extends uvm_env;
my_producer producer;
my_subscriber subscriber;
`uvm_component_utils(my_env)
function new(string name = "my_env", uvm_component parent = null);
super.new(name, parent);
producer = my_producer::type_id::create("producer", this);
subscriber = my_subscriber::type_id::create("subscriber", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
producer.analysis_port.connect(subscriber.analysis_imp1);
producer.analysis_port.connect(subscriber.analysis_imp2);
endfunction
endclass
// Test
class my_test extends uvm_test;
my_env env;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
endfunction
endclass
endpackage
// Top module
module top;
import uvm_pkg::*;
import my_pkg::*;
initial begin
uvm_top.enable_print_topology = 1;
run_test("my_test");
end
endmodule
✨ Summary#
✅ With the _decl
macro, we can create multiple port implementations.
✅ Each implementation directs calls to a different write_*()
function.
✅ This is ideal for processing data separately by source or by port.