Analysis Port - naveen0215/uvm_sequence_library GitHub Wiki
Earlier we have seen TLM put() and get() methods which was having one-on-one connections that is either port-to-export or port-to-imp connections. whereas this analysis port are used for one-to many connections that is broadcast of transactions to any number of components or receivers(even if there is no receiver analysis port broadcasts the data). hence we can conclude it as analysis port may be connected to zero, one or many Analysis Exports.

uvm_analysis_port : Broadcast port
The uvm_analysis_port is a specialized TLM port which has non-blocking function, write(). The uvm_analysis_port calls an analysis_port.write(transaction) method to broadcast a transaction. A uvm_analysis_port can be connected to other uvm_analysis_ports, uvm_analysis_exports and uvm_analysis_imps.
uvm_analysis_export : Transfer port
The uvm_analysis_export is similar to a transfer‐point connection and it lies between the broadcasting uvm_analysis_port source and each uvm_analysis_imp termination point. uvm_analysis_export(s) can be viewed as a transfer port.
uvm_analysis_imp : Termination port
Each uvm_analysis_imp defines a write() method that is executed when the analysis port calls the write() method. In the parent environment, the analysis port gets connected to the analysis export of the desired components, such as coverage collectors and scoreboards in a typical UVM testbench.
The uvm_analysis_port is a specialized TLM class which consists of a function write() and this can be used to transfer the data from where we need. This port also contains list of analysis exports and analysis implementations that are connected to it. Later by using the analysis_port.write() in one component we can broadcast the data to each connected exports. Components which have a read() function will receive the datas which are broadcasted.
Generally monitor will be using analysis_port to broadcast the data to multiple components like scoreboard, coverage collector, subscribers.
-
Syntax:
class my_class_name extends uvm_component; ..... uvm_analysis_port #(my_data) port_name; ..... endclass
Example:

In this example, let us consider a componentB from which we broadcast or send the data to three different subscribers (sub1,sub2,sub3). Since we need to perform broadcasting of the data ,we use a special type of connection called uvm_analysis_port.
Producer Class
class transaction extends uvm_sequence_item;
rand bit[2:0]a;
rand bit[2:0]b;
`uvm_object_utils_begin(transaction)
`uvm_field_int(a,UVM_DEFAULT+UVM_DEC)
`uvm_field_int(b,UVM_DEFAULT+UVM_DEC)
`uvm_object_utils_end
function new (string name = "transaction");
super.new(name);
endfunction
endclass
class componentB extends uvm_component;
`uvm_component_utils(componentB)
transaction trans;
uvm_analysis_port #(transaction) ap;
function new (string name = "componentB" , uvm_component parent);
super.new (name,parent);
ap = new("WRITE",this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
trans = transaction::type_id::create("trans",this);
endfunction
virtual task run_phase(uvm_phase phase);
repeat(3) begin
`uvm_info(get_type_name(),"sending Data to subscribers.......",UVM_NONE)
void'(trans.randomize());
ap.write(trans);
trans.print();
end
endtask
endclass
In the above code snippet, we are extending transaction class from uvm_sequence_item which is a base class. we defined two class properties 'a' and 'b' of rand bit type. since uvm_sequence_item is a object, we registered this class using pre-defined factory macro uvm_component_utils and the fields are also registered within this factory registration.
We have defined a class componentB extending from uvm_component. This class is registered using factory macro. Now, in this example we are defining a special type of port called uvm_analysis_port. After declaring some random_name('ap') to analysis_port, we have to specify that port operations as write(to specify this as a producer). using ap.write(trans) we broadcasted the data from the componentB.
Consumer Classes
class sub1 extends uvm_component;
transaction trans;
`uvm_component_utils(sub1)
uvm_analysis_imp #(transaction , sub1) aimp;
function new (string name = "sub1" , uvm_component parent);
super.new (name,parent);
aimp = new("READ",this);
endfunction
virtual function void write (input transaction t);
t.print();
`uvm_info(get_type_name(),"Data Received to sub1!!",UVM_NONE)
endfunction
endclass
class sub2 extends uvm_component;
transaction trans;
`uvm_component_utils(sub2)
uvm_analysis_imp #(transaction , sub2) aimp;
function new (string name = "sub2" , uvm_component parent);
super.new (name,parent);
aimp = new("READ",this);
endfunction
virtual function void write (input transaction t);
t.print();
`uvm_info(get_type_name(),"Data Received to sub2!!",UVM_NONE)
endfunction
endclass
class sub3 extends uvm_component;
transaction trans;
`uvm_component_utils(sub3)
uvm_analysis_imp #(transaction , sub3) aimp;
function new (string name = "sub2" , uvm_component parent);
super.new (name,parent);
aimp = new("READ",this);
endfunction
virtual function void write (input transaction t);
t.print();
`uvm_info(get_type_name(),"Data Received to sub3!!",UVM_NONE)
endfunction
endclass
We extended 3 different class sub1,sub2 and sub3 from uvm_component. Since this class need to receive the datas which are broadcasted from componentB we are defining a uvm_analysis_imp with a name as 'aimp' and during its build phase we are declaring this port as READ type(consuming type). We have defined a virtual function with name as write and we are passing the transaction as an argument to it. This is similar for all 3 consumer classes.
class my_env extends uvm_env;
componentB comB;
sub1 s1;
sub2 s2;
sub3 s3;
`uvm_component_utils(my_env)
function new (string name = "my_env" , uvm_component parent);
super.new (name,parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase(phase);
comB = componentB::type_id::create("comB",this);
s1 = sub1::type_id::create("s1",this);
s2 = sub2::type_id::create("s2",this);
s3 = sub3::type_id::create("s3",this);
endfunction
virtual function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
comB.ap.connect(s1.aimp);
comB.ap.connect(s2.aimp);
comB.ap.connect(s3.aimp);
endfunction
endclass
class test extends uvm_test;
my_env env;
`uvm_component_utils(test)
function new (string name = "test" , uvm_component parent);
super.new (name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env",this);
endfunction
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
`uvm_info(get_type_name(),"end_of_elaboration_phase",UVM_NONE)
uvm_top.print_topology();
endfunction
endclass
We extended my_env class from uvm_env and instantiated the producer and the consumer classes with a name and also at this level it is very easier to have connection between the components.So, we created object for the instantiated classes during the build_phase and during the connect phase we connected the producer_port to the consumer_port.
Finally we extended the test class from uvm_test and we instatiated and created a object using create method for derived env class. Also during end_of_elaboration_phase we are printing the whole topology which includes the name of derived class and the base class.
- Output:


From the above output snaps, we can see that componentB is sending the data to consumers and all 3 consumers are driving or receiving the same output from componentB.