I came across execute_item, which seems like a shorthand for sending transaction items directly to a sequencer (and subsequently to the driver). According to the uvm_sequencer documentation, a temporary sequence is created and run on the transaction item.

  // Task: execute_item
  //
  // Executes the given transaction ~item~ directly on this sequencer. A temporary
  // parent sequence is automatically created for the ~item~. There is no capability to
  // retrieve responses. If the driver returns responses, they will accumulate in the
  // sequencer, eventually causing response overflow unless
  // <set_response_queue_error_report_disabled> is called.

  extern virtual task execute_item(uvm_sequence_item item);

Naturally, I had to look at the execute_item implementation to see it in action. It does exactly what is described in the documentation.

task uvm_sequencer_base::execute_item(uvm_sequence_item item);
  uvm_sequence_base seq;
  
  seq = new();
  item.set_sequencer(this);
  item.set_parent_sequence(seq);
  seq.set_sequencer(this);
  seq.start_item(item);
  seq.finish_item(item);
endtask

response_queue_error_report_disabled Link to heading

I took a little detour to examine response_queue_error_report_disabled and how it is used with responses. Essentially, if put_base_response fails to push into the queue, it will print a UVM error (unless disabled with response_queue_error_report_disabled).

  virtual function void put_base_response(input uvm_sequence_item response);
    if ((response_queue_depth == -1) ||
        (response_queue.size() < response_queue_depth)) begin
      response_queue.push_back(response);
      return;
    end
    if (response_queue_error_report_disabled == 0) begin
      uvm_report_error(get_full_name(), "Response queue overflow, response was dropped", UVM_NONE);
    end
  endfunction

I haven’t traced it fully, but I assume this would be called at some point when put_response is invoked. Here is an example of driver code that calls put_response after item_done.

  // get_and_drive 
  virtual protected task get_and_drive();
    @(negedge vif.sig_reset);
    forever begin
      @(posedge vif.sig_clock);
      seq_item_port.get_next_item(req);
      $cast(rsp, req.clone());
      rsp.set_id_info(req);
      drive_transfer(rsp);
      seq_item_port.item_done();
      seq_item_port.put_response(rsp);
    end
  endtask : get_and_drive