了解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来关闭线程。