Friday, July 31, 2015

UVM: communication between uvm_sequencer and uvm_sequence

uvm_sequence -> uvm_sequence_base -> uvm_sequence_item -> uvm_transaction -> uvm_object

uvm_sequencer -> uvm_sequencer_param_base -> uvm_sequencer_base -> uvm_sequencer_base -> uvm_component -> uvm_object

When running each phases which extends class uvm_task_phase, it will call the function start_phase_sequence of the sequencer and then the sequencer will lookup for the default sequence for that phase and call seq.start(this).

virtual class uvm_task_phase extends uvm_phase;

  function void m_traverse(uvm_component comp,
                           uvm_phase phase,
                           uvm_phase_state state);
    ...

    if (phase_domain == uvm_domain::get_common_domain() ||
        phase_domain == comp_domain) begin
      case (state)
        UVM_PHASE_STARTED: begin
          comp.m_current_phase = phase;
          comp.m_apply_verbosity_settings(phase);
          comp.phase_started(phase);
          if ($cast(seqr, comp))
            seqr.start_phase_sequence(phase);
          end
          ...
      endcase
    end

  endfunction
endclass

class uvm_sequencer_base extends uvm_component;
function void uvm_sequencer_base::start_phase_sequence(uvm_phase phase);
  ...
  rq = rp.lookup_name({get_full_name(), ".", phase.get_name(), "_phase"},
                      "default_sequence", null, 0);
  ...

  `uvm_info("PHASESEQ", {"Starting default sequence '",
                         seq.get_type_name(),"' for phase '", phase.get_name(),"'"}, UVM_FULL)

  seq.print_sequence_info = 1;
  seq.set_sequencer(this);
  seq.reseed();
  seq.set_starting_phase(phase);

  if (!seq.do_not_randomize && !seq.randomize()) begin
    `uvm_warning("STRDEFSEQ", {"Randomization failed for default sequence '",
                               seq.get_type_name(),"' for phase '", phase.get_name(),"'"})
    return;
  end

  fork begin
    uvm_sequence_process_wrapper w = new();
    // reseed this process for random stability
    w.pid = process::self();
    w.seq = seq;
    w.pid.srandom(uvm_create_random_seed(seq.get_type_name(), this.get_full_name()));
    m_default_sequences[phase] = w;
    // this will either complete naturally, or be killed later
    seq.start(this);
    m_default_sequences.delete(phase);
  end
  join_none

endfunction
endclass


seq.start(this) will call pre_start(), pre_body(), body(), post_body() and post_start(). In the body of extended sequence class of uvm_sequence, we will realize the detailed sequences by using `uvm_do, etc.

class uvm_sequence_base extends uvm_sequence_item;
  virtual task start (uvm_sequencer_base sequencer,
                      uvm_sequence_base parent_sequence = null,
                      int this_priority = -1,
                      bit call_pre_post = 1);
    bit                  old_automatic_phase_objection;
   
    set_item_context(parent_sequence, sequencer);

    if (!(m_sequence_state inside {UVM_CREATED,UVM_STOPPED,UVM_FINISHED})) begin
      uvm_report_fatal("SEQ_NOT_DONE",
         {"Sequence ", get_full_name(), " already started"},UVM_NONE);
    end

    if (m_parent_sequence != null) begin
       m_parent_sequence.children_array[this] = 1;
    end

    if (this_priority < -1) begin
      uvm_report_fatal("SEQPRI", $sformatf("Sequence %s start has illegal priority: %0d",
                                           get_full_name(),
                                           this_priority), UVM_NONE);
    end
    if (this_priority < 0) begin
       if (parent_sequence == null) this_priority = 100;
       else this_priority = parent_sequence.get_priority();
    end

    // Check that the response queue is empty from earlier runs
    clear_response_queue();

    m_priority           = this_priority;

    if (m_sequencer != null) begin
       integer handle;
       uvm_tr_stream stream;
       if (m_parent_sequence == null) begin
          stream = m_sequencer.get_tr_stream(get_name(), "Transactions");
          handle = m_sequencer.begin_tr(this, get_name());
          m_tr_recorder = uvm_recorder::get_recorder_from_handle(handle);
       end else begin
          stream = m_sequencer.get_tr_stream(get_root_sequence_name(), "Transactions");
          handle = m_sequencer.begin_child_tr(this,
                                              (m_parent_sequence.m_tr_recorder == null) ? 0 : m_parent_sequence.m_tr_recorder.get_handle(),
                                              get_root_sequence_name());
          m_tr_recorder = uvm_recorder::get_recorder_from_handle(handle);
       end
    end

    // Ensure that the sequence_id is intialized in case this sequence has been stopped previously
    set_sequence_id(-1);
    // Remove all sqr_seq_ids
    m_sqr_seq_ids.delete();

    // Register the sequence with the sequencer if defined.
    if (m_sequencer != null) begin
      void'(m_sequencer.m_register_sequence(this));
    end

    // Change the state to PRE_START, do this before the fork so that
    // the "if (!(m_sequence_state inside {...}" works
    m_sequence_state = UVM_PRE_START;
    fork
      begin
        m_sequence_process = process::self();

        // absorb delta to ensure PRE_START was seen
        #0;

        // Raise the objection if enabled
        // (This will lock the uvm_get_to_lock_dap)
        if (get_automatic_phase_objection()) begin
           m_safe_raise_starting_phase("automatic phase objection");
        end
       
        pre_start();

        if (call_pre_post == 1) begin
          m_sequence_state = UVM_PRE_BODY;
          #0;
          pre_body();
        end

        if (parent_sequence != null) begin
          parent_sequence.pre_do(0);    // task
          parent_sequence.mid_do(this); // function
        end

        m_sequence_state = UVM_BODY;
        #0;
        body();

        m_sequence_state = UVM_ENDED;
        #0;

        if (parent_sequence != null) begin
          parent_sequence.post_do(this);
        end

        if (call_pre_post == 1) begin
          m_sequence_state = UVM_POST_BODY;
          #0;
          post_body();
        end

        m_sequence_state = UVM_POST_START;
        #0;
        post_start();

        // Drop the objection if enabled
        if (get_automatic_phase_objection()) begin
           m_safe_drop_starting_phase("automatic phase objection");
        end
       
        m_sequence_state = UVM_FINISHED;
        #0;

      end
    join

    if (m_sequencer != null) begin    
      m_sequencer.end_tr(this);
    end
     
    // Clean up any sequencer queues after exiting; if we
    // were forcibly stoped, this step has already taken place
    if (m_sequence_state != UVM_STOPPED) begin
      if (m_sequencer != null)
        m_sequencer.m_sequence_exiting(this);
    end

    #0; // allow stopped and finish waiters to resume

    if ((m_parent_sequence != null) && (m_parent_sequence.children_array.exists(this))) begin
       m_parent_sequence.children_array.delete(this);
    end

    old_automatic_phase_objection = get_automatic_phase_objection();
    m_init_phase_daps(1);
    set_automatic_phase_objection(old_automatic_phase_objection);
  endtask
endclass


SEQ_OR_ITEM may be an object of a class which extends uvm_sequence or REQ which is an object of a class which extends uvm_sequence_item.

If SEQ_OR_ITEM is an object of a class which extends uvm_sequence, SEQ_OR_ITEM may call another object of extension of uvm_sequence through `uvm_do, etc.
REQ is the final atomic transaction which is defined by us.

