The `uvm_send family of sequence action macros sends a sequence item or sequence that already exists, without creating a new one. `uvm_do creates, randomizes and sends in a single step. `uvm_send skips the first two steps and sends the object exactly as it is, while `uvm_rand_send skips only the creation and randomizes before sending.
These macros are useful when you want control over an object before it goes out: setting fields by hand, sending the same transaction twice, or turning off a constraint for one transfer.
7 min read | Intermediate Level
What You'll Learn
- Choose between
`uvm_do,`uvm_sendand`uvm_rand_sendfor a given job - Send an item created with
`uvm_createafter setting its fields by hand - Add a priority or inline constraints with the
_priand_withvariants - Send a whole sequence, not only an item, with these macros
Syntax
`uvm_create (SEQ_OR_ITEM) // create only
`uvm_send (SEQ_OR_ITEM) // send as is
`uvm_send_pri (SEQ_OR_ITEM, PRIORITY)
`uvm_rand_send (SEQ_OR_ITEM) // randomize, then send
`uvm_rand_send_pri (SEQ_OR_ITEM, PRIORITY)
`uvm_rand_send_with (SEQ_OR_ITEM, CONSTRAINTS)
`uvm_rand_send_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
Every macro takes either a uvm_sequence_item or a uvm_sequence handle and is called from inside a sequence's body(). The argument of the send macros must already point to an object. They never create one.
Reading the table from top to bottom: `uvm_create only builds the object, `uvm_do does everything, `uvm_rand_send picks up after creation, and `uvm_send only sends. A common pattern is `uvm_create followed by manual field assignments and then `uvm_send.
Example: uvm_do Compared With uvm_send
The sequence below mixes both kinds of macro on the same sequencer so the log can show the difference. my_data is a sequence item with random data and addr fields, and the driver prints each item it receives.
class seq1 extends base_sequence;
`uvm_object_utils (seq1)
seq2 m_seq2;
my_data m_data0; // Created in the constructor
my_data m_data1; // Left for `uvm_do to create
my_data m_data2; // Will be used with `uvm_send
virtual task pre_start ();
`uvm_info (get_type_name (), "Executing pre_start()", UVM_MEDIUM)
endtask
function new (string name = "seq1");
super.new (name);
m_data0 = my_data::type_id::create ("m_data0");
endfunction
virtual task body ();
// The test raises and drops the objection around seq1.start()
`uvm_info ("SEQ1", "Starting seq1", UVM_MEDIUM)
// req is null here. `uvm_do creates it, randomizes it and sends it
`uvm_info ("SEQ1", "uvm_do (req) - Create, randomize and send req", UVM_MEDIUM)
`uvm_do (req)
// `uvm_do creates a brand new object again; the old one is not reused
`uvm_info ("SEQ1", "uvm_do (req) - Randomize again, and send", UVM_MEDIUM)
`uvm_do (req)
// m_data0 already exists, but `uvm_do still replaces it with a new object
`uvm_info ("SEQ1", "uvm_do (m_data0) - Data already exists, simply randomize and send", UVM_MEDIUM)
`uvm_do (m_data0)
// m_data1 is null, so `uvm_do creates, randomizes and sends it
`uvm_info ("SEQ1", "uvm_do (m_data1) - Data was not created, so create it, randomize and send", UVM_MEDIUM)
`uvm_do (m_data1)
// req exists from the last `uvm_do (req); send it again without randomization
`uvm_info ("SEQ1", "uvm_send (req) - req already exists from previous create, Simply send it without randomization", UVM_MEDIUM)
`uvm_send (req)
`ifdef RUNTIME_ERR
// m_data2 is still null and `uvm_send will not create it - run-time error
`uvm_send (m_data2)
`endif
`uvm_info ("SEQ1", "uvm_send (req) - Manually create, randomize and send", UVM_MEDIUM)
// Create the object, randomize it and send it to the sequencer
`uvm_create (m_data2)
void'(m_data2.randomize ());
`uvm_send (m_data2)
`uvm_info ("SEQ1", "uvm_send_pri (req) - Send with priority", UVM_MEDIUM)
`uvm_send_pri (m_data2, 72)
`uvm_info ("SEQ1", "Ending seq1", UVM_MEDIUM)
// Start the child sequence seq2, shown further below
`uvm_do (m_seq2)
endtask
endclass
----------------------------------------------------------------
CDNS-UVM-1.1d (14.22-s009)
(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 - @2644
m_top_env my_env - @2709
m_drv0 my_driver - @2754
rsp_port uvm_analysis_port - @2854
seq_item_port uvm_seq_item_pull_port - @2803
m_seqr0 my_sequencer - @2885
rsp_export uvm_analysis_export - @2944
seq_item_export uvm_seq_item_pull_imp - @3492
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(90) @ 0: uvm_test_top.m_top_env.m_drv0 [my_driver] Applying initial reset
UVM_INFO ./tb/my_pkg.sv(94) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] DUT is now out of reset
UVM_INFO ./tb/my_pkg.sv(155) @ 390000: uvm_test_top.m_top_env.m_seqr0@@seq1 [seq1] Executing pre_start()
UVM_INFO ./tb/my_pkg.sv(165) @ 390000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] Starting seq1
UVM_INFO ./tb/my_pkg.sv(169) @ 390000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] uvm_do (req) - Create, randomize and send req
----------------------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------------------
req my_data - @3648
data integral 8 'he2
addr integral 8 'hde
begin_time time 64 390000
depth int 32 'd2
parent sequence (name) string 4 seq1
parent sequence (full name) string 35 uvm_test_top.m_top_env.m_seqr0.seq1
sequencer string 30 uvm_test_top.m_top_env.m_seqr0
----------------------------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(173) @ 410000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] uvm_do (req) - Randomize again, and send
----------------------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------------------
req my_data - @3700
data integral 8 'hc
addr integral 8 'hc0
begin_time time 64 410000
depth int 32 'd2
parent sequence (name) string 4 seq1
parent sequence (full name) string 35 uvm_test_top.m_top_env.m_seqr0.seq1
sequencer string 30 uvm_test_top.m_top_env.m_seqr0
----------------------------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(177) @ 430000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] uvm_do (m_data0) - Data already exists, simply randomize and send
----------------------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------------------
m_data0 my_data - @3711
data integral 8 'h74
addr integral 8 'h50
begin_time time 64 430000
depth int 32 'd2
parent sequence (name) string 4 seq1
parent sequence (full name) string 35 uvm_test_top.m_top_env.m_seqr0.seq1
sequencer string 30 uvm_test_top.m_top_env.m_seqr0
----------------------------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(180) @ 450000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] uvm_do (m_data1) - Data was not created, so create it, randomize and send
----------------------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------------------
m_data1 my_data - @3721
data integral 8 'h43
addr integral 8 'h88
begin_time time 64 450000
depth int 32 'd2
parent sequence (name) string 4 seq1
parent sequence (full name) string 35 uvm_test_top.m_top_env.m_seqr0.seq1
sequencer string 30 uvm_test_top.m_top_env.m_seqr0
----------------------------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(184) @ 470000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] uvm_send (req) - req already exists from previous create, Simply send it without randomization
----------------------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------------------
req my_data - @3700
data integral 8 'hc
addr integral 8 'hc0
begin_time time 64 470000
end_time time 64 430000
depth int 32 'd2
parent sequence (name) string 4 seq1
parent sequence (full name) string 35 uvm_test_top.m_top_env.m_seqr0.seq1
sequencer string 30 uvm_test_top.m_top_env.m_seqr0
----------------------------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(192) @ 490000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] uvm_send (req) - Manually create, randomize and send
----------------------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------------------
m_data2 my_data - @3749
data integral 8 'hd5
addr integral 8 'h46
begin_time time 64 490000
depth int 32 'd2
parent sequence (name) string 4 seq1
parent sequence (full name) string 35 uvm_test_top.m_top_env.m_seqr0.seq1
sequencer string 30 uvm_test_top.m_top_env.m_seqr0
----------------------------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(198) @ 510000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] uvm_send_pri (req) - Send with priority
----------------------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------------------
m_data2 my_data - @3749
data integral 8 'hd5
addr integral 8 'h46
begin_time time 64 510000
end_time time 64 510000
depth int 32 'd2
parent sequence (name) string 4 seq1
parent sequence (full name) string 35 uvm_test_top.m_top_env.m_seqr0.seq1
sequencer string 30 uvm_test_top.m_top_env.m_seqr0
----------------------------------------------------------------------------------
UVM_INFO ./tb/my_pkg.sv(200) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] Ending seq1
Reading the log
Watch the object IDs after the @ sign. The two `uvm_do (req) calls print @3648 and then @3700: the second call built a fresh object rather than re-randomizing the first. The same happens to m_data0, which already existed but comes out as a new object, @3711. Don't write code that expects `uvm_do to reuse an object you created earlier.
The `uvm_send (req) step is the opposite. It prints @3700 again with the same data and addr values, so the exact object from the previous `uvm_do was sent a second time, untouched. Its end_time field is even left over from that earlier transfer.
m_data2 shows the manual route: `uvm_create builds it, a plain randomize() call fills it, and `uvm_send delivers it. `uvm_send_pri then sends the same @3749 object once more with priority 72. Priority only changes the outcome when the sequencer uses a priority-aware arbitration mode and another sequence is competing for the driver.
The RUNTIME_ERR block shows what happens when `uvm_send gets a null handle. m_data2 has not been created at that point, and `uvm_send never creates anything, so the simulation stops with a null-object error.
Why use `uvm_create first?
Creating with `uvm_create instead of calling type_id::create() directly ties the new object to the sequence and its sequencer. That context is what the send macros use when they deliver the object. It also leaves room to set fields by hand before sending, for example:
`uvm_create (req)
req.addr = 8'h10; // fixed address for this transfer
req.data = 8'hFF;
`uvm_send (req)
Quick Check
Q: You want to send ten transactions that differ only in a random data field, while addr stays at a value you set once. Which macros would you use?
Show Answer
Create the item once with `uvm_create, set addr, turn off its randomization with req.addr.rand_mode(0), and then call `uvm_rand_send ten times in a loop. `uvm_do would build a new object each time and re-randomize addr.
Sending a Sequence With These Macros
The same macros accept a sequence. When the argument is a sequence, "send" means "start it as a child of the current sequence". In the example above, seq1 ends by starting seq2, which uses each variant to run seq3.
class seq2 extends base_sequence;
`uvm_object_utils (seq2)
seq3 m_seq3;
function new (string name = "seq2");
super.new (name);
m_seq3 = seq3::type_id::create ("m_seq3");
endfunction
virtual task pre_body ();
`uvm_info (get_type_name(), "Executing pre_body", UVM_MEDIUM)
endtask
virtual task body ();
`uvm_info ("SEQ2", "Starting seq2", UVM_MEDIUM)
// Start a sequence without randomizing it
`uvm_info ("SEQ2", "uvm_send (m_seq3) - Send sequence", UVM_MEDIUM)
`uvm_send (m_seq3)
// Same, with a priority
`uvm_info ("SEQ2", "uvm_send_pri (m_seq3, 9) - Send with Priority", UVM_MEDIUM)
`uvm_send_pri (m_seq3, 9)
`uvm_info ("SEQ2", "uvm_rand_send (m_seq3) - Send after randomization", UVM_MEDIUM)
`uvm_rand_send (m_seq3)
`uvm_info ("SEQ2", "uvm_rand_send_pri (m_seq3, 8) - Send with priority after randomization", UVM_MEDIUM)
`uvm_rand_send_pri (m_seq3, 8)
`uvm_info ("SEQ2", "uvm_rand_send_with (m_seq3, {data == 4; addr == 23;) - Send with randomization constraints", UVM_MEDIUM)
`uvm_rand_send_with (m_seq3, { m_data3.data == 8'h4;
m_data3.addr == 8'h23;})
`uvm_info ("SEQ2", "Ending seq2", UVM_MEDIUM)
endtask
virtual task post_body ();
`uvm_info (get_type_name(), "Executing post_body", UVM_MEDIUM)
endtask
endclass
seq3 holds a rand handle to a data item. Randomizing the sequence therefore randomizes m_data3 as well, which makes it easy to see which macros randomize and which do not.
class seq3 extends base_sequence;
`uvm_object_utils (seq3)
rand my_data m_data3;
function new (string name = "seq3");
super.new (name);
m_data3 = my_data::type_id::create ("m_data3");
endfunction
virtual task body ();
`uvm_info ("SEQ3", "Starting seq3", UVM_MEDIUM)
m_data3.print (uvm_default_table_printer);
`uvm_info ("SEQ3", "Ending seq3", UVM_MEDIUM)
endtask
endclass
UVM_INFO ./tb/my_pkg.sv(227) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] Starting seq2
UVM_INFO ./tb/my_pkg.sv(230) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] uvm_send (m_seq3) - Send sequence
UVM_INFO ./tb/my_pkg.sv(270) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Starting seq3
------------------------------
Name Type Size Value
------------------------------
m_data3 my_data - @3777
data integral 8 'h0
addr integral 8 'h0
------------------------------
UVM_INFO ./tb/my_pkg.sv(272) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(234) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] uvm_send_pri (m_seq3, 9) - Send with Priority
UVM_INFO ./tb/my_pkg.sv(270) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Starting seq3
------------------------------
Name Type Size Value
------------------------------
m_data3 my_data - @3777
data integral 8 'h0
addr integral 8 'h0
------------------------------
UVM_INFO ./tb/my_pkg.sv(272) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(237) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] uvm_rand_send (m_seq3) - Send after randomization
UVM_INFO ./tb/my_pkg.sv(270) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Starting seq3
------------------------------
Name Type Size Value
------------------------------
m_data3 my_data - @3777
data integral 8 'h61
addr integral 8 'h31
------------------------------
UVM_INFO ./tb/my_pkg.sv(272) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(240) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] uvm_rand_send_pri (m_seq3, 8) - Send with priority after randomization
UVM_INFO ./tb/my_pkg.sv(270) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Starting seq3
------------------------------
Name Type Size Value
------------------------------
m_data3 my_data - @3777
data integral 8 'ha2
addr integral 8 'h9f
------------------------------
UVM_INFO ./tb/my_pkg.sv(272) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(243) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] uvm_rand_send_with (m_seq3, {data == 4; addr == 23;) - Send with randomization constraints
UVM_INFO ./tb/my_pkg.sv(270) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Starting seq3
------------------------------
Name Type Size Value
------------------------------
m_data3 my_data - @3777
data integral 8 'h4
addr integral 8 'h23
------------------------------
UVM_INFO ./tb/my_pkg.sv(272) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(247) @ 530000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] Ending seq2
UVM_INFO ./tb/my_pkg.sv(118) @ 530000: uvm_test_top.m_top_env.m_drv0 [my_driver] Finished DUT simulation
--- UVM Report catcher Summary ---
The same @3777 item appears in every run of seq3, because none of these macros create anything. The two plain send calls print zeros, the values it was constructed with. The two `uvm_rand_send calls produce new random values, and `uvm_rand_send_with produces exactly the constrained 'h4 and 'h23.
Two other details are worth noticing. The full name seq1.m_seq2.m_seq3 shows each child was started with its caller as the parent sequence. And Executing pre_body from seq2 never appears: sequences started through these macros run with pre_body() and post_body() skipped, as described in How to execute sequences via start( ).
Which Macro to Pick
- Fully random transaction:
`uvm_door`uvm_do_with. One line, nothing to manage. - Directed transaction with exact values:
`uvm_create, assign the fields, then`uvm_send. - Same object, new random values each time:
`uvm_rand_sendor`uvm_rand_send_within a loop, which also lets you keeprand_mode()andconstraint_mode()settings between sends. - Replay an exact transaction:
`uvm_sendon the handle you already have.
All of these macros expand into ordinary calls to start_item() and finish_item() for items, or start() for sequences. Many teams write those calls directly because they are easier to debug, and the macros remain a convenient shorthand.
Common Mistakes
Mistake #1: Passing a null handle to `uvm_send
The send macros never create the object. Create it first with `uvm_create or with an earlier `uvm_do, or the sequence fails with a null-object error at run time.
Mistake #2: Expecting `uvm_do to reuse an existing object
As the log shows, `uvm_do builds a new object on each call. If you need to keep a handle on an earlier transaction, copy or clone it before the next `uvm_do, or switch to `uvm_rand_send on an object you control.
Mistake #3: Putting must-run code in pre_body() of a child sequence
A sequence started with any of these macros skips its pre_body() and post_body(). Use pre_start() and post_start() for code that must always run.
Summary
In this article, you learned:
`uvm_sendsends an existing object as it is, and`uvm_rand_sendrandomizes it first; neither creates anything`uvm_createfollowed by manual assignments and`uvm_sendgives full control over a transaction_privariants add a priority, and_withvariants add inline constraints`uvm_docreates a new object every time it runs- The macros also start child sequences, with
pre_body()andpost_body()skipped