【UVM基础】6、sequence和sequencer相关宏

sequence首先是从driver中剥夺了产生激励的功能,目的是增强可扩展性,添加新的需求代码时就不需要重新编写main_phase了。

 解决办法就是:引入了sequence 机制,在解决的过程中还使用了factory机制、config机制。使用sequence机制之后,在不同的测试用例中,将不同的sequence设置 成sequencermain_phasedefault_sequence。当sequencer执行到main_phase时,发现有default_sequence,那么它就启动sequence 。

sequence的启动与执行

直接启动:start();

default_sequence启动:两种方法调用start任务

sequence宏

start()

使用场景:将sequence挂载到sequencer上的应用。

uvm_sequence::start(uvm_sequencer_base sequencer,
                    uvm_sequence_base parent_sequence=null
                    ini this_priority=-1,bit call_pre_post=1)

这个方法中,应该首先指明sequencer的句柄,如果这个sequence是顶部的sequence(没有上层嵌套他),那么可以省略对第二个parent_sequence的指定。

第三个参数的系统默认值是-1,会使得这个sequence会继承parent_sequence的优先值级,如果是顶部的sequence,会自动设置为100,也可以自己设置优先级数值。

第四个参数值一般使用默认值,这样uvm_sequence::pre_body()和uvm_sequence::post_body会在uvm_sequence::body()前后执行。

start_item()和finish_item()

使用场景:将item挂载到sequencer上的应用

uvm_sequence::start_item(uvm_sequencer_item item, ini this_priority=-1,
                    uvm_sequence_base sequence=null);
uvm_sequence::finish_item(uvm_sequencer_item item, ini this_priority=-1);                 

uvm_sequence::start_item中第三个参数需要用户注意是否将item和parent sequence挂载到不同的sequencer上面去。

使用item挂载到sequencer上的方法,需要创建item,还需要完成item的随机化,创建的方法;uvm_object::create()或者uvm_sequence::creat_item()

一个完整的item传送到driver有以下步骤:

1:创建item

2:通过start_item()方法获取的sequencer的授权许可,然后执行parent sequencer的方法pre_do();

3:对item进行随机化处理

4:通过finish_item()方法对随机化后的item进行mid_do()(也是parent sequence的方法 ),调用uvm_sequencer::send_request()和uvm_sequencer::wait_for_item_done()来完成item的发送以及sequence和driver的握手,最后在执行post_do()。

需要注意的是sequence和item的自身优先级,可以决定什么时候可以获得sequencer的授权。parent sequence的虚方法pre_do()、mid_do()、post_do()都是发生在发送item的过程中间。

以上两种方法的区别:

首先是挂载对象的不同,然后start()过程中,默认情况下或执行sequence的pre_body()和post_body(call_pre_post=0决定),但是并不是一定会执行。

start()方法执行顺序图:

 start_item()/finish_item执行顺序图:

 发送序列相关宏

uvm_do_on: 用于显式地指定使用哪个 sequencer 发送此 transaction 。它有两个参数,第一个是 transaction 的指针,第二个是 sequencer的指针
uvm_do_on_pri :它有三个参数,第一个参数是 transaction 的指针,第二个是 sequencer 的指针,第三个是优先级
uvm_do_on_with :它有三个参数,第一个参数是 transaction 的指针,第二个是 sequencer 的指针,第三个是约束
uvm_do_on_pri_with :它有四个参数,是所有 uvm_do 宏中参数最多的一个。第一个参数是 transaction的指针,第二个是sequencer 的指针,第三个是优先级,第四个是约束
除了使用 uvm_do 宏产生 transaction ,还可以使用 uvm_create 宏与 uvm_send 宏来产生

 可以通过`uvm_do/`uvm_do_with来发送,无论是sequence还是item直接不进行区分。

 sequencer的仲裁特性

可以通过uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)函数来设置仲裁模式,对于UVM_SEQ_ARB_TYPE的选择有以下六种:

优先级仲裁 

sequencer是根据优先级来选择从哪个sequence中选择transaction,在上面的仲裁模式中,与priority有关的模式有 UVM_SEQ_ARB_WEIGHTED、UVM_SEQ_ARB_STRICT_FIFO、 UVM_SEQ_ARB_STRICT_RANDOM这三种模式

