UVM Sequence Arbitration

 

Hi, Uptill now we’ve seen different aspects of Sequences in UVM such as:

One more dimension of Sequences came in my thoughts which is yet to be covered i.e. Sequence Arbitration in UVM.

Before explaining the Sequence Arbitration lets have a very quick Sequences recap – UVM Sequences are used to generate input stimulus for the Design Under Test i.e. DUT. Sequences are executed on a particular Sequencer which passes the generated Transactions to the connected Driver. To find out more details about this process click here: UVM Driver and Sequencer Communication.

Since we now know that Sequencer is the physical entity which executes the Sequences. So for a Sequencer if there is only one Sequence to get the allocation, it is very straight forward to do. But in case there are multiple Sequences competing at the same time to get the allocation of the Sequencer, there needs to be some mechanism, which is technically called “Arbitration”, is required to resolve the situation. In UVM this process is called “Sequence Arbitration”.

In UVM, if the Sequencer is extended from the uvm_sequencer it will have a built-in mechanism to arbitrate between Sequences running concurrently to get the allocation of the Sequencer. The built-in algorithm decides which Sequence gets the grant to send its Transaction items to the Driver. As per UVM 1.2, there are total 6 Arbitration schemes, out of them one can be selected using “set_arbitration()” Sequencer method from the controlling Sequence.

Following are the six Sequence Arbitration mechanism:

  1. UVM_SEQ_ARB_FIFO
  2. UVM_SEQ_ARB_WEIGHTED
  3. UVM_SEQ_ARB_RANDOM
  4. UVM_SEQ_ARB_STRICT_RANDOM
  5. UVM_SEQ_ARB_STRICT_FIFO
  6. UVM_SEQ_ARB_USER

Note: In UVM 1.2, “UVM_” is appended in the begining of the name of these six Arbitration mechanism. UVM 1.1d/UVM 1.1 have names without “UVM_“.

Please use the Arbitration mechanism names according to your UVM version to avoid the complication errors.


Now lets understand the process using the below given example –

SeqArbt

Figure 1: Concurrent Sequences competing for the grant on Sequencer

In the shown Figure 1, master Sequence “my_seq” spawned four concurrent sub-sequences called “seq1“, “seq2“, “seq3” & “seq4“. These 4 concurrent sequences races to gain the grant so that their Transaction items could be sent to the DUT via Driver.

Lets see the UVM code of the master Sequence i.e. “master_seq” class instantiated as “my_seq” inside the Test:


