Analysis Ports in VMM 1.2
Posted by John Aynsley on March 3rd, 2010
John Aynsley, CTO, Doulos
Analysis ports are another feature from the SystemC TLM-2.0 standard that has been incorporated into VMM 1.2. Analysis ports provide a mechanism for distributing transactions to passive components in a verification environment, such as checkers and scoreboards.
Analysis ports and exports are a variant on the TLM ports and exports that I have discussed in previous blog posts. The main difference between analysis ports and regular ports is that a single analysis port can be bound to multiple exports, in which case the same transaction is sent to each and every export or “subscriber” or “observer” connected to the analysis port. The terms subscriber and observer are used interchangeably in the VMM documentation.
Let us take a look at an example:
class my_tx extends vmm_data; // User-defined transaction class
…
class transactor extends vmm_xactor;
vmm_tlm_analysis_port #(transactor, my_tx) m_ap; // The analysis port
…
virtual task main;
my_tx tx;
…
m_ap.write(tx);
…
The transactor above sends a transaction tx out through an analysis port m_ap.
The type of the analysis port is parameterized with the type of the transactor and of the transaction my_tx. The call to write sends the transaction to any object that has registered itself with the analysis port. There could be zero, one, or many such observers registered with the analysis port.
To continue the example, let us look at one observer:
class observer extends vmm_object;
vmm_tlm_analysis_export #(observer, my_tx) m_export;
function new (string inst, vmm_object parent = null);
…
m_export = new(this, “m_export”);
…
function void write(int id, my_tx tx);
…
The observer has an instance of an analysis export and must implement the write method that the export will provide to the transactors. Note that the observer extends vmm_object. Since an observer is passive, it need not extend vmm_xactor.
The analysis port may be bound to any number of observers in the surrounding environment:
class tb_env extends vmm_group;
transactor m_transactor;
observer m_observer_1;
another m_observer_2;
yet_another m_observer_3;
virtual function void build_ph;
m_transactor = new( “m_transactor”, this );
m_observer = new( “m_observer”, this );
…
endfunction
virtual function void connect_ph;
m_transactor.m_ap.tlm_bind( m_observer_1.m_export );
m_transactor.m_ap.tlm_bind( m_observer_2.m_export );
m_transactor.m_ap.tlm_bind( m_observer_3.m_export );
…
Note the use of the predefined phase methods from VMM 1.2. Transactors are created during the build phase, and ports are connected during the connect phase.
Finally, let us compare analysis ports with VMM callbacks:
m_ap.write(tx);
versus
`vmm_callback(callback_facade, write(tx));
The effect is very similar, but there are differences. Unlike VMM callbacks, the name of the method called through an analysis port is fixed at write. A VMM callback method is permitted to modify the transaction object, whereas a transaction sent through an analysis port cannot be modified. When multiple callbacks are registered, the prepend_callback and append_callback methods allow you to determine the order in which the callbacks are made, whereas you have no control over the order in which write is called for multiple observers bound to an analysis port. Because of these differences, only VMM callbacks are appropriate for modifying the behavior of transactors. Analysis ports are only appropriate for sending transactions to passive components that will not attempt to modify the transaction object. On the other hand, that in itself is the feature and strength of analysis ports; they are only for analysis.
It can make sense to combine a VMM callback with an analysis port in the same transactor, using the callback to inject an error and the analysis port to send the modified transaction to a scoreboard, for example:
`vmm_callback(callback_facade, inject_error(tx));
m_ap.write(tx);
In this situation, the VMM recommendation is to make the analysis call after the callback, as shown here.