UVM Tutorial for Candy Lovers – 19. Analysis FIFO

Last Updated: February 14, 2015

This post will explain how to use analysis FIFOs. Let’s assume I wanted a scoreboard that compares two streams of jelly beans; one stream is for “expected” jelly beans, the other is for “actual” jelly beans. Also assume the jelly beans are fed to the scoreboard asynchronously. To synchronize the jelly beans, I used two analysis FIFOs:

Asynchronous Jelly Bean Scoreboard
Asynchronous Jelly Bean Scoreboard

This is a sample code to implement the above scoreboard. The code should be self-explanatory. One note is that the get is a blocking task to get the next item from the FIFO (lines 43 and 44). Somehow this task is not documented in the UVM Class Reference, though.

February 14, 2015: Changed to explicitly use the get_peek_export instead of using undocumented get task.

When both the expected jelly bean and the actual jelly bean become available, the scoreboard compares them (line 45). Before finishing a simulation (extract_phase), the scoreboard checks whether there are leftover jelly beans in either FIFO. Isn’t it easy?

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class asynchronous_jelly_bean_scoreboard extends uvm_component;
   `uvm_component_utils( asynchronous_jelly_bean_scoreboard )
 
   uvm_analysis_export  #( jelly_bean_transaction ) expected_analysis_export;
   uvm_analysis_export  #( jelly_bean_transaction )   actual_analysis_export;
   uvm_tlm_analysis_fifo#( jelly_bean_transaction ) expected_jelly_bean_fifo;
   uvm_tlm_analysis_fifo#( jelly_bean_transaction )   actual_jelly_bean_fifo;
 
   // Function: new
   //-------------------------------------------------------------------------
   function new( string name, uvm_component parent );
      super.new( name, parent );
   endfunction: new
 
   // Function: build_phase
   //-------------------------------------------------------------------------
   virtual function void build_phase( uvm_phase phase );
      super.build_phase( phase );
 
      expected_analysis_export = new( "expected_analysis_export", this );
        actual_analysis_export = new(   "actual_analysis_export", this );
      expected_jelly_bean_fifo = new( "expected_jelly_bean_fifo", this );
        actual_jelly_bean_fifo = new(   "actual_jelly_bean_fifo", this );
   endfunction: build_phase
 
   // Function: connect_phase
   //-------------------------------------------------------------------------
   virtual function void connect_phase( uvm_phase phase );
      super.connect_phase( phase );
 
      expected_analysis_export.connect( expected_jelly_bean_fifo.analysis_export );
        actual_analysis_export.connect(   actual_jelly_bean_fifo.analysis_export );
   endfunction: connect_phase
 
   // Task: main_phase
   //-------------------------------------------------------------------------
   virtual task main_phase( uvm_phase phase );
      jelly_bean_transaction expected_jelly_bean;
      jelly_bean_transaction   actual_jelly_bean;
 
      super.main_phase( phase );
      forever begin
         expected_jelly_bean_fifo.get_peek_export.get( expected_jelly_bean );           actual_jelly_bean_fifo.get_peek_export.get(   actual_jelly_bean );         if ( expected_jelly_bean.compare( actual_jelly_bean ) == 0 ) begin
            `uvm_error( "main_phase", 
                        { "jelly bean mismatch: ",
                          "expected:", expected_jelly_bean.convert2string(),
                          "actual:",     actual_jelly_bean.convert2string() } )
         end
      end
   endtask: main_phase
 
   // Function: extract_phase - checks leftover jelly beans in the FIFOs
   //-------------------------------------------------------------------------
   virtual function void extract_phase( uvm_phase phase );
      jelly_bean_transaction jelly_bean;
 
      super.extract_phase( phase );
      if ( expected_jelly_bean_fifo.try_get( jelly_bean ) ) begin
         `uvm_error( "expected_jelly_bean_fifo", 
                     { "found a leftover jelly bean: ", jelly_bean.convert2string() } )
      end
 
      if ( actual_jelly_bean_fifo.try_get( jelly_bean ) ) begin
         `uvm_error( "actual_jelly_bean_fifo",
                     { "found a leftover jelly bean: ", jelly_bean.convert2string() } )
      end
   endfunction: extract_phase
endclass: asynchronous_jelly_bean_scoreboard

EDA Playground

You can view and compile the code on EDA Playground.