///// Master Sequence To Generate Sub-Sequences /////
class master_seq extends uvm_sequence #(my_txn);

 `uvm_object_utils(master_seq)

 /// Sub-sequences class handle declaration
 arb_seq seq1, seq2, seq3, seq4;

 /// Constructor
 function new(string name = "master_seq");
  super.new(name);
 endfunction: new

 /// Body Task
task body;
 seq1 = arb_seq::type_id::create("seq1");
 seq1.seq_no = 1;
 seq2 = arb_seq::type_id::create("seq2");
 seq2.seq_no = 2;
 seq3 = arb_seq::type_id::create("seq3");
 seq3.seq_no = 3;
 seq4 = arb_seq::type_id::create("seq4");
 seq4.seq_no = 4;

 m_sequencer.set_arbitration(arb_type);
 fork
  begin
   repeat(4) begin
   #1;
   seq1.start(m_sequencer, this, 400); ///Highest priority
   end
  end
  begin
   repeat(4) begin
   #2;
   seq2.start(m_sequencer, this, 400); ///Highest priority
   end
  end
  begin
   repeat(4) begin
   #3;
   seq3.start(m_sequencer, this, 200); ///Medium priority
  end
  end
  begin
   repeat(4) begin
   #4;
   seq4.start(m_sequencer, this, 100); ///Lowest priority
   end
  end
 join

endtask: body

endclass: master_seq

The above code i.e. “master_seq” class is the master Sequence which instantiates the 4 sub-Sequences i.e. seq1, seq2, seq3 & seq4. Inside the body() task, first these sub-Sequences are created along with allocation of Sequence no. to each one of those. Next, the Arbitration mechanism is selected using set_arbitration() method. Finally, using the fork..join construct all the four sub-Sequences are spawned on the same Sequencer. Few things to observe – There is an offset provided for each sub-Sequence generation inside the repeat loop. Priorities are also defined for each sub-Sequences. Priorities are needed in some of the Arbitration mechanism as we’ll see in the section below. Since as per the topic of discussion of Sequence Arbitration, it does not really matter, much, what each of the sub-Sequence functionally performs, hence sub-Sequence code is not shown here.

Ok, now lets analyze the behavior of the six UVM Sequence Arbitration mechanism using the above given example & UVM master Sequence code.

1) UVM_SEQ_ARB_FIFO

This is the default Arbitration mechanism in UVM. It is order dependent & no priority advantage given to the sub-Sequences. It means, Sequencer send the Transaction items of the Sequences in the order these are received. Based on the above code, order by which Sequencer would send the Transactions to Driver would be: seq1, seq2, seq3 & seq4. Resultant log file would be as follows:

UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO @ 0: uvm_test_top [Sequencer Arbitration selected:] UVM_SEQ_ARB_FIFO
UVM_INFO @ 1: [RECVD_SEQ] Access totals: seq1:1 seq2:0 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:1 seq2:1 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:2 seq2:1 seq3:0 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:2 seq2:1 seq3:1 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:1 seq4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:1 seq4:1
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:3 seq2:2 seq3:1 seq4:1
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:2 seq3:1 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:2 seq3:2 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:3 seq3:2 seq4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:3 seq3:2 seq4:2
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:2 seq4:2
UVM_INFO @ 9: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:3 seq4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:3 seq4:3
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:3
UVM_INFO @ 16: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:4

2) UVM_SEQ_ARB_WEIGHTED

Using this algorithm,  grant is allocated based on the random basis but the Sequences with higher weight is given priority. In the given example/code above, seq1 and seq2 have equal weightage so first seq1 and seq2 are consumed, once available, on random basis then seq3 and at last seq4. The resultant log file shows this:

UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO @ 0: uvm_test_top [Sequencer Arbitration selected:] UVM_SEQ_ARB_WEIGHTED
UVM_INFO @ 1: [RECVD_SEQ] Access totals: seq1:1 seq2:0 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:2 seq2:0 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:2 seq2:1 seq3:0 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:0 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:1 seq4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:1 seq3:1 seq4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:1 seq3:1 seq4:1
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:2 seq3:1 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:3 seq3:1 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:3 seq3:2 seq4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:2 seq4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:2 seq4:2
UVM_INFO @ 9: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:3 seq4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:3
UVM_INFO @ 16: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:4

3) UVM_SEQ_ARB_RANDOM

This mode of Arbitration is totally random in nature. It does not consider any weight or priority and grant the access just on the random basis to the available completing Sequences.

UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO @ 0: uvm_test_top [Sequencer Arbitration selected:] UVM_SEQ_ARB_RANDOM
UVM_INFO @ 1: [RECVD_SEQ] Access totals: seq1:1 seq2:0 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:2 seq2:0 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:2 seq2:1 seq3:0 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:0 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:1 seq4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:1 seq4:1
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:3 seq2:2 seq3:1 seq4:1
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:2 seq3:1 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:2 seq3:2 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:3 seq3:2 seq4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:2 seq4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:2 seq4:2
UVM_INFO @ 9: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:3 seq4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:3
UVM_INFO @ 16: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:4

4) UVM_SEQ_ARB_STRICT_RANDOM

In this Arbitration scheme, random grant is allocated but weighted by the priority of the sequences. In the given example code, seq1 is selected randomly first before seq2, followed by seq3, then seq4.

UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO @ 0: uvm_test_top [Sequencer Arbitration selected] UVM_SEQ_ARB_STRICT_RANDOM
UVM_INFO @ 1: [RECVD_SEQ] Access totals: seq1:1 seq2:0 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:2 seq2:0 seq3:0 seq4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: seq1:2 seq2:1 seq3:0 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:0 seq4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: seq1:3 seq2:1 seq3:1 seq4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:1 seq3:1 seq4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:2 seq3:1 seq4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: seq1:4 seq2:2 seq3:1 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:3 seq3:1 seq4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: seq1:4 seq2:3 seq3:2 seq4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:2 seq4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:2 seq4:2
UVM_INFO @ 9: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:3 seq4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:3
UVM_INFO @ 16: [RECVD_SEQ] Access totals: seq1:4 seq2:4 seq3:4 seq4:4

5) UVM_SEQ_ARB_STRICT_FIFO

This mechanism allocates grant to the Sequences based on their priorities and order in the FIFO, with highest priority items being sent in the order received. As per the given example, seq_1 and seq_2 Sequences are being sent first interleaved with each other according to the order of their arrival in the Sequencer queue, followed by seq_3 Sequence, and then the sequence seq_4.

UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO @ 0: uvm_test_top [Sequencer Arbitration selected:] UVM_SEQ_ARB_STRICT_FIFO
UVM_INFO @ 1: [RECVD_SEQ] Access totals: SEQ_1:1 SEQ_2:0 SEQ_3:0 SEQ_4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: SEQ_1:1 SEQ_2:1 SEQ_3:0 SEQ_4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: SEQ_1:2 SEQ_2:1 SEQ_3:0 SEQ_4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: SEQ_1:3 SEQ_2:1 SEQ_3:0 SEQ_4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: SEQ_1:3 SEQ_2:1 SEQ_3:1 SEQ_4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: SEQ_1:3 SEQ_2:2 SEQ_3:1 SEQ_4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:2 SEQ_3:1 SEQ_4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:2 SEQ_3:1 SEQ_4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:3 SEQ_3:1 SEQ_4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:3 SEQ_3:2 SEQ_4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:2 SEQ_4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:2 SEQ_4:2
UVM_INFO @ 9: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:3 SEQ_4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:4 SEQ_4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:4 SEQ_4:3
UVM_INFO @ 16: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:4 SEQ_4:4

6) UVM_SEQ_ARB_USER

This Arbitration mechanism allows user defined method or algorithm for the Arbitration process. To enable this feature, first condition is that Sequencer must be derived from uvm_squencer base class. Next, it requires to override a method called user_priority_arbitration(). This method inputs a Sequences queue as an argument. The user implemented algorithm needs to return an integer to select one of the Sequence from the Sequence queue.

In the following example of user defined user_priority_arbitration() method, Sequence priority order is reversed, lets take a look at the UVM code below:

///// Sequencer declaration /////
class my_sqnr extends uvm_sequencer #(my_txn);
 `uvm_component_utils(my_sqnr)

 /// The method to override the default method in base classes
 function integer user_priority_arbitration(integer avail_sequences[$]);
  int end_index;
  end_index = avail_sequences.size() - 1;
  return (avail_sequences[end_index]);
 endfunction: user_priority_arbitration

