Subscribers are basically listeners of an analysis port. They subscribe to a broadcaster and receive objects whenever an item is broadcasted via the connected analysis port. A uvm_component
class does not have an in-built analysis port, while a uvm_subscriber
is an extended version with an analysis port named analysis_export
.
Class definition
virtual class uvm_subscriber #(type T=int) extends uvm_component;
typedef uvm_subscriber #(T) this_type;
uvm_analysis_imp #(T, this_type) analysis_export;
function new (string name, uvm_component parent);
super.new (name, parent);
analysis_export = new ("analysis_imp", this);
endfunction
pure virtual function void write (T, t);
endclass
Use Case
In a typical case, an agent would have a TLM analysis port for it's monitor to share the data object it collected on the agent's interface with other testbench components. So it is easier to create a user class inherited from uvm_subscriber
and use the in-built analysis_export
implementation to connect to the analysis port of the agent. For example, you could have functional coverage groups and coverpoints in a subscriber and have that sampled whenever it receives an object from the agent.
class my_coverage extends uvm_subscriber #(bus_pkt);
covergroup cg_bus;
...
endgroup
virtual function void write (bus_pkt pkt);
cg_bus.sample ();
endfunction
endclass
class my_env extends uvm_env;
...
virtual function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
my_agent.custom_ap.connect (my_cov.analysis_export);
endfunction
endclass
It is not mandatory to inherit from uvm_subscriber
, but it is a recommended practice so that there exists a uniform way through which a component can connect with an analysis port.