UVM Tutorial for Candy Lovers – 26. Sequence Arbitration

Our jelly-bean business has been doing so well that we started to receive multiple jelly-bean orders at the same time. Some customers requested expedited shipping, too. But how to prioritize the requests? It’s time to learn about sequence arbitration.

Today’s Jelly-Bean Orders

Let’s assume we received the following jelly-bean orders today:

  • Standard order — four APPLE jelly-beans
  • Priority order — four BLUEBERRY jelly-beans
  • Priority order — four BUBBLE_GUM jelly-beans
  • Overnight order — four CHOCOLATE jelly-beans

In our jelly_bean_test, we created four jelly_bean_order_sequence, one per a jelly-bean order (lines 39 to 42). Then, we set the priorities of the sequence and started them at the same time to let the sequencer arbitrate them. Note that the default priority is 100 and higher numbers indicate higher priority. In our case, we set the standard order to be 100, the priority order to be 200, and the overnight order to be 300 (lines 62 to 65).

The UVM sequencer has six arbitration modes:

The set_arbitration function of the uvm_sequencer_base class specifies which mode to use (lines 54 to 59). Let’s look at each mode one by one.

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
class jelly_bean_test extends uvm_test;
  `uvm_component_utils( jelly_bean_test )
 
  jelly_bean_env jb_env;
 
  //----------------------------------------------------------------------------
  // Function: new
  //----------------------------------------------------------------------------
 
  function new( string name, uvm_component parent );
    super.new( name, parent );
  endfunction: new
 
  //----------------------------------------------------------------------------
  // Function: build_phase
  //----------------------------------------------------------------------------
 
  function void build_phase( uvm_phase phase );
    super.build_phase( phase );
 
    jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
  endfunction: build_phase
 
  //----------------------------------------------------------------------------
  // task: main_phase
  //----------------------------------------------------------------------------
 
  task main_phase( uvm_phase phase );
    jelly_bean_order_sequence standard_order_seq;
    jelly_bean_order_sequence priority_order_seq1;
    jelly_bean_order_sequence priority_order_seq2;
    jelly_bean_order_sequence overnight_order_seq;
 
    standard_order_seq  = jelly_bean_order_sequence::type_id::create( "standard_order_seq" );
    priority_order_seq1 = jelly_bean_order_sequence::type_id::create( "priority_order_seq1" );
    priority_order_seq2 = jelly_bean_order_sequence::type_id::create( "priority_order_seq2" );
    overnight_order_seq = jelly_bean_order_sequence::type_id::create( "overnight_order_seq" );
 
    assert( standard_order_seq .randomize() with { num_jelly_beans == 4; jb_flavor == APPLE;      } );    assert( priority_order_seq1.randomize() with { num_jelly_beans == 4; jb_flavor == BLUEBERRY;  } );    assert( priority_order_seq2.randomize() with { num_jelly_beans == 4; jb_flavor == BUBBLE_GUM; } );    assert( overnight_order_seq.randomize() with { num_jelly_beans == 4; jb_flavor == CHOCOLATE;  } ); 
    standard_order_seq .set_starting_phase( phase );
    priority_order_seq1.set_starting_phase( phase );
    priority_order_seq2.set_starting_phase( phase );
    overnight_order_seq.set_starting_phase( phase );
 
    standard_order_seq .set_automatic_phase_objection( .value( 1 ) );
    priority_order_seq1.set_automatic_phase_objection( .value( 1 ) );
    priority_order_seq2.set_automatic_phase_objection( .value( 1 ) );
    overnight_order_seq.set_automatic_phase_objection( .value( 1 ) );
 
    //jb_env.jb_agent.jb_seqr.set_arbitration( UVM_SEQ_ARB_FIFO ); // default
    //jb_env.jb_agent.jb_seqr.set_arbitration( UVM_SEQ_ARB_RANDOM );
    //jb_env.jb_agent.jb_seqr.set_arbitration( UVM_SEQ_ARB_STRICT_FIFO );
    //jb_env.jb_agent.jb_seqr.set_arbitration( UVM_SEQ_ARB_STRICT_RANDOM );
    //jb_env.jb_agent.jb_seqr.set_arbitration( UVM_SEQ_ARB_WEIGHTED );
    //jb_env.jb_agent.jb_seqr.set_arbitration( UVM_SEQ_ARB_USER );
 
    fork
      standard_order_seq .start( jb_env.jb_agent.jb_seqr, .this_priority( 100 ) ); // default priority      priority_order_seq1.start( jb_env.jb_agent.jb_seqr, .this_priority( 200 ) );      priority_order_seq2.start( jb_env.jb_agent.jb_seqr, .this_priority( 200 ) );      overnight_order_seq.start( jb_env.jb_agent.jb_seqr, .this_priority( 300 ) );    join
  endtask: main_phase
 
endclass: jelly_bean_test

UVM_SEQ_ARB_FIFO

This is the default arbitration mode and probably the easiest to understand. UVM sequencer grants sequences in FIFO order regardless of their priorities. When we run the test with this mode, we should see the sequences like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# KERNEL: UVM_INFO @   0: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @  15: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @  35: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @  55: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  75: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @  95: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 115: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 135: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 155: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 175: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 195: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 215: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 235: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 255: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 275: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 295: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE

We illustrated the request FIFO in the UVM sequencer below. The numbers in parentheses are the priority numbers we specified when we started the sequences.

  1. The top-left shows the initial FIFO state just after we started the sequences. At step 1, the UVM sequencer selected APPLE because it was located at the front of the FIFO.
    • When the APPLE was processed, it went out of the FIFO, but since we ordered four APPLE jelly-beans, another APPLE was put into the FIFO.
    • The UVM sequencer selected BLUEBERRY at step 2 because it was located at the front of the FIFO.
    • When the BLUEBERRY was processed, it went out of the FIFO. Since we ordered four BLUEBERRY jelly-beans, another BLUEBERRY was put into the FIFO.
    • The UVM sequencer selected BUBBLE_GUM at step 3 because it was located at the front of the FIFO.
  2. Similar arbitration continued until there was no more requests to process (steps 4 to 16).

Because of round robin nature of this mode, it won’t satisfy the customers who placed a priority order or an overnight order.

How UVM_SEQ_ARB_FIFO works
How UVM_SEQ_ARB_FIFO works

UVM_SEQ_ARB_RANDOM

This mode should be also easy to understand. It randomly grants sequences in the FIFO regardless of their priorities. When we run the test with this mode, we should see the sequences like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# KERNEL: UVM_INFO @   0: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @  15: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq BLUEBERRY
# KERNEL: UVM_INFO @  35: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 CHOCOLATE
# KERNEL: UVM_INFO @  55: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BUBBLE_GUM
# KERNEL: UVM_INFO @  75: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq BUBBLE_GUM
# KERNEL: UVM_INFO @  95: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq BUBBLE_GUM
# KERNEL: UVM_INFO @ 115: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 135: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BUBBLE_GUM
# KERNEL: UVM_INFO @ 155: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq BLUEBERRY
# KERNEL: UVM_INFO @ 175: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq APPLE
# KERNEL: UVM_INFO @ 195: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq BLUEBERRY
# KERNEL: UVM_INFO @ 215: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 APPLE
# KERNEL: UVM_INFO @ 235: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 CHOCOLATE
# KERNEL: UVM_INFO @ 255: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 APPLE
# KERNEL: UVM_INFO @ 275: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 CHOCOLATE
# KERNEL: UVM_INFO @ 295: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE

We illustrated the request FIFO in the UVM sequencer below. The numbers in parentheses are the priority numbers we specified when we started the sequences.

  1. The top-left shows the initial FIFO state just after we started the sequences. At step 1, the UVM sequencer randomly selected BLUEBERRY.
    • When the BLUEBERRY was processed, it went out of the FIFO, but since we ordered four BLUEBERRY jelly-beans, another BLUEBERRY was put into the FIFO.
    • The UVM sequencer happened to select this BLUEBERRY at step 2, too.
    • When the second BLUEBERRY was processed, it went out of the FIFO. Since there were two more BLUEBERRY jelly-beans to process, another BLUEBERRY was put into the FIFO.
    • The UVM sequencer selected CHOCOLATE at step 3.
  2. Similar arbitration continued until there was no more requests to process (steps 4 to 16).

Because of random nature of this mode, it won’t satisfy the customers who placed a priority order or an overnight order, either.

How UVM_SEQ_ARB_RANDOM works
How UVM_SEQ_ARB_RANDOM works

UVM_SEQ_ARB_STRICT_FIFO

This mode always grants the highest priority sequence. If more than one sequence has the same priority, the sequencer grants the one closest to the front of the FIFO. When we run the test with this mode, we should see the sequences like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# KERNEL: UVM_INFO @   0: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  15: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  35: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  55: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  75: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @  95: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 115: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 135: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 155: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 175: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 195: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 215: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 235: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 255: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 275: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 295: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE

We illustrated the request FIFO in the UVM sequencer below. The numbers in parentheses are the priority numbers we specified when we started the sequences.

  1. The top-left shows the initial FIFO state just after we started the sequences. At step 1, the UVM sequencer selected CHOCOLATE because it was the highest priority request.
    • When the CHOCOLATE was processed, it went out of the FIFO, but since we ordered four CHOCOLATE jelly-beans, another CHOCOLATE was put into the FIFO.
    • The UVM sequencer selected this CHOCOLATE again at step 2 because it was the highest priority request.
    • When the second CHOCOLATE was processed, it went out of the FIFO. Since there were two more CHOCOLATE jelly-beans to process, another CHOCOLATE was put into the FIFO.
    • The UVM sequencer selected this CHOCOLATE again at step 3 because it was the highest priority request.
  2. After all CHOCOLATE requests (overnight order) were processed, the UVM sequencer went to the BLUEBERRY and BUBBLE_GUM requests (priority orders). Because both of them had the same priority, the UVM_SEQ_ARB_STRICT_FIFO mode selected the sequence closest to the front of the FIFO (steps 5 to 12).
  3. APPLE requests (standard order) were the lowest priority, so the UVM sequencer granted them last (steps 13 to 16).

This mode will satisfy the customers who placed a priority order or an overnight order.

How UVM_SEQ_ARB_STRICT_FIFO works
How UVM_SEQ_ARB_STRICT_FIFO works

UVM_SEQ_ARB_STRICT_RANDOM

Similar to the UVM_SEQ_ARB_STRICT_FIFO mode, this mode grants the highest priority sequence. The difference is that when more than one sequence has the same priority, the sequencer selects a sequence randomly from the sequences that have the same priority. When we run the test with this mode, we should see the sequences like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# KERNEL: UVM_INFO @   0: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  15: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  35: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  55: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  75: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @  95: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 115: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 135: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 155: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 175: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 195: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 215: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 235: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 255: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 275: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 295: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE

We illustrated the request FIFO in the UVM sequencer below. The numbers in parentheses are the priority numbers we specified when we started the sequences.

  1. The top-left shows the initial FIFO state just after we started the sequences. At step 1, the UVM sequencer selected CHOCOLATE because it was the highest priority request.
    • When the CHOCOLATE was processed, it went out of the FIFO, but since we ordered four CHOCOLATE jelly-beans, another CHOCOLATE was put into the FIFO.
    • The UVM sequencer selected this CHOCOLATE again at step 2 because it was the highest priority request.
    • When the second CHOCOLATE was processed, it went out of the FIFO. Since there were two more CHOCOLATE jelly-beans to process, another CHOCOLATE was put into the FIFO.
    • The UVM sequencer selected this CHOCOLATE again at step 3 because it was the highest priority request.
  2. After all CHOCOLATE requests (overnight order) were processed, the sequencer went to the BLUEBERRY and BUBBLE_GUM requests (priority orders). Because both of them had the same priority, the UVM_SEQ_ARB_STRICT_RANDOM mode selected the sequence randomly from the two. This is the only difference between the UVM_SEQ_ARB_STRICT_FIFO and UVM_SEQ_ARB_STRICT_RANDOM modes (steps 5 to 12).
  3. APPLE requests (standard order) were the lowest priority, so the UVM sequencer granted them last (steps 13 to 16).

This mode will satisfy the customers who placed a priority order or an overnight order.

How UVM_SEQ_ARB_STRICT_RANDOM works
How UVM_SEQ_ARB_STRICT_RANDOM works

UVM_SEQ_ARB_WEIGHTED

This is the most complicated and misleading mode in my opinion. The idea of this mode is that the higher priority sequences will get more chance to be granted. Here is how it works under the hood: This mode totals up the priority numbers in the request FIFO. Let’s say, the sum is 800. Then, it picks a random number between 0 and 799 (800 minus 1). Then, from the front of the FIFO, it accumulates the priority number each sequence has. When the accumulated number becomes more than the randomly selected number (threshold), that sequence is selected. OK, I know you are confused. Let’s see an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# KERNEL: UVM_INFO @   0: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @  15: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @  35: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @  55: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @  75: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  95: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 115: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 135: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 155: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 175: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 195: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 215: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 235: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 255: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 275: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 295: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE

We illustrated the request FIFO in the UVM sequencer below. The numbers in parentheses are the priority numbers we specified when we started the sequences.

  1. The top-left shows the initial FIFO state just after we started the sequences. Firstly, the UVM sequencer totalled up the priority numbers in the FIFO. In our case it was 800 (=100+200+200+300). Then, the sequencer picked a random number between 0 and 799 (=800-1). Let’s assume the random number was 219. We call this number a threshold. The sequencer looked at the front of the FIFO, which was APPLE and its priority number was 100. Since 100 was less than the threshold (219), the sequencer continued to look at the second entry of the FIFO. The second entry was BLUEBERRY and its priority number was 200. The accumulated priority number became 300, which was greater than the threshold, so the sequencer selected the BLUEBERRY at step 1.
    • When the BLUEBERRY was processed, it went out of the FIFO, but since we ordered four BLUEBERRY jelly-beans, another BLUEBERRY was put into the FIFO.
    • The UVM sequencer repeated the same procedure as the step 1. Firstly, it totalled up the priority numbers in the FIFO, which was 800. Let’s assume the random number the sequencer picked was 37 this time. Since the priority number of the first entry of the FIFO (100) was greater than 37, the sequencer selected APPLE at step 2.
    • When the APPLE was processed, it went out of the FIFO. Since there are three more APPLE jelly-beans to process, another APPLE was put into the FIFO.
    • The UVM sequencer used the same procedure and selected BUBBLE_GUM at step 3.
  2. Similar arbitration continued until there was no more orders to process (steps 4 to 16).

This mode will give more chance to be granted to the higher priority sequencers, but not as strict as UVM_SEQ_ARB_STRICT_FIFO or UVM_SEQ_ARB_STRICT_RANDOM.

How UVM_SEQ_ARB_WEIGHTED works
How UVM_SEQ_ARB_WEIGHTED works

UVM_SEQ_ARB_USER

If none of the above modes satisfies your need, you can create your own arbitration scheme. To do that, you need to extend the uvm_sequencer class and define its user_priority_arbitration function. As an example, we created the arbitration scheme that always selects the second entry of the FIFO if it is available. This might be quite artificial, but I hope you get the idea.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class jelly_bean_sequencer extends uvm_sequencer#( jelly_bean_transaction );
  `uvm_component_utils( jelly_bean_sequencer )
 
  //----------------------------------------------------------------------------
  // Function: new
  //----------------------------------------------------------------------------
 
  function new( string name, uvm_component parent );
    super.new( name, parent );
  endfunction: new
 
  //----------------------------------------------------------------------------
  // Function: user_priority_arbitration
  //----------------------------------------------------------------------------
 
  virtual function integer user_priority_arbitration( integer avail_sequences[$] );    if ( avail_sequences.size() >= 2 ) return avail_sequences[1]; // second entry of the request FIFO    else                               return avail_sequences[0];  endfunction: user_priority_arbitration 
