UVM Tutorial for Candy Lovers – 3. Transactions and Sequences

Last Updated: June 29, 2014

This post will provide an explanation on the SystemVerilog code itself. Please see Recipe for the class diagram.

Transactions

Jelly-Bean Transaction

The jelly_bean_transaction class defines the jelly-bean recipe based on flavor, color, and other characteristics. It also has the property called taste to hold the reaction of the jelly_bean_taster (DUT) to the flavor. The relationship between the flavor and color is defined in the constraint block (line 12). For example, if the flavor is apple, the color must be red or green (not blue).

Update (April 2, 2014): In this example, we used so-called UVM field macros (lines 23 to 27) to save the effort of writing “do-hook” functions. I would recommend writing your own “do-hook” functions instead of using these macros once you become familiar with UVM. For more information, please see Field Macros and “Do” Hooks.

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
class jelly_bean_transaction extends uvm_sequence_item;
   typedef enum bit[2:0] { NO_FLAVOR, APPLE, BLUEBERRY, BUBBLE_GUM, CHOCOLATE } flavor_e;
   typedef enum bit[1:0] { RED, GREEN, BLUE } color_e;
   typedef enum bit[1:0] { UNKNOWN, YUMMY, YUCKY } taste_e;
 
   rand flavor_e flavor;
   rand color_e  color;
   rand bit      sugar_free;
   rand bit      sour;
   taste_e       taste;
 
   constraint flavor_color_con {      flavor != NO_FLAVOR;      flavor == APPLE     -> color != BLUE;      flavor == BLUEBERRY -> color == BLUE;   } 
   function new(string name = "");
      super.new(name);
   endfunction: new
 
   `uvm_object_utils_begin(jelly_bean_transaction)
      `uvm_field_enum(flavor_e, flavor, UVM_ALL_ON)
      `uvm_field_enum(color_e, color, UVM_ALL_ON)
      `uvm_field_int(sugar_free, UVM_ALL_ON)
      `uvm_field_int(sour, UVM_ALL_ON)
      `uvm_field_enum(taste_e, taste, UVM_ALL_ON)
   `uvm_object_utils_end
endclass: jelly_bean_transaction

Sugar-Free Jelly Bean

The jelly_bean_transaction class can be extended to various classes. For example, to create only sugar-free jelly beans, the child class can define the constraint as shown in line 4.

1
2
3
4
5
6
7
8
9
10
11
class sugar_free_jelly_bean_transaction extends jelly_bean_transaction;
   `uvm_object_utils(sugar_free_jelly_bean_transaction)
 
   constraint sugar_free_con {      sugar_free == 1;   } 
   function new(string name = "");
      super.new(name);
   endfunction: new
endclass: sugar_free_jelly_bean_transaction

Sequences

One Jelly Bean

The sequence creates the recipes of the jelly beans being generated. The first sequence is the simplest, as it is designed to create a single jelly bean. The line 10 creates a single jelly-bean transaction (a recipe), then the line 12 randomizes the recipe.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class one_jelly_bean_sequence extends uvm_sequence#(jelly_bean_transaction);
   `uvm_object_utils(one_jelly_bean_sequence)
 
   function new(string name = "");
      super.new(name);
   endfunction: new
 
   task body();
      jelly_bean_transaction jb_tx;
      jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));      start_item(jb_tx);
      assert(jb_tx.randomize());      finish_item(jb_tx);
   endtask: body
endclass: one_jelly_bean_sequence

Same-Flavored Jelly Beans (Sequence of Transactions)

Now let’s create the multiple jelly beans of the same flavor. The number of jelly beans being created is specified with the class property called num_jelly_beans. The line 4 constrains the num_jelly_beans to be between 2 and 4. The line 14 creates a single jelly bean, and the line 15 randomizes its color and flavor. With this jelly bean as the standard, the line 18 creates jelly beans of the same flavor, as many as specified by the num_jelly_beans. In-line constraint on the line 21 guarantees the same flavor to be generated.

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
class same_flavored_jelly_beans_sequence extends uvm_sequence#(jelly_bean_transaction);
   rand int unsigned num_jelly_beans; // knob
 
   constraint num_jelly_beans_con { num_jelly_beans inside { [2:4] }; } 
   function new(string name = "");
      super.new(name);
   endfunction: new
 
   task body();
      jelly_bean_transaction           jb_tx;
      jelly_bean_transaction::flavor_e jb_flavor;
 
      jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));      assert(jb_tx.randomize());      jb_flavor = jb_tx.flavor;
 
      repeat (num_jelly_beans) begin         jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));         start_item(jb_tx);         assert(jb_tx.randomize() with { jb_tx.flavor == jb_flavor; });         finish_item(jb_tx);      end   endtask: body
 
   `uvm_object_utils_begin(same_flavored_jelly_beans_sequence)
      `uvm_field_int(num_jelly_beans, UVM_ALL_ON)
   `uvm_object_utils_end
