UVM provide a weird pattern to define user defined phases. Usually, it’s useful for adding phases for VIP’s
User defined phase boiler-plate Link to heading
According to UVM class reference manual, we need to extends on of the class
class my_PHASE_phase extends uvm_task_phase;
class my_PHASE_phase extends uvm_topdown_phase;
class my_PHASE_phase extends uvm_bottomup_phase;
then override exec_task or exec_func
task exec_task(uvm_component comp, uvm_phase schedule);
function void exec_func(uvm_component comp, uvm_phase schedule);
the important part about exec method that it calls the phase from the component
comp.PHASE_phase(uvm_phase phase);
Here is example of foobar
phase boiter-plate
class my_foobar_phase extends uvm_task_phase;
static local my_foobar_phase m_inst;
protected function new(string name="foobar");
super.new(name);
endfunction : new
static function my_foobar_phase get();
if (m_inst == null)
m_inst = new();
return m_inst;
endfunction : get
task exec_task(uvm_component comp, uvm_phase phase);
dummy_comp dcomp;
if($cast(dcomp, comp))
dcomp.foobar_phase(phase);
endtask
endclass
it’s important to notice that exec_task casts the comp
handle to dummy_comp
? so what dummy_comp
?
class dummy_comp extends uvm_component;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task foobar_phase(uvm_phase phase);
endtask
endclass
dummy_comp
is an abstract class the defines virtual task foobar_phase
. component that defines the foobar_phase
need to inherits from dummy_comp
. This way we can use $csat
to check if we need to call foobar_phase
.
If we don’t have $case
, the call will happen on all component even ones that don’t have foobar_phase which results in an error.
Here is exec_task
task exec_task(uvm_component comp, uvm_phase phase);
dummy_comp dcomp;
// Will only work if component inherits from dummy_comp and we are sure it has foobar_phase
if($cast(dcomp, comp))
dcomp.foobar_phase(phase);
endtask
Register the phase with domain schedule Link to heading
finally, we need to insert the new phase somewhere in domain schedule. For this example, i chose between build and run phases.
function void build_phase(uvm_phase phase);
uvm_phase shed;
shed = uvm_domain::get_common_domain();
shed.add(my_foobar_phase::get(),null,uvm_build_phase::get(),uvm_run_phase::get());
endfunction
Putting it all together Link to heading
`include "uvm_macros.svh"
import uvm_pkg::*;
/*
*
*/
class dummy_comp extends uvm_component;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task foobar_phase(uvm_phase phase);
endtask
endclass
/*
*
*/
class my_foobar_phase extends uvm_task_phase;
static local my_foobar_phase m_inst;
protected function new(string name="foobar");
super.new(name);
endfunction : new
static function my_foobar_phase get();
if (m_inst == null)
m_inst = new();
return m_inst;
endfunction : get
task exec_task(uvm_component comp, uvm_phase phase);
dummy_comp dcomp;
if($cast(dcomp, comp))
dcomp.foobar_phase(phase);
endtask
endclass
/*
*
*/
class test extends dummy_comp;
`uvm_component_utils(test)
function new(string name="", uvm_component parent=null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
uvm_phase shed;
shed = uvm_domain::get_common_domain();
shed.add(my_foobar_phase::get(),null,uvm_build_phase::get(),uvm_run_phase::get());
endfunction
task foobar_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("FOOBAR", "foorbar_phase called", UVM_MEDIUM)
phase.drop_objection(this);
endtask
task run_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_warning("Test","Hello World!")
phase.drop_objection(this);
endtask
endclass
module top;
initial run_test("test");
endmodule