uvm sequence是怎么跑起来的?

总览

在测试中,test 调用 sequence,并将 sequence 传递给 sequencer。sequencer 生成事务并将它们传递给 driver,driver 将事务转换为信号和数据,并将它们发送到 DUT 中。DUT 处理信号和数据,并将响应发送回 driver。driver 接收响应并将其转换为事务,然后将其传递给 sequencer。sequencer 接收事务并返回给 test。

总的来说,sequence 在 UVM 中的工作流程如下:

test 调用 sequence。
sequencer 生成事务并将其传递给 driver。
driver 将事务转换为信号和数据,并将其发送到 DUT 中。
DUT 处理信号和数据,并将响应发送回 driver。
driver 接收响应并将其转换为事务,然后将其传递给 sequencer。
sequencer 接收事务并返回给 test。

sequence的启动(调用)方式

sequence怎么启动,即sequence传递给哪个sequencer,或者sequencer调用哪个sequnce的body;

sequence的启动方式有显示启动和隐士启动两种套路,但其本质一样。

  • 显式启动(直接启动)——调用start()方法启动。
    在base_test中启动
class base_test extends uvm_test;
my_env env;
...
endclass

task base_test::main_phase(uvm_phase);
	base_sequence seq;
	seq = base_sequence::type_id::create("seq");
	seq.start( env.agt.sqr);
endtask
...
  • 隐式启动 ——使用uvm_config_db机制配置default_sequence启动。
class base_test extends uvm_test;
my_env env;
	...
endclass
function base_test::build_phase(uvm_phase phase);
	super.build_phase(phase);
	base_sequence seq = new("seq");
	uvm_config_db#(uvm_object_wrapper)::set(this,
											"env.agt.sqr.main_phase",
											"default_sequence",
											seq);										
endfunction

sequence与sequencer中的数据结构

在这里插入图片描述
如上图所示,
sequence中包含sequencer的句柄;
sequencer中包含sequence的句柄;
所谓的sequence启动,就是将sequence中的sequencer句柄执行sequencer对象,将sequencer中的sequence句柄指向sequence对象。
完成连接后,sequencer就可以调用sequence中的方法,sequence就可以调用sequencer中的方法,从此双方就可以相互通信。

virtual sequencer, p_sequencer, m_sequencer

在这里插入图片描述

为什么要引入virtual sequencer?

如上图所示,env中引入了Vsqr, 包含sqrA和sqrB。
引入Vsqr,本质是可以进一步去耦合,在一个Vsqr中将所有的sqr管理在一起,方便查找/修改/调用等等。
如果没有Vsqr,想要查找sqrA,则需要通过xxx.env.agtA.sqrA的路径找到sqrA,但当有一个Vsqr后,我们就可以直接通过Vsqr.sqrA直接找到sqrA。
因此virtual sequencer是一个管理思想,引入后,无需关心每个sqr的具体路径,具体名字等等,做到了去耦合。
注意,Vsqr中的sqrA和agtA中的sqrA要指向同一个实体,否则就乱套了,因此要在connect_phase中(也就是上图中的第0步),做以下连接

// step 0
Vsqr.sqrA = agtA.sqrA;
Vsqr.sqrB = agtB.sqrB;

为什么要引入m_sequencer?

sequence中藏了一个sequencer基类指针,指向sequencer,该指针就是m_sequencer。

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

m_sequencer的连接过程是在sequence启动过程中完成连接的。(手动启动,或者自动启动时)

// step 1 , sequence启动时,完成sequencer中的sequence指针指向sequence实体
// step 2 , sequence启动时,完成sequence中的sequencer指针指向sequencer实体

为什么要引入p_sequencer?

为什么需要p_sequencer?
就要回到最初的目的,每个sequence中,需要有一个启动该sequence的sequencer指针,我们把它起名叫p_sequencer(子类)。有了该指针后,在sequence中便可以个性化的调用对应sequencer的变量和方法。
但每个sequence中只有sequencer的基类指针m_sequencer, 该m_sequencer指向的是子类的对象。
因此,sequence中若要事项子类对象的方法和属性,需要先将m_sequencer 做个类型转换,转换成子类的句柄p_sequencer。

这个过程每个用户sequence中均可能需要,因此UVM中,实现了一个宏叫uvm_declare_p_sequencer,专门给用户实现上述过程的声明和转换等。

`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  
// step 3/4
完成p_sequencer的声明和赋值

sequence, sequencer, driver的交互流程

参考内容:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值