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

Tuesday, July 14, 2015

UVM: study of uvm_phase and how it is executed

In the top of the simulation, global task run_test will be called, which will instantiate top which is type of uvm_root and then call top.run_tesst(test_name), which will call uvm_phase::m_run_phase(). In uvm_phases::m_run_phases(), it will first call uvm_domain::get_common_domain() to create common phases.

task run_test (string test_name="");
  uvm_root top;
           ...
  top.run_test(test_name);
endtask

uvm_root::run_test will call uvm_phase::m_run_phase, which will first call uvm_domain::get_common_domain to create the phases.

task uvm_root::run_test(string test_name="");
             ...
  // phase runner, isolated from calling process
  fork begin
    // spawn the phase runner task
    phase_runner_proc = process::self();
    uvm_phase::m_run_phases();
  end
  join_none
  #0; // let the phase runner start

  wait (m_phase_all_done == 1);

             ...
endtask

task uvm_phase::m_run_phases();
             ...
  // initiate by starting first phase in common domain
  begin
    uvm_phase ph = uvm_domain::get_common_domain();
    void'(m_phase_hopper.try_put(ph));
  end

  forever begin
    uvm_phase phase;
    m_phase_hopper.get(phase);
    fork
      begin
        phase.execute_phase();
      end
    join_none
    #0;  // let the process start running
  end
endtask


The phases created by uvm_domain::get_common_domain are uvm_build_phase, uvm_connect_phase, uvm_end_of_elaboration_phase, uvm_start_of_simulation_phase, uvm_run_phase, uvm_extract_phase, uvm_check_phase, uvm_report_phase and uvm_final_phase.

Then uvm_domain::get_common_domain will call get_uvm_domain() to create phases uvm_pre_reset_phase, uvm_reset_phase, uvm_post_reset_phase, uvm_pre_configure_phase, uvm_configure_phase, uvm_post_configure_phase, uvm_pre_main_phase, uvm_main_phase, uvm_post_main_phase, uvm_pre_shutdown_phase, uvm_shutdown_phase and uvm_post_shutdown_phase.

  static function uvm_domain get_common_domain();

    uvm_domain domain;
    uvm_phase schedule;

    if (m_common_domain != null)
      return m_common_domain;

    domain = new("common");
    domain.add(uvm_build_phase::get());
    domain.add(uvm_connect_phase::get());
    domain.add(uvm_end_of_elaboration_phase::get());
    domain.add(uvm_start_of_simulation_phase::get());
    domain.add(uvm_run_phase::get());
    domain.add(uvm_extract_phase::get());
    domain.add(uvm_check_phase::get());
    domain.add(uvm_report_phase::get());
    domain.add(uvm_final_phase::get());
    m_domains["common"] = domain;

    // for backward compatibility, make common phases visible;
    // same as uvm_<name>_phase::get().
    build_ph               = domain.find(uvm_build_phase::get());
    connect_ph             = domain.find(uvm_connect_phase::get());
    end_of_elaboration_ph  = domain.find(uvm_end_of_elaboration_phase::get());
    start_of_simulation_ph = domain.find(uvm_start_of_simulation_phase::get());
    run_ph                 = domain.find(uvm_run_phase::get());
    extract_ph             = domain.find(uvm_extract_phase::get());
    check_ph               = domain.find(uvm_check_phase::get());
    report_ph              = domain.find(uvm_report_phase::get());
    m_common_domain = domain;

    domain = get_uvm_domain();
    m_common_domain.add(domain,
                     .with_phase(m_common_domain.find(uvm_run_phase::get())));


    return m_common_domain;

  endfunction

  static function uvm_domain get_uvm_domain();

    if (m_uvm_domain == null) begin
      m_uvm_domain = new("uvm");
      m_uvm_schedule = new("uvm_sched", UVM_PHASE_SCHEDULE);
      add_uvm_phases(m_uvm_schedule);
      m_uvm_domain.add(m_uvm_schedule);
    end
    return m_uvm_domain;
  endfunction

  static function void add_uvm_phases(uvm_phase schedule);

    schedule.add(uvm_pre_reset_phase::get());
    schedule.add(uvm_reset_phase::get());
    schedule.add(uvm_post_reset_phase::get());
    schedule.add(uvm_pre_configure_phase::get());
    schedule.add(uvm_configure_phase::get());
    schedule.add(uvm_post_configure_phase::get());
    schedule.add(uvm_pre_main_phase::get());
    schedule.add(uvm_main_phase::get());
    schedule.add(uvm_post_main_phase::get());
    schedule.add(uvm_pre_shutdown_phase::get());
    schedule.add(uvm_shutdown_phase::get());
    schedule.add(uvm_post_shutdown_phase::get());

  endfunction


