TO Be coming

Oct 31, 2026

UVM Course: Day 3: Sequences, Sequencer, Driver — The Stimulus Handshake

Day 3: Sequences, Sequencer, Driver — The Stimulus Handshake
Advertisement
Top banner — drop your 728×90 or responsive ad unit here.

Day 3: Sequences, Sequencer, Driver — The Stimulus Handshake

A 30-day SystemVerilog/UVM verification series. Day 1 built the skeleton. Day 2 wired the lifecycle. Today we put the testbench in motion.

By now you have a working UVM environment: driver, monitor, sequencer, agent, scoreboard, env, test. But there is one piece we've been treating as a black box — how does a transaction actually get from a sequence (pure data, no concept of time) to a driver (pure time, no concept of what to send)?

That bridge is the sequencer, and the handshake between sequence and driver is the single most misunderstood part of UVM for beginners. Today we take it apart.


1. Three Classes, Three Jobs

Before the handshake, get the roles straight. These are different classes with different responsibilities, and confusing them is the number one source of bugs:

ClassBaseJobKnows about time?
Sequence itemuvm_sequence_itemHolds the data for one transactionNo
Sequenceuvm_sequence #(item)Decides what to send, in what orderIndirectly (via handshake)
Sequenceruvm_sequencer #(item)Arbitrates between sequences, buffers itemsNo
Driveruvm_driver #(item)Translates an item into pin wigglesYes — the only one that drives pins
Mental model: a sequence is the playwright — it writes the script but doesn't act. The driver is the actor — it performs whatever script it's handed. The sequencer is the stage manager — it takes scripts from playwrights and hands them to the actor, one at a time.

2. The Handshake — Who Calls Whom

The sequence-driver handshake has two halves, one on each side:

SEQUENCE side (inside body()):
    start_item(item);      // "sequencer, I want to send this"
    item.randomize();      // (after start_item — see section 3)
    finish_item(item);     // "sequencer, send it now"

SEQUENCER (in the middle):
    holds the item until the driver is ready

DRIVER side (inside run_phase):
    seq_item_port.get_next_item(req);   // "give me the next item"
    drive_on_pins(req);                 // do the actual driving
    seq_item_port.item_done();          // "I'm done with this one"

Every transaction passes through all four calls. Miss any one and the testbench either hangs or races.

Most common bug: calling randomize() before start_item(). It compiles and sometimes even runs, but start_item can trigger a sequence's arbitration and pre/post hooks that expect the item to be unmodified. The UVM LRM says randomize after start_item. Follow it.

3. Why start_item Can Block

start_item is not just a "register interest" call. It can block. Two reasons:

  • Sequencer arbitration. If multiple sequences are running on the same sequencer, one may be locked out while another has priority. start_item waits for the arbitration to grant access.
  • Grab / lock. If another sequence called sequencer.grab() or lock(), your sequence is blocked until the grab is released.

Once start_item returns, the sequencer has granted the sequence access to send that one item. It randomizes, then finish_item sends the item to the driver and waits for completion. The driver's item_done completes this item handshake; it does not release an explicit sequencer lock or grab, which must be released separately.


4. The Canonical Write Sequence

Here's the write sequence for our FIFO, with what each line actually does:

class fifo_write_seq extends uvm_sequence #(fifo_item);
    `uvm_object_utils(fifo_write_seq)

    function new(string name = "fifo_write_seq");
        super.new(name);
    endfunction

    task body();
        fifo_item tr;
        repeat (20) begin
            tr = fifo_item::type_id::create("tr");
            start_item(tr);                       // (1) wait for access
            if (!tr.randomize() with { is_write == 1; })
              `uvm_fatal("SEQ", "randomize failed")  // (2) randomize
            finish_item(tr);                      // (3) hand to driver
        end
    endtask
endclass

Three lines in the loop body do all the work. That's the whole sequence. It's small because the sequencer handles all the coordination.


5. The Driver's Side