区别在于UVM_SEQ_ARB_WEIGHTED的授可能会到各个优先级Sequence的请求上面,而 UVM_SEQ_ARB_STRICT_RANDOM则只会将授朽随机安排到最高优先级的请求上面,UVM_SEQ_ARB_STRICT_FIFO则不会随机授权,而 严格按照优先级以及抵达顺序来依次授权。

transaction有优先级设置,sequence也有优先级,在sequence启动的时候指定其优先级即可。

sequencer的锁定机制

 uvm_sequencer提供了两种锁定机制,分别通过lock()和grab()方法实现, 这两种方法的区别在于:

lock操作

lock()与unlock()这一对方法可以为sequence提供排外的访问权限,但前提件是,该sequence首先需要按照sequencer的仲裁机制获得授权。而—旦  sequence获得授,则无需担心权限被收回,只有该Sequence主动解锁 (unlock) ,它的Sequencer才可以释放这一锁定的权限。lock()是一种阻塞 任务,只有获得了权限,它才会返回;

如果两个sequence都试图使用lock任务来获取sequencer的所有权则会如何呢?答案是先获得所有权的sequence在执行完毕后才 会将所有权交还给另外一个sequence。

grab操作 

lock 操作一样, grab 操作也用于暂时拥有 sequencer 的所有权,只是 grab 操作比 lock 操作优先级更高。 lock 请求是被插入 sequencer仲裁队列的最后面,等到它时,它前面的仲裁请求都已经结束了。 grab 请求则被放入 sequencer 仲裁队列的最前面,它几乎是一发出就拥有了sequencer 的所有权。可以阻止它只有已经预先获得授权的其它lock或者grab的sequence。

sequence的有效性

sequencer 在仲裁时,会查看 sequence is_relevant 函数的返回结果。如果为 1 ,说明此 sequence 有效,否则无效。因此可以通过重载is_relevant 函数来使 sequence 失效。
sequence中还有一个任务 wait_for_relevant 也与 sequence的有效性相关,当 sequencer 发现在其上启动的所有 sequence 都无效时,此时会调用 wait_for_relevant 并等待 sequence 变有效。

sequence的嵌套使用

在一个 sequence body 中,除了可以使用 uvm_do 宏产生 transaction 外,其实还可以启动其他的 sequence ,即一个 sequence 内启动另 外一个sequence ,这就是嵌套的 sequence:

在sequence中使用rand类型变量:

transaction的定义中,通常使用rand来对变量进行修饰是一个道理

transaction类型的匹配

一个 sequencer 只能产生一种类型的 transaction ,一个 sequence 如果要想在此 sequencer 上启动,那么其所产生的 transaction 的类型 必须是这种transaction 或者派生自这种 transaction
嵌套 sequence 的前提是:在套里面的所有sequence 产生的 transaction 都可以被同一个 sequencer 所接受
两个不同的transaction交给同一个sequencer:将sequencer和driver能够接受的数据类型设置为uvm_sequence_item
会带来的问题就是需要进行$cast()转换,因为driver中接收的数据类型 是uvm_sequence_item,如果它要使用my_transaction或者your_transaction中的成员变量,必须使用cast转换

 p_sequencer的使用

在实际的验证平台中,用到 sequencer 中成员变量的情况非常多。 UVM 考虑到这种情况,内建了一个宏: uvm_declare_p_sequencer( SEQUENCER )。这个宏的本质是声明了一个 SEQUENCER 类型的成员变量,如在定义 sequence 时,使用此宏声明sequencer的类型。之后会自动将 m_sequencer 通过 cast 转换成 p_sequencer 。这个过程在 pre_body ()之前就会完成

sequence的派生与继承

在同一个项目中各 sequence 都是类似的,所以可以将很多公用的函数或者任务写在 base sequence 中,其他 sequence 都从此sequence派生。

Virtual sequence

virtual sequence的作用:称在不同的sequencer的sequence群落,实现sequence的同步。

virtual sequencer的作用:桥接其他的sequencer连接所有底层的sequencer的句柄,等同于一个中心化路由器。

