UVM Tutorial for Candy Lovers – 32. Using randc

One of the loyal jelly-bean customers reported that his gift box had repeated flavors. After the investigation, we found a potential issue with the gift_boxed_jelly_bean_sequence created in Transactions and Sequences. Here is the original code snippet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class gift_boxed_jelly_bean_sequence extends uvm_sequence#( jelly_bean_transaction );
 
  task body();
    same_flavored_jelly_bean_sequence jb_seq;
 
    repeat ( num_jelly_bean_flavors ) begin
      jb_seq = same_flavored_jelly_bean_sequence::type_id::create( .name( "jb_seq" ) );      assert( jb_seq.randomize() );      `uvm_info( get_name(), jb_seq.convert2string(), UVM_NONE )
      jb_seq.start( m_sequencer );
    end
  endtask: body
  // ...
endclass: gift_boxed_jelly_bean_sequence

The gift box is supposed to have num_jelly_bean_flavors of mutually different flavors. However, there is a chance that we get repeated flavors. This is because we created a new same_flavored_jelly_bean_sequence in the loop (line 7) and randomized it without taking care of the duplicated flavor (line 8). Let’s fix it using randc.

Jelly Bean Transaction

Firstly, we are going to replace the rand modifier of the flavor with randc so that it will cycle through all the flavors (line 4).

1
2
3
4
5
6
7
8
9
10
class jelly_bean_transaction extends uvm_sequence_item;
  `uvm_object_utils( jelly_bean_transaction )
 
  randc flavor_e flavor;  rand  color_e  color;
  rand  bit      sugar_free;
  rand  bit      sour;
  taste_e        taste;
  // ...
endclass: jelly_bean_transaction

Unfortunately, merely changing the modifier won’t achieve our goal. Here is why.

Same-Flavored Jelly Beans

As you see below, the same_flavored_jelly_bean_sequence creates a new jelly_bean_transaction every time the body task is called (line 16). Because of this, the flavor will never cycle through.

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
class same_flavored_jelly_bean_sequence extends uvm_sequence#( jelly_bean_transaction );
  `uvm_object_utils( same_flavored_jelly_bean_sequence )
 
  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;
    flavor_e               jb_flavor;
 
    jb_tx = jelly_bean_transaction::type_id::create( .name( "jb_tx" ) );    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" ) );
      start_item( jb_tx );
      assert( jb_tx.randomize() with { jb_tx.flavor == jb_flavor; } );
      finish_item( jb_tx );
    end
  endtask: body
  // ...
endclass: same_flavored_jelly_bean_sequence

Instead of creating a new object in the body task, let’s create a class property (lines 5 and 11) so that we can reuse the jelly bean (flavor_setter) between the body calls. See the highlighted lines for the other changes.

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
class same_flavored_jelly_bean_sequence extends uvm_sequence#( jelly_bean_transaction );
  `uvm_object_utils( same_flavored_jelly_bean_sequence )
 
  rand int unsigned num_jelly_beans; // knob
  rand jelly_bean_transaction flavor_setter; 
  constraint num_jelly_beans_con { num_jelly_beans inside { [2:4] }; }
 
  function new( string name = "" );
    super.new( name );
    flavor_setter = jelly_bean_transaction::type_id::create( .name( "flavor_setter" ) );  endfunction: new
 
  task body();
    jelly_bean_transaction jb_tx;
/* no longer used    flavor_e               jb_flavor;     jb_tx = jelly_bean_transaction::type_id::create( .name( "jb_tx" ) );    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" ) );
      start_item( jb_tx );
      assert( jb_tx.randomize() with { jb_tx.flavor == flavor_setter.flavor; } );      finish_item( jb_tx );
    end
  endtask: body
  // ...
endclass: same_flavored_jelly_bean_sequence

Gift-Boxed Jelly Beans

Similarly, create the same_flavored_jelly_bean_sequence outside of the loop only once (line 6) rather than creating it in the loop (line 8).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class gift_boxed_jelly_bean_sequence extends uvm_sequence#( jelly_bean_transaction );
 
  task body();
    same_flavored_jelly_bean_sequence jb_seq;
 
    jb_seq = same_flavored_jelly_bean_sequence::type_id::create( .name( "jb_seq" ) );    repeat ( num_jelly_bean_flavors ) begin
//    jb_seq = same_flavored_jelly_bean_sequence::type_id::create( .name( "jb_seq" ) );      assert( jb_seq.randomize() );
      `uvm_info( get_name(), jb_seq.convert2string(), UVM_NONE )
      jb_seq.start( m_sequencer );
    end
  endtask: body
  // ...
endclass: gift_boxed_jelly_bean_sequence

This is just one of the many ways to avoid the repeated flavor. The diagram below shows how the flavor is randomized in the sequences.
How the flavor is randomized

EDA Playground

You can view and run the code on EDA Playground.

2 thoughts on “UVM Tutorial for Candy Lovers – 32. Using randc”

  1. Thanks for the detailed explanation. As you said, there are multiple ways to achieve this. I prefer to create the transaction only once and randomize it repeatedly. But, before sending it, I clone it. This way, the randc modifier works properly. Thanks.

Leave a Reply

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