UVM_DO宏学习

首先看一些uvm_do宏的定义

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

其实uvm_do系列大部分都是调用了`uvm_do_on_pri_with,再看一下`uvm_do_on_pri_with做了什么

`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_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

其中就出现了熟悉的start_item/finish_item和start,该宏对第一个argument进行类型转换来进行sequence和sequence_item的鉴别,如果do的是item那么自然无法与uvm_sequence_base进行转换,所以会调用执行item对应的start_item/finish_item。否则调用start

uvm_sequence_base::start_item

 // Function: start_item
  //
  // ~start_item~ and <finish_item> together will initiate operation of
  // a sequence item.  If the item has not already been
  // initialized using create_item, then it will be initialized here to use
  // the default sequencer specified by m_sequencer.  Randomization
  // may be done between start_item and finish_item to ensure late generation
  //

  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
      
    if (sequencer == null)
      sequencer = item.get_sequencer();
    
    if (sequencer == null) begin
        uvm_report_fatal("STRITM", "sequence_item has null sequencer", UVM_NONE);
    end

    item.set_use_sequence_info(1);
    item.set_sequencer(sequencer);
    item.set_parent_sequence(this);
    item.reseed();

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

    `ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
      void'(sequencer.begin_child_tr(item, m_tr_handle, item.get_root_sequence_name()));
    `endif

    pre_do(1);

  endtask  

start_item不可启动seq,启动seq需要使用start。

如果没有传入sqr,调用item的get_sequencer获得sqr

如果item的sqr为null,调用get_sequencer获得sqr

set_use_sequence_info具体不了解

set_sequencer为item指定sqr

  virtual function void set_sequencer(uvm_sequencer_base sequencer);
    m_sequencer = sequencer;
    m_set_p_sequencer();
  endfunction

reseed:不理解

重新为随机数生成器分配seed,默认采用uvm seed生成机制,即将type_name & inst_name传递给uvm_create_random_seed,生成的seed再通过srandom传递给随机数生成器。若不使用uvm seed,该函数不执行任何操作

传入的set_priority默认为-1,则会继承当前seq的优先级

wait_for_grant

// wait_for_grant
// --------------

task uvm_sequencer_base::wait_for_grant(uvm_sequence_base sequence_ptr,
                                        int item_priority = -1,
                                        bit lock_request = 0);
  uvm_sequence_request req_s;
  int my_seq_id;

  if (sequence_ptr == null)
    uvm_report_fatal("uvm_sequencer",
       "wait_for_grant passed null sequence_ptr", UVM_NONE);

  my_seq_id = m_register_sequence(sequence_ptr);
  
  // If lock_request is asserted, then issue a lock.  Don't wait for the response, since
  // there is a request immediately following the lock request
  if (lock_request == 1) begin
    req_s = new();
    req_s.grant = 0;
    req_s.sequence_id = my_seq_id;
    req_s.request = SEQ_TYPE_LOCK;
    req_s.sequence_ptr = sequence_ptr;
    req_s.request_id = g_request_id++;
    arb_sequence_q.push_back(req_s);
  end
      
  // Push the request onto the queue
  req_s = new();
  req_s.grant = 0;
  req_s.request = SEQ_TYPE_REQ;
  req_s.sequence_id = my_seq_id;
  req_s.item_priority = item_priority;
  req_s.sequence_ptr = sequence_ptr;
  req_s.request_id = g_request_id++;
  arb_sequence_q.push_back(req_s);
  m_update_lists();

  // Wait until this entry is granted
  // Continue to point to the element, since location in queue will change
  m_wait_for_arbitration_completed(req_s.request_id);

  // The wait_for_grant_semaphore is used only to check that send_request
  // is only called after wait_for_grant.  This is not a complete check, since
  // requests might be done in parallel, but it will catch basic errors
  req_s.sequence_ptr.m_wait_for_grant_semaphore++;

endtask

其中核心函数是 m_register_sequence(sequence_ptr)

function int uvm_sequencer_base::m_register_sequence(uvm_sequence_base sequence_ptr);

  if (sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1) > 0)
    return sequence_ptr.get_sequence_id();
  
  sequence_ptr.m_set_sqr_sequence_id(m_sequencer_id, g_sequence_id++);
  reg_sequences[sequence_ptr.get_sequence_id()] = sequence_ptr;
  return sequence_ptr.get_sequence_id();
endfunction

m_sequencer_id如下,其中g_sequencer_id是静态变量,环境中每例化一次sqr,该sqr的m_sequencer_id就会自加,sqr的id各不相同

function uvm_sequencer_base::new (string name, uvm_component parent);
  super.new(name, parent);
  m_sequencer_id = g_sequencer_id++;
  m_lock_arb_size = -1;
endfunction

m_get_sqr_sequence_id/m_set_sqr_sequence_id如下

  function int m_get_sqr_sequence_id(int sequencer_id, bit update_sequence_id);
    if (m_sqr_seq_ids.exists(sequencer_id)) begin
      if (update_sequence_id == 1) begin
        set_sequence_id(m_sqr_seq_ids[sequencer_id]);
      end
      return m_sqr_seq_ids[sequencer_id];
    end

    if (update_sequence_id == 1)
      set_sequence_id(-1);

    return -1;
  endfunction
  
  // m_set_sqr_sequence_id
  // ---------------------

  function void m_set_sqr_sequence_id(int sequencer_id, int sequence_id);
    m_sqr_seq_ids[sequencer_id] = sequence_id;
    set_sequence_id(sequence_id);
  endfunction

如果m_sqr_seq_ids存在该sqr_id,现在先学习一个sqr do一个seq,因为一个sqr do多个seq,m_sqr_seq_ids就会多次写入同一键

m_set_sqr_sequence_id每在该sqr上do一次seq,g_sequence_id就会自加

reg_sequences键为sqr_id,索引为seq_id

再次为seq set  seq id

wait_for_grant会为所发送的seq打上seq id

arb_sequence_q中放入req_s

function void uvm_sequencer_base::m_update_lists();
  m_lock_arb_size++;
endfunction

每do一个item,m_lock_arb_size就是自加

m_wait_for_arbitration_completed等待仲裁,以req_s的seq id来进行仲裁,仲裁结束才可以继续执行

task uvm_sequencer_base::m_wait_for_arbitration_completed(int request_id);
  int lock_arb_size;
  
  // Search the list of arb_wait_q, see if this item is done
  forever 
    begin
      lock_arb_size  = m_lock_arb_size;
      
      if (arb_completed.exists(request_id)) begin
        arb_completed.delete(request_id);
        return;
      end
      wait (lock_arb_size != m_lock_arb_size);
    end
endtask

首先需要arb_completed里装有req_id,这一步是在m_set_arbitration_completed中实现的,在m_select_sequence中调用该函数,后续再看,假设已经存在该id,存在的话delete后就可跳出forever

wait_for_grant的最后m_wait_for_grant_semaphore++

随后看一下finish_item

  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);
    `ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
    sequencer.end_tr(item);
    `endif
    post_do(item);

  endtask

send_request

function void uvm_sequencer_param_base::send_request(uvm_sequence_base sequence_ptr,
                                                     uvm_sequence_item t,
                                                     bit rerandomize = 0);
  REQ param_t;

  if (sequence_ptr == null) begin
    uvm_report_fatal("SNDREQ", "Send request sequence_ptr is null", UVM_NONE);
  end

  if (sequence_ptr.m_wait_for_grant_semaphore < 1) begin
    uvm_report_fatal("SNDREQ", "Send request called without wait_for_grant", UVM_NONE);
  end
  sequence_ptr.m_wait_for_grant_semaphore--;
  
  if ($cast(param_t, t)) begin
    if (rerandomize == 1) begin
      if (!param_t.randomize()) begin
        uvm_report_warning("SQRSNDREQ", "Failed to rerandomize sequence item in send_request");
      end
    end
    if (param_t.get_transaction_id() == -1) begin
      param_t.set_transaction_id(sequence_ptr.m_next_transaction_id++);
    end
    m_last_req_push_front(param_t);
  end else begin
    uvm_report_fatal(get_name(),$sformatf("send_request failed to cast sequence item"), UVM_NONE);
  end

  param_t.set_sequence_id(sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1));
  t.set_sequencer(this);
  if (m_req_fifo.try_put(param_t) != 1) begin
    uvm_report_fatal(get_full_name(), 
                     $sformatf("Sequencer send_request not able to put to fifo, depth; %0d", m_req_fifo.size()), UVM_NONE);
  end

  m_num_reqs_sent++;
  // Grant any locks as soon as possible
  grant_queued_locks();
endfunction

send_request做的是将req放入m_req_fifo,driver调用get_next_item就是从这个fifo获取req

wait_for_item_done


task uvm_sequencer_base::wait_for_item_done(uvm_sequence_base sequence_ptr,
                                            int transaction_id);
  int sequence_id;

  sequence_id = sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1);
  m_wait_for_item_sequence_id = -1;
  m_wait_for_item_transaction_id = -1;

  if (transaction_id == -1)
    wait (m_wait_for_item_sequence_id == sequence_id);
  else
    wait ((m_wait_for_item_sequence_id == sequence_id &&
           m_wait_for_item_transaction_id == transaction_id));
endtask
function void uvm_sequencer::item_done(RSP item = null);
  REQ t;

  // Set flag to allow next get_next_item or peek to get a new sequence_item
  sequence_item_requested = 0;
  get_next_item_called = 0;
  
  if (m_req_fifo.try_get(t) == 0) begin
    uvm_report_fatal(get_full_name(), {"Item_done() called with no outstanding requests.",
      " Each call to item_done() must be paired with a previous call to get_next_item()."});
  end else begin
    m_wait_for_item_sequence_id = t.get_sequence_id();
    m_wait_for_item_transaction_id = t.get_transaction_id();
  end
  
  if (item != null) begin
    seq_item_export.put_response(item);
  end

  // Grant any locks as soon as possible
  grant_queued_locks();
endfunction

当打完包之后driver会调用item_done。将sequence-base的变量m_wait_for_item_sequence_id/m_wait_for_item_transaction_id都设置成m_req_fifo的seq id 和transaction_id,那么在wait_for_item_done时,就会相等。

接下来看一下driver和sqr的交互,上述的都是seq和sqr的交互

task uvm_sequencer::get_next_item(output REQ t)

task uvm_sequencer::get_next_item(output REQ t);
  REQ req_item;

  // If a sequence_item has already been requested, then get_next_item()
  // should not be called again until item_done() has been called.

  if (get_next_item_called == 1)
    uvm_report_error(get_full_name(),
      "Get_next_item called twice without item_done or get in between", UVM_NONE);
  
  if (!sequence_item_requested)
    m_select_sequence();

  // Set flag indicating that the item has been requested to ensure that item_done or get
  // is called between requests
  sequence_item_requested = 1;
  get_next_item_called = 1;
  m_req_fifo.peek(t);
endtask

如果多次调用get_next_item且没有调用item_done,那么就会报错

task uvm_sequencer_base::m_select_sequence();
   int selected_sequence;

    // Select a sequence
    do begin
      wait_for_sequences();
      selected_sequence = m_choose_next_request();
      if (selected_sequence == -1) begin
        m_wait_for_available_sequence();
      end
    end while (selected_sequence == -1);
    // issue grant
    if (selected_sequence >= 0) begin
      m_set_arbitration_completed(arb_sequence_q[selected_sequence].request_id);
      arb_sequence_q.delete(selected_sequence);
      m_update_lists();
    end
endtask

wait_for_sequences等待seq发送有效的item

m_choose_next_request

m_set_arbitration_completed就是在arb_completed放入索引

先看简单一些的start方法

  virtual task start (uvm_sequencer_base sequencer,
                      uvm_sequence_base parent_sequence = null,
                      int this_priority = -1,
                      bit call_pre_post = 1);
    if (parent_sequence != null) begin
       set_parent_sequence(parent_sequence);
       set_use_sequence_info(1);
       if (sequencer == null) sequencer = parent_sequence.get_sequencer();
       reseed();
    end
    set_sequencer(sequencer);

start可以传入parent_sequence参数作为该sequence的parent,同时set_sequencer一个挂载的sequencer,set_parent_sequence简单地将parent_sequence赋值给m_parent_sequence, set_use_sequence_info会对m_use_sequence_info进行设置,设置为1代表该sequence的信息可以被拷贝以及打印,如果没有传入sequencer,会将parent_sequence的sequencer作为该sequence的sequencer

reseed同上

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

 优先级默认-1,且不能小于-1,


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

  // Function: clear_response_queue
  //
  // Empties the response queue for this sequence.

    virtual function void clear_response_queue();
        response_queue.delete();
    endfunction

clear_response_queue不理解后续补充

    // 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();

 不理解后续补充

后续在执行body前会分别执行pre_start/pre_start/pre_do/mid_do,pre_do/mid_do记得是可以通过开关来进行选择是否执行

其次看一下start_item方法

  virtual task start_item (uvm_sequence_item item,
                           int set_priority = -1,
                           uvm_sequencer_base sequencer=null);
    uvm_sequence_base seq;
    if (sequencer == null)
        sequencer = item.get_sequencer();
        
    if(sequencer == null)
        sequencer = get_sequencer();

  sequencer.wait_for_grant(this, set_priority);

 首先检查item是否存在sequencer,再检查parent_sequence的sequencer

其核心方法是wait_for_grant,接下来学习一下wait_for_grant

task uvm_sequencer_base::wait_for_grant(uvm_sequence_base sequence_ptr,
                                        int item_priority = -1,
                                        bit lock_request = 0);
 int my_seq_id;
 my_seq_id = m_register_sequence(sequence_ptr);

// m_register_sequence
// -------------------

function int uvm_sequencer_base::m_register_sequence(uvm_sequence_base sequence_ptr);

  if (sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1) > 0)
    return sequence_ptr.get_sequence_id();
  
  sequence_ptr.m_set_sqr_sequence_id(m_sequencer_id, g_sequence_id++);
  reg_sequences[sequence_ptr.get_sequence_id()] = sequence_ptr;
  return sequence_ptr.get_sequence_id();
endfunction

function void uvm_sequencer_param_base::send_request(uvm_sequence_base sequence_ptr,
                                                     uvm_sequence_item t,
                                                     bit rerandomize = 0);
//...
if (m_req_fifo.try_put(param_t) != 1) begin
    uvm_report_fatal(get_full_name(), 
                     $sformatf("Sequencer send_request not able to put to fifo, depth; %0d", m_req_fifo.size()), UVM_NONE);
  end

send_request会往m_req_fifo put REQ

目 录 1 这是一个什么样的故事...........................................................................................................6 2 这是关于谁的故事...................................................................................................................8 2.1 sequence........................................................................................................................8 2.2 sequence_item...............................................................................................................8 2.3 sequencer.....................................................................................................................10 3 故事背后的故事.....................................................................................................................13 3.1 整体架构.....................................................................................................................14 3.2 sequence_item生成.....................................................................................................14 3.2.1 my_sequence_item..........................................................................................14 3.2.2 òvm_object_utils_begin.................................................................................16 3.2.3 òvm_field_utils_begin...................................................................................18 3.2.4 òvm_object_utils_end....................................................................................19 3.3 sequencer的生成.........................................................................................................20 3.3.1 my_sequnecer..................................................................................................20 3.3.2 òvm_sequencer_utils.....................................................................................20 3.3.3 òvm_sequencer_utils_begin...........................................................................21 3.3.4 òvm_componen

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值