After the phases are created, uvm_phase::m_run_phases() will call phase.execute_phase() to run phases.

task uvm_phase::m_run_phases();
             ...
  // initiate by starting first phase in common domain
  begin
    uvm_phase ph = uvm_domain::get_common_domain();
    void'(m_phase_hopper.try_put(ph));
  end

  forever begin
    uvm_phase phase;
    m_phase_hopper.get(phase);
    fork
      begin
        phase.execute_phase();
      end
    join_none
    #0;  // let the process start running
  end
endtask

task uvm_phase::execute_phase();

  uvm_task_phase task_phase;
  uvm_root top;
  uvm_phase_state_change state_chg;
  uvm_coreservice_t cs;

  cs = uvm_coreservice_t::get();
  top = cs.get_root();

  // If we got here by jumping forward, we must wait for
  // all its predecessor nodes to be marked DONE.
  // (the next conditional speeds this up)
  // Also, this helps us fast-forward through terminal (end) nodes
  foreach (m_predecessors[pred])
    wait (pred.m_state == UVM_PHASE_DONE);


  // If DONE (by, say, a forward jump), return immed
  if (m_state == UVM_PHASE_DONE)
    return;

  state_chg = uvm_phase_state_change::type_id::create(get_name());
  state_chg.m_phase      = this;
  state_chg.m_jump_to    = null;

  //---------
  // SYNCING:
  //---------
  // Wait for phases with which we have a sync()
  // relationship to be ready. Sync can be 2-way -
  // this additional state avoids deadlock.
  state_chg.m_prev_state = m_state;
  m_state = UVM_PHASE_SYNCING;
  `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
  #0;
   
  if (m_sync.size()) begin
    
    foreach (m_sync[i]) begin
      wait (m_sync[i].m_state >= UVM_PHASE_SYNCING);
    end
  end

  m_run_count++;


  if (m_phase_trace) begin
    `UVM_PH_TRACE("PH/TRC/STRT","Starting phase",this,UVM_LOW)
  end


  // If we're a schedule or domain, then "fake" execution
  if (m_phase_type != UVM_PHASE_NODE) begin
    state_chg.m_prev_state = m_state;
    m_state = UVM_PHASE_STARTED;
    `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))

    #0;

    state_chg.m_prev_state = m_state;
    m_state = UVM_PHASE_EXECUTING;
    `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))

    #0;
  end


  else begin // PHASE NODE

    //---------
    // STARTED:
    //---------
    state_chg.m_prev_state = m_state;
    m_state = UVM_PHASE_STARTED;
    `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))

    m_imp.traverse(top,this,UVM_PHASE_STARTED);
    m_ready_to_end_count = 0 ; // reset the ready_to_end count when phase starts
    #0; // LET ANY WAITERS WAKE UP


    //if (m_imp.get_phase_type() != UVM_PHASE_TASK) begin
    if (!$cast(task_phase,m_imp)) begin

      //-----------
      // EXECUTING: (function phases)
      //-----------
      state_chg.m_prev_state = m_state;
      m_state = UVM_PHASE_EXECUTING;
      `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))

      #0; // LET ANY WAITERS WAKE UP
      m_imp.traverse(top,this,UVM_PHASE_EXECUTING);

    end
    else begin
        m_executing_phases[this] = 1;

        state_chg.m_prev_state = m_state;
        m_state = UVM_PHASE_EXECUTING;
        `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))

        fork : master_phase_process
          begin
  
            m_phase_proc = process::self();
  
            //-----------
            // EXECUTING: (task phases)
            //-----------
            task_phase.traverse(top,this,UVM_PHASE_EXECUTING);
  
            wait(0); // stay alive for later kill
  
          end
        join_none
  
        uvm_wait_for_nba_region(); //Give sequences, etc. a chance to object
  
        // Now wait for one of three criterion for end-of-phase.
        fork
          begin // guard
          
           fork
             // JUMP
             begin
                wait (m_premature_end);
                `UVM_PH_TRACE("PH/TRC/EXE/JUMP","PHASE EXIT ON JUMP REQUEST",this,UVM_DEBUG)
             end
  
             // WAIT_FOR_ALL_DROPPED
             begin
               bit do_ready_to_end  ; // bit used for ready_to_end iterations
               // OVM semantic: don't end until objection raised or stop request
               if (phase_done.get_objection_total(top) ||
                   m_use_ovm_run_semantic && m_imp.get_name() == "run") begin
                 if (!phase_done.m_top_all_dropped)
                   phase_done.wait_for(UVM_ALL_DROPPED, top);
                 `UVM_PH_TRACE("PH/TRC/EXE/ALLDROP","PHASE EXIT ALL_DROPPED",this,UVM_DEBUG)
               end
               else begin
                  if (m_phase_trace) `UVM_PH_TRACE("PH/TRC/SKIP","No objections raised, skipping phase",this,UVM_LOW)
               end
               
               wait_for_self_and_siblings_to_drop() ;
               do_ready_to_end = 1;
                  
               //--------------
               // READY_TO_END:
               //--------------
               while (do_ready_to_end) begin
                 uvm_wait_for_nba_region(); // Let all siblings see no objections before traverse might raise another 
                 `UVM_PH_TRACE("PH_READY_TO_END","PHASE READY TO END",this,UVM_DEBUG)
                 m_ready_to_end_count++;
                 if (m_phase_trace)
                   `UVM_PH_TRACE("PH_READY_TO_END_CB","CALLING READY_TO_END CB",this,UVM_HIGH)
                 state_chg.m_prev_state = m_state;
                 m_state = UVM_PHASE_READY_TO_END;
                 `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
                 if (m_imp != null)
                   m_imp.traverse(top,this,UVM_PHASE_READY_TO_END);
                  
                 uvm_wait_for_nba_region(); // Give traverse targets a chance to object 

                 wait_for_self_and_siblings_to_drop();
                 do_ready_to_end = (m_state == UVM_PHASE_EXECUTING) && (m_ready_to_end_count < max_ready_to_end_iter) ; //when we don't wait in task above, we drop out of while loop
               end
             end
  
             // TIMEOUT
             begin
               if (this.get_name() == "run") begin
                  if (top.phase_timeout == 0)
                    wait(top.phase_timeout != 0);
                  if (m_phase_trace)
                    `UVM_PH_TRACE("PH/TRC/TO_WAIT", $sformatf("STARTING PHASE TIMEOUT WATCHDOG (timeout == %t)", top.phase_timeout), this, UVM_HIGH)
                  `uvm_delay(top.phase_timeout)
                  if ($time == `UVM_DEFAULT_TIMEOUT) begin
                     if (m_phase_trace)
                       `UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
                     foreach (m_executing_phases[p]) begin
                        if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
                           if (m_phase_trace)
                             `UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN", 
                                           $sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
                                           this,
                                           UVM_LOW)
                        end
                     end
                        
                     `uvm_fatal("PH_TIMEOUT",
                                $sformatf("Default timeout of %0t hit, indicating a probable testbench issue",
                                          `UVM_DEFAULT_TIMEOUT))
                  end
                  else begin
                     if (m_phase_trace)
                       `UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
                     foreach (m_executing_phases[p]) begin
                        if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
                           if (m_phase_trace)
                             `UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN", 
                                           $sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
                                           this,
                                           UVM_LOW)
                        end
                     end
                        
                     `uvm_fatal("PH_TIMEOUT",
                                $sformatf("Explicit timeout of %0t hit, indicating a probable testbench issue",
                                          top.phase_timeout))
                  end
                  if (m_phase_trace)
                    `UVM_PH_TRACE("PH/TRC/EXE/3","PHASE EXIT TIMEOUT",this,UVM_DEBUG)
               end // if (this.get_name() == "run")
               else begin
                  wait (0); // never unblock for non-run phase
               end
             end // if (m_phase_trace)

  
           join_any
           disable fork;
        
          end
  
        join // guard

    end

  end

  m_executing_phases.delete(this);

  //---------
  // JUMPING:
  //---------

  // If jump_to() was called then we need to kill all the successor
  // phases which may still be running and then initiate the new
  // phase.  The return is necessary so we don't start new successor
  // phases.  If we are doing a forward jump then we want to set the
  // state of this phase's successors to UVM_PHASE_DONE.  This
  // will let us pretend that all the phases between here and there
  // were executed and completed.  Thus any dependencies will be
  // satisfied preventing deadlocks.
  // GSA TBD insert new jump support

  if (m_phase_type == UVM_PHASE_NODE) begin

    if(m_premature_end) begin
      if(m_jump_phase != null) begin 
        state_chg.m_jump_to = m_jump_phase;
      
        `uvm_info("PH_JUMP",
              $sformatf("phase %s (schedule %s, domain %s) is jumping to phase %s",
               get_name(), get_schedule_name(), get_domain_name(), m_jump_phase.get_name()),
              UVM_MEDIUM);
      end
      else begin
        `uvm_info("PH_JUMP",
              $sformatf("phase %s (schedule %s, domain %s) is ending prematurely",
               get_name(), get_schedule_name(), get_domain_name()),
              UVM_MEDIUM);
      end
  
  
      #0; // LET ANY WAITERS ON READY_TO_END TO WAKE UP
      if (m_phase_trace)
        `UVM_PH_TRACE("PH_END","ENDING PHASE PREMATURELY",this,UVM_HIGH)
    end
    else begin
      // WAIT FOR PREDECESSORS:  // WAIT FOR PREDECESSORS:
      // function phases only
      if (task_phase == null)
        m_wait_for_pred();
    end
  
    //-------
    // ENDED:
    //-------
    // execute 'phase_ended' callbacks
    if (m_phase_trace)
      `UVM_PH_TRACE("PH_END","ENDING PHASE",this,UVM_HIGH)
    state_chg.m_prev_state = m_state;
    m_state = UVM_PHASE_ENDED;
    `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
    if (m_imp != null)
      m_imp.traverse(top,this,UVM_PHASE_ENDED);
    #0; // LET ANY WAITERS WAKE UP
  
  
    //---------
    // CLEANUP:
    //---------
    // kill this phase's threads
    state_chg.m_prev_state = m_state;
    if(m_premature_end) m_state = UVM_PHASE_JUMPING;
    else m_state = UVM_PHASE_CLEANUP ;
    `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
    if (m_phase_proc != null) begin
      m_phase_proc.kill();
      m_phase_proc = null;
    end
    #0; // LET ANY WAITERS WAKE UP
    if (phase_done != null)
      phase_done.clear();
  end

  //------
  // DONE:
  //------
  m_premature_end = 0 ;
  if(m_jump_fwd || m_jump_bkwd) begin
    if(m_jump_fwd) begin
      clear_successors(UVM_PHASE_DONE,m_jump_phase);
    end
    m_jump_phase.clear_successors();
  end
  else begin

    if (m_phase_trace)
      `UVM_PH_TRACE("PH/TRC/DONE","Completed phase",this,UVM_LOW)
    state_chg.m_prev_state = m_state;
    m_state = UVM_PHASE_DONE;
    `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
    m_phase_proc = null;
    #0; // LET ANY WAITERS WAKE UP
  end
  #0; // LET ANY WAITERS WAKE UP
  if (phase_done != null)
    phase_done.clear();

