UVM—virtual sequencer and virtual sequence详解
目录
3、 virtual sequencer 和virtual sequence的作用
1、前言
在UVM里Sequence层次化这一块是重点,这里主要介绍virtual sequence。 对于顶层的测试环境, 测试序列所要协调的不再只是面向一个sequencer的sequence群,而是要面向多个sequencer的sequence群, 面向不同sequencer的sequence群如何挂载到不同的sequencer上呢? 这就需要用到virtual sequence和virtual sequencer来解决。对于单一的sequencer下的sequence群,其挂接较为简单,通过uvm_sequence::start()来挂载root sequence,而在内部的child sequence可以通过宏`uvm_do()来实现。
2、virtual sequencer使用环境
考虑到环境的可扩展性,一般会在一开始生成环境的时候就把virtual sequencer给加上。即使暂时用不到,也比以后再去加的要好。而且目前的验证环境大部分是脚本生成的,加virtual sequencer其实也不存在什么额外的工作量 。
使用条件:
- 环境中只有一个driver agent,此时不需要virtual sequencer;
- 环境中有多个driver_agent,但是这些driver之间不需要同步,此时不需要virtual sequencer;
- 环境中有多个driver_agent,且这些driver之间需要同步,此时需要virtual sequencer。
3、 virtual sequencer 和virtual sequence的作用
- virtual sequence:承载不同目标sequencer的sequence群落,实现sequence同步;virtual sequence一般只会挂载到virtual sequencer上,且没有自己的sequence_item,只用于控制其他的sequence执行顺序,起统一调度作用。
- virtual sequencer:桥接其它sequencer,即连接所有底层sequencer的句柄(指针),是一个中心化的路由器。virtual sequencer本身并不传送item数据对象,因此不需要与driver进行TLM连接。所以用户需在顶层的connect阶段做好virtual sequencer中各个sequencer句柄与sequencer实体对象的一一连接,避免句柄悬空。
virtual sequencer和(real) sequencer都继承自uvm_sequencer;
virtual sequence 和(real) sequence 都继承自uvm_sequence;
virtual sequence/sequencer的virtual主要是指这种sequence/sequencer不像直接作用在具体driver上的sequence/sequencer,它不处理具体的transaction,主要是来做不同类型sequence间的控制和调度的。
4、m_sequencer与p_sequencer
4.1 m_sequencer
当我们使用宏`uvm_do或者xxx_seq.start(sqr)方法启动sequence后(当sequence和sequencer关联后),sequence内部自有的m_sequencer句柄就自动地指向了对应的sequencer上了。这个操作是start()方法内部实现的,对用户不可见,因此一般我们都不会直接去操作m_sequencer变量。事实上,uvm中直接以'm_'开头的变量都不应该直接被用户使用。
但通过m_sequencer不能直接使用seqr里的变量,否则会出现编译错误。只能使用
cast强制向子类转换
后,才能通过m_sequencer.xxx来访问该seqr内的xxx变量。
4.2 p_sequencer
与m_sequencer变量作用相反,p_sequencer变量就是为了方便用户而提供的。p_sequencer变量需要用户使用宏`uvm_declare_p_sequencer去定义和设置。
`uvm_declare_p_sequencer宏一般在sequence的内部使用,主要完成以下两件事情:
- 创建了p_sequencer变量;
- 将uvm内部的m_sequencer变量赋给了p_sequencer变量;
5、实例
该例子来自于红宝书394页
typedef class mcdf_virtual_sequencer;
//底层sequence定义,分别属于不同的sequencer
//clk_rst_seq
//reg_cfg_seq
//data_trans_seq
//fmt_slv_cfg_seq
class mcdf_normal_seq extends uvm_sequence; //mcdf_normal_seq相当于virtual sequence
`uvm_object_utils(mcdf_normal_seq)//注册
`uvm_declare_p_sequencer(mcdf_virtual_sequencer);
... //省略
task body();
clk_rst_seq clk_seq;
reg_cfg_seq cfg_seq;
data_trans_seq data_seq;
fmt_slv_cfg_seq fmt_seq;
//配置formatter slave agent
`uvm_do_on(fmt_seq,p_sequencer.fmt_sqr)
//打开时钟完成复位
`uvm_do_on(clk_seq,p_sequencer.cr_sqr)
//配置MCDF寄存器
`uvm_do_on(cfg_seq,p_sequencer.cr_sqr)
//传送channel数据包
`uvm_do_on(clk_seq,p_sequencer.cr_sqr)
fork
`uvm_do_on(data_seq,p_sequencer.chnl_sqr0)
`uvm_do_on(data_seq,p_sequencer.chnl_sqr1)
`uvm_do_on(data_seq,p_sequencer.chnl_sqr2)
join
endtask
endclass
通过宏`uvm_declare_p_sequencer( )来绑定virtual sequencer同时声明p_sequencer,并操作virtual sequencer中的内容;通过宏`uvm_do_on( )将虚序列器句柄与虚序列句柄相连接,这就可以把sequence挂载到对应的sequencer上面。从上述代码来看,mcdf_normal_seq(即virtual sequence)可以承载各个子模块环境的elment sequence。
//子一级的sequencer和agent定义
// cr_master_sequencer | cr_master_agent
// reg_master_sequencer | reg_master_agent
// chnl_master_sequencer | chnl_master_agent
// fmt_slave_sequencer | fmt_slave_agent
//
class mcdf_virtual_sequencer extends uvm_sequencer
cr_master_sequencer cr_sqr;
reg_master_sequencer reg_sqr;
chnl_master_sequencer chn_sqr0;
chnl_master_sequencer chn_sqr1;
chnl_master_sequencer chn_sqr2;
fmt_slave_sequencer fmt_sqr;
`uvm_compoent_utils(mcdf_virtual_sequencer) //注册
function new(string name, uvm_compoent_parent);
super.new(name,parent);
endfunction
endclass
mcdf_virtual_sequncer(即virtual sequencer)拿到各sequencer的句柄。
class mcdf_env extends uvm_env;
cr_master_agent cr_agt;
reg_master_agent re_agt;
chnl_master_agent chnl_agt0;
chnl_master_agent chnl_agt1;
chnl_master_agent chnl_agt2;
fmt_slave_agent fmt_agt;
mcdf_virtual_agent virt_sqr;
`uvm_component_utils(mcdf_env)
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_pahse phase);
cr_agt = cr_master_agent::type_id::create("cr_agt",this);
re_agt = reg_master_agent::type_id::create("re_agt",this);
chnl_agt0 = chnl_master_agent::type_id::create("chnl_agt0",this);
chnl_agt1 = chnl_master_agent::type_id::create("chnl_agt1",this);
chnl_agt2 = chnl_master_agent::type_id::create("chnl_agt2",this);
fmt_agt = fmt_slave_agent::type_id::create("fmt_agt",this);
virt_sqr = mcdf_virtual_agent::type_id::create("virt_sqr",this);
endfunction
function void connect_phase(uvm_pahse phase);
virt_sqr.cr_sqr = cr_agt.sqr;
virt_sqr.reg_sqr = re_agt.sqr;
virt_sqr.chnl_sqr0 = chnl_agt0.sqr;
virt_sqr.chnl_sqr1 = chnl_agt0.sqr;
virt_sqr.chnl_sqr2 = chnl_agt0.sqr;
virt_sqr.fmt_sqr = fmt_agt.sqr;
endfunction
endclass
在build_phase中利用工厂创建并例化对象,方便接下来对接,即UVM用户需在顶层的connect阶段做好virtual sequencer中各sequencer句柄与底层sequencer实体对象对接,避免句柄悬空。
class test1 extends uvm_test;
mcdf_env e;
...
task run_phase(uvm_phase phase)
mcdf_normal_seq seq;
phase.raise_objection(phase);
seq = new();
seq.start(e.virt_sqr);
phase.drop_objection(phase);
endtask
endclass
在test层中奖virtual sequence 挂载到virtual sequencer上,但这种挂载的根本目的是给virtual sequence提供一个中心化的sequencer路由,而借助virtual sequence(此处mcdf_normal_seq)中使用宏`uvm_declare_p_sequencer( ),virtual sequence 可以使用声明后的成员变量p_sequncer(类型为mcdf_virtual_sequencer),进一步回溯到virtual sequncer内部的各个sequencer句柄。
这里使用宏`uvm_declare_p_sequencer( )较为方便,因为在宏的后台,可以新创建一个p_sequncer变量,而将m_sequncer的默认变量(uvm_sequencer_base类型)通过动态的转换变为类型为mcdf_virtual_sequencer的p_sequncer。
virtual sequence的协调作用和virtual sequencer的路由作用如下图
参考链接:
理解UVM中的virtual sequencer和virtual sequence_kevindas的博客-CSDN博客