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_pri and `uvm_do_pri_with in a sequence
  • Run an item or sequence on another sequencer with the _on variants
  • 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_send instead)
  • randomizes it, applying any inline constraints
  • sends it through start_item() and finish_item() if it is an item, or calls its start() 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.

Output
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_do macros create, randomize and send an item or start a sequence in one line
  • _with adds inline constraints, which combine with the class constraints
  • _pri sets a priority, which matters only with a priority-aware arbitration mode
  • _on runs 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
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5