//-----------
// SCHEDULED:
//-----------
  if(m_jump_fwd || m_jump_bkwd) begin
    void'(m_phase_hopper.try_put(m_jump_phase));
    m_jump_phase = null;
    m_jump_fwd = 0;
    m_jump_bkwd = 0;
  end
  // If more successors, schedule them to run now
  else if (m_successors.size() == 0) begin
    top.m_phase_all_done=1;
  end 
  else begin
    // execute all the successors
    foreach (m_successors[succ]) begin
      if(succ.m_state < UVM_PHASE_SCHEDULED) begin
        state_chg.m_prev_state = succ.m_state;
        state_chg.m_phase = succ;
        succ.m_state = UVM_PHASE_SCHEDULED;
        `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(succ, state_chg))
        #0; // LET ANY WAITERS WAKE UP
        void'(m_phase_hopper.try_put(succ));
        if (m_phase_trace)
          `UVM_PH_TRACE("PH/TRC/SCHEDULED",{"Scheduled from phase ",get_full_name()},succ,UVM_LOW)
      end
    end
  end



endtask

class uvm_build_phase extends uvm_topdown_phase;
   virtual function void exec_func(uvm_component comp, uvm_phase phase);
      comp.build_phase(phase);
   endfunction
   local static uvm_build_phase m_inst;
   static const string type_name = "uvm_build_phase";

   static function uvm_build_phase get();
      if(m_inst == null)
         m_inst = new();
      return m_inst;
   endfunction
   protected function new(string name="build");
      super.new(name);
   endfunction
   virtual function string get_type_name();
      return type_name;
   endfunction
