virtual sequence 和virtual sequencer
1. 为什么要使用virtual sequencer
在实际应用中,dut往往是很复杂的系统,不单单只有一种接口。而我们testbench中的driver只能驱动一种接口,对应一种transaction的sequence。如果需要对多个接口同时进行激励,就需要的virtual sequence/sequencer。
Virtual sequencer 的特点:含有sub sequencer的句柄,用以控制这些sub sequencer。它并不和任何driver相连 Virtual sequencer,本身并不处理具体的trans/seq。
Virtual sequence 的作用:和virtual sequencer相关联的就是virtual sequence,它的作用是协调不同的subsequencer中sequence的执行顺序。
Virtual 的含义: 这里的virtual 区别用 system Verilog中用在function/task/class声明前,用于修饰的virtual。virtual sequence/sequencer的virtual主要是指这种sequence/sequencer不像直接作用在具体driver上的sequence/sequencer,它不处理具体的transaction,主要是来做不同类型sequence间的控制和调度的。

基于设计复杂性的不同测试台架构如图(1)所示。
不同的TB情形
在这些模型中,图1.C有一个额外的组件来管理多个序列 sequences,称为虚拟序列器virtual sequencer。
如果我们只处理一个agent和一个序列sequence,如果存在多个agent和testcase,但它们之间不需要协调,这都不需要virtual sequence和virtual sequencer
但在复杂协议和设计的世界中,这些事情可能变得很复杂。让我们以4个双端口ram的SoC为例。
ram SoC的UVM测试台架构(图2)为每个ram都有一个读写agent,这意味着每个ram都会有一个读取sequence和写入sequence,以及一个读取sequencer和写入sequencer。
假设我们想在偶数寻址地址验证ram SoC的读写属性。为此,我们使用了一个名为“even_address_sequence”的sequence。必须将此sequence驱动到所有4个写agent和4个读agent。因此,通常我们将使用测试类中的start方法,如代码片段所示。
even_addr_sequence.start(env.wr_agnt_top.wr_agnt_1.sequencer);
even_addr_sequence.start(env.rd_agnt_top.rd_agnt_1.sequencer);
对于其他的agent也必须重复此操作。为了验证该芯片,我们必须编写涵盖所有场景的多个sequence,以测试单个ram的读取操作、写入操作和重置操作。因为我们有四个ram,所以这些sequence必须驱动到所有四个ram。

突然之间,我们必须管理的sequence和sequencer的数量增加了,这要求驱动到特定agent的特定驱动程序中的每个sequence具有很好的协调性和可控性。
此外,如果您注意到从图2和示例1中的test case调用的start方法,则会使用每个sequencer的完整层次路径。除了处理sequenc的复杂性日益增加之外,对sequencer的分层路径的依赖也造成了另一种情况。
考虑到sequencer的所有分层路径将是测试用例编写者的负担,他们的工作是提出多个场景,比如我们在图(2)中以地址sequence为例的场景。他们通过TB开发人员开发的TB驱动DUT创建的test case。当然,TB开发人员和测试用例编写人员可以是同一个人。但考虑到您正在开发一个验证IP,该IP将在市场上销售一空。购买您的IP的客户将创建test case,他们希望通过您的tb驱动DUT的刺激。要做到这一点,他们应该了解测试台的内部架构,或者至少了解序列器的层次路径。这种依赖性与UVM的通用方面相矛盾。
简而言之,我们要的是virtual sequencer和sequence
通过保持通用的行为来保持tb的通用性。
处理具有多个agent和处理不同transactions的tb的复杂性。
1.2 virtual sequencer和virtual sequence
virtual sequencer和virtual sequence只是TB体系结构中增加的一层。虽然我们在UVM中做的大多数事情都依赖于预定义的类,但有趣的是,UVM中没有单独的virtual sequencer或virtual sequence类。他们只是现有UVM class的扩展。
在图(3)中的Ram SoC示例中,我们使用even_addr_v_sequence作为virtual sequence,使用v_seqcer作为virtual sequencer。
class even_addr_v_sequence extends uvm_sequence #(uvm_sequence_item);
…
…
endclass
class v_sequencer extends uvm_sequencer;
…
…
endclass
1.3 如何实现
默认情况下,uvm_sequence类有一个名为m_sequencer的uvm_sequencer句柄。因此,这个句柄将出现在所有从uvm_sequence扩展的子类中。当使用start方法从test case启动sequence时,该sequence中的m_sequencer将指向作为start方法参数传递的sequencer。在此操作之后,将执行sequence主体。此m_sequencer用于连接virtual sequence和virtual sequencer。

