UVM Sequence
UVM sequences are the core of stimulus generation in a UVM testbench. They define the order and content of transactions that are sent to the driver, which in turn drives the design under verification (DUV). Sequences provide a powerful mechanism for creating complex and reusable test scenarios.
Diagram of UVM Sequences
Your Roadmap
+-----------------------+
| UVM Environment |
| |
| +------------------+ |
| | Agent | |
| | +--------------+ | |
| | | Driver | | |
| | +--------------+ | |
| +------------------+ |
| |
| +------------------+ |
| | Sequencer | |
| +------------------+ |
| ^ |
| | |
| +--------------+ |
| | Sequence | |
| +--------------+ |
| ^ |
| | |
| +--------------+ |
| | Sequence Item| |
| +--------------+ |
+-----------------------+
|
v
+----------------------+
| DUT |
+----------------------+
Why Use Sequences?
- Organization: Sequences organize transactions into logical units, making tests easier to understand and maintain.
- Randomization: Sequences can randomize transaction data, allowing for comprehensive coverage of different input scenarios.
- Control Flow: Sequences can control the flow of transactions, including loops, conditional branching, and synchronization with other sequences.
- Reusability: Sequences can be reused in different tests, saving development time and effort.
- Abstraction: Sequences operate at a higher level of abstraction than drivers, focusing on the “what” (what transactions to send) rather than the “how” (how to drive the interface).
Structure of a UVM Sequence:
A UVM sequence is a class that extends uvm_sequence
. It typically contains the following key elements:
- `uvm_object_utilsĀ Macro: This macro is essential for registering the sequence with the UVM factory.
body()
Task: This task defines the sequence’s main behavior, including creating, randomizing, and sending sequence items to the driver.- Sequence Item Declarations: Declarations of the sequence item types that the sequence will use.
Diagram 1: Sequence in the Testbench Architecture
+-----------+ +-----------+ +---------+ +-----+
| Sequencer |---->| Sequence |---->| Driver |---->| DUV |
+-----------+ +-----------+ +---------+ +-----+
| get_next_ | | create() | | drive() | | |
| item() | | randomize()| | | | |
+-----------+ +-----------+ +---------+ +-----+
^
| Sequence Item
Defining a UVM Sequence
1. Sequence Class
A sequence class is derived from uvm_sequence
. It implements the main functionality to generate and send sequence items to the driver.
class my_sequence extends uvm_sequence#(my_transaction);
`uvm_object_utils(my_sequence)
function new(string name = "my_sequence");
super.new(name);
endfunction
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
2. Creating and Starting Sequences
Sequences are typically created and started within the test class or environment.
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_env env;
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
// Start the sequence
env.m_sequencer.start(my_sequence::type_id::create("my_seq"));
phase.drop_objection(this);
endtask
endclass
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_sequence extends uvm_sequence#(my_transaction);
`uvm_object_utils(my_sequence)
function new(string name = "my_sequence", uvm_component parent = null);
super.new(name, parent);
endfunction
task body();
my_transaction req;
repeat (10) begin // Repeat the sequence 10 times
req = my_transaction::type_id::create("req"); // Create a transaction
if (!req.randomize()) begin // Randomize the transaction
`uvm_error("SEQ_RAND_FAIL", "Failed to randomize transaction");
end
start_item(req); // Send the transaction to the driver
finish_item(req); // Signal transaction completion
end
endtask
endclass
Diagram 2: Sequence Class Structure
+-----------------+
| my_sequence |
+-----------------+
| `uvm_object_utils|
| body() |
| ... |
+-----------------+
Starting Sequences:
Sequences are started on a sequencer using the start()
method.
my_sequence seq = new();
seq.start(env.agent.sequencer); // Start the sequence on the agent's sequencer
Sequence Item Methods:
start_item(item)
: Sends a sequence item to the driver.finish_item(item)
: Signals the completion of a sequence item.get_response(rsp)
: Retrieves a response from the driver (if applicable).
Sequence Control Flow:
repeat()
: Repeats a block of code a specified number of times.if-else
: Conditional branching.fork-join
: Parallel execution of multiple blocks of code.wait_for()
: Waits for a specific event.
Example with fork-join
:
task body();
fork
begin
my_sequence seq1 = new();
seq1.start(m_sequencer);
end
begin
my_other_sequence seq2 = new();
seq2.start(m_sequencer);
end
join
endtask