endclass: same_flavored_jelly_beans_sequence

Gift-Boxed Jelly Beans (Sequence of Sequences)

The code below demonstrates how to create multiple-flavored jelly beans. The number of flavors is specified with the class property called num_jelly_bean_flavors (line 2). On the 4th line, the sequence randomly picks two to three flavors. The main part of the sequence is the repeat block (from the line 12), where a sequence of same_flavored_jelly_beans_sequences is created.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class gift_boxed_jelly_beans_sequence extends uvm_sequence#(jelly_bean_transaction);
   rand int unsigned num_jelly_bean_flavors; // knob 
   constraint num_jelly_bean_flavors_con { num_jelly_bean_flavors inside { [2:3] }; } 
   function new(string name = "");
      super.new(name);
   endfunction: new
 
   task body();
      same_flavored_jelly_beans_sequence jb_seq;
      repeat (num_jelly_bean_flavors) begin         jb_seq = same_flavored_jelly_beans_sequence::type_id::create(.name("jb_seq"), .contxt(get_full_name()));         assert(jb_seq.randomize());         jb_seq.start(.sequencer(m_sequencer), .parent_sequence(this));      end   endtask: body
 
   `uvm_object_utils_begin(gift_boxed_jelly_beans_sequence)
      `uvm_field_int(num_jelly_bean_flavors, UVM_ALL_ON)
   `uvm_object_utils_end
endclass: gift_boxed_jelly_beans_sequence

This post has an emphasis on the transactions and sequences in the jelly-bean verification environment. The next post will provide an explanation on the verification components.

Get source code

106 thoughts on “UVM Tutorial for Candy Lovers – 3. Transactions and Sequences”

  1. Hello Keisuke,

    Congratulations for these articles about UVM. I wonder if there is a reason why you do not use the uvm_do macro in your sequence.

    Best regards,
    Cedric

      1. If you want fine control you can use `uvm_do_with or `uvm_do_on_with. Can you explain about what kind of fine control you are talking about ?

        1. Hi Digesh,

          For example, if you want to display the jb_tx just after you randomize it but before calling the finish_item(), then you might want to code like what I did, instead of using these macros. What I meant by “fine control” was anything beyond the macros provide.

  2. Hello keisuke,
    i belive the UVM automation macro which you have used is for the components and not for sequence…It should have been `uvm_sequence_utils(sequence_name,sequencer_name)…………

  3. Very helpful seeing so many different examples. Not much on explanation, but that wasn’t my problem…I needed to see some understandable examples that made my understand of the explanations I have read elsewhere make sense. Thanks much for the posts!!!

  4. I’m a bit new to uvm, what advantage does registering all of your fields for automation have.
    i.e why not just use
    ‘uvm_object_utils(jelly_bean_transaction)

      1. Ah I see, and understand.
        Your examples are superb.

        Would you consider adding in a reset, to demonstrate how a dut reset can be detected by transactors and passed to subscribers/scoreboards. This would include an elegant way of terminating an active sequence if one was running.

        Keep up the good work

        Steve

      2. Hello Shimizu-san,

        Thank you very much for your great tutorial.
        Could you please explain the advantage, application of implementing the “do” hooks for rand variables in sequence?
        Because I think your example runs well without the implementation of “do” hooks ( use `uvm_object_utils(jelly_bean_transaction) as Steve already mentioned )

        P.s about the advantage of “do” hooks in transaction class (sequence item) I already see but I’m not clear about advantage of “do” hooks in sequence.

        Thank you very much

  5. Your tutorial is very helpful. I have been studying SV and UV for the past couple of weeks and your tutorial has made a lot of the concepts start to “click”. Thank you very much.

  6. Hi Keisuke, thanks for this tutorial. Can you please explain what m_sequencer is in “jb_seq.start(m_sequencer);”. It looks that m_sequencer is used to reference a default sequencer. In the case, the pkg has multiple sequencers, how do I specify a particular sequencer to be run?

    Thanks!

    1. Hi Kaushal,

      Let’s say you created a gift_boxed_jelly_beans_sequence in your test as follows:

      // in your test class
      gift_boxed_jelly_beans_sequence gb_jb_seq;
      gb_jb_seq = gift_boxed_jelly_beans_sequence::type_id::create( "gb_jb_seq" );

      Then, you start the sequence on a sequencer, say jb_seqr.

      gb_jb_seq.start( jb_env.jb_agent.jb_seqr );

      The start task will set the m_sequencer to the sequencer you specified. In this case, the m_sequencer is set as the jb_env.jb_agent.jb_seqr. In other words, the user of the gift_boxed_jelly_beans_sequence class specifies the m_sequencer.

      [BEHIND THE SCENES]
      Strictly speaking, the start task does not set the m_sequencer by itself. Instead, the task calls set_item_context() function of uvm_sequence_item, which in turn calls set_sequencer() function of the same class. The set_sequencer() is the function that actually sets the m_sequencer.

      1. Kaushal,
        Still not clear. If start task set the m_sequencer to the one you specified, then in jb_seq.start(m_sequencer), m_sequencer is pointed to itself, m_sequencer ? This seems a dead loop. Can you help me on understanding this ?

        1. The m_sequencer points to the sequencer the gift_boxed_jelly_beans_sequence is running on. By calling jb_seq.start( m_sequencer ), the jb_seq uses the same sequencer as its parent sequence (gift_boxed_jelly_beans_sequence) does. There is no loop. Am I making myself clear?

          1. I agree with Bo, you need to set somewhere a m_sequencer!=uvm_sequencer before using it. Maybe it is hidden somewhere a set_sequencer in your example that sets the default value of m_sequencer. If nothing is set, then i guess m_sequencer points to null.

             virtual function void set_sequencer(uvm_sequencer_base sequencer);
                   m_sequencer = sequencer;
                   m_set_p_sequencer();
             endfunction
            
  7. Hi Keisuke,

    I have a general question on UVM, Can you please explain the usage and importance of p_sequencer and m_sequencer in UVM??

    1. Hi Vittal,

      Each sequence has the m_sequencer property that points to the sequencer on which the sequence is running. The m_sequencer can be used to access configuration information and other resources in the component hierarchy from the sequence.
      For example, if you start a sequence called foo_sequence on a sequencer called bar_sequencer, foo_sequence.start( bar_sequencer ), then the start() task will assign foo_sequence.m_sequencer to be the bar_sequencer. Now the foo_sequence can use the m_sequencer to access the component hierarchy. For example, you can get the full hierarchical name of the sequencer by calling m_sequencer.get_full_name(). Note that the type of the m_sequencer is uvm_sequencer_base. This means if you have your own sequencer and you have defined new properties in your sequencer, you cannot access them through the m_sequencer because it does not know them. Now the p_sequencer comes to the rescue.

      The p_sequencer is instantiated if you use `uvm_declare_p_sequencer macro in your sequence.
      For example, you can use the macro in the foo_sequence like this:

      class foo_sequence extends uvm_sequence #( my_transaction );
        `uvm_object_utils( foo_sequence )
        `uvm_declare_p_sequencer( bar_sequencer_class )
        // ...

      The macro declares the p_sequencer using the sequencer type you provided (in this case, bar_sequencer_class). Then the macro defines the function called m_set_p_sequencer which casts the m_sequencer to the p_sequencer. Now you can access the properties defined in the bar_sequencer_class through the p_sequencer like p_sequencer.my_property from inside the body task of the foo_sequence.
      How m_set_p_sequencer is called
      Let me know if you have further questions.

      1. Hi Keisuke Shimizu,
        I am new to UVM.I have a doubt ,can i have a p_sequencer in single agent,As you have told above that if we have own sequencer we can use p_sequencer.As my own sequencer will be extending from the uvm_sequencer class so it can have access to all configuration details .Then why p_sequencer is required.

        1. Yes, once you have a handle to your own sequencer, you can access your own properties in it. The p_sequencer is the handle to your own sequencer from your sequence. For example, foo_sequence accesses my_property in bar_sequencer using the p_sequencer handle as shown below.
          m_sequencer and p_sequencer

      2. Thanks for the explanation Keisuke. One thing I’m not clear is, why are we writing a macro for p_sequencer in the sequence? Same thing can be done with “seqr_class_name hadnle_name”, and call handle name p_sequencer.

        1. You could do that. Make sure you assign your p_sequencer before you call start, then.

          your_sequence.p_sequencer = your_sequencer;
          your_sequence.start( your_sequencer );
        1. The type of m_sequencer is uvm_sequencer_base, whereas the type of p_sequencer is bar_sequencer_class in this case, which is a subclass of uvm_sequencer_base. You cannot assign the handle of base class to the handle of its subclass unless you cast it.

  8. Thanks alot Keisuke for basic understanding about m_sequence and p_sequencer

    I have two agents and they both are working on different sequences i.e. seq1,seq2 which consumes different sequence item i.e. seq_item1,seq_item2,Now i want to start sequence (seq1) on sequencer-1(seqr1) and sequence-2 on sequencer-2 from test,could it possible like below:

    class my_test extends uvm_test;
    
    task run_phase
    seq1 = sequence1::type_id::create("seq1");
    seq2 = sequence2::type_id::create("seq2");
    fork
    seq1.start(envh.agent1h.seqr1);
    seq2.start(envh.agent2h.seqr2);
    join
    
    endtask
    

    Moreover,Inside both sequences task body,i am using `uvm_do_with macros (instead start_item etc.)

    I am not able to start my sequence using above method could you please guide me on this front.

    thanks

    1. Your code looks fine. I assume you were able to start the sequences but the sequences did not finish. Does your driver call item_done() or put()? The `uvm_do_with() macro might be stuck at wait_for_item_done().

  9. Yes,item_done is there but that piece of code is not getting executed,but i am getting ‘fatal error: bad handle reference’ in test class(my_test).moreover, sequences are registered with uvm_object_utils and i am not using virtual sequence or sequencer.
    kindly suggest forward path.

  10. Hi Keisuke,

    a little question about class same_flavored_jelly_beans_sequence :

    ” jelly_bean_transaction::flavor_e jb_flavor;”

    is it correct? don’t need to declare flavor_e as static property in class jelly_bean_transaction ?

    Tong

  11. Hello!

    I noticed that in the section “Same-Flavored Jelly Beans”, inside of the repeat cycle, every time a new transaction is created, the methods start_item() and finish_item() are called in the code (lines 20 and 22) but before that, no start_item() and finish_item() were called for the single transaction. Why?

    1. The lines 14 to 16 are used only for randomizing the flavor. Then, the line 21 uses this flavor in the in-line constraint so that all jelly beans have the same flavor. It is fine to “sandwich” the line 15 with start_item() and finish_item() as you mentioned. That will create (num_jelly_beans+1) jelly beans, though.

  12. HI Keisuke,

    In the Same-Flavored Jelly Beans example, you specified “The line 14 creates a single jelly bean, and the line 15 randomizes its color and flavor”. But in the transaction item there are other properties or types like taste, sugar free etc. defined as rand. Won’t they also get randomized.

    Thank You in Advance..!

  13. Hi Keisuke,
    I found this example very helpful and I will like to see some more complex sequence scenarios.

    I am trying to build a AHB vip, just to get hold of the basics of UVM. I am finding difficulty in randomizing the various AHB protocol signals. For e.g, I can randomize HBURST type , HSIZE type and the starting address. But how do i randomize or create the HTRANS, HADDR for the same burst type. Based on the burst type, my HADDR values will change; similarly the HTRANS values. I am presently handling such things in the driver by generating a loop for the number of beat transfer as dictated by HBURST type. What will be the best way to handle this?

    1. I think you are doing fine. You don’t need to randomize the HADDR for each beat because the driver can calculate the next HADDR based on the other information. For the HTRANS, you can also let the driver insert random BUSY cycles. However, this approach will not give you the controllability of the delays. I would create an array of BUSY cycles in the AHB transaction so that I can control the BUSY cycle of each beat. For example, you could have rand int unsigned busy_cycles[]; in the transaction. You should also have a constraint for the busy_cycles[] so that you can have no BUSY-, some BUSY-, or many BUSY-cycle scenarios.

  14. Hello,

    task body();
      same_flavored_jelly_beans_sequence jb_seq;
      repeat (num_jelly_bean_flavors) begin
      jb_seq = same_flavored_jelly_beans_sequence::type_id::create(.name("jb_seq"), .contxt(get_full_nam()));          assert(jb_seq.randomize());
     jb_seq.start(.sequencer(m_sequencer), .parent_sequence(this));
     end   
    endtask: body
    

    why are you using the start method within the sequence, I think its good to do that in test. Correct me if I am wrong

    Regards

    Shreemant

    1. You can start another sequence from within a sequence. In this example, jb_seq is started using the same sequencer (m_sequencer) that the gift_boxed_jelly_beans_sequence is running on.

      1. In that code Shreemant mentions, you are doing a randomize too.

        When you call the start on jb_seq, it’s the same_flavored_jelly_beans_sequence that executes, and it does the start_item, randomize, then finish_item. Why are you doing the randomize below, prior to jb_seq.start() if that’s done in the same_flavored_jelly_beans_sequence?

        task body();
          same_flavored_jelly_beans_sequence jb_seq;
          repeat (num_jelly_bean_flavors) begin
          jb_seq = same_flavored_jelly_beans_sequence::type_id::create(.name("jb_seq"), .contxt(get_full_nam()));          
        
          assert(jb_seq.randomize());
        
         jb_seq.start(.sequencer(m_sequencer), .parent_sequence(this));
         end   
        endtask: body
        
  15. Hi Keisuke,

    I have question that the sequence same_flavored_jelly_beans_sequence is not register with any of the sequenceas there is no uvm_sequence_utils or uvm_object_utils so when the sequence start by calling jb_seq.start(.sequencer(m_sequencer), .parent_sequence(this)); on which sequencer will in be run will it run on the parent sequencer
    If i want to run the sequence in a particular sequencer then I need to register with that particular sequencer in the sequence right ? please correct me it iam wrong

    Thanks
    Aditya

    1. Hi Aditya,

      1. The same_flavored_jelly_beans_sequence uses `uvm_object_utils macros. Please see the lines 26 to 28.
      2. However, the `uvm_object_utils macro is nothing to do with registering the sequence to a sequencer. The macro registers the same_flavored_jelly_beans_sequence type with the UVM factory.
      3. The same_flavored_jelly_beans_sequence will be run on the same sequencer the gift_boxed_jelly_beans_sequence runs on. Please see the diagram below.
      4. You specify which sequencer to use when you call start(). In the example below, the sequencer jb_env.jb_agent.jb_seqr is used. Please see this article for how to start the gift_boxed_jelly_beans_sequence.

      Start sequences

      1. Hi kesuki,
        Thanks for your tutorial and its very useful. If we are having our own sequencer where we have defined new properties then can we use the same m_sequencer – “jb.seq.start (m_sequencer)”?? Or we need to use p_sequencer ?? If we need to use p_sequencer then can you please tell us how to use that. Please correct me if I am wrong.

        Thanks
        Sharath

    1. The jelly_bean_transaction is a uvm_sequence_item. That means it is a uvm_object, but not a uvm_component. Therefore it does not have a hierarchical name. .contxt( get_full_name() ) specifies the context (hierarchical name) of the jelly_bean_transaction. By specifying the context, we can override a specific instance of jelly_bean_transaction later. You can use any string as the context, but in our one_jelly_bean_sequence case, we used the return value of the get_full_name function. The get_full_name function is defined in the uvm_sequence_item class (an ancestor class of the one_jelly_bean_sequence), and it returns a string that concatenates the full name of the sequencer this one_jelly_bean_sequence was started on and the name of this one_jelly_bean_sequence instance.

  16. Hi keisuke…

    I am new to UVM…why sequence is required? I mean if the testcase is simple then driver and interface combination can solve my query…please help like how sequence and driver ia differentiated.

    1. Traditional driver-BFMs use functions and tasks to drive DUT pins. One of the disadvantages of the BFMs is that it is not easy to randomize stimulus. UVM uses sequence objects as the scenario of stimulus. Since they are objects, it is easy to randomize and we often obtain interesting scenarios we have not thought of. UVM drivers get the sequences and converts them into pin-level activity.

  17. Hi keisuke,
    In jelly_bean_transaction, taste_e is declared so what is the use of that?
    Since taste is decided by jelly_bean_taster. Correct me if I am wrong.

    Regards,
    Sharath H V

  18. Hi Keisuke,

    Thank you so much for such a wonderful and detailed tutorial.

    Can you tell me the difference between uvm_object_utils and uvm_component_utils. And how to decide which one to use where?

    Thank you.

    1. Both macros expand to define the utility functions for factory operation. If you are interested, please see Inside Candy Factory to look under the hood, although you don’t have to know the details. If your class extends uvm_component, then use `uvm_component_utils, otherwise, use `uvm_object_utils. For example, in our tutorial, we used `uvm_component_utils for jelly_bean_test, jelly_bean_env, jelly_bean_agent, and jelly_bean_driver, which are sub-classes of uvm_component. On the other hand, we used `uvm_object_utils for jelly_bean_transaction and sequences, which are uvm_object, but not uvm_component.

    1. All `uvm_do* macros call `uvm_do_on_pri_with macro internally. The `uvm_do_on_pri_with macro takes four arguments (SEQ_OR_ITEM, SEQR, PRIORITY, and CONSTRAINTS). Each `uvm_do* macro specifies a subset of the arguments as follows:

      Macro\Argument SEQ_OR_ITEM SEQR PRIORITY CONSTRAINTS
      `uvm_do SEQ_OR_ITEM m_sequencer -1 {}
      `uvm_do_with SEQ_OR_ITEM m_sequencer -1 CONSTRAINTS
      `uvm_do_pri SEQ_OR_ITEM m_sequencer PRIORITY {}
      `uvm_do_pri_with SEQ_OR_ITEM m_sequencer PRIORITY CONSTRAINTS
      `uvm_do_on SEQ_OR_ITEM SEQR -1 {}
      `uvm_do_on_with SEQ_OR_ITEM SEQR -1 CONSTRAINTS
      `uvm_do_on_pri SEQ_OR_ITEM SEQR PRIORITY {}
      `uvm_do_on_pri_with SEQ_OR_ITEM SEQR PRIORITY CONSTRAINTS

      The `uvm_do_on_pri_with creates a SEQ_OR_ITEM, randomize it with CONSTRAINTS, and start it on the SEQR with specified PRIORITY.

  19. Hi Keisuke,

    I have a question about the constraints.

    say I have the following examples:
    =============================

    class A_seq extends uvm_sequence;
       rand bit [2:0] data_master;
    
    endclass
    
    
    class B_seq extends umv_sequence
      rand bit [2:0] data_master;
    
      constraint c {data_master = 1}; 
    
      virtual task body()
    
       A_seq a_seq;
    
    begin
    
    uvm_do_with (a_seq, {a_seq.data_master == data_master})
    
    end
    endtask
    

    ===============================

    so when the simulation starts, the data_master in A_seq will be 1. is that correct?

    thanks

    1. Your `uvm_do_with macro does not constrain the data_master. Here is why. The `uvm_do_with macro expands to a randomize() function:

      a_seq.randomize() with { a_seq.data_master == data_master; };

      The names in the in-lined constrait block are searched first in the scope of the randomize() with object class (A_seq) followed by the scope containing the method call (B_seq). This means both data_master in the above constraint refer to the same variable in the A_seq, so you do not constrain them. To solve this issue, you can use local:: scope resolution:

      a_seq.randomize() with { data_master == local::data_master; }; 
      // means a_seq.data_master == b_seq.data_master

      This constrains the data_master of a_seq to be equal to the value of data_master of B_seq.
      You can see the entire code here.

      1. Hi Keisuke,

        It means, I can write above code as below rather using of “local::scope”
        a_seq.randomize() with { a_seq.data_master == this.data_master; };

        Thanks
        RSS

        1. Or we can write like this ?
          uvm_do_with (a_seq, {a_seq.data_master == this.data_master})

          Thanks
          RSS

          1. Thanks a lot. Now my doubts are clear.
            I am new in uvm but after studying your articles and queries making me confident daily.

  20. Hi Keisuke,

    Do you know how to debug a test which is getting hung? I am trying one test but the test didn’t actually start running, it stuck at timestamp 0. Also there is no error messages in the NCSIM.

    UVM_INFO if_id_driver.sv(93) @ 0: uvm_test_top.u_tb.u_if_id_agent_0.u_driver [if_id_driver] Initiated interface
    UVM_INFO if_id_bfm.sv(146) @ 0: i_if_id_bfm_0.m_api [IF_ID_BFM] wait_for_reset task called
    UVM_INFO if_id_bfm.sv(150) @ 0: i_if_id_bfm_0.m_api [IF_ID_BFM] wait_for_reset: seen reset go active
    UVM_INFO uvm_sequencer_base.svh(1385) @ 0: uvm_test_top.u_tb.u_ictrl_l2cache_agent_0.u_sequencer [PHASESEQ] No default phase sequence for phase 'run'
    UVM_INFO ictrl_l2cache_monitor.sv(113) @ 0: uvm_test_top.u_tb.u_ictrl_l2cache_agent_0.u_monitor [ictrl_l2cache_monitor] Starting run phase
    

    The above is the piece of message from the NCSIM.

    thanks

    Steve

  21. Hi Keisuke,

    I read p sequencer and m sequencer concept, still i have not understanding can you please elaborate?

    –Taahir

  22. Hi Keisuke

    I am still in doubt about the difference between m_sequencer and p_sequencer?
    Can you give any specific example where both have their significance?

    ~Taahir

  23. Hi Keisuke,

    I have some doubt in same flavoured jelly beans,
    In line 16, if you defined jb_flavor = jb_tx.flavor; then in line 21 why in constraint again you are defining -> with { jb_tx.flavor == jb_flavor; });

    Can you explain about the 16 and 21 line in – > Same-Flavored Jelly Beans (Sequence of Transactions)

    1. The line 19 creates a new jelly_bean_transaction object. This means that the jb_tx on line 16 and the jb_tx on line 21 refer different objects even though they have the same name. The line 21 constrains that the newly created object has the same flavor as the jb_tx created on line 14.

  24. Hi Keisuke
    I really enjoy learning from your code. I do have one question on gift_boxed_jelly_beans_sequence. I wonder, after repeating num_jelly_bean_flavors times, the jb_seq might produce same favor. I saw “randc flavor_e tmp_flavor” from complete src code, but it was not used in task body. how can we pass tmp_flavor to jb_seq, so that it will not get repeated flavors

    Thanks
    Scott

  25. Hello,
    I’ve been struggling with a basic sequence problem: I have the following typical packages apb_pkg, env_pkg, seq_pkg, & test_pkg.
    Package apb_pkg has all the code for the apb agent, in it I have sequence apb_io which has task read and virtual task body, also the sequencer is defined.

    Package seq_pkg imports apb_pkg::* and extends apb_io to apb_io1 and defines body task which calls read.

    The env package has the standard build and connect phase functions.

    In test package is the extended uvm_test where instance of apb_io1 is defined, the build phase has its create & run phase has the instance of apb_io1 .start(sequencer).

    When I simulate the above everything works fine, the read in apb_io1 executes as expected.

    Now in seq_pkg if I change apb_io1 so that it extends uvm_sequence and I create a instance
    apb_io ‘apb = new(apb);
    and call apb.read in the body task, the code compiles but I get a fatal error:
    “neither the item’s sequencer nor dedicated sequencer has been supplied to start item in apb”

    I’m wondering if I need to use something called set_sequencer?

    The reason I’m using an instance apb_io instead of extending apb_io is that I intend to create a instance of a different agent in apb_io1 sequence and perform calls to its methods. Is this methodology unworkable ?
    Thanks for any guidance,
    –Ross

    1. I believe I need to change my apb_pkg sequence read and write tasks to make them stand alone class object including the sequencer_base.

      1. Hi,
        In seq_pkg apb_io1 class I create a instance of the sequencer then in my uvm_test I assign the target sequencer, so I’m past my stated problem “neither the item’s sequencer …”
        Currently debugging why the arguments are ‘x’ at the apb_pkg::apb_io.
        Thank you,
        –Ross

  26. Hi,

    I just wanted to execute body of sequence.
    ————————–

    class apb_write extends uvm_sequence #(utc_transaction);
    utc_transaction item;
    rand logic[31:0] data;
    rand logic[15:0] addr;
     
     `uvm_object_utils_begin(apb_write)
     `uvm_object_utils_end
     `uvm_declare_p_sequencer(utc_tb_sequencer)
    
    function new(string name="apb_write_command_seq");
        super.new(name);
        addr=0;
        data='h5555;
    endfunction
    
     virtual task body();
                              
                              item = utc_transaction::type_id::create("item");
                              start_item(item);
                              item.randomize() with {APB_COMMAND ==WRITE; i_APB_UTC_ADDR  == addr;  i_APB_UTC_WDATA == data;};
                              finish_item(item);
    
     
     endtask
    endclass
    

    From testcase.sv

        apb_write_i.start(null);
    

    i am gettign this fatal error

    "neither the item's sequencer nor dedicated sequencer has been supplied to start item in apb_write_i"
    

    1.I dont want to use seq.start(Sequencer);
    Can i use seq.start(null)?If yes then how can i assign teh sequencer to that sequence?
    I wanted to execute the body of sequence by taking utc_tb_sequencer as its p_sequencer
    How can i do that?

    Thanks in advance?

    1. As far as I know, the start task always uses the given sequencer. So, if the sequencer is null, you get the fatal error when the start_item is called. I’m still not really clear why you don’t want to do like this:

      utc_tb_sequencer utc_tb_seqr = new( "utc_tb_seqr" );
      seq.start( utc_tb_seqr );
  27. Hello sir,

    You are really savior because I’m finding an easy way to learn verification and UVM.
    I have 1 question: In your code you use some namespace “type_id” and “create” like below
    jb_seq = same_flavored_jelly_beans_sequence::type_id::create(.name(“jb_seq”), .contxt(get_full_name()));

    But I don’t know where to find the description about “type_id”. I mean in the website below mentions about “create” but not sure how do you know about “type_id”?
    https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.1d/html/

    I can read UVM macros to find out but I wonder if we have documents about it or not?

  28. Hi Keisuke,

    Let’s say I modify your jelly_bean_transaction as such:
    1. Let’s change flavor into a dynamic array (i.e. multi-flavor jelly bean)
    2. Let’s add a property called “location” that’s of enum type. The location specifies which factory (let’s say you have multiple factories across the world) you are making your jelly bean at. The location will determine the number of flavors a single jelly bean can create in that location’s factory. For example, you have a factory at Los Angeles and at that factory only 3-flavor jelly beans are being made there.
    3. From (2), the flavor dynamic array can now be created with size that matches the location.

    Now my question is this:
    If I was to use SystemVerilog without UVM, this is easy to implement because in my transaction class, I would have a constructor (i.e. new) that accept 1 argument called “location”. Why did I set the argument to be “location”? It is so I, as the user, can choose which location I want to test.
    Within the constructor, I can have a case statement that checks the location and then allocate the appropriate size for the dynamic array accordingly.
    So how would I implement this in UVM? I see that in uvm_object, the only argument that “new” accepts is “name”.

    Thank you.

  29. Hi Keisuke,

    Another question if I may.

    I see that you have made a separate class for sugar-free jelly bean and in that class you have a single constraint. However, usually in a complex system, you might have tens or hundreds of these kind of “more restricted” class. In your example, you can apple-flavor-only jelly bean, blue-color-only jelly bean and so on… That basically means you’ll have tens or hundreds files for each type of special jelly bean you make.

    To solve this case, I would usually make these variables (i.e. flavor, color, etc..) as command line variable. For example, +JELLY_BEAN_FLAVOR=APPLE. Correspondingly, in the sequence class, when you are randomizing the transaction using “randomize() with {}”, you can put if-statement to see if these command line arguments are set, and if set, you can constrain the flavor, color, etc… accordingly.

    Is this how you would go about solving this too?

    Thank you.

    1. I agree using plusargs would be a convenient way to specify constraint knobs. I usually define the knobs in a transaction class itself so that the user of the class (a sequence, for example) does not have to use “randomize() with {}“. The reason I don’t use “randomize with” is that we cannot override the constraints defined by “with” as they are hard-coded. The following is what I would do for the jelly_bean_transaction class to make it more controllable.

      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
      71
      72
      
      class jelly_bean_transaction extends uvm_sequence_item;
         typedef enum bit[2:0] { NO_FLAVOR, APPLE, BLUEBERRY, BUBBLE_GUM, CHOCOLATE } flavor_e;
         typedef enum bit[1:0] { RED, GREEN, BLUE } color_e;
         typedef enum bit[1:0] { UNKNOWN, YUMMY, YUCKY } taste_e;
       
         rand flavor_e flavor;
         rand color_e  color;
         rand bit      sugar_free;
         rand bit      sour;
         taste_e       taste;
       
         // knobs
       
         byte	unsigned apple_wt      = 10;
         byte	unsigned blueberry_wt  = 10;
         byte	unsigned bubble_gum_wt = 10;
         byte	unsigned chocolate_wt  = 10;
       
         byte	unsigned red_wt   = 10;
         byte	unsigned green_wt = 10;
         byte	unsigned blue_wt  = 10;
       
         byte	unsigned sugar_free_pct = 50; // between 0 and 100
         byte	unsigned sour_pct       = 50;
       
         constraint flavor_con {
            flavor dist { APPLE      := apple_wt,
      		    BLUEBERRY  := blueberry_wt,
      		    BUBBLE_GUM := bubble_gum_wt,
      		    CHOCOLATE  := chocolate_wt }; 
         }
       
         constraint color_con {
            color dist { RED   := red_wt,
      		   GREEN := green_wt,
      		   BLUE  := blue_wt };
         }
       
         constraint sugar_free_con {
            sugar_free dist { 0 := ( 100 - sugar_free_pct ),
      			1 :=         sugar_free_pct };
         }
       
         constraint sour_con {
            sour dist { 0 := ( 100 - sour_pct ),
      	          1 :=         sour_pct };
         }
       
         constraint flavor_color_con {
            flavor != NO_FLAVOR;
            flavor == APPLE     -> color != BLUE;
            flavor == BLUEBERRY -> color == BLUE;
         }
       
         function new(string name = "");
            super.new(name);
            void'( $value$plusargs( "APPLE_WT=%d",      apple_wt      ) );
            void'( $value$plusargs( "BLUEBERRY_WT=%d",  blueberry_wt  ) );
            void'( $value$plusargs( "BUBBLE_GUM_WT=%d", bubble_gum_wt ) );
            void'( $value$plusargs( "CHOCOLATE_WT=%d",  chocolate_wt  ) );
       
            void'( $value$plusargs( "RED_WT=%d",   red_wt   ) );
            void'( $value$plusargs( "GREEN_WT=%d", green_wt ) );
            void'( $value$plusargs( "BLUE_WT=%d",  blue_wt  ) );
       
            void'( $value$plusargs( "SUGAR_FREE_PCT=%d", sugar_free_pct ) );
            void'( $value$plusargs( "SOUR_PCT=%d",       sour_pct       ) );
         endfunction: new
       
         // ...
       
      endclass: jelly_bean_transaction
  30. Hi Keisuke,

    For hierarchical sequences, what is the difference between starting the sub-sequence with any of the following methods? Is there any advantage of using one over the others?

       start_item(jb_seq);
       `uvm_do(jb_seq)
       jb_seq.start(m_sequencer, this);
       `uvm_do_on(jb_seq, m_sequencer)
    
    1. You cannot start a sequence using start_item. `uvm_do( jb_seq ) and `uvm_do_on( jb_seq, m_sequencer ) are exactly the same. Both will execute `uvm_do_on_pri_with( jb_seq, m_sequencer, -1, {} ). This macro creates a new jb_seq object and randomize it before starting the sequence. Using the macro is concise if it does what you want to do.

Leave a Reply

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