具有virtual sequencer和virtual sequence的TB体系结构
1.3.1 第一种实现方式:在virtual sequencer 中利用对应的sequencer启动对应的sequence
- 通常我们在test case 中,将virtual sequencer 中的sub sequencer 进行连接,以使virtual sequencer 中的sub sequence 指向特定的实际sequencer:
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
v_sqr = my_vsqr::type_id::create("v_sqr", this);
endfunction
function void base_test::connect_phase(uvm_phase phase);
super.connect_phase(phase);
v_sqr.p_my_sqr = env.i_agt.sqr;
v_sqr.p_bus_sqr = env.bus_agt.sqr;
endfunction
- 然后 virtual sequencer中例化实际的sub sequencer
class my_vsqr extends uvm_sequencer;
my_sequencer p_my_sqr;
bus_sequencer p_bus_sqr;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
`uvm_component_utils(my_vsqr)
endclass
- virtual sequence中 例化实际生成激励的sub sequence,并利用 virtual sequencer中对应的实际sub sequencer启动对应的实际sub sequence:
class case0_vseq extends uvm_sequence;
`uvm_object_utils(case0_vseq)
`uvm_declare_p_sequencer(my_vsqr) //指定virtual sequence的m_sequencer 句柄为my_vsqr
function new(string name= "case0_vseq");
super.new(name);
endfunction
virtual task body();
case0_sequence dseq;
case0_bus_seq bseq;
if(starting_phase != null)
starting_phase.raise_objection(this);
bseq = case0_bus_seq::type_id::create("bseq");
bseq.start(p_sequencer.p_bus_sqr);
dseq = case0_sequence::type_id::create("dseq");
dseq.start(p_sequencer.p_my_sqr);
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
1.3.1 virtual sequencer
virtual sequencer是从environment类(示例中为env)构建的。它们包含所有物理sequencer的句柄。这些句柄指向其分层高级组件env的连接阶段中agent的物理sequencer。
wr_agent1_seqncr
.
.
wr_agent4_seqncr
rd_agent1_seqncr
.
.
rd_agent4_seqncr
这些句柄指向env连接阶段的实际或物理sequencer,也可以在test case中connect。
class ram_env extends uvm_env;
..
..
function void connect_phase(uvm_phase phase);
wr_agent1_seqncr =wr_agnt_top.wr_agnt1.sequencer;
.
.
wr_agent4_seqncr =wr_agnt_top.wr_agnt4.sequencer;
rd_agent1_seqncr =rd_agnt_top.rd_agnt1.sequencer;
.
.
rd_agent4_seqncr =rd_agnt_top.rd_agnt1.sequencer;
endfunction
endclass
1.3.2 vrtual sequence
从test case构建vrtual sequence。此类包含:1. virtual sequencer类的句柄; 2. 所有物理sequencers的句柄; 3.所有sequences的句柄。
我们正在考虑ram SoC示例中的even_addr_seq和even_aaddr_v_sequence作为为给定sequence创建的virtual sequence。
testc ase的start方法:1. 启动virtual sequence,而不是直接调用物理 sequence。2. 传递virtual sequencer的层次路径作为start方法的参数,而不是物理sequencer的路径。
根据图(3)中的test class
even_addr_v_sequence.start(env.v_seqncr);
这在示例virtual sequence中执行两件事,即even_addr_v_sequence。
使泛型m_sequencer指向env.v_seqncr,v_seqncr是一个virtual sequencer。
调用even_addr_v_sequence的body方法。
在步骤(1)之后,m_sequencer现在指向virtual sequencer。由于m_sequencer是父类型,而virtual sequencer(v_seqncr)是子类型,为了访问子类的内容,我们需要另一个子类句柄,并将m_seqncer动态转换为本地子句柄。这就是为什么在virtual sequence中需要virtual sequencer的本地句柄。
$cast(v_seqncr,m_sequencer);
最后,even_addr_v_sequence中的v_seqncr句柄指向env中的v_seqncr。如图3所示,我们将从even_addr_v_seqence调用实际序列even_wr_addr的start方法,将v_seqncr和所需的物理sequencer句柄作为参数传递。
even_wr_addr.start(v_seqncr.wr_agnt1_seqncr);
.
.
even_wr_addr.start(v_seqncr.wr_agnt4_seqncr);
even_rd_addr.start(v_seqncr.rd_agnt1_seqncr);
.
.
even_rd_addr.start(v_seqncr.rd_agnt4_seqncr);
还有一种方法可以处理所有这些连接,即使用p_sequencer。所有序列都有m_sequencer句柄,但它们不会自动有p_sequence句柄。p_sequencer不会自动声明和设置,但可以使`uvm_declare_psequencer宏声明和设置。
1.3.3 p_sequencer
sequence类里有一个uvm_sequencer_base类型m_sequencer指针,当sequence和sequencer关联后,m_sequencer会自动指向该sequencer,但通过m_sequencer不能直接使用seqr里的变量,否则会出现编译错误。只能使用cast强制向子类转换后,才能通过m_sequencer.xxx来访问该seqr内的xxx变量。
UVM引入p_sequencer,可以自动的实现上面所述的cast动作,从而可以在sequence中自由使用关联sequencer内的变量。
p_sequencer并不是UVM自动地在sequence的创建的,需要用户使用`uvm_declare_p_sequencer宏声明,之后UVM会自动实现指向seqr及cast的动作。
1.4 总结
virtual sequencer和virtual sequence没有预定义的类。
virtual sequence从uvm_sequence扩展。
virtual sequencer从uvm_sequencer扩展而来。
默认情况下,uvm_sequence类有一个名为m_sequencer的句柄,其类型为uvm_seuquencer base。从uvm_sequence类扩展的类也是如此。
virtual sequencer类将具有物理sequencer的句柄。
virtual sequencer中的sequencer句柄用于指向环境build phase构建阶段的物理sequencer。
virtual sequence具有物理sequencer和virtual sequencer的句柄。
sequence类中的m_sequencer将指向由test case中的start方法传递的sequencer。
virtual sequence中的virtual sequencer句柄指向m_sequencer指向的对象。
由于m_sequencer是父类型,因此需要动态强制转换`uvm_declare_psequencer。

浙公网安备 33010602011771号