The `uvm_do sequence macros let a sequence create, randomize and send an item, or start a child sequence, in a single line. Instead of writing start_item(), randomize() and finish_item() by hand, you write `uvm_do(req) or `uvm_do_with(req, {...}) and the preprocessor expands it into those calls.
There are eight macros in the family. Four run on the sequence's own sequencer and four, the _on variants, run on a sequencer you name. They differ only in which arguments you supply.
6 min read | Intermediate Level
What You'll Learn
- Use
`uvm_do,`uvm_do_with,`uvm_do_priand`uvm_do_pri_within a sequence - Run an item or sequence on another sequencer with the
_onvariants - Explain how inline constraints combine with the item's own constraints
- Read a simulation log to confirm what each macro sent
Syntax
`uvm_do (SEQ_OR_ITEM)
`uvm_do_pri (SEQ_OR_ITEM, PRIORITY)
`uvm_do_with (SEQ_OR_ITEM, CONSTRAINTS)
`uvm_do_pri_with (SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
`uvm_do_on (SEQ_OR_ITEM, SEQR)
`uvm_do_on_pri (SEQ_OR_ITEM, SEQR, PRIORITY)
`uvm_do_on_with (SEQ_OR_ITEM, SEQR, CONSTRAINTS)
`uvm_do_on_pri_with (SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS)
For every macro, the first argument is a handle to a uvm_sequence_item or a uvm_sequence. The macro then:
- creates a new object of the handle's type with
`uvm_create(to send an object you already have, use`uvm_sendinstead) - randomizes it, applying any inline constraints
- sends it through
start_item()andfinish_item()if it is an item, or calls itsstart()task if it is a sequence
How the Variants Relate
All eight macros end up in the same place. Each one calls `uvm_do_on_pri_with and fills in whatever you left out: the sequence's own sequencer, a priority of -1, and an empty constraint block. For example, inside the UVM library:
// `uvm_do(SEQ_OR_ITEM) expands to:
`uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, {})
// `uvm_do_on_with(SEQ_OR_ITEM, SEQR, CONSTRAINTS) expands to:
`uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, -1, CONSTRAINTS)
m_sequencer is the sequencer the current sequence was started on. A priority of -1 means "use the sequence's own priority". What `uvm_do_on_pri_with does step by step is covered in Executing sequence macros.
What Each Argument Does
CONSTRAINTS
The constraint block is applied with randomize() with. It is added to the constraints already declared in the item class, it does not replace them. If an inline constraint conflicts with a class constraint, randomization fails. In that case the macro prints a RNDFLD warning and sends the item anyway. Inside the block, refer to the item's fields directly, as in { data == 8'h4e; }.
PRIORITY
The priority is used by the sequencer when several sequences want the driver at the same time. Higher numbers win, but only if the sequencer's arbitration mode takes priority into account, for example UVM_SEQ_ARB_STRICT_FIFO. With the default FIFO mode, priority has no effect.
SEQR
The _on variants send the item or start the sequence on the sequencer you pass in, instead of the one the current sequence runs on. This is mostly used in virtual sequences, which start child sequences on the sequencers of several different agents.
Items and sequences
The same macro works for both kinds of argument. The macro checks the type of the handle at run time: an item is sent to the driver through the sequencer, and a sequence is started as a child of the current sequence, on the same sequencer unless an _on variant names another. A child sequence started this way skips its pre_body() and post_body(), so keep must-run setup in pre_start().
Example
The sequence below sends four items of type my_data, one with each of the default-sequencer macros. req is the handle that uvm_sequence #(my_data) declares for you, so it is already of type my_data.
class base_sequence extends uvm_sequence #(my_data);
`uvm_object_utils (base_sequence)
// Rest of the code (constructor)
virtual task body ();
`uvm_info ("BASE_SEQ", $sformatf ("Starting body of %s", this.get_name()), UVM_MEDIUM)
`uvm_do (req) // fully random
`uvm_do_with (req, { data == 8'h4e; // both fields fixed
addr == 8'ha1; })
`uvm_do_pri (req, 9) // random, priority 9
`uvm_do_pri_with (req, 3, { data == 8'hc5; }) // data fixed, addr random
`uvm_info ("BASE_SEQ", $sformatf ("Sequence %s is over", this.get_name()), UVM_MEDIUM)
endtask
endclass
The test starts this sequence (named seq1) on a sequencer called m_seqr1. The driver prints every item it receives.
CDNS-UVM-1.1d (14.10-s013)
(C) 2007-2013 Mentor Graphics Corporation
(C) 2007-2013 Cadence Design Systems, Inc.
(C) 2006-2013 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
----------------------------------------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology:
------------------------------------------------------------
Name Type Size Value
------------------------------------------------------------
uvm_test_top base_test - @2636
m_top_env my_env - @210
m_drv0 my_driver - @204
rsp_port uvm_analysis_port - @2840
seq_item_port uvm_seq_item_pull_port - @2789
m_seqr0 my_sequencer - @2739
rsp_export uvm_analysis_export - @2930
seq_item_export uvm_seq_item_pull_imp - @3478
arbitration_queue array 0 -
lock_queue array 0 -
num_last_reqs integral 32 'd1
num_last_rsps integral 32 'd1
m_seqr1 uvm_sequencer - @2871
rsp_export uvm_analysis_export - @3565
seq_item_export uvm_seq_item_pull_imp - @4105
arbitration_queue array 0 -
lock_queue array 0 -
num_last_reqs integral 32 'd1
num_last_rsps integral 32 'd1
------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(96) @ 0: uvm_test_top.m_top_env.m_drv0 [my_driver] Applying initial reset
UVM_INFO ./tb/my_pkg.sv(100) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] DUT is now out of reset
UVM_INFO ./tb/my_pkg.sv(107) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO ./tb/my_pkg.sv(173) @ 390000: uvm_test_top.m_top_env.m_seqr1@@seq1 [seq1] Starting body of seq1
UVM_INFO ./tb/my_pkg.sv(120) @ 410000: uvm_test_top.m_top_env.m_drv0 [DRV] Driving data item across DUT interface
req: (my_data@4206) {
data: 'hfb
addr: 'h38
begin_time: 390000
depth: 'd2
parent sequence (name): seq1
parent sequence (full name): uvm_test_top.m_top_env.m_seqr1.seq1
sequencer: uvm_test_top.m_top_env.m_seqr1
}
UVM_INFO ./tb/my_pkg.sv(107) @ 410000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO ./tb/my_pkg.sv(120) @ 430000: uvm_test_top.m_top_env.m_drv0 [DRV] Driving data item across DUT interface
req: (my_data@4256) {
data: 'h4e
addr: 'ha1
begin_time: 410000
depth: 'd2
parent sequence (name): seq1
parent sequence (full name): uvm_test_top.m_top_env.m_seqr1.seq1
sequencer: uvm_test_top.m_top_env.m_seqr1
}
UVM_INFO ./tb/my_pkg.sv(107) @ 430000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO ./tb/my_pkg.sv(120) @ 450000: uvm_test_top.m_top_env.m_drv0 [DRV] Driving data item across DUT interface
req: (my_data@4187) {
data: 'hd8
addr: 'h2d
begin_time: 430000
depth: 'd2
parent sequence (name): seq1
parent sequence (full name): uvm_test_top.m_top_env.m_seqr1.seq1
sequencer: uvm_test_top.m_top_env.m_seqr1
}
UVM_INFO ./tb/my_pkg.sv(107) @ 450000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO ./tb/my_pkg.sv(120) @ 470000: uvm_test_top.m_top_env.m_drv0 [DRV] Driving data item across DUT interface
req: (my_data@4339) {
data: 'hc5
addr: 'h8
begin_time: 450000
depth: 'd2
parent sequence (name): seq1
parent sequence (full name): uvm_test_top.m_top_env.m_seqr1.seq1
sequencer: uvm_test_top.m_top_env.m_seqr1
}
UVM_INFO ./tb/my_pkg.sv(107) @ 470000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO ./tb/my_pkg.sv(179) @ 470000: uvm_test_top.m_top_env.m_seqr1@@seq1 [seq1] Sequence seq1 is over
UVM_INFO ./tb/my_pkg.sv(126) @ 470000: uvm_test_top.m_top_env.m_drv0 [my_driver] Finished DUT simulation
--- UVM Report catcher Summary ---
Reading the log
Four items reach the driver, 20 time units apart. The first has random values. The second has exactly 'h4e and 'ha1, as the inline constraints demand. The third is random again, and the fourth has data fixed at 'hc5 while addr is still random.
Each item has a different object ID (@4206, @4256, @4187, @4339), because every macro call creates a fresh object for req. The priorities 9 and 3 make no visible difference here, since this is the only sequence running on m_seqr1.
Quick Check
Q: my_data has a class constraint addr < 8'h80. What happens with `uvm_do_with(req, { addr == 8'hF0; })?
Show Answer
The two constraints conflict, so randomization fails. The macro prints a RNDFLD warning and still sends the item, with whatever values the newly created object holds. Inline constraints never override class constraints.
Running on Another Sequencer
A virtual sequence usually has no driver of its own. It uses the _on macros to start sequences on the agents' sequencers, typically reached through a p_sequencer handle:
class top_vseq extends uvm_sequence;
// Rest of the code (registration, p_sequencer declaration)
bus_seq m_bus_seq;
irq_seq m_irq_seq;
virtual task body ();
`uvm_do_on (m_bus_seq, p_sequencer.m_bus_seqr)
`uvm_do_on_with (m_irq_seq, p_sequencer.m_irq_seqr, { num_irqs == 2; })
endtask
endclass
Here num_irqs is a rand field of irq_seq. Randomizing a sequence works just like randomizing an item. Virtual sequences are covered in detail in UVM Virtual Sequence.
Common Mistakes
Mistake #1: Treating inline constraints as overrides
Inline constraints are combined with the class constraints. If a test needs a value the class forbids, create the item with `uvm_create, turn the class constraint off with its constraint_mode(0), and then send it with `uvm_rand_send_with.
Mistake #2: Expecting priority to work with the default arbitration
The default arbitration mode grants requests in arrival order and ignores priority. Call set_arbitration() on the sequencer with a priority-aware mode first.
Mistake #3: Missing braces around the constraints
The constraint argument must be a complete block in curly braces, including the semicolon after each constraint: { data == 8'h4e; }. Leaving out the braces or the semicolons causes compile errors that point inside the macro expansion and are hard to read.
Practice Exercise
Write the body() of a sequence that sends eight my_data items to consecutive addresses starting at 8'h20, with random data. Every fourth item should carry data == 8'hFF as a marker. Use only `uvm_do_with.
Show Hint
A loop variable can appear inside the inline constraint block, just like in any randomize() with call. An implication constraint (->) handles the marker.
Show Solution
virtual task body ();
for (int i = 0; i < 8; i++) begin
`uvm_do_with (req, { addr == 8'h20 + i;
(i % 4 == 3) -> data == 8'hFF; })
end
endtask
Each pass creates a new req, so no state leaks from one item to the next. Items 3 and 7 carry the 8'hFF marker.
Summary
In this article, you learned:
- The
`uvm_domacros create, randomize and send an item or start a sequence in one line _withadds inline constraints, which combine with the class constraints_prisets a priority, which matters only with a priority-aware arbitration mode_onruns the item or sequence on a sequencer you choose, as virtual sequences do- All eight macros expand into
`uvm_do_on_pri_with, and each call creates a new object