• No results found

5 Interface UVCs

1 class apb_traffic_seq extends uvm_sequence #(apb_transfer);

2 multi_apb_transfer_seq multi_seq; // rand param is num_seq 3 apb_write_read_word_seq wr_rd_seq; // rand param: start_addr 4 // Constructor and uvm_automation macros for sequences

5 ‘uvm_sequence_utils(apb_write_read_word_seq, apb_master_sequencer) 6 // The body executes two pre-defined sequences with constraints.

7 task body();

8 repeat (5)

9 'uvm_do_with(wr_rd_seq, {start_addr inside {['h0000:'h1fff]};}) 10 'uvm_do_with(multi_seq, {num_seq == 5;)

11 repeat (5)

12 'uvm_do_with(wr_rd_seq, {start_addr inside {['h2000:'hffff]};}) 13 endtask : body

14 endclass : apb_traffic_seq

Lines 2-3: Declarations of previously defined sequences. The multi_apb_transfer_seq has a random parameter for num_seq. When we “do”

this sequence on line 10, we specify the value of the num_seq parameter to be 5. The apb_write_read_word_seq has a random parameter for start_addr.

Lines 9 and 12: The sequence is called with a constraint specified on start_addr. This constraint will be solved in conjunction with the apb_write_read_seq constraint (start_addr[1:0] ==0).

5.9.1.1 Sequence and Sequence Item Macros

So far, we have introduced two macros. But what do they do? Let’s take a look at each.

`uvm_do

This macro takes as an argument either a variable of type uvm_sequence_item or of type uvm_sequence. An object is created using the factory and assigned to the specified variable. When the driver requests an item from the sequencer, the item is randomized and provided to the driver.

The do operation translates into a number of steps:

1. The sequence waits until an item is requested from the driver.

2. The transaction (or sequence) is allocated using the factory.

3. The transaction (or sequence) is randomized with built-in data item or sequence field constraints.

4. If you are doing a transaction, it is sent via TLM to the driver for execution. In the case of a sequence, it starts executing the body() task of the sequence (repeating steps 1–3, above).

5. Block code execution until the item was sent. The driver indicates via TLM that the item is done.

Explicit calls to execute each of the steps above results in more code to maintain and can yield human errors. For example, if you forget to use the factory to allocate a data item, you may get unexpected results when a test overrides the default data item to address a coverage hole. UVM introduces one-line macros to simplify this.

introduces one-line macros to simplify this.

`uvm_do_with

This macro is similar to “`uvm_do”. The first argument is a variable of a type derived from uvm_sequence_item, which includes items and sequences. The second argument can be any valid inline constraints that would be legal if used in arg1.randomize() with inline constraints. This enables adding different inline constraints, while still using the same item or sequence variable.

‘uvm_do_with(req, {req.addr == start_addr;

req.direction == APB_WRITE;)

More options exist for creating and executing data items and sequences. For more information, see Chapter 8 “Stimulus Generation Topics”.

5.9.2 Predefined Sequences

There are three built-in sequences: uvm_random_sequence, uvm_exhaustive_sequence, and uvm_simple_sequence.

5.9.2.1 uvm_random_sequence

This built-in sequence is the default sequence to be executed by the sequencer. This sequence randomly selects and executes one or more sequences from the sequencer’s queue of registered sequences (excluding uvm_random_sequence and uvm_exhaustive_sequence). The number of sequences executed depends on the count field of the sequencer. If count is set to –1, the random sequence will randomize a number between 0 and uvm_sequencer::max_random_count. If count is not –1, then count sequences will be executed by uvm_random_sequence.

Note For a sequencer to not execute any items, set the default sequence to uvm_random_sequence and its count to 0. Users often do this in conjunction with a virtual sequencer (see “Controlling Other Sequencers” for more information).

5.9.2.2 uvm_exhaustive_sequence

This built-in sequence is also predefined in the sequencer. This sequence will exhaustively execute all user-defined sequences for the current sequencer. The predefined uvm_simple_sequence will also be executed, but the other two predefined sequence types (uvm_random_sequence and uvm_exhaustive_sequence) will not. The sequences are executed exactly once and in a random order.

The example below shows the body for the uvm_exhaustive_sequence.

task uvm_exhaustive_sequence::body();

l_count = m_sequencer.sequences.size() - 2;

max_kind = m_sequencer.sequences.size();

l_exhaustive_seq_kind =

m_sequencer.get_seq_kind("uvm_exhaustive_sequence");

repeat (l_count) begin

assert(randomize(l_kind) with {

l_kind > l_exhaustive_seq_kind & &l_kind < max_kind; });

// l_kind is randc.

do_sequence_kind(l_kind);

end endtask

Note The l_kind variable is declared as randc in order to randomize without replacement.

5.9.2.3 uvm_simple_sequence

This is the third built-in sequence which is predefined in the sequencer. This sequence is provided to allow default execution of the UVC without any user-defined sequences.

task uvm_simple_sequence::body();

‘uvm_do(item) endtask

5.10 Configuring the Sequencer’s Default Sequence

By default, all sequencers execute their uvm_random_sequence object that, in a loop, selects a sequence from its sequence library and executes it. The sequencer has a string property named default_sequence which can be set to any sequence listed in a sequencer’s queue. This sequence is used as the default sequence for that instance of the sequencer.

To change the default sequence:

1. Declare a user-defined sequence class which derives directly or indirectly from uvm_sequence.

2. Configure the default_sequence property for a specific sequencer or a group of sequencers. Typically, this is done inside the test or testbench class before creating the component that includes the relevant sequencer(s). For example,

set_config_string("*.master.sequencer","default_sequence","apb_traffic_seq");

The first argument uses a wildcard mechanism. Here, any instance name containing .master.sequencer will have its default_sequence property (if it exists) set to the value apb_traffic_seq.

More information about how to configure the sequencer is provided in Chapter 7 “Simple Testbench Integration”.

5.10.1 Controlling the Sequencer and Generated Sequences

Libraries of sequences are compiled as part of UVM testbench. The `uvm_sequence_utils associates each sequence with a sequencer. When the testbench is created, the sequencer builds an array of sequences to be executed in its constructor using the

`uvm_update_sequence_lib_and_item() macro. Figure 5-11 shows the Sequencer/Driver/Sequences Array interaction.

Figure 5-11 Interaction of Sequencer, Driver, and Sequences Array

Here is the general flow of interaction when the driver/sequencer are executing in pull mode:

1. The driver calls seq_item_port.get_next_item(req) to indicate that it is ready for a data item. Inside the TLM code, the seq_item_port.get_next_item(req) function calls sequencer.get_next_item(req).

2. The sequencer chooses which sequence it will allow to execute and acknowledges a sequence to generate a data item.

2. The sequencer chooses which sequence it will allow to execute and acknowledges a sequence to generate a data item.

3. The sequence is randomized and created and then the body() of the sequence is started. A data item is generated and it lets the sequencer know that an item is available.

4. The sequencer delivers the data item to the driver and waits for item_done() from the driver.

5. The driver sends the item to the DUT and calls seq_item_port.item_done() to indicate it is finished.

This flow is repeated with the driver requesting another item via the seq_item_port.get_next_item(req) call. The sequencer acknowledges the sequence to deliver its next data item and the item is sent.

Note Sequences can create other sequences, called nested sequences.

5.10.2 Overriding Sequence Items and Sequences

In a user-defined uvm_test—for example, apb_base_test (discussed in “Creating the Base Test”)—users can configure the simulation environment to use a modified version of an existing sequence or sequence item by using the common factory to create instances of sequence and sequence-item classes. See “UVM Factory” for more information.

To override any reference to a specific sequence or sequence-item type:

1. Declare a user-defined sequence and/or sequence item class. The example in the next step assumes the declaration of a basic sequence item of type apb_transfer, and a derived item of type word_aligned_transfer.

2. Invoke the appropriate uvm_factory override method, depending on whether you are doing a global or instance-specific override. For example, assume the apb_write_read_word_seq sequence is executed by a sequencer of type apb_master_sequencer (both defined in

“User-Defined Sequences”). You can choose to replace all usage of apb_transfer types with word_aligned_transfer types. This can be selected for all requests for apb_transfer types from the factory, or for specific instances of apb_transfer. From within an UVM component, a user can execute the following:

// Affect all factory requests for type apb_transfer.

set_type_override_by_type(apb_transfer::get_type(), word_aligned_transfer::get_type());

// Affect requests for type apb_transfer on only a given sequencer.

set_inst_override_by_type("env0.master.sequencer.*",

apb_transfer::get_type(), world_aligned_transfer::get_type());

// Alternatively, affect requests for type apb_transfer for all // sequencers of a specific env.

set_inst_override_by_type("env0.*.sequencer.*",

apb_transfer::get_type(),word_aligned_transfer::get_type());

3. Use any of the sequence macros that allocate an object (as defined in “Sequence and Sequence Item Macros”); for example, the

`uvm_do macro.

Because the sequence macros call the common factory to create the data item object, existing override requests will take effect and a word_aligned_item will be created instead of an apb_transfer.

5.10.3 Building a Reusable Sequence Library

A reusable sequence library is a set of user-defined sequences. Creating a UVC reusable sequence library is an efficient way to facilitate reuse.

The environment developer can create a meaningful set of sequences to be leveraged by the test writer. Such sequence libraries avoid code

The environment developer can create a meaningful set of sequences to be leveraged by the test writer. Such sequence libraries avoid code duplication in tests, making them more maintainable, readable, and concise.

Tips

Try to think of interesting protocol scenarios that many test writers can use.

As some users may not want to use the reusable sequence library (because the sequences may not match the design requirements of the user), do not `include a reusable sequence library within the UVC package file list. Leave it to the user to decide whether to use them.

5.11 Coordinating End-of-Test

UVM provides an objection mechanism to allow hierarchical status coordination between components. The built-in objection, uvm_test_done, provides a way for components and sequences to synchronize their activity and indicate when it is safe to end the test.

5.11.1 UVM Objection Mechanism

In general, the process is for a component or object in the component’s context (for example, a sequence) to raise a uvm_test_done objection at the beginning of an activity that must be completed before the simulation stops, and to drop the objection at the end of the activity. When all the raised objections are dropped, the simulation terminates. The end of test API includes raise_objection, drop_objection, and all_dropped calls that are described below:

raise_objection

The syntax of raise_objection is as follows:

function void raise_objection (uvm_object obj = null, string description = "", int count = 1 )

Raises the number of objections for the source object by count, which defaults to 1. The object is usually the handle of the caller. If object is not specified or is null, the implicit top-level component, uvm_top, is chosen. The description parameter allows users to specify a user message that typically provides the reason for raising the objection. Rasing an objection causes the following:

The source and total objection counts for object are increased by count.

The raised objection’s virtual method is called, which calls the uvm_component:raised method for all of the components up the hierarchy.

A trace message including the description may be issued if the trace bit is set.

drop_objection

The syntax for dropping an objection is as follows:

function void drop_objection (uvm_object obj = null, string description = '', int count = 1 )

The drop_objection drops the number of objections for the source object by count, which defaults to 1. The object is usually the handle of the caller. If object is not specified or is null, the implicit top-level component, uvm_top, is chosen.

The description parameter allows users to specify a user message that typically provides the reason for dropping the objection. Dropping an objection causes the following:

objection causes the following:

The source and total objection counts for object are decreased by count. It is an error to drop the objection count for object below zero.

A trace message including the description string might be issued.

If the total count is non-zero at for a hierarchical level, the drop is propagated up the hierarchy immediately.

If the total count is zero, the drain time is initiated. If the drain time completes and the count is still zero, the drop is propagate up the hierarchy.

5.11.2 End-of-Test Objection Mechanism Usage

Proactive agents may have a meaningful agenda to be achieved before the test goals can be declared as done. For example, a master agent may need to complete all its read and write operations before the run phase should be allowed to stop. A re-active slave agent may not object end-of-test as it is merely serving requests as they appear without a well-defined agenda.

A typical use is for a sequence to raise an objection when it is started as a root sequence (a sequence that has no parent sequence), and to drop the objection when it is finished as a root sequence.

1 class apb_read_block_seq extends uvm_sequence#(apb_transfer);