> 文章列表 > 二、UVM Sequencer和Sequence

二、UVM Sequencer和Sequence

二、UVM Sequencer和Sequence

了解sequencer与driver之间传递sequence item的握手过程,也掌握了sequence与item之间的关系。接下来对sequence挂载到sequencer的常用方法总结,可以通过这些方法和宏的介绍,了解到它们不同的使用场景面对多个sequence如果需要同时挂到sequence时,那就要面临这仲裁的需要,uvm_sequencer自带有仲裁特性,结合sequence的优先级设定,最终可以实现想要的效果。

Sequence和Item的发送实例

class bus_trans extends uvm_sequence_item; rand int data; `uvm_object_utils_begin(bus_trans)uvm_field_int(data,UVM_ALL_ON)uvm_object_utils_end
endclass
class child_seq extends uvm_sequence; `uvm_object_utils(child_seq)...task body(); uvm_sequence_item tmp; bus_trans req; tmp=create_item(bus_trans::get_type(),m_sequencer,"req"); void'($cast(req, tmp)); start_item(req); req.randomize with {data==10;}; finish_item(req);endtask 
endclass
class top_seq extends uvm_sequence; `uvm_object_utils(top_seq)...task body(); uvm_sequence_item tmp; child_seq cseq; bus_trans req;//create child secuence and items cseg=child_seq::type_id::create("cseq"); tmp=create_item(bus_trans::get_type(),m_sequencer,"req");//send child sequence via start()cseq.start(m_secquencer,this);//send sequence item void'($cast(req, tmp)); start_item(req); req.randomize with {data==20;}; finish_item(req); endtask 
endclass
class sequencer extends uvm_sequencer;`uvm_component_utils(sequencer)...
endclass 
class driver extends uvm_driver; `uvm_component_utils(driver)...task run_phase(uvm_phase phase); REQ tmp; bus_trans req; forever begin seq_item_port.get_next_item(tmp); void'($cast(req, tmp));`uvm_info("DRV",$sformatf("got a item \\n %s", req.sprint()), UVM_LOW) seq_item_port.item_done(); end endtask 
endclass
class env extends uvm_env; sequencer sqr; driver drv; `uvm_component_utils(env)...function void build_phase(uvm_phase phase); sqr=sequencer::type_id::create("sqr", this); drv=driver::type_id::create("drv", this); endfunction function void connect_phase(uvm_phase phase); drv.seq_item_port.connect(sqr.seq_item_export);endfunction
endclass
class test1 extends uvm_test; env e; `uvm_component_utils(test1)...function void build_phase(uvm_phase phase);e=env::type_id::create("e", this); endfunction task run_phase(uvm_phase phase); top_seq seq; phase.raise_objection(phase); seq=new(); seq.start(e.sqr); phase.drop_objection(phase); endtask 
endclass

输出结果:

UVM_INFO @ 0:uvm_test_top.e.drv [DRV] got a item
...
UVM_INFO @ 0:uvm_test_top.e.drv [DRV] got a item
...

发送sequence/item方法建议

在这段代码中,主要使用了两种方法。第一个方法是针对将sequence挂载到sequencer上的应用。

uvm_sequence::start 

uvm_sequence::start(uvm_sequencer_base sequencer, uvm_sequence_base parent_sequence=null int this_priority = -1,bit call_ pre_post=11)
  • 在使用该方法的过程中,首先应该指明sequencer的句柄。如果该sequence是顶部的sequence,即没有更上层的sequence嵌套它,则它可以省略对第二个参数parent_sequence的指定
  • 第三个参数的默认值-1会使得该sequence如果有parent_sequence会继承其优先级值,如果它是顶部(root)sequence,则其优先级会被自动设定为100,用户也可以自己制定优先级数值。
  • 第四个参数建议使用默认值,这样的话uvm_sequence::pre_body()和uvm_sequence::post_body()两个方法会在uvm_sequence::body()的前后执行

