一、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源码如下:
classuvm_sequence_itemextendsuvm_transaction;
...
protecteduvm_sequencer_basem_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源代码如下:
`defineuvm_declare_p_sequencer(SEQUENCER)
SEQUENCERp_sequencer; //SEQUENCER是由用户指定的类型
virtualfunctionvoidm_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的成员变量了。
代码如下:
classyour_sequenceextendsuvm_sequence#(your_transaction);
your_trasactionyour_trans;
`uvm_object_utils(your_squence)
...
virtualtaskbody();
your_sequencerx_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动态转换成子类句柄。
你所需要做的只是考虑需要把哪些信息打印到快递面单上了。
参考资料
《UVM实战 卷Ⅰ》 张强著
《芯片验证漫游指南——从系统理论到UVM的验证全视界》刘斌著
芯片验证面试必考题:m_sequencer与p_sequencer的区别是什么?https://zhuanlan.zhihu.com/p/436911218