I am seeing two ways of implementing virtual sequences:
Method 1: sequencer handle inside virtual sequencer
class v_sqr extends uvm_sequencer;
sqrA aSqr;
sqrB bSqr;
endclass
class v_seq extends uvm_sequence;
`uvm_object_utils (v_seq)
`uvm_declare_p_sequencer (v_sqr)
function new (string name = "v_seq");
super.new (name);
endfunction
seqA aSeq;
seqB bSeq;
task body();
aSeq = seqA::type_id::create ("aSeq");
bSeq = seqB::type_id::create ("bSeq");
...
fork
aSeq.start (p_sequencer.aSqr);
bSeq.start (p_sequencer.bSqr);
join
...
endtask
endclass
class myEnv extends uvm_env
..
myAg1 ag1;
myAg2 ag2;
v_sqr sqrV;
..
sqrV.aSqr = ag1.m_sequencer;
sqrV.bSqr = ag2.m_sequencer;
..
endclass
class myTest extends uvm_test;
`uvm_component_utils (myTest)
myEnv env;
...
task run_phase (uvm_phase phase);
v_seq seq = v_seq::type_id::create ("seq");
phase.raise_objection (this);
seq.start (env.sqrV);
phase.drop_objection (this);
endtask
endclass
Method 2: sequencer handle inside virtual sequence and thus NO virtual sequencer required
class v_seq extends uvm_sequence;
`uvm_object_utils (v_seq)
// no virtual sequencer
// instead handle to sqr
sqrA aSqr;
sqrB bSqr;
function new (string name = "v_seq");
super.new (name);
endfunction
seqA aSeq;
seqB bSeq;
task body();
aSeq = seqA::type_id::create ("aSeq");
bSeq = seqB::type_id::create ("bSeq");
...
fork
aSeq.start (aSqr);
bSeq.start (bSqr);
join
...
endtask
endclass
// env won't have virtual sequencer
class myTest extends uvm_test;
`uvm_component_utils (myTest)
myEnv env;
...
task run_phase (uvm_phase phase);
v_seq seq = v_seq::type_id::create ("seq");
phase.raise_objection (this);
// before starting seq assign sqr handles
seq.aSqr = env.ag1.m_sequencer;
seq.bSqr = env.ag2.m_sequencer;
// start seq
seq.start (null);
phase.drop_objection (this);
endtask
endclass
Which is the suggested way and why?
As on immediate to me Method-1 looks okay as tests can be written by others who might not be aware of agents available in environment. It can simply call “seq.start (env.sqrV);” to make sequence run.