endclass: jelly_bean_sequencer

When we run the test with this mode, you should see the sequences like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# KERNEL: UVM_INFO @   0: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @  15: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @  35: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @  55: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @  75: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @  95: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 115: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 135: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 155: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 175: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq1 BLUEBERRY
# KERNEL: UVM_INFO @ 195: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] priority_order_seq2 BUBBLE_GUM
# KERNEL: UVM_INFO @ 215: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr] overnight_order_seq CHOCOLATE
# KERNEL: UVM_INFO @ 235: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 255: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 275: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE
# KERNEL: UVM_INFO @ 295: uvm_test_top.jb_env.jb_agent.jb_drvr [jb_drvr]  standard_order_seq APPLE

We illustrated the arbitration FIFO in the UVM sequencer below. The numbers in parentheses are the priority numbers we specified when we started the sequences.

  1. The top-left shows the initial FIFO state just after we started the sequences. At step 1, the UVM sequencer selected BLUEBERRY because it was in the second entry of the FIFO.
    • When the BLUEBERRY was processed, it went out of the FIFO, but since we ordered four BLUEBERRY jelly-beans, another BLUEBERRY was put into the FIFO.
    • The UVM sequencer selected BUBBLE_GUM at step 2 because it was in the second entry of the FIFO.
    • When the BUBBLE_GUM was processed, it went out of the FIFO. Since we ordered four BUBBLE_GUM jelly-beans, another BUBBLE_GUM was put into the FIFO.
    • The UVM sequencer selected CHOCOLATE at step 3 because it was in the second entry of the FIFO.
  2. Similar arbitration continued until there was no more orders to process (steps 4 to 16).
