初版内容:
1)基础用法:
在virtual_sequencer中将各个env的sequencer包括进来,同时base_test在connect_phase中将各个env放入指针中,在case在main_phase中设置default_sequence,让其进入phase时自启动。
平常的uvm的object的控制放到sequence的body中实现,引入virtual sequencer后,则将其放入顶层的virtual sequence的body中,而且注意fork join_none的使用,避免提前运行结束。
虽然sequence不是component,但是也可以在sequence中使用uvm_config_db (类似于tp_tb中使用)。
在case中对sequence的成员用uvm_config_db#(类型)::set()的时候要注意: sequence由于是一个object,它的名字可能不确定,所以这里一般用通配符。
而在sequence中get的时候,则使用uvm_root::get()/或者null和get_full_name(),作为前两个参数。
在sequence中可以对testbench中的component的成员使用uvm_config_db#(类型)::set(),同样因为路径的问题,第一个参数一般是null或者uvm_root::get()
2)项目升级用法:与上一致;
3)底层原理分析:
为了解决sequence同步,之前是使用send_over全局变量解决,而vir_sequencer按顺序启动,无需刻意同步。此外config中只需调用一次,就可以将对应的sequence和sequencer匹配上。Object的raise和drop只需在vir_sequence中即可。
virtual sequence本质也就是一个sequence,我们就叫它virtual sequence好了。它里面声明了不同的sequence,把普通sequence的一次create再start的过程做了很多次。同时声明一个virtual sequencer的句柄,再start函数中将自己肚子里不同的sequence跟virtual sequencer句柄联系起来。如果要用普通的sequence类方法来实现生产多个sequence的效果,就需要搞一个新sequence子类,拓展自旧sequence,这种事情搞多了容易混。所以用virtual sequence会方便一点,核心就是在类里搞了个vitual sequencer句柄。
系统级环境里可能有多个env,带来了多个sequencer/sequence, 这样在case里不好维护。实现一个virtual sequencer,里面包括指向各个sequencer的指针;而virtual sequence就像前面介绍的“sequence嵌套”一样实现。
由于virtual sequencer里有实际sequencer的指针,所以肯定不能用“typedef uvm_sequncer….”来实现。 同时,由于virtual sequencer有成员了,所以在virtual sequence里要declare p_sequencer,并且指向virtual sequencer。
base_test的connect_phase函数中,要把virtual sequencer里的成员赋值到各个env的sqr上。---- 所以virtual sequencer和各个env是同级的。
所谓“virtual”是说它本身不会发送transaction,所以virtual sequencer和virtual sequence都不用写transaction的参数(用了默认的参数)。
二次更新:
1.virtual sequence本质也就是一个sequence,我们就叫它virtual sequence好了。它里面声明了不同的sequence,把普通sequence的一次create再start的过程做了很多次。同时声明一个virtual sequencer的句柄,再start函数中将自己肚子里不同的sequence跟virtual sequencer句柄联系起来。
(如果要用普通的sequence类方法来实现生产多个sequence的效果,就需要搞一个新sequence子类,拓展自旧sequence,这种事情搞多了容易混。所以用virtual sequence会方便一点,核心就是在类里搞了个vitual sequencer句柄。)
class my_virtual_seq extends uvm_sequence;
`uvm_object_utils (my_virtual_seq)
`uvm_declare_p_sequencer (my_virtual_sequencer)
function new (string name = "my_virtual_seq");
super.new (name);
endfunction
apb_rd_wr_seq m_apb_rd_wr_seq;
wb_reset_seq m_wb_reset_seq;
pcie_gen_seq m_pcie_gen_seq;
task pre_body();
m_apb_rd_wr_seq = apb_rd_wr_seq::type_id::create ("m_apb_rd_wr_seq");
m_wb_reset_seq = wb_reset_seq::type_id::create ("m_wb_reset_seq");
m_pcie_gen_seq = pcie_gen_seq::type_id::create ("m_pcie_gen_seq");
endtask
task body();
...
m_apb_rd_wr_seq.start (p_sequencer.m_apb_seqr);
fork
m_wb_reset_seq.start (p_sequencer.m_wb_seqr);
m_pcie_gen_seq.start (p_sequencer.m_pcie_seqr);
join
...
endtask
endclass
2.在agent中create一个virtual sequencer,这个东西本质还是sequencer。我们在virtual sequencer里面声明不同的sequencer,在与driver相连的时候就通过virtual sequencer来着手对应相连。(这里其实就是套个套子)
class my_virtual_sequencer extends uvm_sequencer;
`uvm_component_utils (my_virtual_sequencer)
function new (string name = "my_virtual_sequencer", uvm_component parent);
super.new (name, parent);
endfunction
// Declare handles to other sequencers here
apb_sequencer m_apb_seqr;
reg_sequencer m_reg_seqr;
wb_sequencer m_wb_seqr;
pcie_sequencer m_pcie_seqr;
endclass
class top_env extends uvm_env;
...
my_virtual_sequencer m_virt_seqr;
virtual function void build_phase (uvm_phase phase);
...
m_virt_seqr = my_virtual_sequencer::type_id::create ("m_virt_seqr", this);
...
endfunction
// Connect virtual sequencer handles to actual sequencers
virtual function void connect_phase (uvm_phase phase);
...
m_virt_seqr.m_apb_seqr = m_apb_agent.m_apb_seqr;
m_virt_seqr.m_reg_seqr = m_reg_env.m_reg_seqr;
m_virt_seqr.m_pcie_seqr = m_pcie_env.m_pcie_agent.m_pcie_seqr;
...
endfunction
endclass
3.在test中的run_phase中create出多个sequence,然后通过第1步里声明的virtual sequencer句柄来使用start函数。
(说白了还是就套个套,图个方便统一管理吧)
class my_test extends uvm_test;
`uvm_component_utils (my_test)
my_env m_env;
task run_phase (uvm_phase phase);
my_virtual_seq m_vseq = my_virtual_seq::type_id::create ("m_vseq");
phase.raise_objection (this);
m_vseq.start (m_env.m_virtual_seqr);
phase.drop_objection (this);
endtask
endclass
暂时写到这,以后持续补充。