在上面的代码中, child_seq被嵌套到top_seq中继而在挂载时需要指定parent_sequence;而在test一层调用top_seq时,由于它是root sequence,则不需要再指定parent sequence,这一点需要注意。另外,在调用挂载sequence时,需要对这些sequence进行例化。

uvm_sequence::start_item/finish_item

uvm_sequence::start_item(uvm_sequence_item item,int set_priority=-1,uvm_sequencer_base sequencer=null);
uvm_sequence::finish_item(uvm_sequence_item item,int set_priority=-1)
  • 对于start_item()的使用,第三个参数需要注意是否要将item挂载到“非当前parent sequence挂载的sequencer”上面即如果想将item和其parent sequence挂载到不同的sequencer上面,你就需要指定这个参数
  • 在使用这一对方法时,除了需要记得创建item,例如通过uvm_object::create()或者uvm_sequence::create_item(),还需要在它们之间完成item的随机化处理。 

 发送sequence/item方法解析

从这一点建议来看,对于一个item的完整传送,sequence要在sequencer一侧获得通过权限,才可以顺利将item发送至driver。可以通过拆解这些步骤得到更多的细节:

  • 创建item。
  • 通过start_item()方法等待获得sequencer的授权许可,其后执行parent sequence的方法pre_do()。
  • 对item进行随机化处理。
  • 通过finish_item()方法在对item进行了随机化处理之后,执行parent sequence的mid_do(),以及调用uvm_sequencer::send_request()和uvm_sequencer::wait_for_item_done()来将item发送至sequencer再完成与driver之间的握手。最后,执行了parent_sequence的post_do()。

 如果对比start()方法和start_item()/finish_item(),首先要分清它们面向的挂载对象是不同的。

item发送时相关方法的执行顺序如下

 很多情况start_item会立马返回;

发送序列的相关宏

正是通过几个sequence/item宏来打天下的方式,可以通过uvm_do/uvm_do_with来发送无论是sequence还是item。这种不区分对象是sequence还是item的方式,带来了不少便捷。

不同的宏,可能会包含创建对象的过程也可能不会创建对象。例如uvm_do/uvm_do_with会创建对象,而uvm_send则不会创建对象,也不会将对象做随机处理,因此要了解它们各自包含的执行内容和顺序。

此外还有其它的宏,例如,将优先级作为参数传递的uvm_do_prio uvm_do_on_prio等,还有专门针对sequence的uvm_create_seq/uvm_do_seq/uvm_do_seq_with等宏。

序列宏的示例

class child_seq extends uvm_sequence; ...task body(); bus_trans req; `uvm_create(req)`uvm_rand_send_with(req,{data==10;})
endtask 
endclass class top_seq extends uvm_sequence; ...task body(); child_seq cseq; bus_trans req;// send child sequence via start()`uvm_do(cseq)//send secquence item `uvm_do_with(req,{data==20;})endtask
endclass

序列宏建议

在发送sequence/item的几点建议:

  • 无论sequence处于什么层次,都应当让sequence在test结束前执行完毕。但这不是充分条件,一般而言,还应当保留出一部分时间供DUT将所啊发送的激励处理完毕,进入空闲状态才可以结束测试。
  • 尽量避免使用fork-join_any或者fork-join_none来控制sequence的发送顺序。因为这背后隐藏的风险是,如果想终止在后台运行的sequence线程而简单使用disable方式,那么就可能在不恰当的时间点上锁住sequencer。
  • 一旦sequencer被锁住而又无法释放,接下来也就无法发送其它sequence。所以如果想实现类似fork-join_any或者fork-join_none的发送顺序,还应当在使用disable前,对各个sequence线程的后台运行保持关注,尽量在发送完item完成握手之后再终止sequence,这样才能避免sequencer被死锁的问题。
  • 如果要使用fork-join方式,那么应当确保有方法可以让sequence线程在满足一些条件后停止发送item。否则只要有一个sequence线程无法停止,则整个fork-join无法退出。面对这种情况,仍然需要考虑监测合适的事件或者时间点,才能够使用disable来关闭线程。