endclass: my_sqnr

Using this code, the Sequence which is lined up last in the queue at a particular moment of contention or where Arbitration is required, that last most Sequence in the order got allocation. It can be observed & compared that USER mode release order of Sequences is just reversed of the FIFO mode by using the above mentioned user defined priority arbitration method. As a result, following log will appear:

UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO @ 0: uvm_test_top [Sequencer Arbitration selected:] UVM_SEQ_ARB_USER
UVM_INFO @ 1: [RECVD_SEQ] Access totals: SEQ_1:1 SEQ_2:0 SEQ_3:0 SEQ_4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: SEQ_1:2 SEQ_2:0 SEQ_3:0 SEQ_4:0
UVM_INFO @ 2: [RECVD_SEQ] Access totals: SEQ_1:2 SEQ_2:1 SEQ_3:0 SEQ_4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: SEQ_1:3 SEQ_2:1 SEQ_3:0 SEQ_4:0
UVM_INFO @ 3: [RECVD_SEQ] Access totals: SEQ_1:3 SEQ_2:1 SEQ_3:1 SEQ_4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:1 SEQ_3:1 SEQ_4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:2 SEQ_3:1 SEQ_4:0
UVM_INFO @ 4: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:2 SEQ_3:1 SEQ_4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:3 SEQ_3:1 SEQ_4:1
UVM_INFO @ 6: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:3 SEQ_3:2 SEQ_4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:2 SEQ_4:1
UVM_INFO @ 8: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:2 SEQ_4:2
UVM_INFO @ 9: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:3 SEQ_4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:4 SEQ_4:2
UVM_INFO @ 12: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:4 SEQ_4:3
UVM_INFO @ 16: [RECVD_SEQ] Access totals: SEQ_1:4 SEQ_2:4 SEQ_3:4 SEQ_4:4

With this, all the UVM supported Sequence Arbitration algorithms are covered with their intended behavior with elementary/required details. Here purpose is to share the basic operation of the selected Arbitration algorithm.

I believe you found this write-up helpful to get initial information about Sequence Arbitration in UVM. You may get further more information on the topic from available online & offline resources. Keep sharing your inputs as many of you are already doing. I truly appreciate your responses. I always try my best to respond  each one of them individually.

Thank you for your time. See you with next post soon..Till then byee!, Take care 🙂