task run_phase(uvm_phase phase);
    vif.drv_cb.wr_en   <= 0;
    vif.drv_cb.wr_data <= 0;
    forever begin
        seq_item_port.get_next_item(req);    // (1) wait for next item
        vif.drv_cb.wr_en   <= req.is_write;  // (2) drive on pins
        vif.drv_cb.wr_data <= req.data;
        @(vif.drv_cb);                       // (3) one clock of driving
        seq_item_port.item_done();           // (4) signal completion
    end
endtask

Four lines inside forever. The driver doesn't know how many items are coming, in what order, or what values. It just drives what it's given.

Deadlock trap: if you call get_next_item but never item_done, the sequencer blocks forever. No error, no timeout warning until the default 9200-second phase timeout fires. If your test hangs with no output, this is the first thing to check.

6. What randomize() Actually Buys You

The with clause is where the sequence controls the stimulus:

if (!tr.randomize() with { is_write == 1; data inside {[8'h00 : 8'hFF]}; })

Constraints inside with are extra constraints on top of the item's own rand fields. The item might also declare its own constraints (constraint c_data { data != 0; }) that always apply. The with clause is per-call. This is how one sequence can send corner-case data, another can send random data, without either modifying the item class.


7. Common Deadlock Patterns

SymptomCause
Simulation hangs after first itemDriver never calls item_done()
Simulation hangs on second sequenceFirst sequence called grab() but never ungrab()
Sequence body finishes but test doesn't endTest forgot to drop_objection (see Day 2)
Only one sequence's items reach the driverA sequence holds a lock/grab, or an arbitration policy repeatedly favors another sequence
Sequence randomly errors out mid-runrandomize() failed — check constraints are satisfiable

8. A Real Read Sequence

Reads are trickier because the DUT returns data after the request. Here's how a read sequence works with our FIFO:

class fifo_read_seq extends uvm_sequence #(fifo_item);
    `uvm_object_utils(fifo_read_seq)

    task body();
        fifo_item tr;
        repeat (20) begin
            tr = fifo_item::type_id::create("tr");
            start_item(tr);
            if (!tr.randomize() with { is_write == 0; })
                `uvm_error("SEQ", "randomize failed")
            finish_item(tr);
        end
    endtask
endclass

The monitor, not the sequence, observes the returned read data. The sequence just requests the read; the scoreboard checks the response via the monitor's analysis port (Day 7 will build that comparison).


9. How a Test Starts a Sequence

task run_phase(uvm_phase phase);
    fifo_write_seq wseq;
    fifo_read_seq  rseq;

    phase.raise_objection(this);

    wseq = fifo_write_seq::type_id::create("wseq");
    rseq = fifo_read_seq::type_id::create("rseq");

    wseq.start(env.agent.sqr);
    rseq.start(env.agent.sqr);

    phase.drop_objection(this);
endtask

start(sequencer) launches the sequence. It runs to completion (the sequence's body() returns), then control returns to the test. Two sequences run sequentially here — the read only starts after the write completes.

Parallel sequences: to run them simultaneously, wrap in fork...join. Both sequences share the same sequencer; arbitration decides which gets access to the driver at any moment.

10. What Day 4 Will Add

We'll take this further with transactions and TLM: how uvm_analysis_port and uvm_analysis_imp actually connect, what happens to a transaction as it travels from monitor to scoreboard (and whether the driver can modify it in flight), and how to wire up a scoreboard that does real comparison instead of just counting writes.


11. Quiz

Ten questions on sequences, sequencers, and drivers. One question at a time, options shuffled on each page load, Submit reveals all answers with explanations.

Question 1 of 10
Loading…
    0 / 10
    Advertisement
    Bottom banner — drop your 728×90 or responsive ad unit here.

    12. Further Reading


    Coming Up Next: Day 4 — Transactions and TLM

    • TLM ports vs analysis ports — when to use which, and why the analysis port broadcasts while the TLM port is one-to-one.
    • Connecting ports to imports — the exact rule for which port goes to which export, with the FIFO example.
    • Do transactions get copied? — how analysis ports handle multiple subscribers, and where a shallow copy bites you.
    • A real scoreboard — comparing expected vs observed for the FIFO, including ordering and overflow checks.

    Series: 30 days of SystemVerilog/UVM verification. Day 1 built the skeleton. Day 2 wired the lifecycle. Day 3 put it in motion.

    SiteMap