endclass

virtual class uvm_topdown_phase extends uvm_phase;
                  ...
endclass


function void uvm_phase::add(uvm_phase phase,
                             uvm_phase with_phase=null,
                             uvm_phase after_phase=null,
                             uvm_phase before_phase=null);
  uvm_phase new_node, begin_node, end_node, tmp_node;
  uvm_phase_state_change state_chg;

  if (phase == null)
      `uvm_fatal("PH/NULL", "add: phase argument is null")

  if (with_phase != null && with_phase.get_phase_type() == UVM_PHASE_IMP) begin
    string nm = with_phase.get_name();
    with_phase = find(with_phase);
    if (with_phase == null)
      `uvm_fatal("PH_BAD_ADD",
         {"cannot find with_phase '",nm,"' within node '",get_name(),"'"})
  end

  if (before_phase != null && before_phase.get_phase_type() == UVM_PHASE_IMP) begin
    string nm = before_phase.get_name();
    before_phase = find(before_phase);
    if (before_phase == null)
      `uvm_fatal("PH_BAD_ADD",
         {"cannot find before_phase '",nm,"' within node '",get_name(),"'"})
  end

  if (after_phase != null && after_phase.get_phase_type() == UVM_PHASE_IMP) begin
    string nm = after_phase.get_name();
    after_phase = find(after_phase);
    if (after_phase == null)
      `uvm_fatal("PH_BAD_ADD",
         {"cannot find after_phase '",nm,"' within node '",get_name(),"'"})
  end

  if (with_phase != null && (after_phase != null || before_phase != null))
    `uvm_fatal("PH_BAD_ADD",
       "cannot specify both 'with' and 'before/after' phase relationships")

  if (before_phase == this || after_phase == m_end_node || with_phase == m_end_node)
    `uvm_fatal("PH_BAD_ADD",
       "cannot add before begin node, after end node, or with end nodes")

  // If we are inserting a new "leaf node"
  if (phase.get_phase_type() == UVM_PHASE_IMP) begin
    uvm_task_phase tp;
    new_node = new(phase.get_name(),UVM_PHASE_NODE,this);
    new_node.m_imp = phase;
    begin_node = new_node;
    end_node = new_node;

    // The phase_done objection is only required
    // for task-based nodes
    if ($cast(tp, phase)) begin
       if (new_node.get_name() == "run") begin
         new_node.phase_done = uvm_test_done_objection::get();
       end
       else begin
         new_node.phase_done = uvm_objection::type_id::create({phase.get_name(), "_objection"});
       end
    end

  end
  // We are inserting an existing schedule
  else begin
    begin_node = phase;
    end_node   = phase.m_end_node;
    phase.m_parent = this;
  end

  // If 'with_phase' is us, then insert node in parallel
  /*
  if (with_phase == this) begin
    after_phase = this;
    before_phase = m_end_node;
  end
  */

  // If no before/after/with specified, insert at end of this schedule
  if (with_phase == null && after_phase == null && before_phase == null) begin
    before_phase = m_end_node;
  end


  if (m_phase_trace) begin
    uvm_phase_type typ = phase.get_phase_type();
    `uvm_info("PH/TRC/ADD_PH",
      {get_name()," (",m_phase_type.name(),") ADD_PHASE: phase=",phase.get_full_name()," (",
      typ.name(),", inst_id=",$sformatf("%0d",phase.get_inst_id()),")",
      " with_phase=",   (with_phase == null)   ? "null" : with_phase.get_name(), 
      " after_phase=",  (after_phase == null)  ? "null" : after_phase.get_name(),
      " before_phase=", (before_phase == null) ? "null" : before_phase.get_name(), 
      " new_node=",     (new_node == null)     ? "null" : {new_node.get_name(),
                                                           " inst_id=",
                                                           $sformatf("%0d",new_node.get_inst_id())},
      " begin_node=",   (begin_node == null)   ? "null" : begin_node.get_name(),
      " end_node=",     (end_node == null)     ? "null" : end_node.get_name()},UVM_DEBUG)
  end


  // INSERT IN PARALLEL WITH 'WITH' PHASE
  if (with_phase != null) begin
    begin_node.m_predecessors = with_phase.m_predecessors;
    end_node.m_successors = with_phase.m_successors;
    foreach (with_phase.m_predecessors[pred])
      pred.m_successors[begin_node] = 1;
    foreach (with_phase.m_successors[succ])
      succ.m_predecessors[end_node] = 1;
  end
  
  
  // INSERT BEFORE PHASE
  else if (before_phase != null && after_phase == null) begin
    begin_node.m_predecessors = before_phase.m_predecessors;
    end_node.m_successors[before_phase] = 1;
    foreach (before_phase.m_predecessors[pred]) begin
      pred.m_successors.delete(before_phase);
      pred.m_successors[begin_node] = 1;
    end
    before_phase.m_predecessors.delete();
    before_phase.m_predecessors[end_node] = 1;
  end
  

  // INSERT AFTER PHASE
  else if (before_phase == null && after_phase != null) begin
    end_node.m_successors = after_phase.m_successors;
    begin_node.m_predecessors[after_phase] = 1;
    foreach (after_phase.m_successors[succ]) begin
      succ.m_predecessors.delete(after_phase);
      succ.m_predecessors[end_node] = 1;
    end
    after_phase.m_successors.delete();
    after_phase.m_successors[begin_node] = 1;
  end
  

  // IN BETWEEN 'BEFORE' and 'AFTER' PHASES
  else if (before_phase != null && after_phase != null) begin
    if (!after_phase.is_before(before_phase)) begin
      `uvm_fatal("PH_ADD_PHASE",{"Phase '",before_phase.get_name(),
                 "' is not before phase '",after_phase.get_name(),"'"})
    end
    // before and after? add 1 pred and 1 succ
    begin_node.m_predecessors[after_phase] = 1;
    end_node.m_successors[before_phase] = 1;
    after_phase.m_successors[begin_node] = 1;
    before_phase.m_predecessors[end_node] = 1;
    if (after_phase.m_successors.exists(before_phase)) begin
      after_phase.m_successors.delete(before_phase);
      before_phase.m_successors.delete(after_phase);
    end
  end // if (before_phase != null && after_phase != null)

  // Transition nodes to DORMANT state
  if (new_node == null)
    tmp_node = phase;
  else
    tmp_node = new_node;

  state_chg = uvm_phase_state_change::type_id::create(tmp_node.get_name());
  state_chg.m_phase = tmp_node;
  state_chg.m_jump_to = null;
  state_chg.m_prev_state = tmp_node.m_state;
  tmp_node.m_state = UVM_PHASE_DORMANT;
  `uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(tmp_node, state_chg)) 
endfunction


Currently I just went to this step to understand the steps of uvm_phase. I need to to go to more details on these functions/tasks to understand more.

Monday, July 6, 2015

code coverage of cpp program


./src/code.h

#ifndef __CODE_H__
#define __CODE_H__
struct BT {
    int x;
    BT* l;
    BT* r;
};

int foo(int);
void bubbleSort(int*, int);
bool insert(BT*, int);
int nodesBetween(BT*, int, int);

#endif


./src/code.cpp

#include 
#include "code.h"

using std::queue;

int foo(int param) {
    if (param) {
        return 1;
    } else {
        return 0;
    }
}

void bubbleSort(int* data, int size) {
    if(size <= 1) {
        return;
    }

    for(int i=0; i<size-1; i++) {         

        for(int j=0; j<size-1; j--) {             
            if(data[j] > data[j+1]) {
                int dataTmp = data[j];
                data[j] = data[j+1];
                data[j+1] = dataTmp;
            }
        }
    }
}

bool insert(BT* root, int value) {
    BT* node = root;
    while(1) {
        if(value < node->x) {
            if(node->l != nullptr) {
                node = node->l;
            } else {
                node->l = new BT;

                node = node->l;
                node->x = value;
                node->l = nullptr;
                node->r = nullptr;

                return true;
            }
        } else if(value > node->x) {
            if(node->r != nullptr) {
                node = node->r;
            } else {
                node->r = new BT;
      
                node = node->r;
                node->x = value;
                node->l = nullptr;
                node->r = nullptr;

                return true;
            }
        } else {
            return false;
        }
    }
}

int nodesBetween(BT* root, int min, int max) {
    int nodeNum = 0;
    queue nodes;

    nodes.push(root);

    while(nodes.size()) {
        BT* node = nodes.front();
        nodes.pop();

        if(node->x > min && node->x < max) {
            nodeNum++;

            if(node->l != nullptr) {
                nodes.push(node->l);
            }
            if(node->r != nullptr) {
                nodes.push(node->r);
            }
        } else if(node->x <= min) {
            if(node->x == min) {
                nodeNum++;
            }

            if(node->r != nullptr) {
                nodes.push(node->r);
            }
        } else if(node->x >= max) {
            if(node->x == max) {
                nodeNum++;
            }

            if(node->l != nullptr) {
                nodes.push(node->l);
            }
        }
    }

    return nodeNum;
}


./src/main.cpp

#include 
#include "code.h"
#include 

using std::queue;
using std::cout;
using std::endl;

int main(int argc, char* argv[]) {
    foo(0);

    int data[] = {1, 2, 7, 3, 8, 4, 5, 8, 1, 0, 9, 2};
    int size = sizeof(data)/sizeof(int);
    bubbleSort(data, size);

    for(int i=0; i         cout << data[i] << " ";
    }
    cout << endl;

    BT* root;
    root = new BT;
    root->x = 29;
    root->l = nullptr;
    root->r = nullptr;

    insert(root, 18);
    insert(root, 37);
    insert(root, 12);
    insert(root, 19);
    insert(root, 30);
    insert(root, 41);

    queue nodes;
    nodes.push(root);
    while(nodes.size()) {
        BT* node = nodes.front();
        nodes.pop();

        cout << node->x << " ";

        if(node->l != nullptr) {
            nodes.push(node->l);
        }
        if(node->r != nullptr) {
            nodes.push(node->r);
        }
    }
    cout << endl;

    int nodeNum = nodesBetween(root, 12, 19);
    cout << nodeNum << endl;

    return 0;
}


./Makefile

SRCDIR = src
SIMDIR = sim

SOURCE = code.cpp main.cpp
PROGRAM = $(SIMDIR)/code.out

COMPFILES = $(patsubst %.cpp,%.o,$(SOURCE))

INCDIR = ./$(SRC)
COMPARG = -std=c++11

SOURCEWITHPATH = $(addprefix src/,$(SOURCE))

all: clean coverageHtmlGen
 echo $(COMPFILES)

clean:
 rm -rf $(SIMDIR) *.gcno *.out *.gcda *.o coverage.info coverage_html

%.o: $(SRCDIR)/%.cpp
 g++ --coverage -I$(INCDIR) $(COMPARG) -c $< -o $@

$(PROGRAM): $(COMPFILES)
 if [ ! -e $(SIMDIR) ]; then mkdir $(SIMDIR); fi
 g++ --coverage $? -o $@

run: $(PROGRAM)
 ./$(PROGRAM)

coverageGen: run
 gcov $(SOURCEWITHPATH)

coverageHtmlGen: coverageGen
 lcov --capture --directory ./ --output-file coverage.info
 genhtml coverage.info --output-directory coverage_html



Wednesday, July 1, 2015

UVM: how driver analysis port connect and write data to scoreboard port

 1) Create an uvm_analysis_port in driver or monitor. The uvm_analysis_port is inherited from uvm_port_base which extends uvm_tlm_if_base.
class uart_driver extends uvm_driver #(uart_transaction); uvm_analysis_port #(uart_transaction) drv2sb_port;
endclass

class uvm_analysis_port # (type T = int)
  extends uvm_port_base # (uvm_tlm_if_base #(T,T));
endclass

virtual class uvm_port_base #(type IF=uvm_void) extends IF;
endclass

2) Use macro uvm_analysis_imp_decl(SFX) to define a new uvm_analysis_imp class in scoreboard and then create an object of the class in the scoreboard. uvm_analysis_imp extends the class uvm_port_base.
`uvm_analysis_imp_decl(_rcvd_uart)
class uart_scoreboard extends uvm_scoreboard; uvm_analysis_imp_sent_uart #(uart_transaction, uart_scoreboard) drv2sb_port;
endclass

