UVM Sequence Macros

UVM (Universal Verification Methodology) provides several predefined macros to simplify the creation and management of sequences in a testbench. These macros significantly reduce boilerplate code, making sequences easier to write, debug, and maintain.

1. `uvm_object_utils

The uvm_object_utils macro is used to register a sequence class with the UVM factory. This enables dynamic creation of sequences using the factory mechanism, which is essential for features like factory overrides and reusability. We need to place this macro in the sequence class definition.

Code Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  function new(string name = "my_sequence");
    super.new(name);
  endfunction
endclass

Explanation:

  • The macro registers the my_sequence class with the factory.
  • This allows the sequence to be dynamically created using the create() method.
my_sequence seq = my_sequence::type_id::create("seq");

2. `uvm_do

The uvm_do macro simplifies the process of creating, randomizing, and sending a sequence item. It is a one-step macro that performs the following tasks:

  1. Creates the sequence item.
  2. Randomizes the item.
  3. Sends it to the driver via the sequencer.

Use this macro inside a sequence’s body() method.

Code Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  virtual task body();
    `uvm_do(req)  // Creates, randomizes, and sends a transaction
  endtask
endclass

The macro automatically creates the req object (an instance of my_transaction), randomizes it, and sends it to the sequencer using start_item() and finish_item().

3. `uvm_do_with

The uvm_do_with macro is similar to uvm_do, but it allows you to specify constraints for randomization. This is useful when you need to generate transactions with specific values. Use this macro inside a sequence’s body() method.

Code Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  virtual task body();
    `uvm_do_with(req, { req.addr == 32'hAABBCCDD; req.data > 100; })
  endtask
endclass

4. `uvm_create

The `uvm_create macro creates a sequence item without sending it to the driver. This is useful when you need to perform additional operations (e.g., manual randomization or customization) on the transaction before sending it.

Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  virtual task body();
    `uvm_create(req)  // Creates a transaction but does not send it

     start_item(req);  // Send the transaction to the sequencer
    // Custom logic or manual randomization
    req.addr = 32'hDEADBEEF;
    req.data = 123;
    finish_item(req);
  endtask
endclass

The macro creates the req object, but you can modify its fields or randomize it manually before sending it to the sequencer.

5. `uvm_send

The `uvm_send macro is used to send a pre-created sequence item to the sequencer. This is often used after creating and customizing a transaction.

Code Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  virtual task body();
    `uvm_create(req)  // Create the transaction
    req.addr = 32'hCAFEBABE;  // Customize the transaction
    req.data = 456;

    `uvm_send(req)  // Send the transaction to the sequencer
  endtask
endclass

The macro automatically calls start_item() and finish_item() for the req object.

6. `uvm_do_pri_with

The uvm_do_pri_with macro allows you to create, randomize, and send a sequence item with specified constraints, while also assigning a priority to the transaction. This is useful in scenarios where multiple sequences or transactions have different priorities. Use this macro inside a sequence’s body() method when priority and constraints are required.

Code Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  virtual task body();
    `uvm_do_pri_with(req, 5, { req.addr[15:0] == 16'h1234; req.data < 500; })
  endtask
endclass

The macro creates the req object, assigns it a priority of 5, randomizes it with the specified constraints, and sends it to the sequencer.

7. `uvm_create_on

The `uvm_create_on macro is similar to `uvm_create macro except that it creates a sequence item and associates it with a specific sequencer. This is useful when you have multiple sequencers and want to explicitly bind a transaction to a particular sequencer.

Code Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  virtual task body();
    `uvm_create_on(req, p_sequencer)  // Create the transaction on a specific sequencer

    req.addr = 32'hABCDEF01;  // Customize the transaction
    `uvm_send(req)  // Send it to the sequencer
  endtask
endclass

The macro binds the req object to the p_sequencer (the parent sequencer).

8. `uvm_rand_send(Item/Seq)

The uvm_rand_send macro randomizes an already instantiated sequence item or sub-sequence and sends it to the sequencer. This macro is useful when the sequence item or sub-sequence has already been created and you want to randomize and send it in one step.

Code_Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)
  
  my_transaction req;

  virtual task body();

    // Explicitly create the transaction object
    req = my_transaction::type_id::create("req");

    // Randomize and send the transaction
    `uvm_rand_send(req)
  endtask
endclass

The req object is explicitly created using the create() method. The macro randomizes req using its constraints and sends it to the sequencer by internally calling start_item(req) and finish_item(req).

9. `uvm_rand_send_with(Item/Seq, Constraints)

The uvm_rand_send_with macro works like uvm_rand_send but allows the user to apply custom constraints to the randomization process. Like uvm_rand_send, it skips the creation step and directly randomizes and sends an already created sequence item or sub-sequence.

Code_Example:

class my_sequence extends uvm_sequence #(my_transaction);
  `uvm_object_utils(my_sequence)

  my_transaction req;

  virtual task body();

    // Explicitly create the transaction object
    req = my_transaction::type_id::create("req");

    // Randomize with constraints and send the transaction
    `uvm_rand_send_with(req, { req.addr < 32'hFFFF; req.data > 100; })
  endtask
endclass

The req object is explicitly created using create(). The macro applies constraints like req.addr < 32'hFFFF and req.data > 100 during the randomization process. The macro randomizes the req object with the specified constraints and sends it to the sequencer.

10. `uvm_declare_p_sequencer(SEQUENCER)

The uvm_declare_p_sequencer macro declares a typed handle (p_sequencer) for the parent sequencer within a sequence. This macro is essential when a sequence needs to interact with sequencer-specific variables or methods. It enables access to sequencer-specific resources, variables, or methods.

Summary of UVM Sequence Macros:

MacroDescription
`uvm_object_utilsRegisters a sequence class with the UVM factory.
`uvm_doCreates, randomizes, and sends a transaction to the sequencer.
`uvm_do_withSame as uvm_do, but with additional constraints for randomization.
`uvm_createCreates a sequence item without sending it.
`uvm_sendSends a pre-created sequence item to the sequencer.
`uvm_do_pri_withCreates, randomizes, and sends a transaction with constraints and a specified priority.
`uvm_create_onCreates a transaction and explicitly binds it to a specific sequencer.
`uvm_rand_send(Item/Seq)Randomizes and sends an already created sequence item or sub-sequence to the sequencer.
`uvm_rand_send_with(Item/Seq, Constraints)Randomizes and sends an already created sequence item or sub-sequence with user-defined constraints.
`uvm_declare_p_sequencer(SEQUENCER)Declares a typed handle (p_sequencer) to access variables or methods in the parent sequencer.