这周看了Cliff的文章《Using UVM Virtual Sequencers & Virtual Sequences》,本文相当于是一个阅读记录。
一、什么时候需要virtual sequencer?
- 环境中只有一个driver agent,此时不需要virtual sequencer;
- 环境中有多个driver_agent,但是这些driver之间不需要同步,此时不需要virtual sequencer;
- 环境中有多个driver_agent,且这些driver之间需要同步,此时需要virtual sequencer。
一般考虑到环境的可扩展性,我们会在一开始生成环境的时候就把virtual sequencer给加上。即使暂时用不到,也比以后再去加的要好。而且目前的验证环境大部分是脚本生成的,加virtual sequencer其实也不存在什么额外的工作量。
二、为什么叫virtual sequencer / virtual sequence?
- 使用virtual sequencer时,我们的sequence实际上是运行在对应的subsequencer上的,此时virtual sequencer只是一个容器,所以称之为"virtual"。
- 同理,virtual sequence上可以包含多种不同的pkt/seq,它同样是一个容器,所以称之为"virtual"。
- 需要注意,这里的“virtual”是抽象概念上的,并不是关键字的意思。实际上:
virtual sequencer和(real) sequencer都继承自uvm_sequencer;
virtual sequence 和(real) sequence 都继承自uvm_sequence。
三、m_sequencer与p_sequencer
3.1 m_sequencer
当我们使用宏`uvm_do或者xxx_seq.start(sqr)方法启动sequence后,sequence内部自有的m_sequencer句柄就自动地指向了对应的sequencer上了。这个操作是start()方法内部实现的,对用户不可见,因此一般我们都不会直接去操作m_sequencer变量。事实上,uvm中直接以'm_'开头的变量都不应该直接被用户使用。
3.2 p_sequencer
与m_sequencer变量作用相反,p_sequencer变量就是为了方便用户而提供的。p_sequencer变量需要用户使用宏`uvm_declare_p_sequencer去定义和设置,该宏的展开如下:
`define uvm_declare_p_sequencer(SEQUENCER) \
SEQUENCER p_sequencer;\
virtual function void m_set_p_sequencer();\
super.m_set_p_sequencer(); \
if( !$cast(p_sequencer, m_sequencer)) \
`uvm_fatal("DCLPSQ", \
$sformatf("%m %s Error casting p_sequencer, please verify that this
sequence/sequence item is intended to execute on this type of sequencer",
get_full_name())) \
endfunction
`uvm_declare_p_sequencer宏一般在sequence的内部使用,主要完成以下两件事情:
- 创建了p_sequencer变量;
- 将uvm内部的m_sequencer变量赋给了p_sequencer变量;
宏`uvm_declare_p_sequencer的使用可以参考后续4.2.1的代码。或许你存在疑惑,这个宏只是定义了m_set_p_sequencer()函数,并没有直接调用该函数。实际上,m_set_p_sequencer()函数也是在sequence的start()函数内部被调用的(所以这个函数是virtual的)。
在创建和设置好p_sequencer变量后,我们就可以很方便地通过p_sequencer变量去访问sequencer了。比如在4.2.1的代码中,我们在virtual sequence中,通过p_sequencer索引到多个实际的sequencer句柄,并将他们传递给实际的sequence。
四、举例
4.1 virtual sequencer
class vsequencer extends uvm_sequencer;
`uvm_component_utils(vsequencer)
tb_ahb_sequencer ahb_sqr;
tb_eth_sequencer eth_sqr;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
-
virtual sequencer只是一个sequencer的容器。
4.2 virtual sequence
4.2.1 virtual sequence base
class vseq_base extends uvm_sequence;
`uvm_object_utils(vseq_base)
`uvm_declare_p_sequencer(vsequencer)
function new(string name="vseq_base");
super.new(name);
endfunction
tb_ahb_sequencer ahb_sqr;
tb_eth_sequencer eth_sqr;
virtual task body();
ahb_sqr = p_sequencer.ahb_sqr;
eth_sqr = p_sequencer.eth_sqr;
endtask
endclass
-
在virtual sequence base中,我们借助p_sequencer变量的帮助,得到多个sequencer的句柄。
4.2.2 virtual sequence extension
class v_seq1 extends vseq_base;
`uvm_object_utils(v_seq1)
function new(string name="v_seq1");
super.new(name);
endfunction
virtual task body();
ahb_seq1 ahb_seq;
eth_seq1 eth_pkts;
//---------------------------------------------------
super.body();
`uvm_info("v_seq1", "Executing sequence", UVM_HIGH)
`uvm_do_on(ahb_seq, ahb_sqr)
`uvm_do_on(eth_pkts, eth_sqr)
`uvm_do_on(eth_pkts, eth_sqr)
`uvm_do_on(ahb_seq, ahb_sqr)
`uvm_info("v_seq1", "Sequence complete", UVM_HIGH)
endtask
endclass
-
在virtual sequence中,将不同sequence和对应的sequencer联系起来。
4.3 environment
class env extends uvm_env;
`uvm_component_utils(env)
tb_eth_agent eth_agnt;
tb_ahb_agent ahb_agnt;
vsequencer v_sqr;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
eth_agnt = tb_eth_agent::type_id::create("eth_agnt", this);
ahb_agnt = tb_ahb_agent::type_id::create("ahb_agnt", this);
v_sqr = vsequencer::type_id::create("v_sqr" , this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
v_sqr.ahb_sqr = ahb_agnt.sqr);
v_sqr.eth_sqr = eth_agnt.sqr);
endfunction
endclass
-
显然,我们还需要在env中,将各个sequencer的句柄传递给virtual sequencer。这里是直接赋值的方式,你也可以采用config_db进行配置。
4.4 testcase
`timescale 1ns/1ns
class test1 extends test_base;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
v_seq1 vseq = v_seq1::type_id::create("vseq");
uvm_test_done.raise_objection(this);
`uvm_info("test1 run", "Starting test", UVM_MEDIUM)
vseq.start(e.v_sqr);
`uvm_info("test1 run", "Ending test", UVM_MEDIUM)
uvm_test_done.drop_objection(this);
endtask
endclass
- 在testcase中,我们可以通过start()函数来启动virtual sequence。