How UVM_SEQ_ARB_USER works
How UVM_SEQ_ARB_USER works

This was a long article, but I hope you understand the difference between the arbitration modes.

EDA Playground

You can view and run the code on EDA Playground.

11 thoughts on “UVM Tutorial for Candy Lovers – 26. Sequence Arbitration”

  1. Hi Sir,
    Thanks for the wonderful illustration and explaining it in a simple manner.

    Regards,
    Jaswanth

  2. Kenisuke san,

    Thanks so much to spend time on this article. It is very helpful for me to understand the UVM_SEQ_ARB. It is especially true for the UVM_SEQ_ARB_WEIGHT. However, I am still not clear when the request reach to the arbitrator.

    You explain that the start does 2 things — set the m_sequencer to the sequence, and start the body.
    In the first picture, we show that 4 seq_items, APPLE, BLUEBERRY, BUBBLE_GUM and CHOCOLATE, in the fifo. Is it before the execution of the sequence’s body?
    Since the 4 starts are in parallel, how come the first fifo always has the same order (APPLE, BLUEBERRY, BUBBLE_GUM and CHOCOLATE) for all SEQ_ARB types? Many thanks.

    Loren

    1. The first FIFO picture shows the state after the body task was called (and just after the start_item task of the body was called).
      Since we did not use delay for each statement in the fork-join parallel block, the order we saw in the FIFO was just happened to be like that. If you re-arrange the order of the statements, or use another simulator, you might see different order.

  3. Kenisuke san,

    I still have a question about raise/drop objection. Do you need a couple of raise/drop objection in the main_phase of your example above? Do you prefer to use raise/drop in pre_start/post_start of sequence or main_phase of test or both? Because I read some documents which explain it a little bit mess. They always recommend to use the couple in pre_start/post_start of sequence, but they usually add them in a lot of test examples which explicitly call seq.start(…). Are they redundant? I look forward to your opinion.

    Thanks,

    1. This is a good point. I think I should not have used the set_automatic_phase_objection in the main_phase of the jelly_bean_test. Instead, I should have raised the objection at the beginning of the main_phase and dropped it at the end because I don’t have to raise/drop the objection every time a sequence is started/ended.

  4. Keisuke,
    Your illustrations and straight-forward descriptions are a rare treat. You are doing this field a great service!

      1. Hi Keisuke,
        Is there a place for people to ask you a general verification question without replying to a post. I want to
        use a uvm_component to model a data cache (just the array with the read, write lookup ports, etc) and would
        like to have a response driver to cache outputs (control logic) to the talk to the cache array uvm_component
        to update the array and read data/perform lookup using tlm 1 mechanisms (transport and analysis port).

        Is this a reasonable thing to do or do you have a cleaner solution?

        1. Sorry for the very slow reply. I’m afraid I don’t fully understand your requirement, but if it’s about the communication between two uvm_components, TLM 1 would be a good choice.
          I don’t have a place for a general verification question, but feel free to post your question using one of my articles if you have one. Thanks.

Leave a Reply