A question was raised by verification engineer I work with regarding using phases in uvm.
A monitor is designed to constantly check the outputs of the design. However, at the very beginning, before the completion of reset process, the outputs may be invalid or even X’s, which causes monitor to report false errors.
The ask is to have a way to start the monitor after reset and stops after main phase. The monitor should be turned off during shutdown process.
It seems there’s no built-in way of doing this in UVM framework.
There might be several ways to do this.
a) Put the monitor logic in run_phase and have the codes wait for a reset_complete event and exit after detecting another main_complete event. To do this, we can use one of the two following ways.
a-1) generate event signals from design or somewhere in the test environment (but usually can’t do this from monitor itself as monitor may not have access to such info/signals). This causes dependency between components, which compromises the re-use of the monitor.
a-2) generate the event within the monitor itself, from phase_ready_to_end function in reset/main phases, or simply in post_reset & post_main phases. This seems much simpler but because of the parallel nature of components running in environment there’s no guarantee that the event is generated really as the last event of the phase. E.g. someone can raise objection in phase_ready_to_end function in another component in the same phase and extend the phase after the event generated in this monitor. Thus, the logic in run_phase may have already started even though the reset process hasn’t been fully completed. Same thing to the post_xxx phases.
b) Create a user defined phase that spans from pre_configure all the way to post_main phase.
Therefore I created a couple of class, as experiments, to make option b) easier. See codes below. Basically with the uvm_flex_phase_monitor class you can set the after/before phases easily. A flex phase that spans between the after/before phases will be automatically created and all you need to do is putting your logic code in this new flex_phase task.
We don’t have resources to extensively test. So before I extend this from monitor to other UVM components and put these in real project use I’d really like to hear from experts 1) What’s the recommended way to do this? 2) Are there any issues in the codes that might cause other unexpected problems?
// uvm_flex_phase.svh
typedef class uvm_flex_phase_component;
class uvm_flex_phase #(type AFTER_PHASE = uvm_post_reset_phase,
type BEFORE_PHASE = uvm_pre_shutdown_phase) extends uvm_task_phase;
virtual task exec_task(uvm_component comp, uvm_phase phase);
uvm_flex_phase_component#(AFTER_PHASE, BEFORE_PHASE) flex_phase_comp;
if ($cast(flex_phase_comp, comp))
flex_phase_comp.flex_phase(phase);
endtask : exec_task
local static uvm_flex_phase #(AFTER_PHASE, BEFORE_PHASE) m_inst;
static const string type_name = "uvm_flex_phase";
protected function new(string name = {"flex", "_after_", AFTER_PHASE::get().get_name(), "_before_", BEFORE_PHASE::get().get_name()});
super.new(name);
endfunction : new
static function uvm_flex_phase #(AFTER_PHASE, BEFORE_PHASE) get();
if (m_inst == null)
m_inst = new();
return m_inst;
endfunction : get
virtual function string get_type_name();
return {type_name, "_after_", AFTER_PHASE::get().get_type_name(), "_before_", BEFORE_PHASE::get().get_type_name()};
endfunction : get_type_name;
endclass : uvm_flex_phase
// uvm_flex_phase_component.svh
typedef class uvm_flex_phase;
virtual class uvm_flex_phase_component
#(type AFTER_PHASE = uvm_post_reset_phase,
type BEFORE_PHASE = uvm_pre_shutdown_phase) extends uvm_component;
local uvm_phase uvm_schedule;
local uvm_phase after_phase, before_phase;
function new (string name, uvm_component parent);
super.new(name, parent);
after_phase = AFTER_PHASE::get();
before_phase = BEFORE_PHASE::get();
if (uvm_flex_phase#(AFTER_PHASE, BEFORE_PHASE)::get().is_after(after_phase) ||
uvm_flex_phase#(AFTER_PHASE, BEFORE_PHASE)::get().is_before(before_phase))
return;
uvm_schedule = uvm_domain::get_uvm_schedule();
uvm_schedule.add(uvm_flex_phase#(AFTER_PHASE, BEFORE_PHASE)::get(), .after_phase(after_phase), .before_phase(before_phase));
endfunction : new
const static string type_name = "uvm_flex_phase_component #(AFTER_PHASE, BEFORE_PHASE)";
virtual function string get_type_name();
return type_name;
endfunction : get_type_name
virtual task flex_phase(uvm_phase phase);
return;
endtask: flex_phase
endclass : uvm_flex_phase_component
// uvm_flex_phase_monitor.svh
typedef class uvm_flex_phase_component;
virtual class uvm_flex_phase_monitor
#(type AFTER_PHASE = uvm_post_reset_phase,
type BEFORE_PHASE = uvm_pre_shutdown_phase) extends uvm_flex_phase_component #(AFTER_PHASE, BEFORE_PHASE);
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
const static string type_name = "uvm_flex_phase_monitor #(AFTER_PHASE, BEFORE_PHASE)";
virtual function string get_type_name();
return type_name;
endfunction : get_type_name;
endclass : uvm_flex_phase_monitor
// Usage -- set the after/before phases parameters when you extends the monitor
// and put your logic codes in the flex_phase task.
class your_monitor extends uvm_flex_monitor #(uvm_post_reset_phase, uvm_pre_shutdown_phase);
virtual task flex_phase(uvm_phase phase);
... Your code ...;
endtask
....
endclass