A ripple counter is an asynchronous counter in which the all the flops except the first are clocked by the output of the preceding flop.

Design
module dff ( input d,
input clk,
input rstn,
output reg q,
output qn);
always @ (posedge clk or negedge rstn)
if (!rstn)
q <= 0;
else
q <= d;
assign qn = ~q;
endmodule
module ripple ( input clk,
input rstn,
output [3:0] out);
wire q0;
wire qn0;
wire q1;
wire qn1;
wire q2;
wire qn2;
wire q3;
wire qn3;
dff dff0 ( .d (qn0),
.clk (clk),
.rstn (rstn),
.q (q0),
.qn (qn0));
dff dff1 ( .d (qn1),
.clk (q0),
.rstn (rstn),
.q (q1),
.qn (qn1));
dff dff2 ( .d (qn2),
.clk (q1),
.rstn (rstn),
.q (q2),
.qn (qn2));
dff dff3 ( .d (qn3),
.clk (q2),
.rstn (rstn),
.q (q3),
.qn (qn3));
assign out = {qn3, qn2, qn1, qn0};
endmodule
Hardware Schematic

Testbench
module tb_ripple;
reg clk;
reg rstn;
wire [3:0] out;
ripple r0 ( .clk (clk),
.rstn (rstn),
.out (out));
always #5 clk = ~clk;
initial begin
rstn <= 0;
clk <= 0;
repeat (4) @ (posedge clk);
rstn <= 1;
repeat (25) @ (posedge clk);
$finish;
end
endmodule
When multiple sequences try to access a single driver, the sequencer that is executing sequences schedules them in a certain order through a process called arbitration. The sequencer can be configured to grant driver access to certain sequences over others based on certain criteria called as arbitration modes.
Example
To understand the different modes of arbitration, lets first build a testbench containing an environment with a sequencer and driver. Note that sequences that do not require driver access or execute sequence items using start_item
and finish_item
do not have to undergo arbitration. So if multiple plain sequences are started on a sequencer, they will be executed in parallel.
1. Data class
The class called base_class represents a data object that will be sent to the driver so that it can convert this into protocol level pin toggles at the DUT interface. This class definition is kept simple for our purpose to illustrate the arbitration scheme
class base_pkt extends uvm_sequence_item;
`uvm_object_utils(base_pkt)
function new(string name = "base_pkt");
super.new(name);
endfunction
rand bit[7:0] addr;
endclass
2. Driver
Assume a driver accepts a data object of type base_pkt and toggles pins of the DUT.
class base_driver extends uvm_driver #(base_pkt);
`uvm_component_utils(base_driver)
function new(string name = "base_driver", uvm_component parent=null);
super.new(name, parent);
endfunction
// Assume that the packet is driven across to the DUT within 10ns
// after which the driver calls item_done()
virtual task run_phase(uvm_phase phase);
base_pkt m_pkt;
forever begin
seq_item_port.get_next_item(m_pkt);
#10 `uvm_info ("DRV", $sformatf("Drive packet with addr=0x%0h", m_pkt.addr), UVM_LOW)
seq_item_port.item_done();
end
endtask
endclass
3. Environment
The environment class instantiates a sequencer and driver and makes the connection between them.
class base_env extends uvm_env;
`uvm_component_utils(base_env)
function new(string name="base_env", uvm_component parent=null);
super.new(name, parent);
endfunction
base_driver m_drv;
uvm_sequencer#(base_pkt) m_seqr;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_drv = base_driver::type_id::create("m_drv", this);
m_seqr = uvm_sequencer#(base_pkt)::type_id::create("m_seqr", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_drv.seq_item_port.connect(m_seqr.seq_item_export);
endfunction
endclass
4. Sequence
The sequence class called base_seq creates a data packet and is executed by the sequencer defined above. The data packet will be sent to the driver and will be blocked until the driver calls item_done
method.
Since multiple instances of this sequence will be started on a single sequencer, a variable called seq_num is declared to distinguish between sequences. The address of the packet is also made unique to identify which sequence is responsible for sending data through the driver.
class base_seq extends uvm_sequence;
`uvm_object_utils(base_seq)
function new(string name="base_seq");
super.new(name);
endfunction
int seq_num;
virtual task body();
base_pkt m_pkt = base_pkt::type_id::create("m_pkt");
`uvm_info(get_name(), $sformatf("Started body"), UVM_LOW)
start_item(m_pkt);
m_pkt.randomize() with { addr == seq_num; };
finish_item(m_pkt);
#10 `uvm_info(get_name(), $sformatf("Ended body"), UVM_LOW)
endtask
endclass
5. Test
A base test class is defined below that builds the environment and all sequences. It also has an additional function to display the current arbitration scheme of the sequencer within environment. Based on the arbitration scheme, the sequencer follows a certain of order of granting access to all sequences to the driver.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name="base_test", uvm_component parent=null);
super.new(name, parent);
endfunction
base_seq m_seq[6];
base_env m_env;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
foreach(m_seq[i]) begin
m_seq[i] = base_seq::type_id::create($sformatf("m_seq_%0d", i));
m_seq[i].seq_num = i;
end
m_env = base_env::type_id::create("m_env", this);
endfunction
virtual function void show_arb_cfg();
UVM_SEQ_ARB_TYPE cur_arb;
cur_arb = m_env.m_seqr.get_arbitration();
`uvm_info("TEST", $sformatf("Seqr set to %s", cur_arb.name()), UVM_LOW)
endfunction
endclass
UVM_SEQ_ARB_FIFO
This is the default arbitration mode where the UVM sequencer starts them regardless of their priorities.
Without priority
class arb_fifo_test extends base_test;
`uvm_component_utils(arb_fifo_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_FIFO);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr);
m_seq[1].start(m_env.m_seqr);
m_seq[2].start(m_env.m_seqr);
m_seq[3].start(m_env.m_seqr);
join
phase.drop_objection(this);
endtask
endclass
UVM_INFO @ 0: reporter [RNTST] Running test arb_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
With priority
Lets add some priorities to the sequence when they are started on the sequencer and see how they behave.
class arb_fifo_test extends base_test;
`uvm_component_utils(arb_fifo_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_FIFO);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr, .this_priority(100));
m_seq[1].start(m_env.m_seqr, .this_priority(200));
m_seq[2].start(m_env.m_seqr, .this_priority(300));
m_seq[3].start(m_env.m_seqr, .this_priority(400));
join
phase.drop_objection(this);
endtask
endclass
Note that priorities do not take effect in UVM_SEQ_ARB_FIFO arbitration mode
UVM_INFO @ 0: reporter [RNTST] Running test arb_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
UVM_SEQ_ARB_RANDOM
In here the sequencer will randomly start sequences regardless of their priorities.
Without priority
class arb_random_test extends base_test;
`uvm_component_utils(arb_random_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_RANDOM);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr);
m_seq[1].start(m_env.m_seqr);
m_seq[2].start(m_env.m_seqr);
m_seq[3].start(m_env.m_seqr);
join
phase.drop_objection(this);
endtask
endclass
UVM_INFO @ 0: reporter [RNTST] Running test arb_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
With priority
Lets add some priorities to the sequence when they are started on the sequencer and see how they behave.
class arb_random_test extends base_test;
`uvm_component_utils(arb_random_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_RANDOM);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr, .this_priority(100));
m_seq[1].start(m_env.m_seqr, .this_priority(200));
m_seq[2].start(m_env.m_seqr, .this_priority(300));
m_seq[3].start(m_env.m_seqr, .this_priority(400));
join
phase.drop_objection(this);
endtask
endclass
Note that priorities do not take effect in UVM_SEQ_ARB_RANDOM arbitration mode
UVM_INFO @ 0: reporter [RNTST] Running test arb_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
UVM_SEQ_ARB_STRICT_FIFO
This mode will always grant the sequence with highest priority first. Multiple sequences with the same priority will be arbitrated similar to a FIFO. For this test, let us increase the number of sequences just to have multiple sequences on the same priority.
Without priority
class arb_strict_fifo_test extends base_test;
`uvm_component_utils(arb_strict_fifo_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr);
m_seq[3].start(m_env.m_seqr);
m_seq[1].start(m_env.m_seqr);
m_seq[2].start(m_env.m_seqr);
m_seq[4].start(m_env.m_seqr);
m_seq[5].start(m_env.m_seqr);
join
phase.drop_objection(this);
endtask
endclass
See that output is the same as UVM_SEQ_ARB_FIFO arbitration mode when no priorities are specified.
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
With priority
class arb_strict_fifo_test extends base_test;
`uvm_component_utils(arb_strict_fifo_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr, .this_priority(100));
m_seq[1].start(m_env.m_seqr, .this_priority(200));
m_seq[2].start(m_env.m_seqr, .this_priority(200));
m_seq[3].start(m_env.m_seqr, .this_priority(200));
m_seq[4].start(m_env.m_seqr, .this_priority(200));
m_seq[5].start(m_env.m_seqr, .this_priority(400));
join
phase.drop_objection(this);
endtask
endclass
Note that priorities affect output in UVM_SEQ_ARB_STRICT_FIFO mode.
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_fifo_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_FIFO
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
UVM_SEQ_ARB_STRICT_RANDOM
This mode will always grant the sequence with highest priority first. Multiple sequences with the same priority will be randomly picked. For this test, let us increase the number of sequences just to have more sequences on the same priority.
Without priority
class arb_strict_random_test extends base_test;
`uvm_component_utils(arb_strict_random_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_RANDOM);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr);
m_seq[3].start(m_env.m_seqr);
m_seq[1].start(m_env.m_seqr);
m_seq[2].start(m_env.m_seqr);
m_seq[4].start(m_env.m_seqr);
m_seq[5].start(m_env.m_seqr);
join
phase.drop_objection(this);
endtask
endclass
Note that this is similar to UVM_SEQ_ARB_RANDOM mode when there are no priorities.
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
With priority
class arb_strict_random_test extends base_test;
`uvm_component_utils(arb_strict_random_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
m_env.m_seqr.set_arbitration(UVM_SEQ_ARB_STRICT_RANDOM);
show_arb_cfg();
fork
m_seq[0].start(m_env.m_seqr, .this_priority(100));
m_seq[1].start(m_env.m_seqr, .this_priority(200));
m_seq[2].start(m_env.m_seqr, .this_priority(200));
m_seq[3].start(m_env.m_seqr, .this_priority(200));
m_seq[4].start(m_env.m_seqr, .this_priority(200));
m_seq[5].start(m_env.m_seqr, .this_priority(400));
join
phase.drop_objection(this);
endtask
endclass
Note that m_seq_1 through m_seq_4 that had the same priority come out in a random order. Compare this with UVM_SEQ_ARB_STRICT_FIFO mode, where these sequences came out in a FIFO order.
UVM_INFO @ 0: reporter [RNTST] Running test arb_strict_random_test...
UVM_INFO testbench.sv(90) @ 0: uvm_test_top [TEST] Seqr set to UVM_SEQ_ARB_STRICT_RANDOM
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Started body
UVM_INFO testbench.sv(23) @ 0: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Started body
UVM_INFO testbench.sv(41) @ 10: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x5
UVM_INFO testbench.sv(27) @ 20: uvm_test_top.m_env.m_seqr@@m_seq_5 [m_seq_5] Ended body
UVM_INFO testbench.sv(41) @ 20: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x1
UVM_INFO testbench.sv(27) @ 30: uvm_test_top.m_env.m_seqr@@m_seq_1 [m_seq_1] Ended body
UVM_INFO testbench.sv(41) @ 30: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x4
UVM_INFO testbench.sv(27) @ 40: uvm_test_top.m_env.m_seqr@@m_seq_4 [m_seq_4] Ended body
UVM_INFO testbench.sv(41) @ 40: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x2
UVM_INFO testbench.sv(27) @ 50: uvm_test_top.m_env.m_seqr@@m_seq_2 [m_seq_2] Ended body
UVM_INFO testbench.sv(41) @ 50: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x3
UVM_INFO testbench.sv(27) @ 60: uvm_test_top.m_env.m_seqr@@m_seq_3 [m_seq_3] Ended body
UVM_INFO testbench.sv(41) @ 60: uvm_test_top.m_env.m_drv [DRV] Drive packet with addr=0x0
UVM_INFO testbench.sv(27) @ 70: uvm_test_top.m_env.m_seqr@@m_seq_0 [m_seq_0] Ended body
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 70: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 70: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
UVM_SEQ_ARB_WEIGHTED
There is a higher chance of picking higher priority sequences in this mode.
UVM_SEQ_ARB_USER
If none of the above modes satisfies your requirement, then you can create a user defined arbitration scheme. All you need to do is to create a new sequencer extended from uvm_sequencer
and define its user_priority_arbitration
function.
UVM has an internal database table in which we can store values under a given name and can be retrieved later by some other testbench component. The uvm_config_db
class provides a convenience interface on top of the uvm_resource_db
to simplify the basic interface used for uvm_component instances. Note that all the functions are static and must be called using the ::
scope operator.
Such a configuration database allows us to store different configuration settings under different names that could potentially configure testbench components when required without modifying the actual testbench code. For example, to turn on functional coverage for an agent, we would simply have to give the path to that agent and set a variable within the configuration database to value 1. The agent could check for the value under this variable and start collecting coverage if it is turned on.

set()
static function void set ( uvm_component cntxt,
string inst_name,
string field_name,
T value);
Use this static function of the class uvm_config_db
to set a variable in the configuration database. In the example below, set()
function will set a variable of name cov_enable at the path uvm_test_top.m_env.m_apb_agent with value 1.
virtual function void build_phase (uvm_phase phase);
...
uvm_config_db #(int) :: set (null, "uvm_test_top.m_env.m_apb_agent", "cov_enable", 1);
...
endfunction
A virtual sequence is a container to start multiple sequences on different sequencers in the environment. This virtual sequence is usually executed by a virtual sequencer which has handles to real sequencers. The need for a virtual sequence arises when you require different sequences to be run on different environments. For example, an SoC design might have multiple different interfaces that might need to be driven by a different set of sequences on individual sequencers. Hence the best way to start and control these different sequences would be from a virtual sequence. It becomes virtual because it is not associated with any particular data type.

Use of a virtual sequencer
class my_virtual_seq extends uvm_sequence;
`uvm_object_utils (my_virtual_seq)
`uvm_declare_p_sequencer (my_virtual_sequencer)
function new (string name = "my_virtual_seq");
super.new (name);
endfunction
apb_rd_wr_seq m_apb_rd_wr_seq;
wb_reset_seq m_wb_reset_seq;
pcie_gen_seq m_pcie_gen_seq;
task pre_body();
m_apb_rd_wr_seq = apb_rd_wr_seq::type_id::create ("m_apb_rd_wr_seq");
m_wb_reset_seq = wb_reset_seq::type_id::create ("m_wb_reset_seq");
m_pcie_gen_seq = pcie_gen_seq::type_id::create ("m_pcie_gen_seq");
endtask
task body();
...
m_apb_rd_wr_seq.start (p_sequencer.m_apb_seqr);
fork
m_wb_reset_seq.start (p_sequencer.m_wb_seqr);
m_pcie_gen_seq.start (p_sequencer.m_pcie_seqr);
join
...
endtask
endclass
Note the following from the example above.
- my_virtual_seq is derived from
uvm_sequence
just like any other sequence - A handle called
p_sequencer
is created within the sequence via macro`uvm_declare_p_sequencer
and assigned to be run with my_virtual_sequencer - Each sequence is started on its corresponding sequencer using the
start()
method - Each sequencer is referenced by p_sequencer handle which points to the virtual sequencer
In simple terms it's a UVM sequencer that contain handles to other sequencers. Why do we need this ? Because we plan to use virtual sequences and want to have control over all sequencers from a central place. A request type is not required here because this sequencer is generic and not limited to handle only one particular data type.
Click to refresh the concept of a virtual sequence.

The environment shown above has an APB agent, Wishbone agent, PCIE environment and a register layer environment. Each of these components have their own sequences and the respective sequencers on which they are launched. A virtual sequencer called m_virt_seqr is instantiated to hold references to each individual sequencer. Hence a virtual sequence executing on this virtual sequencer will have access to all the sequencers in the testbench.
Example
class my_virtual_sequencer extends uvm_sequencer;
`uvm_component_utils (my_virtual_sequencer)
function new (string name = "my_virtual_sequencer", uvm_component parent);
super.new (name, parent);
endfunction
// Declare handles to other sequencers here
apb_sequencer m_apb_seqr;
reg_sequencer m_reg_seqr;
wb_sequencer m_wb_seqr;
pcie_sequencer m_pcie_seqr;
endclass