Virtual Sequences
Virtual Sequences是使用多个sequencer控制激励生成的sequence。由于sequence、sequencer和driver(proxy和 BFM)专注于interface,几乎所有测试平台都需要一个sequence来协调不同接口之间的激励以及它们之间的交互。Virtual Sequences通常是sequence层次结构的顶层。Virtual Sequences也可以称为master sequence或coordinator sequence。
vritual sequence和普通sequence的区别在于,目的不是要发送sequence item。相反,它会在不同的目标代理上生成和执行sequence。为了做到这一点,它包含了目标sequencer的句柄。
// Creating a useful virtual sequence type:
typedef uvm_sequence #(uvm_sequence_item) uvm_virtual_sequence;
// Virtual sequence example:
class my_vseq extends uvm_virtual_sequence;
...
// Handles for the target sequencers:
a_sequencer_t a_sequencer;
b_sequencer_t b_sequencer;
task body();
...
// Start interface specific sequences on the appropriate target sequencers:
aseq.start( a_sequencer , this );
bseq.start( b_sequencer , this );
endtask
endclass
为了使vritual sequence正常工作,必须分配sequencer句柄。通常,test类的run_phase中创建vritual sequence,并且对vritual sequence中的sequencer句柄进行分配。分配好了以后就可以直接在空句柄上启动。
my_seq vseq = my_seq::type_id::create("vseq");
vseq.a_sequencer = env.subenv1.bus_agent.sequencer;
vseq.b_sequencer = env.subenv2.subsubenv1.bus_agent3.sequencer;
vseq.start( null );
vritual sequence有几个变体。vritual sequence也可以在sequencer上启动。vritual sequence不必由test执行,它可以由封装多个代理的环境执行。对于一个具有许多代理的大型测试环境,可能会有几个vritual sequence同时运行。
除了sequencer句柄,vritual sequence还可以包含其他所需要的测试平台资源,包括寄存器模型。
推荐的vritual sequence初始化方法
为了有效地使用UVM,许多公司将测试环境的实现与测试用例的实现分开。这要么是概念分离,要么是组织分离。测试环境实现应该提供一个测试基类和一个vritual sequence的基类,从中可以导出测试用例。测试基类负责构建和配置验证环境组件层次结构,并指定将运行哪个vritual sequence。测试基类还应该包含一种用于为从vritual sequence基类派生的vritual sequence分配sequencer句柄的方法。通过几层垂直重用,到目标sequencer的分层路径可以变得相当长。由于测试环境知道到目标sequence器的层次路径,因此这些信息可以封装用于所有未来的测试用例编写者。
例如图中的测试环境,为了解释vritual sequence的重用性,在top下的两个子环境有四个代理。vritual sequence积累包含了每个sequencer的句柄:
class top_vseq_base extends uvm_sequence #(uvm_sequence_item);
`uvm_object_utils(top_vseq_base)
uvm_sequencer #(a_seq_item) A1;
uvm_sequencer #(a_seq_item) A2;
uvm_sequencer #(b_seq_item) B;
uvm_sequencer #(c_seq_item) C;
function new(string name = "top_vseq_base");
super.new(name);
endfunction
endclass: top_vseq_base
测试基类中有一个为vritual sequence分配sequencer句柄的方法。
class test_top_base extends uvm_test;
`uvm_component_utils(test_top_base)
env_top m_env;
function new(string name = "test_top_base", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
m_env = env_top::type_id::create("m_env", this);
endfunction: build_phase
// Method to initialize the virtual sequence handles
function void init_vseq(top_vseq_base vseq);
vseq.A1 = m_env.m_env_1.m_agent_a.m_sequencer;
vseq.C = m_env.m_env_1.m_agent_c.m_sequencer;
vseq.A2 = m_env.m_env_2.m_agent_a.m_sequencer;
vseq.B = m_env.m_env_2.m_agent_b.m_sequencer;
endfunction: init_vseq
endclass: test_top_base
在从测试基类派生的测试用例中,在vritual sequence启动之前调用vritual sequence初始化方法。
class init_vseq_from_test extends test_top_base;
`uvm_component_utils(init_vseq_from_test)
function new(string name = "init_vseq_from_test", uvm_component parent = null);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
vseq_A1_B_C vseq = vseq_A1_B_C::type_id::create("vseq");
phase.raise_objection(this);
init_vseq(vseq); // Using method from test base class to assign sequence handles
vseq.start(null); // null because no target sequencer
phase.drop_objection(this);
endtask: run_phase
endclass: init_vseq_from_test
从virtual sequence基类中派生出的virtual sequence就不需要初始化了
class vseq_A1_B_C extends top_vseq_base;
`uvm_object_utils(vseq_A1_B_C)
function new(string name = "vseq_A1_B_C");
super.new(name);
endfunction
task body();
a_seq a = a_seq::type_id::create("a");
b_seq b = b_seq::type_id::create("b");
c_seq c = c_seq::type_id::create("c");
a.start(A1);
fork
b.start(B);
c.start(C);
join
endtask: body
endclass: vseq_A1_B_C
用相同的办法就能把其他测试环境的资源传递给vritual sequence,例如寄存器模型和config对象。
virtual sequencer-一种运行virtual sequence的替代方法
除了直接在空句柄上启动virtual sequence,还可以在virtual sequencer内置所需要的sequencer句柄,然后在上面启动。这种方式存在一个问题,virtual sequencer是一个具体实现,他和env紧密的耦合在一起,增加了垂直复用的复杂性。