UVM Sequencer and Driver Communication:

The UVM sequence-driver API majorly uses blocking methods on sequencer and driver side as explained below for transferring a sequence item from sequencer to driver and collecting response back from driver.

Sequencer side operations:  There are two methods as follows:

  1. start_item(<item>): This requests the sequencer to have access to the driver for the sequence item and returns when the sequencer grants access.
  2. finish_item(<item>): This method results in the driver receiving the sequence item and is a blocking method which returns only after driver calls the item_done() method.

Driver side operations:

  1. get_next_item(req) : This is a blocking method in driver that blocks until a sequence item is received on the port connected to sequencer. This method returns the sequence item which can be translated to pin level protocol by the driver.
  2. item_done(req): The driver uses this non blocking call to signal to the sequencer that it can unblock the sequences finish_item() method, either when the driver accepts the sequences request or it has executed it.  

Following diagram illustrates this protocol handshake between sequencer and driver which is the most commonly used handshake to transfer requests and responses between sequence and driver.

Let’s understand with the following example:

Sequence Code:

class axi_seq extends uvm_sequence #(axi_txn);
  `uvm_object_utils(axi_seq)
  
  function new (string name = “”);
    super.new(name);
  endfunction
  
  task body;
    repeat(n)
      begin
        axi_txn txn;
        // Step 1
        start_item(txn);
        
        // Step 2
        txn = axi_txn::type_id::create(“txn”, this);
        
        // Step 3
        assert(txn.randomize() with (cmd == 0) );
        
        // Step 4
        finish_item(txn);
      end
  endtask: body
 
endclass: axi_seq

Driver side operation Code:

class axi_driver extends uvm_driver #(axi_txn);
  `uvm_component_utils(axi_driver)
  ………
  ………
  
  task run_phase (uvm_phase phase);
    
    // Step 1
    axi_txn txn;
    
    forever begin
      // Step 2
      seq_item_port.get_next_item(txn);
      
      // Step 3
      @ (posedge vif.clk)
      begin
        vif.addr = txn.awaddr;
        vif.data = txn.wdata;
        ...
        ...
      end
      
      // Step 4
      seq_item_port.item_done();
    end
  endtask: run_phase
  ………
 
endclass: axi_driver

Few other alternatives methods are: get() method in driver which is equivalent to calling get_next_item() along with item_done(). Sometimes,there would also be a need for a separate response port if the response from driver to sequence contains more information than what could be encapsulated in the request class. In this case, sequencer will use a get_response() blocking method that gets unblocked when the driver sends a separate response on this port using put() method. This is illustrated in below diagram.