This post will explain how analysis port and analysis export work.
In Agent, we connected the analysis port (jb_ap
) of the jelly-bean monitor (jb_mon
) to the analysis port (jb_ap
) of the jelly-bean agent (jb_agent
) as follows:
jb_mon.jb_ap.connect( jb_ap ); // in the jelly_bean_agent
Then, in Environment, we connected the analysis port (jb_ap
) of the jelly-bean agent to the analysis export (analysis_export
) of the jelly-bean-functional-coverage subscriber (jb_fc_sub
):
jb_agent.jb_ap.connect( jb_fc_sub.analysis_export ); // in the jelly_bean_env
You might want to look at the verification component diagram in Overview to check these connections.
Finally, in the run_phase()
of the jelly-bean monitor, the monitor called the write()
function of its analysis port, every time it created a jelly-bean transaction (jb_tx
):
jb_ap.write( jb_tx ); // in the jelly_bean_monitor
We will look at how the above code works in detail in this post. The class diagram related to the analysis port is shown below. UVM standard library classes are shown in pink, while the UVM classes specialized with the jelly_bean_transaction
type are shown in yellow.
Analysis Port
The jb_ap
of the jelly-bean monitor is an object of the uvm_analysis_port
class specialized with the jelly_bean_transaction
type. The following pseudo code shows how the class is specialized.
class uvm_analysis_port #( type T = jelly_bean_transaction )
extends uvm_port_base #( uvm_tlm_if_base #(T,T) );
function new( string name, uvm_component parent );
super.new( name, parent, UVM_PORT, 0, UVM_UNBOUNDED_CONNECTIONS );
m_if_mask = `UVM_TLM_ANALYSIS_MASK; // = 1 < < 8
endfunction // new
virtual function string get_type_name();
return "uvm_analysis_port";
endfunction // get_type_name
function void write( input jelly_bean_transaction t );
uvm_tlm_if_base #( jelly_bean_transaction, jelly_bean_transaction ) tif;
for ( int i = 0; i < this.size(); i++ ) begin
tif = this.get_if( i );
if ( tif == null )
uvm_report_fatal( "NTCONN",
{ "No uvm_tlm interface is connected to ",
get_full_name(), " for executing write()" },
UVM_NONE );
tif.write( t );
end
endfunction // write
endclass // uvm_analysis_port
As you have seen above, the write()
function of the uvm_analysis_port
delegates its job to tif
, which is an object of uvm_tlm_if_base
class. The uvm_tlm_if_base
class is the base class of uvm_port_base
class, which in turn is the base class of uvm_analysis_imp
class (line 22).
Analysis Export
The analysis_export
of the jelly-bean-functional-coverage subscriber (jb_fc_sub
) is an object of the uvm_analysis_imp
class specialized with the jelly_bean_transaction
type. The following pseudo code shows how the class is specialized.
class uvm_analysis_imp #( type T = jelly_bean_transaction,
type IMP = uvm_subscriber #( jelly_bean_transaction ) )
extends uvm_port_base #( uvm_tlm_if_base #( jelly_bean_transaction,
jelly_bean_transaction ) );
// `UVM_IMP_COMMON( `UVM_TLM_ANALYSIS_MASK, "uvm_analysis_imp",
// | uvm_subscriber #( jelly_bean_transaction,
// | jelly_bean_transaction ) )
// V
local uvm_subscriber #( jelly_bean_transaction, jelly_bean_transaction ) m_imp;
function new( string name, uvm_subscriber #( jelly_bean_transaction,
jelly_bean_transaction ) imp );
super.new( name, imp, UVM_IMPLEMENTATION, 1, 1 );
m_imp = imp;
m_if_mask = `UVM_TLM_ANALYSIS_MASK; // = 1 < < 8
endfunction // new
// +--> `UVM_TLM_GET_TYPE_NAME( "uvm_analysis_imp" )
virtual function string get_type_name();
return "uvm_analysis_imp";
endfunction // get_type_name
// end of macro expansion
function void write( input jelly_bean_transaction t );
m_imp.write( t );
endfunction // write
endclass // uvm_analysis_imp
The write()
function of the uvm_analysis_imp
class delegates its job to m_imp
. The m_imp
is the jelly-bean-functional-coverage subscriber (jb_fc_sub
) we passed as a parameter to the uvm_analysis_imp
class. This means the write()
function of the uvm_analysis_imp
simply calls the write()
function of the jb_fc_sub
.
Connection
Now let’s put the things together. The following sequence diagram shows the steps involved in the connection.
jb_mon.jb_ap.connect( jb_ap ); // in the jelly_bean_agent
When the connect()
function is called in the jelly_bean_agent
, the jb_ap
of the jb_mon
stores the jb_ap
of the jb_agent
in an array called m_provided_by
(step 1).
jb_agent.jb_ap.connect( jb_fc_sub.analysis_export ); // in the jelly_bean_env
Similarly, when the connect()
function is called in the jelly_bean_env
, the jb_ap
of the jb_agent
stores the analysis_export
of the jb_fc
in its m_provided_by
array (step 2).
Just before entering the end_of_elaboration_phase()
of the jb_mon
, resolve_bindings()
function is called. This function traverses the port connection. If the port type is UVM_IMPLEMENTATION
, the port is supposed to provide “implementation”. The function stores the “implementation” to an array called m_imp_list
(steps 3 to 7).
jb_ap.write( jb_tx ); // in the jelly_bean_monitor
The write()
function iterates over the implementations stored in the m_imp_list
array and calls the write()
function of each implementation (step 8).
That’s about the analysis port. I hope this post helped you to understand how the analysis port works.