sequence首先是从driver中剥夺了产生激励的功能,目的是增强可扩展性,添加新的需求代码时就不需要重新编写main_phase了。
解决办法就是:引入了sequence 机制,在解决的过程中还使用了factory机制、config机制。使用sequence机制之后,在不同的测试用例中,将不同的sequence设置 成sequencer的main_phase的default_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/`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()是一种阻塞 任务,只有获得了权限,它才会返回;
grab操作
sequence的有效性
sequence的嵌套使用
在sequence中使用rand类型变量:
跟在transaction的定义中,通常使用rand来对变量进行修饰是一个道理
transaction类型的匹配
p_sequencer的使用
sequence的派生与继承
Virtual sequence
virtual sequence的作用:称在不同的sequencer的sequence群落,实现sequence的同步。
virtual sequencer的作用:桥接其他的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
嵌入序列,控制序列
通过宏`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