virtual sequence只是一个容器,它在不同的sequencer上启动多个sequence。
virtual sequencer控制其他sequencer,它不连接到任何driver。
1 Virtual Sequence and Virtual Sequencer Usage
在SOC中,可能有不同的模块与不同的协议交互,因此,我们需要不同的driver来驱动相应的接口。因此,我们通常用单独的agent来处理不同的协议。所以,我们需要在相应的sequencer上执行sequence。
另一个例子是多核的SOC。SOC中可能存在多个核,可以对提供的输入进行不同的操作,并对设备作出不同的响应。在这种情况下,不同的sequence执行对不同的sequencer也很重要。
virtual sequence通常在virtual sequencer上执行。virtual sequence控制不同sequence的启动。
如果您有多个agent,并且需要激励的协同调配,建议使用virtual sequencer。
1.1 Why are the virtual_sequence and virtual_sequencer named virtual?
System Verilog具有虚方法、虚接口和虚类。“virtual”关键字在所有这些词中都很常见。但是,virtual_sequence和virtual_sequencer不需要任何virtual关键字。UVM没有将uvm_virtual_sequence和uvm_virtual_sequencer作为基类。virtual sequence是从uvm_sequence派生出来的。virtual_sequencer是从uvm_sequence派生而来。
virtual sequencer控制其他sequencers。它没有附加到任何driver,也无法处理任何sequence_items。因此,它被命名为virtual。
2 Examples
完整代码可以从EDA playground中查看。
2.1 Without virtual sequence and virtual sequencer
// No Virtual Sequencer
class core_A_sequencer extends uvm_sequencer #(seq_item);
`uvm_component_utils(core_A_sequencer)
function new(string name = "core_A_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
class core_B_sequencer extends uvm_sequencer #(seq_item);
`uvm_component_utils(core_B_sequencer)
function new(string name = "core_B_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
// base_test
class base_test extends uvm_test;
env env_o;
core_A_seq Aseq;
core_B_seq Bseq;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_o = env::type_id::create("env_o", this);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
Aseq = core_A_seq::type_id::create("Aseq");
Bseq = core_B_seq::type_id::create("Bseq");
Aseq.start(env_o.agt_A.seqr_A);
Bseq.start(env_o.agt_B.seqr_B);
phase.drop_objection(this);
endtask
endclass
Output:
UVM_INFO sequence.sv(10) @ 0: uvm_test_top.env_o.agt_A.seqr_A@@Aseq [core_A_seq] core_A_seq: Inside Body
UVM_INFO driver.sv(38) @ 0: uvm_test_top.env_o.agt_A.drv_A [core_A_driver] Driving from core A
UVM_INFO sequence.sv(30) @ 50: uvm_test_top.env_o.agt_B.seqr_B@@Bseq [core_B_seq] core_B_seq: Inside Body
UVM_INFO driver.sv(55) @ 50: uvm_test_top.env_o.agt_B.drv_B [core_B_driver] Driving from core B
2.2 With virtual sequence and without a virtual sequencer
// virtual sequence
class virtual_seq extends uvm_sequence #(seq_item);
core_A_seq Aseq;
core_B_seq Bseq;
core_A_sequencer seqr_A;
core_B_sequencer seqr_B;
`uvm_object_utils(virtual_seq)
function new (string name = "virtual_seq");
super.new(name);
endfunction
task body();
`uvm_info(get_type_name(), "virtual_seq: Inside Body", UVM_LOW);
Aseq = core_A_seq::type_id::create("Aseq");
Bseq = core_B_seq::type_id::create("Bseq");
Aseq.start(seqr_A);
Bseq.start(seqr_B);
endtask
endclass
// No Virtual sequencer
class core_A_sequencer extends uvm_sequencer #(seq_item);
`uvm_component_utils(core_A_sequencer)
function new(string name = "core_A_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
class core_B_sequencer extends uvm_sequencer #(seq_item);
`uvm_component_utils(core_B_sequencer)
function new(string name = "core_B_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
Output:
UVM_INFO sequence.sv(56) @ 0: reporter@@v_seq [virtual_seq] virtual_seq: Inside Body
UVM_INFO sequence.sv(10) @ 0: uvm_test_top.env_o.agt_A.seqr_A@@Aseq [core_A_seq] core_A_seq: Inside Body
UVM_INFO driver.sv(38) @ 0: uvm_test_top.env_o.agt_A.drv_A [core_A_driver] Driving from core A
UVM_INFO sequence.sv(30) @ 50: uvm_test_top.env_o.agt_B.seqr_B@@Bseq [core_B_seq] core_B_seq: Inside Body
UVM_INFO driver.sv(55) @ 50: uvm_test_top.env_o.agt_B.drv_B [core_B_driver] Driving from core B
2.3 With virtual sequence and virtual sequencer using p_senquencer handle
// Virtual sequence
class virtual_seq extends uvm_sequence #(seq_item);
core_A_seq Aseq;
core_B_seq Bseq;
core_A_sequencer seqr_A;
core_B_sequencer seqr_B;
`uvm_object_utils(virtual_seq)
`uvm_declare_p_sequencer(virtual_sequencer)
function new (string name = "virtual_seq");
super.new(name);
endfunction
task body();
`uvm_info(get_type_name(), "virtual_seq: Inside Body", UVM_LOW);
Aseq = core_A_seq::type_id::create("Aseq");
Bseq = core_B_seq::type_id::create("Bseq");
Aseq.start(p_sequencer.seqr_A);
Bseq.start(p_sequencer.seqr_B);
endtask
endclass
// Virtual p_sequencer
class virtual_sequencer extends uvm_sequencer;
`uvm_component_utils(virtual_sequencer)
core_A_sequencer seqr_A;
core_B_sequencer seqr_B;
function new(string name = "virtual_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
Output:
UVM_INFO sequence.sv(56) @ 0: uvm_test_top.env_o.v_seqr@@v_seq [virtual_seq] virtual_seq: Inside Body
UVM_INFO sequence.sv(10) @ 0: uvm_test_top.env_o.agt_A.seqr_A@@Aseq [core_A_seq] core_A_seq: Inside Body
UVM_INFO driver.sv(38) @ 0: uvm_test_top.env_o.agt_A.drv_A [core_A_driver] Driving from core A
UVM_INFO sequence.sv(30) @ 50: uvm_test_top.env_o.agt_B.seqr_B@@Bseq [core_B_seq] core_B_seq: Inside Body
UVM_INFO driver.sv(55) @ 50: uvm_test_top.env_o.agt_B.drv_B [core_B_driver] Driving from core B
2.4 With virtual sequence and virtual sequencer but without using p_senquencer handle
// virtual sequence
class virtual_seq extends uvm_sequence #(seq_item);
core_A_seq Aseq;
core_B_seq Bseq;
core_A_sequencer seqr_A;
core_B_sequencer seqr_B;
`uvm_object_utils(virtual_seq)
function new (string name = "virtual_seq");
super.new(name);
endfunction
task body();
env env_s;
`uvm_info(get_type_name(), "virtual_seq: Inside Body", UVM_LOW);
Aseq = core_A_seq::type_id::create("Aseq");
Bseq = core_B_seq::type_id::create("Bseq");
// virtual_sequencer is created in env, so we need env handle to find v_seqr.
if(!$cast(env_s, uvm_top.find("uvm_test_top.env_o"))) `uvm_error(get_name(), "env_o is not found");
Aseq.start(env_s.v_seqr.seqr_A);
Bseq.start(env_s.v_seqr.seqr_B);
endtask
endclass
// virtual_sequencer
class virtual_sequencer extends uvm_sequencer;
`uvm_component_utils(virtual_sequencer)
core_A_sequencer seqr_A;
core_B_sequencer seqr_B;
function new(string name = "virtual_sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
Output:
UVM_INFO sequence.sv(55) @ 0: uvm_test_top.env_o.v_seqr@@v_seq [virtual_seq] virtual_seq: Inside Body
UVM_INFO sequence.sv(10) @ 0: uvm_test_top.env_o.agt_A.seqr_A@@Aseq [core_A_seq] core_A_seq: Inside Body
UVM_INFO driver.sv(38) @ 0: uvm_test_top.env_o.agt_A.drv_A [core_A_driver] Driving from core A
UVM_INFO sequence.sv(30) @ 50: uvm_test_top.env_o.agt_B.seqr_B@@Bseq [core_B_seq] core_B_seq: Inside Body
UVM_INFO driver.sv(55) @ 50: uvm_test_top.env_o.agt_B.drv_B [core_B_driver] Driving from core B