Why we use p_sequencer in UVM?



p_sequencer

经常会看到在sequence和virtual sequence中,使用了宏`uvm_declare_p_sequencer。那么,为什么我们要使用p_sequencer呢?

class base_vseq extends uvm_sequence;
  function new(string name="base_vseq");
    super.new(name);
  endfunction

  `uvm_object_utils(base_vseq)
  `uvm_declare_p_sequencer(uart_ctrl_virtual_sequencer)
 endclass

从继承的关系上,uvm_sequence_item->uvm_sequence_base->uvm_sequence,uvm_sequence_item中有一个成员变量m_sequencer(喂喂喂,怎么说m_sequencer了?嗯嗯,别急,往后看),当我们在test的run_phase中start一个sequence的时候,其实就是给m_sequencer赋值了,把我们环境的sequencer赋给m_sequencer。m_sequencer是不带parameter的。

class uvm_sequence_item extends uvm_transaction;

  local      int                m_sequence_id = -1;
  protected  bit                m_use_sequence_info;
  protected  int                m_depth = -1;

  protected  uvm_sequencer_base m_sequencer;

把宏`uvm_declare_p_sequencer展开看,就是把m_sequencer赋值给了p_sequencer,当然了,m_sequncer所指向的对象,一定要是p_sequencer或者p_sequencer的子类,否者cast函数会报错的。

`define uvm_declare_p_sequencer(SEQUENCER) \
  SEQUENCER p_sequencer;\
  virtual function void m_set_p_sequencer();\
    super.m_set_p_sequencer(); \
    if( !$cast(p_sequencer, m_sequencer)) \
	`uvm_fatal("DCLPSQ", \
	$sformatf("%m %s Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer", get_full_name())) \
  endfunction 

那么,费了这么多事,要做什么呢?其实就是传递配置。因为sequence和component不一样,component是在build_phase环节就有了,而sequence是在run_phase阶段才会第一次出现,sequence想要去从环境上拿到配置信息,一定要通过它的m_sequencer,但是m_sequencer是不带parameter的,和我们start的时候一定不match,所以UVM就事先在定义sequence的时候,把p_sequencer这个带parameter的sequencer准备好,方便后面的使用。

virtual task body();
      response_queue_error_report_disabled = 1;
      start_addr = `TX_FIFO_REG;
      while (num_of_wr) begin
	`uvm_info(get_type_name(), $sformatf("Starting %0d Writes...", num_of_wr), UVM_LOW)
	for (int i = 0; i < num_of_wr; i++) begin
	  if (p_sequencer.tful) begin
	    num_of_wr -= i;
	    `uvm_info("UART_APB_SEQLIB", $sformatf("Breaking from apb_to_uart_wr since tfifo is not empty yet, pending num_of_wr = %d", num_of_wr), UVM_LOW)
	    #10000;
	    break;
	  end
	  `uvm_do_with(req, 
	    { req.addr == start_addr;
	      req.direction == APB_WRITE;
	      req.delay == del; } )
	  #200;
	  if (i == num_of_wr - 1)
	     num_of_wr = 0;
	end
      end
    endtask

上面这段代码就是sequence的片段,可以看到直接访问了p_sequencer的变量tful,如果写成m_sequencer.tful,是一定不成的。