sequence 基础
sequence机制用于生成激励,提高代码重用性,使得进行新的测试变得更加方便。
sequence的启动主要有以下几种。
/*一 使用start任务*/
my_sequence my_seq;
my_seq = my_sequence::type_id::create("my_seq");
my_seq.start(sequencer);/*指定接收的sequencer*/
/*二 default sequence*/
uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_sequence::type_id::get());/*要指明phase*/
/*三 实例化后再用default sequence*/
my_sequence my_seq;
my_seq = new("my_seq");
uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_seq);
sequence启动后,会自动执行内部的body任务。
sequence的仲裁机制
当sequencer面对多个sequence时,就要运用仲裁机制来决定优先级。
/*
SEQ_ARB_FIFO,先进先出,不考虑优先级
SEQ_ARB_WIGHTED
SEQ_ARB_RANDOM
SEQ_ARB_STRICT_FIFO,先进先出,考虑优先级
SEQ_ARB_STRICT_RANDOM,随机,考虑优先级
SEQ_ARB_USER
*/
env.i_agt.sqt.set_arbitration(SEQ_ARB_STRICT_FIFO);
/*在sequence中设置优先级别,第二个参数大于-1,越大越高*/
`uvm_do_pri(tr,100)
/*在启动sequence时设置优先级,第二个参数是parent sequence*/
my_seq0.start(env.i_at.sqr,null,200);
可以使用lock以及grab操作控制sequencer的所有权。当一个sequence中出现lock()时,它就会占有sequencer,直到unlock()。同样grab也有这样功能。两者的区别在于,grab的优先级别更高,grab请求会被插入仲裁队列的最前面,而lock则是最后面。
可以使用is_relevant函数控制sequence的有效与否,返回1则有效。当sequencer发现所有sequence都无效时,会启动wait_for_sequence函数,等待某个sequence生效。这两个函数一般成对出现。在is_relevant中设置失效条件,在wait_for_relevant中清除失效条件。
sequence 相关宏及其实现
涉及到sequence的主要宏有:
`uvm_do(SEQ_OR_ITEM)
`uvm_do_pri(SEQ_OR_ITEM, PRIORITY)
`uvm_do_with(SEQ_OR_ITEM,CONSTRAINTS)
`uvm_do_pri_with(SEQ_OR_ITEM,PRIORITY,CONSTRAINTS)
`uvm_do_on(SEQ_OR_ITEM, SEQR) /*第二个参数指明sequencer指针*/
...
`uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) /*其他宏由此实现*/
/*uvm_do宏其实是进行了下列动作的封装*/
tr = new("tr");
start_item(tr);
assert(tr.randomize());
finish_item(tr);
`uvm_create(tr); /*实例化transaction*/
`uvm_send(tr); /*发送*/
`uvm_rand_send(tr); /*对实例化的transaction进行随机化*/
/*UVM提供三个接口,pre_do,mid_do,post_do,来增加uvm_do的功能*/
/*三者分别是在start_item末尾,finish_item开头与末尾执行*/
sequence 进阶应用
sequence内部可以嵌套sequence:
class case0_sequence extends uvm_sequence # (my_transaction);
virtual task body();
crc_seq cseq;
long_seq lseq;
repeat (10) begin
cseq = new("cseq");
cseq.start(m_sequencer); /*m_sequencer是case0_sequence使用的sequencer对应指针*/
lseq = new("lseq");
lseq.start(m_sequencer);
/*或者*/
/*`uvm_do(cseq)*/
/*`uvm_do(lseq)*/
end
endtask
endclass
从上可以看出,sequence与transaction之间的界限比较模糊,sequence中也可以有rand修饰的变量,也可以对其随机化,因而uvm_do可以接收sequence作为参数。当uvm_do接收的是transaction时,会自动调用start_item/finish_item,当接收到sequence时,会自动执行其内部的start。
一个sequencer只能接受一种transaction,所以嵌套sequence的前提是,所有的sequence生成的transaction类型相同。可以把sequencer或driver的接收参数改为uvm_sequencer_item以适应多种transaction的接收。不过driver在使用其成员变量时,要先用cast进行转换。
上述代码中的m_sequencer是对应的sequencer指针,但是不能用它来调用成员变量。同样需要cast进行类型转换,或者使用宏uvm_declare_p_sequencer(SEQUENCER)来自动生成真实的sequencer指针(p_sequencer)。
除了嵌套外,sequence也支持派生继承。并且上述的p_sequencer只需在父类定义即可。
virtual sequence 的使用
当面对多个sequencer时,可以用virtual sequence进行统一调度。virtual sequence其实就是用一个包含了多个sequencer的sequencer进行操作。在vritual sequencer内定义指向其他sequencer的指针,然后在test中将已生成的sequencer实例赋值给指针;在virtual sequence中进行调度各个sequencer将会接受的sequence,这一步要用到`uvm_do_on()宏。但是使用了这个宏,就无法使用starting_phase进行提起撤销objection。而使用default_sequence会自动为starting_phase赋值。所以一般在最顶层的virtual sequence进行控制objection。
/*virtual sequencer*/
class my_vsqr extends uvm_sequencer;
my_sequencer p_sqr0;
my_sequencer p_sqr1;
endclass
/*base_test*/
class base_test extends uvm_test;
my_env env0;
my_env env1;
my_vsqr v_sqr;
endclass
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env0 = my_env::type_id::create("env0",this);
env1 = my_env::type_id::create("env1",this);
v_sqr = my_vsqr::type_id::create("v_sqr",this);
endfunction
function void base_test::connect_phase(uvm_phase phase);
v_sqr.p_sqr0 = env0.i_agt.sqr;
v_sqr.p_sqr1 = env1.i_agt.sqr;
endfunction
/*virtual sequence*/
class case0_vseq extends uvm_sequence;
`uvm_object_utils(case0_vseq)
`uvm_declare_p_sequencer(my_vsqr) /*生成指向virtual sequencer的指针*/
virtual task body();
my_transaction tr;
drv0_seq seq0;
drv1_seq seq1;
/*调配sequencer之前的额外操作,控制sequence之间的差别*/
`uvm_do_on_with(tr,p_sequencer.p_sqr0,{tr.pload.size == 1500;})
/*调配sequencer*/
fork
`uvm_do_on(seq0,p_sequencer.p_sqr0);/*第二个参数指向对应sequencer*/
`uvm_do_on(seq1,p_sequencer.p_sqr1);
join
endtask
endclass
在sequence中使用fork join_none时要注意,任务结束时,会主动杀死任务内的fork线程,显然会影响到激励的生成。所以在结束前可以用wait fork,等待所有fork线程结束;或者使用fork join。
在sequence中使用config_db
由于config_db::get/set的前两个参数是component的路径,所以为了适配sequence机制,需要用到通配符,或利用get_full_name()。可以用wait_modified检查参数是否被get。
/*在component中使用,通配符一般指sequence的实例名*/
uvm_config_db#(int)::set(this,"env.i_agt.sqr.*","count",9);
/*在sequence中使用*/
uvm_config_db#(int)::get(null,get_full_name(),"count",count);
/*检查是否获取到参数*/
uvm_config_db#(int)::wait_modified(null,get_full_name(),"count");
response 的使用
sequence允许接收response,可以使用get_response任务,而在driver中则需要put_response任务。并且可以多次调用任务。
get_response(rsp);
rsp = new("rsp");
rsp.set_id_info(req);/*需要用该函数告知rsp的去向*/
seq_item_port.put_response(rsp);
seq_item_port.item_done();
由于发送接收response是阻塞的,可以利用reponse handler来分开与transaction的发送工作。
/*在sequence中*/
virtual task pre_body();
use_response_handler(1);/*启动response handler*/
endtask
virtual function void response_handler(uvm_sequence_item response);
if (!$cast(rsp,response))/*首先应该转换成my_transaction*/
`uvm_error("seq","can't cast")
else begin
`uvm_info("seq","get one response",UVM_MEDIUM)
rsp.print();
end
endfunction
virtual task body();
...
put/get_response以及response handler是新建了一个transaction并返回给sequence。另外,uvm_do执行完毕后,第一个参数指向的是刚刚送给driver的transaction。
sequence library
sequence library 是sequence的集合,本质上也是一个sequence。定义时应该注意:
class simple_sequence_library extends uvm_sequence_library#(my_transaction);/*注明transaction类型*/
function new(string name="simple_sequence_library");
super.new(name);
init_sequence_library(); /*初始化*/
endfunction
`uvm_object_utils(simple_seq_library)
`uvm_sequence_library_utils(simple_seq_library);/*注册*/
endclass
`uvm_add_to_seq_lib(seq0,simple_seq_library)/*将seq0注册到sequence library中,可以注册进多个library,也可以多个seq注册到单个library*/
实例化sequence library时,可以用default sequence,就如同实例化普通sequence一样。sequence library执行时,会随机从库中选取sequence,有多种控制选择:
seq_lib.selection_mode = UVM_SEQ_LIB_RANDC;/*随机算法*/
seq_lib.min_random_count = 10;/*执行次数上下限*/
seq_lib.max_random_count = 15;
可以使用类uvm_sequence_library_cfg进行统一配置。