`define uvm_analysis_imp_decl(SFX) \
class uvm_analysis_imp``SFX #(type T=int, type IMP=int) \
 extends uvm_port_base #(uvm_tlm_if_base #(T,T)); \
 ...\
 \
 endclass

3) In the uvm_env, connect the driver uvm_analysis_port with scoreboard uvm_analysis_imp port.
In uvm_analysis_port, which is inherited from uvm_port_base, function resolve_bindings will automatically be called just before the start of the end_of_elaboration phase, which will add the scoreboard port in the list m_imp_list.
class uart_env extends uvm_env; uart_agent uart_agt;
uart_scoreboard uart_sb;
function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  uart_agt.uart_drv.drv2sb_port.connect(uart_sb.drv2sb_port);
endfunction endclass

virtual class uvm_port_base #(type IF=uvm_void) extends IF;

  virtual function void connect (this_type provider);
                     ...
      m_provided_by[provider.get_full_name()] = provider;
      provider.m_provided_to[get_full_name()] = this;

  endfunction

  local function void m_add_list           (this_type provider);
    string sz;
    this_type imp;

    for (int i = 0; i < provider.size(); i++) begin
      imp = provider.get_if(i);
      if (!m_imp_list.exists(imp.get_full_name()))
        m_imp_list[imp.get_full_name()] = imp;
    end

  endfunction

  virtual function void resolve_bindings();
    if (m_resolved) // don't repeat ourselves
     return;

    if (is_imp()) begin
      m_imp_list[get_full_name()] = this;
    end
    else begin
      foreach (m_provided_by[nm]) begin
        this_type port;
        port = m_provided_by[nm];
        port.resolve_bindings();
        m_add_list(port);
      end
    end
                    ...
  endfunction

endclass

4) In the running phase, when driver call the write function of the uvm_analysis_port, it will go through all it's connected ports when connected in uvm_env and call the write function of the uvm_analysis_imp``SFX, which will call the m_imp.write``SFX( t) function in the scoreboard. In the example below m_imp.write``SFX( t) is write_sent_uart function.
class uart_driver extends uvm_driver #(uart_transaction);
forever begin
uart_transaction uart_tr; seq_item_port.get_next_item(uart_tr);          drv2sb_port.write(uart_tr); end
endtask endclass

class uvm_analysis_port # (type T = int)
  extends uvm_port_base # (uvm_tlm_if_base #(T,T));
                            ...
  function void write (input T t);
    uvm_tlm_if_base # (T, T) tif;
    for (int i = 0; i < this.size(); i++) begin
      tif = this.get_if (i);
      if ( tif == null )
        uvm_report_fatal ("NTCONN", {"No uvm_tlm interface is connected to ", get_full_name(), " for executing write()"}, UVM_NONE);
      tif.write (t);
    end
  endfunction

endclass

virtual class uvm_port_base #(type IF=uvm_void) extends IF;
  function int size ();
    return m_imp_list.num();
  endfunction

  function uvm_port_base #(IF) get_if(int index=0);
    string s;
    if (size()==0) begin
      m_comp.uvm_report_warning("get_if",
        "Port size is zero; cannot get interface at any index", UVM_NONE);
      return null;
    end
    if (index < 0 || index >= size()) begin
      $sformat(s, "Index %0d out of range [0,%0d]", index, size()-1);
      m_comp.uvm_report_warning(s_connection_error_id, s, UVM_NONE);
      return null;
    end
    foreach (m_imp_list[nm]) begin
      if (index == 0)
        return m_imp_list[nm];
      index--;
    end
  endfunction

endclass

`define uvm_analysis_imp_decl(SFX) class uvm_analysis_imp``SFX #(type T=int, type IMP=int) \
  extends uvm_port_base #(uvm_tlm_if_base #(T,T)); \        `UVM_IMP_COMMON(`UVM_TLM_ANALYSIS_MASK,`"uvm_analysis_imp``SFX`",IMP) \
  function void write( input T t); \
  m_imp.write``SFX( t); \
  endfunction \
  \
 endclass
`uvm_analysis_imp_decl(_rcvd_uart)

class uart_scoreboard extends uvm_scoreboard;


uvm_analysis_imp_sent_uart #(uart_transaction, uart_scoreboard) drv2sb_port;
    $cast(cb_uart_tr, uart_tr.clone());
endfunction
virtual function void write_sent_uart(input uart_transaction uart_tr);


endclass