UVM Tutorial for Candy Lovers – 12. Analysis Port

Last Updated: May 22, 2015 (fixed a typo)

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.

Class Diagram Related to the Analysis Port

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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.

Sequence Diagram
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.

24 thoughts on “UVM Tutorial for Candy Lovers – 12. Analysis Port”

  1. Hi Keisuke,

    It would be great if you can talk on analysis_fifo also in this tutorial.
    Also basically I have an requirement to verify the signals for two interfaces in the scoreboard.
    Can you please add some info this with an easy going example showing how we pump in the transaction into this fifo and how do we retrieve etc..

    Thank You in Advance..!
    -Surya

  2. Hi Keisuke,
    Thanks for this really great post to lead me in the UVM world.
    I usually browse your post when I have some questions about UVM and I can found the answers from it.

    Today I just found there is a typo in this post by chance, which I copied as follows. I think it must be the jb_ap instead of jb_ab.

    Similarly, when the connect() function is called in the jelly_bean_env, the jb_ab of the jb_agent stores the analysis_export of the jb_fc in its m_provided_by array (step 2).

    Best Regards,
    Rui

    1. You should be able to connect multiple analysis ports to one analysis export like this:

      jb_mon1.jb_ap.connect( jb_fc_sub.analysis_export );
      jb_mon2.jb_ap.connect( jb_fc_sub.analysis_export );
  3. Hi Keisuke,
    If I put more than one analysis_export in an extended uvm_subscriber, how do I implement the related write function of each uvm_analysis_imp? I mean how to distinguish the write function of each port?
    Thanks,
    Rui

    1. The `uvm_analysis_imp_decl macro allows you to use more than one write function in the uvm_subscriber (or in any components that implement the write function). Suppose you want to add another uvm_analysis_imp called expected_candy to the jelly_bean_sb_subscriber:

      `uvm_analysis_imp_decl(_expected_candy)

      The macro will take the macro argument (_expected_candy) and create a new class called uvm_analysis_imp_expected_candy. You also need to define the write_expected_candy function in the subscriber. For example:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      `uvm_analysis_imp_decl(_expected_candy)
       
      class jelly_bean_sb_subscriber extends uvm_subscriber#( jelly_bean_transaction );
        uvm_analysis_imp_expected_candy#( jelly_bean_transaction,                                    jelly_bean_sb_subscriber ) another_analysis_export; 
        function new( string name, uvm_component parent );
          super.new( name, parent );
          another_analysis_export = new( "another_analysis_export", this );
        endfunction: new
       
        // The write function of uvm_analysis_imp class calls this function.
       
        virtual function void write( jelly_bean_transaction t );
          // ...
        endfunction: write
       
        // The write function of uvm_analysis_imp_expected_candy class calls this function.
        // You must implement the function with this name.
       
        virtual function void write_expected_candy( jelly_bean_transaction t );    // ...
        endfunction: write_expected_candy
        // ...
      endclass: jelly_bean_sb_subscriber

      The class relation looks like this:
      Class Diagram

      1. Hello Keisuke,

        in the code line21, ” virtual function void write_expected_candy” , here can use “virtual task write_expected_candy” , is there any risk ?

        thank u so much

        1. The write() of uvm_analysis_imp_expected_candy is a function and calls the write_expected_candy() of jelly_bean_sb_subscriber. It is probably okay to define the write_expected_candy() as a task as long as it has no delay or synchronization in it, but then why do you want to make it a task in the first place?

  4. Mr.Keisuke Shimizu,

    This website is really awesome !!! I really appreciate your interest and work with my full heart..!!
    The concepts are like ice cake to eat….

  5. Dear Keisuke Shimizu,

    If I have a bigger environment (bench) owns several small environment …

    class bench extends uvm_env

    jelly_bean_env jb_env
    // and create it in the build phase

    endclass

    the connect will like that…
    jb_env.jb_agent.jb_ap.connect( bench_fc_sub.analysis_export ); // in the big_environment(bench)

    Right?

  6. Hi,

    I am a beginner.

    I understand there are ports/exports, analysis ports/exports, analysis fifos, tlm ports.
    Will be great if you can list out the difference between above and when to use what.

    Thanks in advance!

    1. It depends on what API (functions and tasks) you want to use. For example, if you just want to use put(), then uvm_blocking_put_port should be OK. But if you want to use try_put() too, then use uvm_put_port. Please see TLM 1 for more information.

  7. HI Keisuke, Nice blog.
    I have a doubt. In the last segment you mentioned argument of the connect() should be the provider.
    But in env you have used agnt.ap.connect(fc_sub.analysis_export). I was under assumption here agent analysis port is the provider.
    Let me know your thoughts.

  8. Hi Keisuke,

    Thanks for the great material. I had a question regarding monitor’s analysis port connection to agent’s port.
    Why can’t the connection be done this way?
    jb_ap.connect(jb_mon.jb_ap)
    Is it because it violates this relationship requirement of .connect method “If this port is an UVM_PORT type, the provider can be a parent port, or a sibling export or implementation port.”
    OR
    Because argument of .connect should be provider but here agent’s port might eventually get connected to a provider?

    Thanks,
    Pooja

    1. Donโ€™t confuse the functionality of the connect() by its name. It does not “connect” the two analysis ports by a wire. The purpose of the function is to specify who (eventually) provides the implementation of the function of the analysis port. Pretend the function is called is_provided_by. Then you would see jb_mon.jp_ap.is_provided_by( jb_ap ) makes sense, but jb_ap.is_provided_by( jb_mon.jb_ap ) doesnโ€™t.

Leave a Reply

Your email address will not be published. Required fields are marked *