A UVM sequence library is a special sequence that holds a collection of registered sequence types and runs them a configurable number of times, picking which one to run next by random, cyclic-random or user-defined selection. It is the built-in way to get varied stimulus from a set of existing sequences without writing a new top-level sequence for every combination.
8 min read | Intermediate Level
What You'll Learn
- What
uvm_sequence_libraryis and how its selection modes work - Create a sequence library and register sequences statically and dynamically
- Configure iteration count and start the library on a sequencer
- Read the library's log output and avoid common setup errors
What is a UVM sequence library ?
A sequence defines how a series of uvm_sequence_item objects are created, randomized and sent to the DUT. Its main logic lives in the body() task.
Unlike UVM components, which are created once and form the fixed testbench hierarchy, sequences are transient objects. They are created, run and then discarded.
The uvm_sequence_library is itself a sequence, but one that holds a list of other sequence types. When it is started like any other sequence, it repeatedly selects a sequence from that list and executes it. The selection_mode field decides how each sequence is picked.
| Mode | Description |
|---|---|
UVM_SEQ_LIB_RAND | Randomly select a sequence from the list each iteration. Repeats are allowed. |
UVM_SEQ_LIB_RANDC | Randomly select without repeating until every sequence in the list has been executed once. |
UVM_SEQ_LIB_ITEM | Run no sequences. Each iteration sends a single randomized item of the library's item type. |
UVM_SEQ_LIB_USER | Call the select_sequence() method to get the index of the next sequence. Override this method to define your own selection order. |
The uvm_sequence_library class extends the uvm_sequence base class, so it can do anything a sequence can do.
class uvm_sequence_library #(type REQ=uvm_sequence_item, RSP=REQ) extends uvm_sequence#(REQ,RSP);
Syntax
class LIB_NAME extends uvm_sequence_library #(ITEM_TYPE); // Same item type as member sequences
`uvm_object_utils (LIB_NAME) // Normal factory registration
`uvm_sequence_library_utils (LIB_NAME) // Adds the library's registration table
function new (string name = "LIB_NAME");
super.new (name);
init_sequence_library (); // Copies registered sequences into this instance
endfunction
endclass
Key fields you set before starting the library:
selection_mode- one of the four modes in the table above (defaultUVM_SEQ_LIB_RAND)min_random_count/max_random_count- limits for the number of iterationssequence_count- the actual number of iterations, chosen between the two limits when the library is randomized
Why use a sequence library ?
- Automated stimulus selection: The library picks and runs sequences from its list on its own. This produces varied and less predictable stimulus without hard-coding every scenario, which helps reach DUT corner cases.
- Regression testing: The same library can run a different mix of sequences with every random seed, so a single test becomes many useful regression runs.
- Configurable generation: Changing one field switches between pure random, cyclic random, item-only and user-defined selection, without touching the member sequences.
Where are Sequence Libraries Used?
Sequence libraries are common in block-level regressions for bus and memory controllers. A library of read, write, burst and error-injection sequences is run with hundreds of seeds, so each run exercises a different order and mix of traffic from the same test.
How to Use
1. Define Your Library
Extend the uvm_sequence_library class and parameterize it with the sequence item type its member sequences produce.
class my_seq_lib extends uvm_sequence_library #(my_item);
2. Factory Registration
Register the library with the factory using `uvm_object_utils. Also call `uvm_sequence_library_utils, which adds the static table that member sequences register into.
`uvm_object_utils(my_seq_lib)
`uvm_sequence_library_utils(my_seq_lib)
3. Initialization
Call init_sequence_library() in the library's constructor. It copies every sequence registered with this library type into the new instance.
function new(string name="my_seq_lib");
super.new(name);
init_sequence_library();
endfunction
4. Add Member Sequences
Sequences can be added in two ways.
Statically: The `uvm_add_to_seq_lib macro is placed inside the sequence class itself. It registers that sequence with one or more library types.
class my_seq1 extends my_seq;
`uvm_object_utils(my_seq1);
`uvm_add_to_seq_lib(my_seq1, my_seq_lib)
`uvm_add_to_seq_lib(my_seq1, my_another_seq_lib)
...
endclass
Dynamically: The static methods add_typewide_sequence() and add_typewide_sequences() register sequences with the library type, so every instance created afterwards includes them. The simplest place to call them is the library's own constructor, before init_sequence_library().
class my_seq_lib extends uvm_sequence_library #(my_item);
`uvm_object_utils(my_seq_lib)
`uvm_sequence_library_utils(my_seq_lib)
function new(string name="my_seq_lib");
super.new(name);
add_typewide_sequences({mem_seq1::get_type(), mem_seq2::get_type()});
init_sequence_library();
endfunction
endclass
To add a sequence to one library instance only, use add_sequence() or add_sequences(). This is usually done in the test that creates the instance.
class my_seq_lib_test extends base_test;
...
task main_phase(uvm_phase phase);
phase.raise_objection(this, "Raising Main Objection");
// Register another sequence with this sequence library instance
seq_lib.add_sequence( data_err_seq::get_type() );
// Start the data sequence
seq_lib.start(data_sequencer); // This task call is blocking
phase.drop_objection(this, "Dropping Main Objection");
endtask : main_phase
endclass : my_seq_lib_test
start() does not randomize the library. When it is started this way, sequence_count keeps its default value of 10 unless you set it directly or call randomize() on the library first. A library started as a sequencer's default_sequence is randomized automatically.
Quick Check
Q: You call add_typewide_sequence() and add_sequence() on the same library. Which one affects other instances of that library type?
Show Answer
add_typewide_sequence(). It registers the sequence with the library type, so instances initialized later include it. add_sequence() changes only the instance it is called on.
Sequence Library Example
A large verification environment can have hundreds of tests with different seeds and configurations. One way to manage this is to write small sequences that each do one job, and let a sequence library combine them at random.
Take three sequences, all derived from the same base_sequence class. They are independent sequences, not nested inside each other.
class seq1 extends base_sequence;
...
virtual task body ();
`uvm_info ("SEQ1", "Starting seq1", UVM_MEDIUM)
#10;
`uvm_info ("SEQ1", "Ending seq1", UVM_MEDIUM)
endtask
...
endclass
class seq2 extends base_sequence;
...
virtual task body ();
`uvm_info ("SEQ2", "Starting seq2", UVM_MEDIUM)
#10;
`uvm_info ("SEQ2", "Ending seq2", UVM_MEDIUM)
endtask
...
endclass
class seq3 extends base_sequence;
...
virtual task body ();
`uvm_info ("SEQ3", "Starting seq3", UVM_MEDIUM)
#10;
`uvm_info ("SEQ3", "Ending seq3", UVM_MEDIUM)
endtask
...
endclass
A custom sequence library is then derived from uvm_sequence_library.
class my_seq_lib_v2 extends uvm_sequence_library #(my_data);
`uvm_object_utils (my_seq_lib_v2)
`uvm_sequence_library_utils (my_seq_lib_v2)
function new (string name="my_seq_lib_v2");
super.new (name);
init_sequence_library();
endfunction
endclass
Always call `uvm_sequence_library_utils in a library class. Without it, the library has no registration table, and sequences added with `uvm_add_to_seq_lib cannot find it.
Each library is itself a sequence, so it can be started on different sequencers or in different phases of the same sequencer. A test creates an instance, configures it, adds the sequences it needs, and then makes it the sequencer's default sequence.
class feature_test extends base_test;
`uvm_component_utils (feature_test)
my_seq_lib m_seq_lib0;
seq1 m_seq1;
seq2 m_seq2;
seq3 m_seq3;
function new (string name, uvm_component parent = null);
super.new (name, parent);
endfunction
function void build_phase (uvm_phase phase);
super.build_phase (phase);
m_seq_lib0 = my_seq_lib::type_id::create ("m_seq_lib0");
endfunction
virtual task configure_phase (uvm_phase phase);
super.configure_phase (phase);
`uvm_info ("CFG_PHASE", "Add sequences to library", UVM_MEDIUM)
m_seq_lib0.selection_mode = UVM_SEQ_LIB_RANDC;
m_seq_lib0.min_random_count = 5;
m_seq_lib0.max_random_count = 10;
m_seq_lib0.add_typewide_sequence (m_seq1.get_type());
m_seq_lib0.add_typewide_sequence (m_seq2.get_type());
m_seq_lib0.add_typewide_sequence (m_seq3.get_type());
m_seq_lib0.init_sequence_library();
endtask
function void start_of_simulation_phase (uvm_phase phase);
super.start_of_simulation_phase (phase);
uvm_config_db#(uvm_sequence_base)::set(this,"m_top_env.m_seqr0.main_phase",
"default_sequence", m_seq_lib0);
endfunction
endclass
Here is what this test does:
selection_modeis set toUVM_SEQ_LIB_RANDC, so no sequence repeats until all three have run once- The library will run at least 5 and at most 10 iterations
- The three sequences are registered with
add_typewide_sequence(), theninit_sequence_library()is called again so this existing instance picks them up - The library instance is set as the
default_sequenceof m_seqr0 inmain_phase, so the sequencer randomizes and starts it automatically
Note that m_seq1, m_seq2 and m_seq3 are never created. This works because get_type() is a static method, so it can be called through a null class handle.
----------------------------------------------------------------
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 feature_test...
UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology:
------------------------------------------------------------
Name Type Size Value
------------------------------------------------------------
uvm_test_top feature_test - @2677
m_top_env my_env - @229
m_drv0 my_driver - @2793
rsp_port uvm_analysis_port - @2893
seq_item_port uvm_seq_item_pull_port - @2842
m_seqr0 my_sequencer - @223
rsp_export uvm_analysis_export - @2983
seq_item_export uvm_seq_item_pull_imp - @3531
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(92) @ 0: uvm_test_top.m_top_env.m_drv0 [my_driver] Applying initial reset
UVM_INFO ./tb/my_pkg.sv(96) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] DUT is now out of reset
UVM_INFO ./tb/test_pkg.sv(89) @ 390000: uvm_test_top [CFG_PHASE] Add sequence to lib
UVM_INFO ./tb/my_pkg.sv(103) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO /pkg/cadence-incisiv-/14.10.013/i686-linux/tools/methodology/UVM/CDNS-1.1d/sv/src/seq/uvm_sequence_library.svh(664) @ 390000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0 [SEQLIB/START] Starting sequence library my_seq_lib in main phase: 5 iterations in mode UVM_SEQ_LIB_RANDC
UVM_INFO ./tb/my_pkg.sv(166) @ 390000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq1:1 [seq1] Executing pre_start()
UVM_INFO ./tb/my_pkg.sv(174) @ 390000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq1:1 [SEQ1] Starting seq1
UVM_INFO ./tb/my_pkg.sv(176) @ 400000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq1:1 [SEQ1] Ending seq1
UVM_INFO ./tb/my_pkg.sv(223) @ 400000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq3:2 [SEQ3] Starting seq3
UVM_INFO ./tb/my_pkg.sv(225) @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq3:2 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(233) @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq3:2 [seq3] Executing post_start
UVM_INFO ./tb/my_pkg.sv(197) @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq2:3 [SEQ2] Starting seq2
UVM_INFO ./tb/my_pkg.sv(199) @ 420000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq2:3 [SEQ2] Ending seq2
UVM_INFO ./tb/my_pkg.sv(223) @ 420000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq3:4 [SEQ3] Starting seq3
UVM_INFO ./tb/my_pkg.sv(225) @ 430000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq3:4 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(233) @ 430000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq3:4 [seq3] Executing post_start
UVM_INFO ./tb/my_pkg.sv(197) @ 430000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq2:5 [SEQ2] Starting seq2
UVM_INFO ./tb/my_pkg.sv(199) @ 440000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0.seq2:5 [SEQ2] Ending seq2
UVM_INFO /pkg/cadence-incisiv-/14.10.013/i686-linux/tools/methodology/UVM/CDNS-1.1d/sv/src/seq/uvm_sequence_library.svh(738) @ 440000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0 [SEQLIB/END] Ending sequence library in phase main
UVM_INFO /pkg/cadence-incisiv-/14.10.013/i686-linux/tools/methodology/UVM/CDNS-1.1d/sv/src/seq/uvm_sequence_library.svh(740) @ 440000: uvm_test_top.m_top_env.m_seqr0@@m_seq_lib0 [SEQLIB/DSTRB] '{"seq1":1, "seq2":2, "seq3":2}
UVM_INFO ./tb/my_pkg.sv(122) @ 440000: uvm_test_top.m_top_env.m_drv0 [my_driver] Finished DUT simulation
--- UVM Report catcher Summary ---
The log shows the library at work:
- SEQLIB/START reports that randomization picked 5 iterations, within the 5 to 10 limit, in
UVM_SEQ_LIB_RANDCmode - The first three iterations run seq1, seq3 and seq2, so each sequence runs once before any repeats, as RANDC requires. A new cycle then starts with seq3 and seq2
- The suffix in names like
seq3:2is the iteration number, and each sequence runs as a child of m_seq_lib0 - SEQLIB/DSTRB summarizes how many times each sequence ran, which is useful for checking that a regression exercised every sequence
How Teams Use the Distribution Report
In regression flows, scripts often parse the SEQLIB/DSTRB line across all seeds. If one sequence almost never runs, the team adjusts the selection mode, the iteration limits or the library contents rather than writing new tests.
Common Mistakes
Mistake #1: Adding typewide sequences to an existing instance without re-initializing
Wrong: calling add_typewide_sequence() on a library instance that is already created, and then starting it
Right: call init_sequence_library() again after the additions, as feature_test does
Why? An instance copies the typewide list only when init_sequence_library() runs. Sequences registered with the type afterwards do not appear in existing instances until it is called again.
Mistake #2: Expecting min_random_count and max_random_count to apply with start()
Wrong: setting min_random_count = 20 and max_random_count = 20, then calling start() directly
Right: call randomize() on the library before start(), or set sequence_count directly
Why? The limits only constrain sequence_count during randomization. start() does not randomize the library, so it runs the default 10 iterations. A default sequence is randomized by the sequencer, which is why the limits work in feature_test.
Mistake #3: Forgetting init_sequence_library() in the constructor
Sequences registered with `uvm_add_to_seq_lib are stored in the library type's table. Without init_sequence_library(), a new instance never copies them, so the library starts with an empty list and has nothing to run.
Practice Exercise
Challenge: Run Exactly 20 Random Iterations
Create a library burst_seq_lib of item type my_data that contains seq1 and seq2. In a test's main_phase, run it on m_top_env.m_seqr0 using start(), in UVM_SEQ_LIB_RAND mode, for exactly 20 iterations.
Hint
Setting min_random_count and max_random_count to the same value fixes the count, but only if the library is randomized before start().
Solution
class burst_seq_lib extends uvm_sequence_library #(my_data);
`uvm_object_utils (burst_seq_lib)
`uvm_sequence_library_utils (burst_seq_lib)
function new (string name = "burst_seq_lib");
super.new (name);
add_typewide_sequences ({seq1::get_type(), seq2::get_type()}); // Register before init
init_sequence_library (); // Copy into this instance
endfunction
endclass
class burst_test extends base_test;
`uvm_component_utils (burst_test)
// Rest of the code (constructor, build_phase, etc.)
virtual task main_phase (uvm_phase phase);
burst_seq_lib m_lib = burst_seq_lib::type_id::create ("m_lib");
phase.raise_objection (this);
m_lib.selection_mode = UVM_SEQ_LIB_RAND;
m_lib.min_random_count = 20; // min = max fixes the count
m_lib.max_random_count = 20;
if (!m_lib.randomize ()) // start() does not randomize
`uvm_fatal ("RAND", "burst_seq_lib randomization failed")
m_lib.start (m_top_env.m_seqr0); // Blocks until all 20 iterations finish
phase.drop_objection (this);
endtask
endclass
The SEQLIB/START message should report 20 iterations in UVM_SEQ_LIB_RAND mode, and the SEQLIB/DSTRB counts should add up to 20.
Summary
In this tutorial, you learned:
- A
uvm_sequence_libraryis a sequence that repeatedly selects and runs sequences from a registered list - Selection modes RAND, RANDC, ITEM and USER control how each iteration is chosen
- A library needs
`uvm_object_utils,`uvm_sequence_library_utilsandinit_sequence_library()in its constructor - Sequences are added statically with
`uvm_add_to_seq_lib, typewide withadd_typewide_sequence(), or per instance withadd_sequence() - The iteration count comes from
sequence_count, which the min and max limits constrain only when the library is randomized - The SEQLIB/START and SEQLIB/DSTRB messages show the iteration count and how often each sequence ran