UVM Sequencer
The UVM sequencer is a crucial component in a UVM testbench, responsible for generating and managing sequences of transactions (sequence items). It acts as the source of stimulus for the driver, orchestrating the flow of data to the Design Under Verification (DUV).
Your Roadmap
Diagram of UVM Sequencer
+-----------------------+
| UVM Environment |
| |
| +------------------+ |
| | Agent | |
| | +--------------+ | |
| | | Sequencer | | |
| | +--------------+ | |
| | +--------------+ | |
| | | Driver | | |
| | +--------------+ | |
| +------------------+ |
| |
| +------------------+ |
| | Sequence | |
| +------------------+ |
| |
| +------------------+ |
| | Sequence Item | |
| +------------------+ |
+-----------------------+
|
v
+----------------------+
| DUT |
+----------------------+
Defining a UVM Sequencer
1. Sequencer Class
A sequencer class is derived from uvm_sequencer
. It manages the flow of sequence items from sequences to the driver.
class my_sequencer extends uvm_sequencer#(my_transaction);
`uvm_component_utils(my_sequencer)
function new(string name = "my_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
2. Using the Sequencer in an Agent
Sequencers are typically instantiated and connected within an agent.
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
my_driver drv;
my_sequencer seqr;
function new(string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = my_driver::type_id::create("drv", this);
seqr = my_sequencer::type_id::create("seqr", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(seqr.seq_item_export);
endfunction
endclass
Creating and Running Sequences
Sequences generate and send sequence items to the sequencer.
class my_sequence extends uvm_sequence#(my_transaction);
`uvm_object_utils(my_sequence)
task body();
my_transaction tx;
tx = my_transaction::type_id::create("tx");
start_item(tx);
if (!tx.randomize())
`uvm_fatal("RAND_ERR", "Randomization failed");
finish_item(tx);
endtask
endclass
Why Use a Dedicated Sequencer?
- Centralized Sequence Management: Provides a single point for generating and controlling sequences.
- Sequence Prioritization: Allows you to prioritize different sequences, controlling which transactions are sent to the driver first.
- Sequence Randomization: Works in conjunction with sequences to randomize transaction data, creating diverse test scenarios.
- Phasing and Synchronization: Helps synchronize the execution of sequences with the rest of the testbench.
- Reusability: Sequencers can be reused across different tests and even different projects, as long as the transaction type remains the same.
Structure of a UVM Sequencer:
A UVM sequencer is a class that extends uvm_sequencer
. It typically contains the following key elements:
- `uvm_component_utils Macro: This macro is essential for registering the sequencer with the UVM factory and enabling other UVM features.
seq_item_export
: An export of typeuvm_seq_item_pull_port
used to provide sequence items to the driver.new()
Constructor: The constructor is used to create an instance of the sequencer.
Diagram 1: Sequencer in the Testbench Architecture
+-----------+ +-----------+ +---------+ +-----+
| Sequencer |---->| Sequence |---->| Driver |---->| DUV |
+-----------+ +-----------+ +---------+ +-----+
| get_next_ | | create() | | drive() | | |
| item() | | randomize()| | | | |
+-----------+ +-----------+ +---------+ +-----+
Example Code:
`include "uvm_macros.svh"
class my_transaction extends uvm_sequence_item; // (From previous examples)
rand bit [7:0] address;
rand bit [31:0] data;
rand bit write_enable;
`uvm_object_utils(my_transaction)
// ... (rest of the transaction class)
endclass
class my_sequencer extends uvm_sequencer#(my_transaction);
`uvm_component_utils(my_sequencer)
function new(string name = "my_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
Diagram 2: Sequencer Class Structure
+-----------------+
| my_sequencer |
+-----------------+
| `uvm_component_|
| utils(my_seq) |
| seq_item_export|
| new() |
+-----------------+
How Sequences Interact with the Sequencer:
- A sequence is started on a specific sequencer using the
seq.start(sequencer)
method. - The sequence then calls
sequencer.get_next_item()
to request a new sequence item. - The sequencer creates a new sequence item (using the factory).
- The sequence randomizes the sequence item’s data members.
- The sequence sends the randomized sequence item to the driver using
sequencer.seq_item_port.put()
. - The driver receives the sequence item using
seq_item_port.get_next_item()
.
Diagram 3: Sequence and Sequencer Interaction
+-----------+ get_next_item() +-----------+ put() +---------+
| Sequence |---------------------->| Sequencer |------------->| Driver |
+-----------+ +-----------+ +---------+
| create() | | create() | | |
| randomize()| | | | |
+-----------+ +-----------+ +---------+
Example of Sequence Starting on a Sequencer:
class my_sequence extends uvm_sequence#(my_transaction);
`uvm_object_utils(my_sequence)
task body();
my_transaction trans;
repeat (10) begin // Generate 10 transactions
trans = my_transaction::type_id::create("trans");
assert trans.randomize();
start_item(trans); // Send the transaction to the sequencer
finish_item(trans);
end
endtask
endclass
// In a test's run_phase:
task run_phase(uvm_phase phase);
my_sequence seq = new();
phase.raise_objection(this);
seq.start(env.agent.sequencer); // Start the sequence on the sequencer
phase.drop_objection(this);
endtask
Key Takeaways:
- The sequencer generates and manages sequences of transactions.
- It uses a
seq_item_export
to provide sequence items to the driver. - Sequences are started on a specific sequencer using
seq.start(sequencer)
. - The sequencer works in conjunction with sequences and drivers to generate stimulus for the DUV.
- The
start_item()
andfinish_item()
tasks are used within the sequence body to send the transaction to the driver.