HomeProblemsTheoryUVM
Intermediate3 min readChapter 6

Sequences & Sequencers

Learn how to generate stimulus with UVM sequences and control flow through sequencers.

Sequences & Sequencers

Sequences are the primary mechanism for generating stimulus in UVM. They create sequence items (transactions) and send them to the driver through a sequencer.


The Pipeline

text
┌────────────┐ ┌─────────────┐ ┌──────────┐ │ Sequence │────▶│ Sequencer │────▶│ Driver │──▶ DUT │ (generates │ │ (arbitrates │ │ (drives │ │ items) │◀────│ items) │◀────│ pins) │ └────────────┘ └─────────────┘ └──────────┘

Writing a Basic Sequence

systemverilog
class write_sequence extends uvm_sequence #(apb_txn); `uvm_object_utils(write_sequence) function new(string name = "write_sequence"); super.new(name); endfunction task body(); apb_txn txn; repeat(10) begin txn = apb_txn::type_id::create("txn"); start_item(txn); // Request grant from sequencer if (!txn.randomize() with { write == 1; addr inside {[32'h0 : 32'hFF]}; }) `uvm_fatal("RAND", "Randomization failed") finish_item(txn); // Send to driver, wait for completion end endtask endclass

Understanding start_item / finish_item

API CallWhat Happens
start_item(txn)Request grant from sequencer (blocks until ready)
txn.randomize()Randomize between start and finish
finish_item(txn)Send item to driver; block until item_done()

Why randomize between start and finish? The sequencer grants access to one sequence at a time. Randomizing after the grant ensures you have the latest context.


The Driver Side

The driver pulls items from the sequencer and drives them:

systemverilog
class apb_driver extends uvm_driver #(apb_txn); `uvm_component_utils(apb_driver) virtual apb_if vif; function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); apb_txn req; forever begin seq_item_port.get_next_item(req); // Pull from sequencer drive_to_pins(req); // Drive on interface seq_item_port.item_done(); // Tell sequencer we're done end endtask task drive_to_pins(apb_txn txn); @(posedge vif.PCLK); vif.PSEL <= 1; vif.PADDR <= txn.addr; vif.PWRITE <= txn.write; if (txn.write) vif.PWDATA <= txn.data; @(posedge vif.PCLK); vif.PENABLE <= 1; do @(posedge vif.PCLK); while (!vif.PREADY); if (!txn.write) txn.data = vif.PRDATA; vif.PSEL <= 0; vif.PENABLE <= 0; endtask endclass

Running a Sequence from the Test

systemverilog
task run_phase(uvm_phase phase); write_sequence seq; phase.raise_objection(this); seq = write_sequence::type_id::create("seq"); seq.start(env.agent.sequencer); // Blocks until body() completes phase.drop_objection(this); endtask

Sequence Composition

Sequences can start other sequences for complex scenarios:

systemverilog
class read_after_write_seq extends uvm_sequence #(apb_txn); `uvm_object_utils(read_after_write_seq) function new(string name = "read_after_write_seq"); super.new(name); endfunction task body(); write_sequence wr_seq; read_sequence rd_seq; // Run writes first, then reads wr_seq = write_sequence::type_id::create("wr_seq"); wr_seq.start(m_sequencer); rd_seq = read_sequence::type_id::create("rd_seq"); rd_seq.start(m_sequencer); endtask endclass

Parallel Sequences

Run multiple sequences concurrently using fork-join:

systemverilog
task body(); fork write_seq.start(m_sequencer); // Writes read_seq.start(m_sequencer); // Reads (concurrent) join endtask

The sequencer uses its arbitration scheme (default: FIFO) to interleave items.


Key Points

ConceptDetail
Sequences are objectsExtend uvm_sequence, not uvm_component
Sequences are transientCreated, run, destroyed
body() taskContains the stimulus logic
start_item/finish_itemPer-transaction handshake
CompositionSequences can start other sequences
ParallelismUse fork-join for concurrent sequences