So if SEQ_OR_ITEM is an object of a class which extends uvm_sequence, $cast(__seq, SEQ_OR_ITEM) will succeed as uvm_sequence extends uvm_sequence_base. Then it will call the start function of the SEQ_OR_ITEM, which will go through SEQ_OR_ITEM's steps and run that SEQ_OR_ITEM 's body(). Using this way, SEQ_OR_ITEM will recursively go to the final uvm_sequence, which will do `uvm_do on REQ.
If SEQ_OR_ITEM is REQ, $cast(__seq, SEQ_OR_ITEM) will fail and function finish_item will be called.

`define uvm_do(SEQ_OR_ITEM) \
  `uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, {})

`define uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) \
  begin \
  uvm_sequence_base __seq; \
  `uvm_create_on(SEQ_OR_ITEM, SEQR) \
  if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY);\
  if ((__seq == null || !__seq.do_not_randomize) && !SEQ_OR_ITEM.randomize() with CONSTRAINTS ) begin \
    `uvm_warning("RNDFLD", "Randomization failed in uvm_do_with action") \
  end\
  if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY); \
  else __seq.start(SEQR, this, PRIORITY, 0); \
  end

`define uvm_create_on(SEQ_OR_ITEM, SEQR) \
  begin \
  uvm_object_wrapper w_; \
  w_ = SEQ_OR_ITEM.get_type(); \
  $cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
  end

  virtual task start_item (uvm_sequence_item item,
                           int set_priority = -1,
                           uvm_sequencer_base sequencer=null);
    uvm_sequence_base seq;
     
    if(item == null) begin
      uvm_report_fatal("NULLITM",
         {"attempting to start a null item from sequence '",
          get_full_name(), "'"}, UVM_NONE);
      return;
    end
          
    if($cast(seq, item)) begin
      uvm_report_fatal("SEQNOTITM",
         {"attempting to start a sequence using start_item() from sequence '",
          get_full_name(), "'. Use seq.start() instead."}, UVM_NONE);
      return;
    end
          
    if (sequencer == null)
        sequencer = item.get_sequencer();
        
    if(sequencer == null)
        sequencer = get_sequencer();   
        
    if(sequencer == null) begin
        uvm_report_fatal("SEQ",{"neither the item's sequencer nor dedicated sequencer has been supplied to start item in ",get_full_name()},UVM_NONE);
       return;
    end

    item.set_item_context(this, sequencer);

    if (set_priority < 0)
      set_priority = get_priority();
    
    sequencer.wait_for_grant(this, set_priority);

    if (sequencer.is_auto_item_recording_enabled()) begin
      void'(sequencer.begin_child_tr(item, 
                                     (m_tr_recorder == null) ? 0 : m_tr_recorder.get_handle(), 
                                     item.get_root_sequence_name(), "Transactions"));
    end
    
    pre_do(1);

  endtask  

  virtual task finish_item (uvm_sequence_item item,
                            int set_priority = -1);

    uvm_sequencer_base sequencer;
 
    sequencer = item.get_sequencer();

    if (sequencer == null) begin
        uvm_report_fatal("STRITM", "sequence_item has null sequencer", UVM_NONE);
    end

    mid_do(item);
    sequencer.send_request(this, item);
    sequencer.wait_for_item_done(this, -1);

    if (sequencer.is_auto_item_recording_enabled()) begin
      sequencer.end_tr(item);
    end

    post_do(item);

  endtask

No comments:

Post a Comment