You are on page 1of 30

Advanced OVM (& UVM)

Layering Sequences

Tom Fitzpatrick
Verification Technologist

academy@mentor.com
www.verificationacademy.com
Sequences & Sequencers

• Most sequences run on sequencers


• One sequencer per agent
• Env defines default sequence
• Can be overridden via factory
• Test defines other sequences
• Sequences generate
items
• Sequencer sends to driver
Sequences & Sequencers

• Interesting tests require coordinating


multiple sequences
on multiple DUT
interfaces
Coordinating Sequences
class my_test extends ovm_test;

init_seq seq1;
exec_seq seq2;
...
task run;
seq1 = init_seq::type_id::create(“seq1”);
seq2 = exec_seq::type_id::create(“seq2”);
fork
seq1.start( env.my_agent1_h.my_sequencer_h );
seq2.start( env.my_agent2_h.my_sequencer_h );
join

global_stop_request();
Not very reusable
endtask
endclass
Virtual Sequences
class test_seq extends ovm_sequence;

init_seq seq1; No parameters; test_seq
exec_seq seq2; does not generate items
my_seqr my_sequencer1_h,my_sequencer2_h;
task body();
seq1 = init_seq::type_id::create(“seq1”);
seq2 = exec_seq::type_id::create(“seq2”);
fork
seq1.start( my_sequencer1_h );
seq2.start( my_sequencer2_h );
join
… We need to know
what these are
endtask
endclass
Virtual Sequence in a Test
class seq_test extends ovm_test;

test_seq tseq;
my_env e;

virtual task run();
tseq = test_seq::type_id::create(“testseq”);
tseq.my_sequencer1_h = e.my_agent1_h.my_sequencer_h;
tseq.my_sequencer2_h = e.my_agent2_h.my_sequencer_h;
tseq1.start();
… Fill in the details after creating tseq
global_stop_request();
endtask
endclass
Virtual Sequence in a Test
class seq_test extends ovm_test;

test_seq tseq;
my_env e;

virtual task run();
tseq = test_seq::type_id::create(“testseq”);
tseq.my_sequencer1_h = e.my_agent1_h.my_sequencer_h;
tseq.my_sequencer2_h = e.f.g.h.agent_h.seqr_h;
tseq1.start();

global_stop_request();
endtask
endclass Any compatible sequencer will do
Virtual Sequence in an Environment
class seq_env extends ovm_env;

test_seq tseq;
my_agent my_agent1_h, my_agent2_h;

virtual task run();
tseq = test_seq::type_id::create(“testseq”);
tseq.my_sequencer1_h = my_agent1_h.my_sequencer_h;
tseq.my_sequencer2_h = my_agent2_h.my_sequencer_h;
tseq1.start();
… Dependencies
global_stop_request();
endtask
endclass
What About Reuse?

• We want this:

• To look like this:

Need the virtual sequence


to run on a sequencer
What About Reuse?

• It will actually be
something like this:
Sequencer pointers in
virtual sequence point
to handles in virtual
“Virtual Sequencer” sequencer
contains pointers to the
actual sequencers
Virtual sequencer is the
“component anchor “ to
configure sequence
Virtual Sequencer
class virt_sequencer extends ovm_sequencer;
Extend base

component to add
my_seqr ctrl_seqr_h;
my_sequencer1_h; sequencer pointers
my_other_seqr my_sequencer2_h;
data_seqr_h; Use names that
indicate functionality
function new( string name ,
ovm_component parent = null );
super.new( name , parent );
endfunction
endclass
This is the easy part ☺
Setting Up the Virtual Sequence
class virt_sequence_base extends ovm_sequence;
… Hide the details in a
my_seqr ctrl_seqr_h; base sequence
my_other_seqr data_seqr_h;

virt_sequencer virt_sqr_h; Type-specific


