初识m_sequencer、p_sequencer和uvm_declare_p_sequencer宏

一、m_sequencer

1、什么是m_sequencer

m_sequencer是定义在uvm_sequencer_item中的,uvm_sequencer_base类型的句柄,也就是说

  • m_sequencer是uvm_sequencer_item的成员变量
  • m_sequencer是指向uvm_sequencer_base的句柄
  • 任何派生于uvm_sequencer_item的类都会拥有一个m_sequencer

m_sequencer源码如下:

  class uvm_sequence_item extends uvm_transaction;
      ...
   protected uvm_sequencer_base m_sequencer;
      ...
  endclass

m_sequencer在类库中的关系如下图紫色箭头所示:

2、为什么要有m_sequencer

sequence属于object,sequencer属于component。

正常来说,sequence无法与sequencer进行交互,也不可能通过component的结构层次获得顶层配置及其他信息。因此需要一个能沟通sequence和sequencer的桥梁,m_sequencer就是这个桥梁。

3、怎么使用m_sequencer

通常调用uvm_do系列宏时,uvm能够自动根据传入的sequence或sequence_item调用对应的方法。使m_sequencer由指向uvm_sequencer_base,变为指向用户指定的sequencer,(例如类库中的your_sequencer),如下图蓝色虚线将变为紫色实线。

由此搭建了sequence和sequencer的桥梁,调用其他方法同理(不管是什么方法,没有改变m_sequencer指向给定的sequencer这一本质)。

因此使用者只需要调用所需方法,有关于m_sequencer的使用由uvm自动完成了。

值得注意的一点是用户指定的sequencer是uvm_sequencer_base的子类,对于m_sequencer而言,此时是父类句柄指向了子类对象,这也为下面的动态转换打下了基础。

二、p_sequencer

1、什么是p_sequencer

p_sequencer是由`uvm_decalre_p_sequencer宏,定义在用户指定的sequence(例如类库中的your_sequence)中的,用户指定的类型的句柄(例如类库中的your_sequencer),也就是说

  • p_sequencer是由uvm_decalre_p_sequencer宏产生的
  • p_sequencer是用户指定的sequence的成员变量(这里是your_sequence的成员变量,因为`uvm_decalre_p_sequencer宏是在your_sequence中使用的)
  • p_sequencer是指向用户指定的类型的句柄(这里是your_sequencer的成员变量)

p_sequencer源代码如下:

  `define uvm_declare_p_sequencer(SEQUENCER) 
    SEQUENCER p_sequencer; //SEQUENCER是由用户指定的类型
    virtual function void m_set_p_sequencer();
      super.m_set_p_sequencer(); //父类的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

可能到这里还是有些抽象,接下来我们进行一个更为形象的说明。

2、为什么需要p_sequencer

在sequence机制中,sequence就像是一个快递,它可以装入商品(sequence_itm),也可以装入另一个快递(sequence);sequencer就像是商家,他把快递交给物流公司(driver),物流公司再把快递交给用户(DUT)。

当有很多个快递时,物流公司怎么知道把哪个快递交给哪个用户呢,这就需要商家给快递上注明收货人和发货人的信息(your_sequencer中的dmac和smac等)。

也就是说your_sequence要访问your_sequencer中的成员变量dmac和smac,虽然这是your_sequence中的m_sequencer经过调用`uvm_do后已经指向了your_sequencer,但此时他的类型还是uvm_sequencer_base,而uvm_sequencer_base是your_sequencer的父类。父类句柄直接访问子类对象是不允许的。

因此我们需要将m_sequencer的类型转换成子类类型(也就是your_sequencer)。

首先我们在your_sequencer中声明了一个your_sequencer类型的句柄x_sequencer,再将m_sequencer动态转换成子类句柄,这样your_sequence就可以访问your_sequencer的成员变量了。

代码如下:

  class your_sequence extends uvm_sequence #(your_transaction);
	your_trasaction your_trans;
	`uvm_object_utils(your_squence)
	...
	
	virtual task body();
		your_sequencer x_sequencer;//声明x_sequencer
		...
		$cast(x_sequencer,m_sequencer);//动态转换
		...
	repeat(10) 
		begin
		`uvm_do_with(m_trans,{your_trans.dmac==x_sequencer.dmac;
		                      your_trans.smac==x_sequencer.smac;})//访问your_sequencer中的变量
		end
	...
  endclass

如此一来,快递(your_sequence)上就有了一个标明了收货发货信息(dmac和smac)的快递面单,物流公司(driver)就可以按照快递单来派送了。

但是每次都需要通过声明x_sequencer,再动态转换就像人工手写快递面单一样繁琐,因此uvm将上述过程封装成立宏`uvm_declare_p_sequencer,以后就可以自动打印快递面单了

3、怎么使用p_sequencer

只需要在你需要的sequence中调用宏uvm_declare_p_sequencer(SEQUENCER) ,uvm将自动做以下事情:

  • 声明一个x_sequencer,指向SEQUENCER(这里your_sequencer)。
  • 将m_sequencer动态转换成子类句柄。

你所需要做的只是考虑需要把哪些信息打印到快递面单上了。

参考资料

1.《UVM实战 卷Ⅰ》 张强著
2.《芯片验证漫游指南——从系统理论到UVM的验证全视界》刘斌著
3. 芯片验证面试必考题:m_sequencer​与p_sequencer的区别是什么?https://zhuanlan.zhihu.com/p/436911218

posted @ 2023-02-26 14:48  程默白  阅读(875)  评论(0编辑  收藏  举报