24 thoughts on “UVM Tutorial for Candy Lovers – 19. Analysis FIFO”

  1. Hi Keisuke,
    Thank you very much for your responses and solutions..!

    Though I have developed so much confidance in this env, I have some trouble in writing the scoreboard. In the examples you have provided, the expected values are known. But,

    1) If we have to compare the values that are obtained as a result of C-Code, then how do we do that. I know how to use DPI’s, but how do we synchronize the results,

    2) I am also interested to know how a generic scoreboard is written using predictors and compares.

    Can you please provide some different ways of implementing scoreboards along with their corresponding subscribers.

    I am sure this helps a lot of engineers who wish to really understand the key benifits of UVM.

    Thank You in Advance…!

    1. Sorry for this late reply. I was busy preparing my presentation for DVCon next week.

      Calling a C-function is nothing different from calling a SystemVerilog function from the caller’s point of view. You can call a C-function as if it is implemented in SystemVerilog. You can use any synchronization mechanisms for the C-function. For example, you can push the expected values generated by the C-function to a queue, then pop the values when actual values become available.

      Regarding your second question, I will post a blog article about a scoreboard using the uvm_tlm_generic_payload after DVCon.

  2. Hi Keisuke san,

    Thanks for your wonderful post. I want to compare the analysis port and with analysis_ffio. My understanding is analysis_port allows 1 port to many exports, and many ports to 1 export connection. Is this true for analysis_fifo?

    1. If it is true, what is the ordering of the fifo. Let’s say we have 2 producer’s port connects to the analysis_fifo. How do we use it?
    2. Can you give me a case where using analysis port is better than using analysis_fifo?
    3. In this example, you check if there are left-over in the fifo. For the case of analysis port, do we also need to check if the write function is completed before end of simulation

    Many thanks.

    1. Hi Loren,

      The uvm_analysis_port and the uvm_tlm_analysis_fifo are different things. Please see the diagram below. Although the uvm_tlm_analysis_fifo is not a uvm_component, it looks like a uvm_subscriber. Both uvm_tlm_analysis_fifo and uvm_subscriber have one uvm_analysis_imp. Here are my answers to your questions. 1) You could connect two uvm_analysis_ports to the uvm_analysis_imp of the FIFO, but in this case, whoever called write() first puts a transaction to the FIFO. 2) Since the write() is a function, you cannot consume time. The uvm_tlm_analysis_fifo is usually used when you want to process the written transactions later. 3) Left-over check depends on how a subscriber is implemented.
      analysis_fifo

  3. Hi Keisuke san,

    Thanks again for answering many of my questions.
    1. I am a bit confused on the uvm_phase. For example, in your example, the scoreboard is in forever loop. Also many times, driver is in forever loop waiting for the get_next_item. How did we reach to extract phase?
    2. How do we control when to terminate simulation. In your main phase, when we detect error, will the simulation be terminated? Can I let it run until I reach 10 errors?

    Many thanks.

      1. The uvm_phase monitors the number of objections. When nobody is raising an objection, all the processes started in the run_phase are killed and move to the next phase. This is how the forever loop is exited. For more detail, please see my new article inspired by your question!
      2. You can use +UVM_MAX_QUIT_COUNT option when you run a simulation. For example, if you set +UVM_MAX_QUIT_COUNT=10, then the simulation continues until it hits 10 errors.
  4. Hi Keisuke San,
    I really enjoyed this site. I learned a lot.
    1. I have a subtle question on the connect between agent and monitor. Since the agent is pasting monitor’s analysis_port, can I skip the analysis_port::type_id::create step in build and assign agent’s analysis_port = mon.analysis_port. Would that work also?
    2. In this example, does the main_phase requires to raise_objection at the beginning? Since the testbench is asynchronous, the line 44 get might take longer to complete. Will the simulation go to extract_phase before we get the actual result?
    3. In the TLM_analysis_fifo example, I saw the use of put_port, get_port, and analysis_port( mainly for put the item in fifo). In the case of analysis_port, I assume the write function is provided the by class. Is that right?
    4. In the tlm_analysis_fifo example, what is the use of the put_ap, and get_ap port in fifo?

    Many thanks.

    Loren

      1. It should work, but there is a difference. The original code creates two objects of uvm_analysis_port, whereas in your case, there is only one uvm_analysis_port object. The analysis port of the agent merely points to the analysis port of the monitor.
      2. analysis_ports

      3. Yes, I think it’s a good idea to raise/drop_objection in the main_phase.
      4. Yes, the uvm_analysis_port has the write function.
      5. Transactions passed through the put_export are sent out to the put_ap. Similarly, transactions passed through the get_peek_export are sent out to the get_ap. You can monitor the transactions passing through the FIFO via these analysis ports.
  5. Hello Keisuke Shimizu,

    I your reply to : In the tlm_analysis_fifo example, what is the use of the put_ap, and get_ap port in fifo?

    I didn’t understood the meaning of monitoring transactions passing through fifo? Can you please explain with in brief or provide some example. Basically I want to know how to use them.

    Thanks in advance.
    Shreemant

  6. Hi there,

    Does the item get popped out automatically when using “get_peek_export.get(item)”, or need to manually delete it from the fifo? If so, how?

    Thanks,
    Bayshore

      1. Hi Keisuke San,

        Thanks for this easily understandable example. I’m following this to implement a generic scoreboard for use across testbenches in our project.

        I would like to extend this example to implement a simple out-of-order scoreboard. Basically, an element from the expected_fifo has to be compared against a maximum of ‘N’ elements of the actual_fifo, by which time a match is expected to be found. I’m not sure how to do this using the limited methods that come with the uvm_tlm_analysis_fifo class.

        What approach would you recommend?

        Many thanks,
        Sharanya

        1. I would probably use a SystemVerilog queue instead of uvm_analysis_fifo. Here’s a skeleton.

          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
          33
          
          class asynchronous_jelly_bean_scoreboard extends uvm_component;
             `uvm_component_utils( asynchronous_jelly_bean_scoreboard )      
           
             uvm_analysis_export  #( jelly_bean_transaction ) expected_analysis_export; // same as before
             uvm_tlm_analysis_fifo#( jelly_bean_transaction ) expected_jelly_bean_fifo;
           
             uvm_analysis_imp#( jelly_bean_transaction, asynchronous_jelly_bean_scoreboard ) actual_analysis_export;
             jelly_bean_transaction actual_q[$]; // use a queue for the actual jelly beans
           
             const int N = 10; // number of out-of-order jelly beans allowed
           
             function void write( jelly_bean_transaction t );
                actual_q.push_back( t );
             endfunction: write
           
             task main_phase( uvm_phase phase );
                jelly_bean_transaction expected_jelly_bean;
                int qi[$];
           
                super.main_phase( phase );
                forever begin
          	 expected_jelly_bean_fifo.get_peek_export.get( expected_jelly_bean );
          	 wait( actual_q.size() > 0 ); // or > N?
          	 qi = actual_q.find_first_index with ( item.compare( expected_jelly_bean ) );
          	 if ( qi.size() == 0 || qi[0] > N ) begin // not found, or found but out of range
          	    `uvm_error( "main_phase", "cannot find a matched jelly bean" )
          	 end else begin // found match
          	    actual_q.delete( qi[0] ); // delete matched jelly bean from the queue
          	 end
                end
             endtask: main_phase
          // ...
          endclass: asynchronous_jelly_bean_scoreboard
  7. Hi Keisuke ,
    can you please provide me a scoreboard template for if we are getting expected data as from C-function , how to call C in sv?
    and how to connect and how to compare the actual as from SV and expected as from C .

    Please provide me the source

    Regards,
    Jayprakash

  8. Hi Keisuke,

    I am not clearly able to understand below points
    1. How the FIFO is getting the transaction.
    2. Where the expected_analysis_export and actual_analysis_export is connected with analysis_port in testbench. Do they get transaction first and then it is put to FIFO
    3. What does get_peek_export.get task do

    Please answer these.

      1. The FIFO gets the transaction using the FIFO’s write function.
      2. Typically a monitor creates transactions and put them to the FIFO.
      3. The get task gets a transaction from the FIFO. If no transaction is available, then the task is blocked.
  9. Hi Keisuke…
    I have a doubt that… suppose we are having two monitor , one is for actual item and one is for expected. In the both monitor we call the write function like mon_port.write(data_item) … means there will be two write function one is for actual and one is for expected monitor transaction.

    In line 43 and 44, you are taking the transaction.
    My question is how can we know that , which monitor’s write function is connected to which fifo.
    In other way we can say that, which jelly bean, expected or actual (from line 43 & 44) is connected to which monitor write function(expected monitor write function or actual monitor write function).

    1. You need to specify the connection. Suppose you instantiate the asynchronous_jelly_bean_scoreboard, expected_monitor, and actual_monitor in your_env. In the connect_phase function, specify the connection like this:

      class your_env extends uvm_env;
        asynchronous_jelly_bean_scoreboard sb;
        expected_monitor exp_mon;
        actual_monitor   act_mon;
       
        virtual function void connect_phase( uvm_phase phase );
          super.connect_phase( phase );
          exp_mon.mon_port.connect( sb.expected_analysis_export );
          act_mon.mon_port.connect( sb.  actual_analysis_export );
        endfunction: connect_phase
        // ...
      endclass: your_env

      Connecting the monitors

Leave a Reply