就之前的sequence和sequencer而言,和virtual sequence的差别在于:
virtual sequence可以承载不同目标sequencer的sequence群落,而组织协调这些sequence的方式则类似于高层次的hierarchical sequence Virtual Ssequence一般只会挂载到lvirtual sequencer上面。
virtual sequencer与普通的sequencer相比有着很大的不同,它们起到了桥接其它Sequencer的作用,即virtual sequencer是一个链接所有底层  Sequencer句柄的地方,它是一个中心化的路由器。
virtual sequencer本身并不会传送itemn数据对象,因此virtual sequencer不需要与任何的driver进行TLM连接。以UVM用户需要在顶层 ,的COnnect阶段,做好virtual sequenCer中各个Sequencer句柄与底层 Sequencer实体对象的一—对接,避免句柄悬空。
sequence 之间的简单同步 :使用全局变量@send_over,但是不提倡。
sequence之间的复杂同步: 实现 sequence 之间同步的最好的方式就是使用virtual sequence。不发送 transaction ,它只是控制其他的 sequence ,起统一调度的作用

示例

嵌入序列器
在virtual sequencer中定义指向其他sequencer的句柄。
class virtual_sequencer extends uvm_sequencer;
   bfm_sequencer   bfm0_sqr;            //1.插入虚序列器句柄,指向真实sequencer
   bfm_sequencer   bfm1_sqr;  
   function new(string name, uvm_component parent);
      super.new(name,parent);
   endfunction
endclass

嵌入序列,控制序列

在virtual sequence中定义sequence句柄,在body( )任务中控制这些序列;
通过宏`uvm_declare_p_sequencer( )来绑定virtual sequencer同时声明p_sequencer,并操作virtual sequencer中的内容;
通过宏`uvm_do_on( )将虚序列器句柄与虚序列句柄相连接;
注意区别:virtual_sequence中嵌套的sequence和普通sequence中嵌套sequence。普通sequence中嵌套的sequence都在同一个sequencer上启动(通过sequencer仲裁决定);而在virtual sequence中嵌套的sequence可以在不同sequencer实体上同时启动。

class virtual_sequence extends uvm_sequence;
   `uvm_object_utils(virtual_sequence)
   `uvm_declare_p_sequencer(virtual_sequencer)         //1.声明p_sequencer,操作virtual sequencer中的内容
   bfm_sequence   bfm0_seq;                            //2.插入序列句柄
   bfm_sequence   bfm1_seq;                            //序列seq可由不同的seqr启动
   virtual task body();
      `uvm_do_on(bfm0_seq,p_sequencer.bfm0_sqr);       //3. 将虚序列器句柄与虚序列句柄相连接
      `uvm_do_on(bfm1_seq,p_sequencer.bfm1_sqr);
   endfunction
endclass

 在环境/测试用例中连接sequencer到virtual sequencer

在build_phase阶段创建并例化virtual sequencer;将不同agt场景下的sqr发送的default sequence设置为null,关闭相应的sequence;
在connect_phase阶段,将被控制的sequencers与virtual sequencer关联一起;
在env或test中配置virtual_sequence为default sequence,在需要的phase阶段执行virtual sequence;

class top_env extends uvm_env;
   virtual_sequencer       v_sqr;     //and subenvs
   ...
   virtual funvtion void build_phase(uvm_phase phase);
      super.build_phase(phase);
      ...
      v_sqr = virtual_sequencer::type_id::create("v_sqr",this);
      uvm_config_db#(uvm_object_wrapper)::set(this,"subenv0.bfm0_agt.sqr.main_phase","default_sequence",null);
      uvm_config_db#(uvm_object_wrapper)::set(this,"subenv0.bfm1_agt.sqr.main_phase","default_sequence",null);
      uvm_config_db#(uvm_object_wrapper)::set(this,"v_sqr.main_phase","default_sequence",virtual_sequence::get_type());
   endfunction
   
   virtual function void connect_phase(uvm_phase phase);
      v.sqr.bfm0_sqr = bfm_agt0.bfm0_sqr;        //将被控制的真实sequencers赋给virtual sequencer的指针,建立连接
      v.sqr.bfm1_sqr = bfm_agt1.bfm1_sqr;
   endfunction
endclass

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值