The short answer: A UVM sequence (uvm_sequence) generates sequence items in its body() task and sends each one to a sequencer with start_item and finish_item. The sequencer (uvm_sequencer) arbitrates when several sequences run at once and hands items to the driver, which calls get_next_item, drives the item and calls item_done. Tests start sequences on a sequencer with seq.start(sqr). Sequences can be layered and nested, and virtual sequences coordinate several sequencers at once.
Sequences are where most of a verification engineer’s time goes: they describe the scenarios. Getting the handshake with the driver right, and knowing how to build sequences from smaller ones, makes them much easier to write and reuse. This post combines my older posts on sequences and sequencers.
- The handshake
- A basic sequence
- The driver side
- Starting sequences
- Building bigger scenarios
- Habits that help
Table of Contents
Part 1: The handshake

Part 2: A basic sequence
class write_seq extends uvm_sequence #(bus_item);
`uvm_object_utils(write_seq)
rand int unsigned n = 10;
function new(string name = "write_seq");
super.new(name);
endfunction
task body();
repeat (n) begin
req = bus_item::type_id::create("req");
start_item(req);
if (!req.randomize() with { kind == bus_item::WRITE; })
`uvm_error("RAND", "randomize failed")
finish_item(req);
end
endtask
endclass
The sequencer is simply uvm_sequencer #(bus_item); you rarely need to extend it.
Part 3: The driver side
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive(req); // wiggle pins through the virtual interface
seq_item_port.item_done();
end
endtask
If the DUT returns data (a read), the driver writes it into the item before item_done, or sends a separate response with put_response, which the sequence collects with get_response.
Part 4: Starting sequences
task run_phase(uvm_phase phase);
write_seq wseq = write_seq::type_id::create("wseq");
phase.raise_objection(this);
if (!wseq.randomize() with { n == 50; }) `uvm_fatal("RAND", "")
wseq.start(env.agt_in.sqr);
phase.drop_objection(this);
endtask
Part 5: Building bigger scenarios
- Nested sequences: a sequence can start other sequences in its body (
sub.start(m_sequencer, this)), so you build scenarios from small, tested pieces. - Parallel sequences: start several in a fork; the sequencer arbitrates (FIFO by default; priority, random and others are available).
- lock and grab: give one sequence exclusive access, for example for an interrupt handler.
- Virtual sequences: a sequence that runs on no sequencer of its own but starts sub-sequences on several agents’ sequencers, coordinating, say, register setup on APB with traffic on AXI.
class top_vseq extends uvm_sequence;
`uvm_object_utils(top_vseq)
uvm_sequencer #(apb_item) apb_sqr;
uvm_sequencer #(axi_item) axi_sqr;
task body();
cfg_seq c = cfg_seq::type_id::create("c");
traffic_seq t = traffic_seq::type_id::create("t");
c.start(apb_sqr); // configure first
t.start(axi_sqr); // then send traffic
endtask
endclass
Part 6: Habits that help
- Randomise inside start_item/finish_item (late randomisation), so the item reflects the latest state.
- Keep sequences focused: one scenario each, with rand knobs for variation.
- Build a base sequence with common helpers (register access, waits) and extend it.
- Always check randomize() results.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Design Verification problems · Daily challenge
Previous in my UVM series: Sequence items. Next: Drivers, monitors and agents.
FAQ
What is the difference between a sequence and a sequencer?
A sequence is an object that generates items; a sequencer is a component that arbitrates between sequences and passes items to the driver.
What do start_item and finish_item do?
start_item waits for the sequencer to grant access; finish_item sends the item and waits until the driver calls item_done.
What is a virtual sequence?
A sequence that coordinates sub-sequences on several sequencers, typically one per agent, to build system-level scenarios.
What is the default sequencer arbitration?
FIFO (UVM_SEQ_ARB_FIFO). Others include weighted, random, strict FIFO and user-defined.