task body(); virtual sequencer
Ensure proper
if(!$cast(virt_sqr_h, m_sequencer)) m_sequencer type
`ovm_fatal(“SeqrTypeMismatch”,
“Wrong Virtual Sequencer Type”); Set the pointers
ctrl_seqr_h = virt_sqr_h.ctrl_seqr_h;
data_seqr_h = virt_sqr_h.data_seqr_h;
endtask
endclass
The Virtual Sequence Writer Has it Easy
class test_seq extends virt_sequence_base;

init_seq seq1; Use Base Sequence
Create the child
exec_seq seq2; Infrastructure
sequences
task body();
super.body();
seq1 = init_seq::type_id::create(“seq1”);
seq2 = exec_seq::type_id::create(“seq2”);
fork Just need to know the
seq1.start( ctrl_seqr_h ); sequencer names
seq2.start( data_seqr_h );
join

endtask
endclass
Connect the Sequencers in the Environment
class seq_env extends ovm_env;

virt_sequencer vseqr;
my_agent ctrl_agent_h;
my_other_agent data_agent_h;

virtual function void connect();


vseqr.ctrl_seqr_h =
ctrl_agent_h.my_sequencer_h;
vseqr.data_seqr_h =
data_agent_h.my_sequencer_h;
endfunction
endclass
Start the Virtual Sequence Like Any Other
class seq_env extends ovm_env;

virt_sequencer vseqr;
my_agent ctrl_agent_h;
my_other_agent data_agent_h;
test_seq tseq;

virtual task run();


tseq = test_seq::type_id::create(“tseq”);
tseq.start(vseqr);
global_stop_request();
endtask
endclass
Use Test to Override Default Virtual Sequence
class vseq_test extends ovm_test;

tseq2 tseq_h;
my_env e;

function void start_of_simulation();
test_seq::type_id::
set_type_override(tseq2::get_type());
endfunction
endclass
Layered Protocols
Protocol Layering Schemes

• 1:1
• Transaction of one type converted into another
• Register -> bus
• 1:many
• High-level transaction converted to multiple low-
level
• Many:1
• Multiple higher-level transactions aggregated
• Converse happens on the response
Layering Architecture
Preserve the
Agent/Sequener
view

This connection
is the key

Ideally, without
changing the agent

Standard Agent

Convert Upper layer


to Lower layer
Layering Architecture

Look familiar?
The Layering Environment
class layer_env extends ovm_env;
… Parameterized by
lower_agent low_agent_h; upper-level req[,rsp]
ovm_layering_agent #(upper) up_agent_h; Create from Factory

virtual function void build();


low_agent_h = lower_agent::type_id::create(“Lagent”,this);
up_agent_h =
ovm_layer_agent #(upper)::type_id::create(“Uagent”,this);

endfunction
endclass
The Layering Environment
class layer_env extends ovm_env;
… Parameterized by
lower_agent low_agent_h; upper-level req[,rsp]
ovm_layering_agent #(upper) up_agent_h; Create from Factory

virtual function void build();


low_agent_h = lower_agent::type_id::create(“Lagent”,this);
up_agent_h =
ovm_layer_agent #(upper)::type_id::create(“Uagent”,this);
up_agent_h.create_mapping(

);

endfunction Must be called in build()


endclass
The Layering Environment
class layer_env extends ovm_env;

lower_agent low_agent_h;
ovm_layering_agent #(upper) up_agent_h;

virtual function void build();


low_agent_h = lower_agent::type_id::create(“Lagent”,this);
up_agent_h =
ovm_layer_agent #(upper)::type_id::create(“Uagent”,this);
up_agent_h.create_mapping(“upper",

);
Just a name
endfunction
endclass
The Layering Environment
class layer_env extends ovm_env;

lower_agent low_agent_h;
ovm_layering_agent #(upper) up_agent_h;

virtual function void build();


low_agent_h = lower_agent::type_id::create(“Lagent”,this);
up_agent_h =
ovm_layer_agent #(upper)::type_id::create(“Uagent”,this);
up_agent_h.create_mapping(“upper", up_sequencer::get_type(),

);
Upper Sequencer Type
endfunction created in the layering_agent
endclass
The Layering Environment
class layer_env extends ovm_env;

lower_agent low_agent_h;
ovm_layering_agent #(upper) up_agent_h;

virtual function void build();


low_agent_h = lower_agent::type_id::create(“Lagent”,this);
up_agent_h =
ovm_layer_agent #(upper)::type_id::create(“Uagent”,this);
up_agent_h.create_mapping(“upper", up_sequencer::get_type
“Lagent",
);
Lower Agent Name
endfunction
endclass
The Layering Environment
class layer_env extends ovm_env;

lower_agent low_agent_h;
ovm_layering_agent #(upper) up_agent_h;

virtual function void build();


low_agent_h = lower_agent::type_id::create(“Lagent”,this);
up_agent_h =
ovm_layer_agent #(upper)::type_id::create(“Uagent”,this);
up_agent_h.create_mapping(“upper", up_sequencer::get_type
“Lagent", U2L_xlseq::get_type(),
);

endfunction Translation Sequence Type


endclass Started in lower sequencer
The Layering Environment
class layer_env extends ovm_env;

lower_agent low_agent_h;
ovm_layering_agent #(upper) up_agent_h;

virtual function void build();


low_agent_h = lower_agent::type_id::create(“Lagent”,this);
up_agent_h =
ovm_layer_agent #(upper)::type_id::create(“Uagent”,this);
up_agent_h.create_mapping(“upper", up_sequencer::get_type
“Lagent", U2L_xlseq::get_type(),
upper_seq::get_type() );

endfunction Upper sequence type


endclass Started in upper sequencer
The Translation Sequence
class U2L_xlseq extends ovm_translator_base #(.REQ(upper),
.sequence_base(ovm_sequence #(lower));

task body(); Parameterized base type
upper ureq, ursp;
super.body(); Built-in seq_item_pull_port

Looks like a driver


sequencer_port.get(ureq);
req = new(); Built-in of type parameter REQ
req = u2l_xlate(ureq);
start_item(req); Translate upper to lower
finish_item(req);
get_response(rsp); Translate lower back to upper
ursp = l2u_xlate(rsp);
ursp.set_id_info(ureq); Write upper response back out
response_port.write(ursp);
endtask
endclass
Protocol Layering
Preserve the
Agent/Sequener
view

This connection
is the key

Ideally, without
changing the agent

Standard Agent

Convert Upper layer


to Lower layer
Advanced OVM (& UVM)
Layering Sequences

Tom Fitzpatrick
Verification Technologist

academy@mentor.com
www.verificationacademy.com

You might also like