Transactions and Sequences in UVM

We saw in earlier sections how a typical UVM testbench looks like and how factory is used to store the user defined classes in form of a lookup table. This section focuses on the stimulus branch of the UVM class hierarchy – Transactions and Sequences in UVM.

One or more transactions form a Sequence in UVM. Let’s look at transactions and UVM sequences one by one in detail with code for a better understanding and correlation between them.

UVM transaction:

A UVM Transaction represents data items that are processed by the verification environment. It contains the packet of information in form of variables to hold values to send to the DUT or receive from the DUT. It also has several methods to copy, compare and print transactions.

Please note a transaction is registered using the macro `uvm_object_utils as a transaction is from a stimulus branch as opposed to testbench branch. As discussed earlier, use of uvm_transaction class is deprecated and one should extend from uvm_sequence_item class to define a stimulus related class.

Below code is a typical example of how a transaction item class called packet_c is defined. This class definition can be used multiple times to create abstract transaction items which are converted to pin wiggles by driver. After the end of this class, I have explained what each line does.

class packet_c extends uvm_sequence_item;

    rand logic bit [2:0] a;

    rand logic bit [2:0] b;

    rand logic bit [3:0] result;

    //rand bit add_sub; // add_sub 1 – Add ; 0-Sub

    constraint size_c {

     a inside {[0:10]}

     b <=10;

    }

    function new(string name = “”) begin

        super.new(name);

    endfunction

    function void post_randomize();

        $display(“\n Printing sequence item \n”);

        this.print();

    endfunction

    `uvm_object_utils_begin(packet_c)  

      `uvm_field_int(a,UVM_DEFAULT | UVM_NO_COMPARE) ;

      `uvm_field_int(b,UVM_DEFAULT | UVM_NO_COMPARE) ; 

      `uvm_field_int(result,UVM_DEFAULT) ;             

    `uvm_object_utils_end

endclass

packet_c class is being defined by extending it from uvm_sequence_item(stimulus branch). After that variables a, b, result are declared rand so that these can be randomized by UVM. Then, constraint with name size_c is defined to limit the values of a to be from 0 to 10 and b should never get a value of more than 10. The new constructor is defined where only string name is passed as argument(as opposed to two variables that we passed in constructor of class uvm_test belonging to testbench branch). post_randomize is a virtual function in UVM. We have implemented it here to print the sequence item. Everytime an object is created, post_randomize will be called automatically and sequence item will be printed. Please note I have used $display to print but that is not a good practice. User should use standard uvm macros `uvm_info for prints as these have verbosity associated with it. Next, we register packet_c in factory using `uvm_object_utils macro. Notice we have also used `uvm_field macros here to tell UVM that do not compare a and b variables when uvm compare standard function is used to compare transactions. result will be used when all standard functions are called up.

UVM Sequence:

A UVM sequence is a group of abstract transactions that sequencer grabs and puts them on driver which converts these abstract transactions to pin wiggles to drive stimulus to the DUT. The task body() inside the user defined sequence is automatically called when add_seq.start() is called from uvm test.

Below is the code for a typical uvm sequence:

class add_sequence extends uvm_sequence (#packet_c);

    `uvm_sequence_utils(add_sequence, add_sequencer)

    rand unsigned int num_of_add;

    constraint add_c {

         num_of_add inside {[1:15]};     }

function new(string name=”add_sequence”);

        super.new(name);    

endfunction

function build(string name, string parent);

        super.build(name, parent);

    endfunction

    task pre_body();

         phase.raise_objection(this);

    endtask    

    task body()

        repeat (num_of_add) begin  

            `uvm_do(req);

         end

    endtask

    function void post_randomize();

        $display(“\n Printing sequence item \n”);

        req.print();

    endfunction

    task post_body();

         phase.drop_objection(this);

    endtask

endclass

In above code snippet for a uvm sequence, user defined class add_sequence is created by extending it from uvm_sequence(stimulus branch) standard UVM Class. We need to specify packet_c as a parameter to declare the type of transactions associated with this sequence. After that, sequence name is registered in factory using `uvm_sequence_utils macro. Following code is same as what we did for uvm_transaction (see above) and then in pre_body virtual function we raise the objection indicating other uvm components that sequence is now busy and test will not end until sequence is done i.e. till objection is dropped. In task body, `uvm_do macro is called indicating driver that a transaction is ready to be consumed. When driver pulls the transaction, it converts the abstract transaction into pin wiggles and drives the transaction onto the DUT pins(- For now, I am skipping the hand-shaking mechanism between sequence body and driver). After the transaction is randomized as part of `uvm_do macro, post_randomize function is called where randomized values of the transaction are printed. After the task body is executed, post_body drops the objection telling UVM that this sequence is done executing.

In this section we looked at Transactions and Sequences in UVM which are stimulus aspect of UVM. In the next section, lets look at one of the testbench aspects/branch of UVM – UVM Environment.

To get notifications for our upcoming blogs and tutorials, please LIKE our facebook fan page