This post answers the following question “How the typical sequencer to sequence coonection works?!”

An example Link to heading

  • Create sequencer class handle from vanilla uvm_sequencer
uvm_sequencer #(foo_req, foo_rsp) sqr;
  • Connect it to driver port in connect phase
drv.seq_item_port.connect(sqr.seq_item_export);
  • Define uvm_sequence with body
class foo_sequence extends uvm_sequence#(foo_seq_item);
  virtual task body();

  start_item(item);
  // Create the seq item
  ...
  finish_item(item);
  endtask
endclass
  • Call the sequence with sequencer instance.
seq.start(sqr, null);
  • Finally the driver can use get_next_item to get the sequence_item
 seq_item_port.get_next_item(req);
 // Do something with req
 seq_item_port.item_done();

Following seq.start down the rabbit hole Link to heading

let’s start with uvm_sequence and seq.start, it turns out it’s very lean class as most logic is define in uvm_sequence_base in src/seq/uvm_sequence_base.svh

uvm_sequence_base is a sub class of uvm_sequence_item with sequence phases to be overridden.

anyway, start eventually calls body. note that body is a virtual task that issues a warning if not overridden.

  virtual task body();
    uvm_report_warning("uvm_sequence_base", "Body definition undefined");
    return;
  endtask

How the sequencer and sequence are linked? body to get_next_item. Link to heading

after creating the item, body calls finish_item to pass over control to sequencer. finish_item is defined in src/seq/uvm_sequence_base.svh, the task finish_item calls send_request

  virtual task finish_item (uvm_sequence_item item,
                            int set_priority = -1);
    ...
    sequencer.send_request(this, item);
    ...
  endtask

sequencer.send_request is defined in src/seq/uvm_sequence.svh. which uses m_sequencer to call m_sequencer.send_sequest

  function void send_request(uvm_sequence_item request, bit rerandomize = 0);
    REQ m_request;

  ...
    m_sequencer.send_request(this, m_request, rerandomize);
  endfunction

m_sequencer.send_request is called from sequencer is defined at src/seq/uvm_sequencer_param_base.svh. and it pushes the item into tlm fifo m_req_fifo

  if (m_req_fifo.try_put(param_t) != 1) begin
    uvm_report_fatal(get_full_name(), "Concurrent calls to get_next_item() not supported. Consider using a semaphore to ensure that concurrent processes take turns in the driver", UVM_NONE);
  end

at this point, we need to jump to the other side seq_item_port.get_next_item(req); then returns the req in the m_req_fifo.

task uvm_sequencer::get_next_item(output REQ t);
  REQ req_item;
  ...
  m_req_fifo.peek(t);
endtask