//
//----------------------------------------------------------------------
// Copyright 2007-2011 Mentor Graphics Corporation
// Copyright 2007-2010 Cadence Design Systems, Inc.
// Copyright 2010-2013 Synopsys, Inc.
// Copyright 2013 NVIDIA Corporation
// Copyright 2013 Cisco Systems, Inc.
// All Rights Reserved Worldwide
//
// Licensed under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in
// writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See
// the License for the specific language governing
// permissions and limitations under the License.
//----------------------------------------------------------------------
typedef class [docs]uvm_test_done_objection;
typedef class [docs]uvm_sequencer_base;
typedef class [docs]uvm_domain;
typedef class [docs]uvm_task_phase;
typedef class [docs]uvm_phase_cb;
//------------------------------------------------------------------------------
//
// Section: Phasing Definition classes
//
//------------------------------------------------------------------------------
//
// The following class are used to specify a phase and its implied functionality.
//
//------------------------------------------------------------------------------
//
// Class: uvm_phase
//
//------------------------------------------------------------------------------
//
// This base class defines everything about a phase: behavior, state, and context.
//
// To define behavior, it is extended by UVM or the user to create singleton
// objects which capture the definition of what the phase does and how it does it.
// These are then cloned to produce multiple nodes which are hooked up in a graph
// structure to provide context: which phases follow which, and to hold the state
// of the phase throughout its lifetime.
// UVM provides default extensions of this class for the standard runtime phases.
// VIP Providers can likewise extend this class to define the phase functor for a
// particular component context as required.
//
// This base class defines everything about a phase: behavior, state, and context.
//
// To define behavior, it is extended by UVM or the user to create singleton
// objects which capture the definition of what the phase does and how it does it.
// These are then cloned to produce multiple nodes which are hooked up in a graph
// structure to provide context: which phases follow which, and to hold the state
// of the phase throughout its lifetime.
// UVM provides default extensions of this class for the standard runtime phases.
// VIP Providers can likewise extend this class to define the phase functor for a
// particular component context as required.
//
// *Phase Definition*
//
// Singleton instances of those extensions are provided as package variables.
// These instances define the attributes of the phase (not what state it is in)
// They are then cloned into schedule nodes which point back to one of these
// implementations, and calls its virtual task or function methods on each
// participating component.
// It is the base class for phase functors, for both predefined and
// user-defined phases. Per-component overrides can use a customized imp.
//
// To create custom phases, do not extend uvm_phase directly: see the
// three predefined extended classes below which encapsulate behavior for
// different phase types: task, bottom-up function and top-down function.
//
// Extend the appropriate one of these to create a uvm_YOURNAME_phase class
// (or YOURPREFIX_NAME_phase class) for each phase, containing the default
// implementation of the new phase, which must be a uvm_component-compatible
// delegate, and which may be a ~null~ implementation. Instantiate a singleton
// instance of that class for your code to use when a phase handle is required.
// If your custom phase depends on methods that are not in uvm_component, but
// are within an extended class, then extend the base YOURPREFIX_NAME_phase
// class with parameterized component class context as required, to create a
// specialized functor which calls your extended component class methods.
// This scheme ensures compile-safety for your extended component classes while
// providing homogeneous base types for APIs and underlying data structures.
//
// *Phase Context*
//
// A schedule is a coherent group of one or mode phase/state nodes linked
// together by a graph structure, allowing arbitrary linear/parallel
// relationships to be specified, and executed by stepping through them in
// the graph order.
// Each schedule node points to a phase and holds the execution state of that
// phase, and has optional links to other nodes for synchronization.
//
// The main operations are: construct, add phases, and instantiate
// hierarchically within another schedule.
//
// Structure is a DAG (Directed Acyclic Graph). Each instance is a node
// connected to others to form the graph. Hierarchy is overlaid with m_parent.
// Each node in the graph has zero or more successors, and zero or more
// predecessors. No nodes are completely isolated from others. Exactly
// one node has zero predecessors. This is the root node. Also the graph
// is acyclic, meaning for all nodes in the graph, by following the forward
// arrows you will never end up back where you started but you will eventually
// reach a node that has no successors.
//
// *Phase State*
//
// A given phase may appear multiple times in the complete phase graph, due
// to the multiple independent domain feature, and the ability for different
// VIP to customize their own phase schedules perhaps reusing existing phases.
// Each node instance in the graph maintains its own state of execution.
//
// *Phase Handle*
//
// Handles of this type uvm_phase are used frequently in the API, both by
// the user, to access phasing-specific API, and also as a parameter to some
// APIs. In many cases, the singleton phase handles can be
// used (eg. <uvm_run_phase::get()>) in APIs. For those APIs that need to look
// up that phase in the graph, this is done automatically.
class [docs]uvm_phase extends uvm_object;
//`uvm_object_utils(uvm_phase)
`uvm_register_cb(uvm_phase, uvm_phase_cb)
//--------------------
// Group: Construction
//--------------------
// Function: new
//
// Create a new phase node, with a name and a note of its type
// name - name of this phase
// type - a value in <uvm_phase_type>
//
extern function [docs]new(string name="uvm_phase",
uvm_phase_type phase_type=UVM_PHASE_SCHEDULE,
uvm_phase parent=null);
// Function: get_phase_type
//
// Returns the phase type as defined by <uvm_phase_type>
//
extern function uvm_phase_type [docs]get_phase_type();
//-------------
// Group: State
//-------------
// Function: get_state
//
// Accessor to return current state of this phase
//
extern function uvm_phase_state [docs]get_state();
// Function: get_run_count
//
// Accessor to return the integer number of times this phase has executed
//
extern function int [docs]get_run_count();
// Function: find_by_name
//
// Locate a phase node with the specified ~name~ and return its handle.
// With ~stay_in_scope~ set, searches only within this phase's schedule or
// domain.
//
extern function uvm_phase [docs]find_by_name(string name, bit stay_in_scope=1);
// Function: find
//
// Locate the phase node with the specified ~phase~ IMP and return its handle.
// With ~stay_in_scope~ set, searches only within this phase's schedule or
// domain.
//
extern function uvm_phase [docs]find(uvm_phase phase, bit stay_in_scope=1);
// Function: is
//
// returns 1 if the containing uvm_phase refers to the same phase
// as the phase argument, 0 otherwise
//
extern function bit [docs]is(uvm_phase phase);
// Function: is_before
//
// Returns 1 if the containing uvm_phase refers to a phase that is earlier
// than the phase argument, 0 otherwise
//
extern function bit [docs]is_before(uvm_phase phase);
// Function: is_after
//
// returns 1 if the containing uvm_phase refers to a phase that is later
// than the phase argument, 0 otherwise
//
extern function bit [docs]is_after(uvm_phase phase);
//-----------------
// Group: Callbacks
//-----------------
// Function: exec_func
//
// Implements the functor/delegate functionality for a function phase type
// comp - the component to execute the functionality upon
// phase - the phase schedule that originated this phase call
//
virtual function void [docs]exec_func(uvm_component comp, uvm_phase phase); endfunction
// Function: exec_task
//
// Implements the functor/delegate functionality for a task phase type
// comp - the component to execute the functionality upon
// phase - the phase schedule that originated this phase call
//
virtual task [docs]exec_task(uvm_component comp, uvm_phase phase); endtask
//----------------
// Group: Schedule
//----------------
// Function: add
//
// Build up a schedule structure inserting phase by phase, specifying linkage
//
// Phases can be added anywhere, in series or parallel with existing nodes
//
// phase - handle of singleton derived imp containing actual functor.
// by default the new phase is appended to the schedule
// with_phase - specify to add the new phase in parallel with this one
// after_phase - specify to add the new phase as successor to this one
// before_phase - specify to add the new phase as predecessor to this one
//
extern function void [docs]add(uvm_phase phase,
uvm_phase with_phase=null,
uvm_phase after_phase=null,
uvm_phase before_phase=null);
// Function: get_parent
//
// Returns the parent schedule node, if any, for hierarchical graph traversal
//
extern function uvm_phase [docs]get_parent();
// Function: get_full_name
//
// Returns the full path from the enclosing domain down to this node.
// The singleton IMP phases have no hierarchy.
//
extern virtual function string [docs]get_full_name();
// Function: get_schedule
//
// Returns the topmost parent schedule node, if any, for hierarchical graph traversal
//
extern function uvm_phase [docs]get_schedule(bit hier = 0);
// Function: get_schedule_name
//
// Returns the schedule name associated with this phase node
//
extern function string [docs]get_schedule_name(bit hier = 0);
// Function: get_domain
//
// Returns the enclosing domain
//
extern function uvm_domain [docs]get_domain();
// Function: get_imp
//
// Returns the phase implementation for this this node.
// Returns ~null~ if this phase type is not a UVM_PHASE_LEAF_NODE.
//
extern function uvm_phase [docs]get_imp();
// Function: get_domain_name
//
// Returns the domain name associated with this phase node
//
extern function string [docs]get_domain_name();
// Function: get_adjacent_predecessor_nodes
//
// Provides an array of nodes which are predecessors to
// ~this~ phase node. A 'predecessor node' is defined
// as any phase node which lies prior to ~this~ node in
// the phase graph, with no nodes between ~this~ node and
// the predecessor node.
//
extern function void [docs]get_adjacent_predecessor_nodes(ref uvm_phase pred[]);
// Function: get_adjacent_successor_nodes
//
// Provides an array of nodes which are successors to
// ~this~ phase node. A 'successor's node' is defined
// as any phase node which comes after ~this~ node in
// the phase graph, with no nodes between ~this~ node
// and the successor node.
//
extern function void [docs]get_adjacent_successor_nodes(ref uvm_phase succ[]);
//-----------------------
// Group: Phase Done Objection
//-----------------------
//
// Task-based phase nodes within the phasing graph provide a <uvm_objection>
// based interface for prolonging the execution of the phase. All other
// phase types do not contain an objection, and will report a fatal error
// if the user attempts to ~raise~, ~drop~, or ~get_objection_count~.
// Function- m_report_null_objection
// Simplifies the reporting of ~null~ objection errors
extern function void m_report_null_objection(uvm_object obj,
string description,
int count,
string action);
// Function: get_objection
//
// Return the <uvm_objection> that gates the termination of the phase.
//
function uvm_objection [docs]get_objection(); return this.phase_done; endfunction
// Function: raise_objection
//
// Raise an objection to ending this phase
// Provides components with greater control over the phase flow for
// processes which are not implicit objectors to the phase.
//
//| while(1) begin
//| some_phase.raise_objection(this);
//| ...
//| some_phase.drop_objection(this);
//| end
//| ...
//
extern virtual function void [docs]raise_objection (uvm_object obj,
string description="",
int count=1);
// Function: drop_objection
//
// Drop an objection to ending this phase
//
// The drop is expected to be matched with an earlier raise.
//
extern virtual function void [docs]drop_objection (uvm_object obj,
string description="",
int count=1);
// Function: get_objection_count
//
// Returns the current number of objections to ending this phase raised by the given ~object~.
//
extern virtual function int [docs]get_objection_count( uvm_object obj=null );
//-----------------------
// Group: Synchronization
//-----------------------
// The functions 'sync' and 'unsync' add soft sync relationships between nodes
//
// Summary of usage:
//| my_phase.sync(.target(domain)
//| [,.phase(phase)[,.with_phase(phase)]]);
//| my_phase.unsync(.target(domain)
//| [,.phase(phase)[,.with_phase(phase)]]);
//
// Components in different schedule domains can be phased independently or in sync
// with each other. An API is provided to specify synchronization rules between any
// two domains. Synchronization can be done at any of three levels:
//
// - the domain's whole phase schedule can be synchronized
// - a phase can be specified, to sync that phase with a matching counterpart
// - or a more detailed arbitrary synchronization between any two phases
//
// Each kind of synchronization causes the same underlying data structures to
// be managed. Like other APIs, we use the parameter dot-notation to set
// optional parameters.
//
// When a domain is synced with another domain, all of the matching phases in
// the two domains get a 'with' relationship between them. Likewise, if a domain
// is unsynched, all of the matching phases that have a 'with' relationship have
// the dependency removed. It is possible to sync two domains and then just
// remove a single phase from the dependency relationship by unsyncing just
// the one phase.
// Function: sync
//
// Synchronize two domains, fully or partially
//
// target - handle of target domain to synchronize this one to
// phase - optional single phase in this domain to synchronize,
// otherwise sync all
// with_phase - optional different target-domain phase to synchronize with,
// otherwise use ~phase~ in the target domain
//
extern function void [docs]sync(uvm_domain target,
uvm_phase phase=null,
uvm_phase with_phase=null);
// Function: unsync
//
// Remove synchronization between two domains, fully or partially
//
// target - handle of target domain to remove synchronization from
// phase - optional single phase in this domain to un-synchronize,
// otherwise unsync all
// with_phase - optional different target-domain phase to un-synchronize with,
// otherwise use ~phase~ in the target domain
//
extern function void [docs]unsync(uvm_domain target,
uvm_phase phase=null,
uvm_phase with_phase=null);
// Function: wait_for_state
//
// Wait until this phase compares with the given ~state~ and ~op~ operand.
// For <UVM_EQ> and <UVM_NE> operands, several <uvm_phase_states> can be
// supplied by ORing their enum constants, in which case the caller will
// wait until the phase state is any of (UVM_EQ) or none of (UVM_NE) the
// provided states.
//
// To wait for the phase to be at the started state or after
//
//| wait_for_state(UVM_PHASE_STARTED, UVM_GTE);
//
// To wait for the phase to be either started or executing
//
//| wait_for_state(UVM_PHASE_STARTED | UVM_PHASE_EXECUTING, UVM_EQ);
//
extern task [docs]wait_for_state(uvm_phase_state state, uvm_wait_op op=UVM_EQ);
//---------------
// Group: Jumping
//---------------
// Force phases to jump forward or backward in a schedule
//
// A phasing domain can execute a jump from its current phase to any other.
// A jump passes phasing control in the current domain from the current phase
// to a target phase. There are two kinds of jump scope:
//
// - local jump to another phase within the current schedule, back- or forwards
// - global jump of all domains together, either to a point in the master
// schedule outwith the current schedule, or by calling jump_all()
//
// A jump preserves the existing soft synchronization, so the domain that is
// ahead of schedule relative to another synchronized domain, as a result of
// a jump in either domain, will await the domain that is behind schedule.
//
// *Note*: A jump out of the local schedule causes other schedules that have
// the jump node in their schedule to jump as well. In some cases, it is
// desirable to jump to a local phase in the schedule but to have all
// schedules that share that phase to jump as well. In that situation, the
// jump_all static function should be used. This function causes all schedules
// that share a phase to jump to that phase.
// Function: jump
//
// Jump to a specified ~phase~. If the destination ~phase~ is within the current
// phase schedule, a simple local jump takes place. If the jump-to ~phase~ is
// outside of the current schedule then the jump affects other schedules which
// share the phase.
//
extern function void [docs]jump(uvm_phase phase);
// Function: set_jump_phase
//
// Specify a phase to transition to when phase is complete.
// Note that this function is part of what jump() does; unlike jump()
// it does not set the flag to terminate the phase prematurely.
extern function void [docs]set_jump_phase(uvm_phase phase) ;
// Function: end_prematurely
//
// Set a flag to cause the phase to end prematurely.
// Note that this function is part of what jump() does; unlike jump()
// it does not set a jump_phase to go to after the phase ends.
extern function void [docs]end_prematurely() ;
// Function- jump_all
//
// Make all schedules jump to a specified ~phase~, even if the jump target is local.
// The jump happens to all phase schedules that contain the jump-to ~phase~,
// i.e. a global jump.
//
extern static function void [docs]jump_all(uvm_phase phase);
// Function: get_jump_target
//
// Return handle to the target phase of the current jump, or ~null~ if no jump
// is in progress. Valid for use during the phase_ended() callback
//
extern function uvm_phase [docs]get_jump_target();
int unsigned max_ready_to_end_iter = 20;
//--------------------------
// Internal - Implementation
//--------------------------
// Implementation - Construction
//------------------------------
protected uvm_phase_type m_phase_type;
protected uvm_phase m_parent; // our 'schedule' node [or points 'up' one level]
uvm_phase m_imp; // phase imp to call when we execute this node
// Implementation - State
//-----------------------
local uvm_phase_state m_state;
local int m_run_count; // num times this phase has executed
local process m_phase_proc;
int m_num_procs_not_yet_returned;
extern function uvm_phase m_find_predecessor(uvm_phase phase, bit stay_in_scope=1, uvm_phase orig_phase=null);
extern function uvm_phase m_find_successor(uvm_phase phase, bit stay_in_scope=1, uvm_phase orig_phase=null);
extern function uvm_phase m_find_predecessor_by_name(string name, bit stay_in_scope=1, uvm_phase orig_phase=null);
extern function uvm_phase m_find_successor_by_name(string name, bit stay_in_scope=1, uvm_phase orig_phase=null);
extern function void m_print_successors();
// Implementation - Callbacks
//---------------------------
// Provide the required component traversal behavior. Called by execute()
virtual function void [docs]traverse(uvm_component comp,
uvm_phase phase,
uvm_phase_state state);
endfunction
// Provide the required per-component execution flow. Called by traverse()
virtual function void [docs]execute(uvm_component comp,
uvm_phase phase);
endfunction
// Implementation - Schedule
//--------------------------
protected bit m_predecessors[uvm_phase];
protected bit m_successors[uvm_phase];
protected uvm_phase m_end_node;
// Track the currently executing real task phases (used for debug)
static protected bit m_executing_phases[uvm_phase];
function uvm_phase [docs]get_begin_node(); if (m_imp != null) return this; return null; endfunction
function uvm_phase [docs]get_end_node(); return m_end_node; endfunction
// Implementation - Synchronization
//---------------------------------
local uvm_phase m_sync[$]; // schedule instance to which we are synced
uvm_objection phase_done; // phase done objection
local int unsigned m_ready_to_end_count;
function int unsigned [docs]get_ready_to_end_count();
return m_ready_to_end_count;
endfunction
extern local function void get_predecessors_for_successors(output bit pred_of_succ[uvm_phase]);
extern local task m_wait_for_pred();
// Implementation - Jumping
//-------------------------
local bit m_jump_bkwd;
local bit m_jump_fwd;
local uvm_phase m_jump_phase;
local bit m_premature_end;
extern function void [docs]clear(uvm_phase_state state = UVM_PHASE_DORMANT);
extern function void [docs]clear_successors(
uvm_phase_state state = UVM_PHASE_DORMANT,
uvm_phase end_state=null);
// Implementation - Overall Control
//---------------------------------
local static mailbox #(uvm_phase) m_phase_hopper = new();
extern static task m_run_phases();
extern local task execute_phase();
extern local function void m_terminate_phase();
extern local function void m_print_termination_state();
extern local task wait_for_self_and_siblings_to_drop();
extern function void [docs]kill();
extern function void [docs]kill_successors();
// TBD add more useful debug
//---------------------------------
protected static bit m_phase_trace;
local static bit m_use_ovm_run_semantic;
function string [docs]convert2string();
//return $sformatf("PHASE %s = %p",get_name(),this);
string s;
s = $sformatf("phase: %s parent=%s pred=%s succ=%s",get_name(),
(m_parent==null) ? "null" : get_schedule_name(),
m_aa2string(m_predecessors),
m_aa2string(m_successors));
return s;
endfunction
local function string m_aa2string(bit aa[uvm_phase]); // TBD tidy
string s;
int i;
s = "'{ ";
foreach (aa[ph]) begin
uvm_phase n = ph;
s = {s, (n == null) ? "null" : n.get_name(),
(i == aa.num()-1) ? "" : ", "};
i++;
end
s = {s, " }"};
return s;
endfunction
function bit [docs]is_domain();
return (m_phase_type == UVM_PHASE_DOMAIN);
endfunction
virtual function void m_get_transitive_children(ref uvm_phase phases[$]);
foreach (m_successors[succ])
begin
phases.push_back(succ);
succ.m_get_transitive_children(phases);
end
endfunction
endclass
//------------------------------------------------------------------------------
//
// Class: uvm_phase_state_change
//
//------------------------------------------------------------------------------
//
// Phase state transition descriptor.
// Used to describe the phase transition that caused a
// <uvm_phase_cb::phase_state_changed()> callback to be invoked.
//
class [docs]uvm_phase_state_change extends uvm_object;
`uvm_object_utils(uvm_phase_state_change)
// Implementation -- do not use directly
/* local */ uvm_phase m_phase;
/* local */ uvm_phase_state m_prev_state;
/* local */ uvm_phase m_jump_to;
function [docs]new(string name = "uvm_phase_state_change");
super.new(name);
endfunction
// Function: get_state()
//
// Returns the state the phase just transitioned to.
// Functionally equivalent to <uvm_phase::get_state()>.
//
virtual function uvm_phase_state [docs]get_state();
return m_phase.get_state();
endfunction
// Function: get_prev_state()
//
// Returns the state the phase just transitioned from.
//
virtual function uvm_phase_state [docs]get_prev_state();
return m_prev_state;
endfunction
// Function: jump_to()
//
// If the current state is ~UVM_PHASE_ENDED~ or ~UVM_PHASE_JUMPING~ because of
// a phase jump, returns the phase that is the target of jump.
// Returns ~null~ otherwise.
//
function uvm_phase [docs]jump_to();
return m_jump_to;
endfunction
endclass
//------------------------------------------------------------------------------
//
// Class: uvm_phase_cb
//
//------------------------------------------------------------------------------
//
// This class defines a callback method that is invoked by the phaser
// during the execution of a specific node in the phase graph or all phase nodes.
// User-defined callback extensions can be used to integrate data types that
// are not natively phase-aware with the UVM phasing.
//
class [docs]uvm_phase_cb extends uvm_callback;
// Function: new
// Constructor
function [docs]new(string name="unnamed-uvm_phase_cb");
super.new(name);
endfunction : new
// Function: phase_state_change
//
// Called whenever a ~phase~ changes state.
// The ~change~ descriptor describes the transition that was just completed.
// The callback method is invoked immediately after the phase state has changed,
// but before the phase implementation is executed.
//
// An extension may interact with the phase,
// such as raising the phase objection to prolong the phase,
// in a manner that is consistent with the current phase state.
//
// By default, the callback method does nothing.
// Unless otherwise specified, modifying the phase transition descriptor has
// no effect on the phasing schedule or execution.
//
virtual function void [docs]phase_state_change(uvm_phase phase,
uvm_phase_state_change change);
endfunction
endclass
//------------------------------------------------------------------------------
//
// Class: uvm_phase_cb_pool
//
//------------------------------------------------------------------------------
//
// Convenience type for the uvm_callbacks#(uvm_phase, uvm_phase_cb) class.
//
typedef uvm_callbacks#(uvm_phase, uvm_phase_cb) [docs]uvm_phase_cb_pool;
//------------------------------------------------------------------------------
// IMPLEMENTATION
//------------------------------------------------------------------------------
typedef class [docs]uvm_cmdline_processor;
`define UVM_PH_TRACE(ID,MSG,PH,VERB) \
`uvm_info(ID, {$sformatf("Phase '%0s' (id=%0d) ", \
PH.get_full_name(), PH.get_inst_id()),MSG}, VERB);
//-----------------------------
// Implementation - Construction
//-----------------------------
// new
function uvm_phase::new(string name="uvm_phase",
uvm_phase_type phase_type=UVM_PHASE_SCHEDULE,
uvm_phase parent=null);
super.new(name);
m_phase_type = phase_type;
// The common domain is the only thing that initializes m_state. All
// other states are initialized by being 'added' to a schedule.
if ((name == "common") &&
(phase_type == UVM_PHASE_DOMAIN))
m_state = UVM_PHASE_DORMANT;
m_run_count = 0;
m_parent = parent;
begin
uvm_cmdline_processor clp = uvm_cmdline_processor::get_inst();
string val;
if (clp.get_arg_value("+UVM_PHASE_TRACE", val))
m_phase_trace = 1;
else
m_phase_trace = 0;
if (clp.get_arg_value("+UVM_USE_OVM_RUN_SEMANTIC", val))
m_use_ovm_run_semantic = 1;
else
m_use_ovm_run_semantic = 0;
end
if (parent == null && (phase_type == UVM_PHASE_SCHEDULE ||
phase_type == UVM_PHASE_DOMAIN )) begin
//m_parent = this;
m_end_node = new({name,"_end"}, UVM_PHASE_TERMINAL, this);
this.m_successors[m_end_node] = 1;
m_end_node.m_predecessors[this] = 1;
end
endfunction
// add
// ---
// TBD error checks if param nodes are actually in this schedule or not
function void uvm_phase::add(uvm_phase phase,
uvm_phase with_phase=null,
uvm_phase after_phase=null,
uvm_phase before_phase=null);
uvm_phase new_node, begin_node, end_node, tmp_node;
uvm_phase_state_change state_chg;
if (phase == null)
`uvm_fatal("PH/NULL", "add: phase argument is null")
if (with_phase != null && with_phase.get_phase_type() == UVM_PHASE_IMP) begin
string nm = with_phase.get_name();
with_phase = find(with_phase);
if (with_phase == null)
`uvm_fatal("PH_BAD_ADD",
{"cannot find with_phase '",nm,"' within node '",get_name(),"'"})
end
if (before_phase != null && before_phase.get_phase_type() == UVM_PHASE_IMP) begin
string nm = before_phase.get_name();
before_phase = find(before_phase);
if (before_phase == null)
`uvm_fatal("PH_BAD_ADD",
{"cannot find before_phase '",nm,"' within node '",get_name(),"'"})
end
if (after_phase != null && after_phase.get_phase_type() == UVM_PHASE_IMP) begin
string nm = after_phase.get_name();
after_phase = find(after_phase);
if (after_phase == null)
`uvm_fatal("PH_BAD_ADD",
{"cannot find after_phase '",nm,"' within node '",get_name(),"'"})
end
if (with_phase != null && (after_phase != null || before_phase != null))
`uvm_fatal("PH_BAD_ADD",
"cannot specify both 'with' and 'before/after' phase relationships")
if (before_phase == this || after_phase == m_end_node || with_phase == m_end_node)
`uvm_fatal("PH_BAD_ADD",
"cannot add before begin node, after end node, or with end nodes")
// If we are inserting a new "leaf node"
if (phase.get_phase_type() == UVM_PHASE_IMP) begin
uvm_task_phase tp;
new_node = new(phase.get_name(),UVM_PHASE_NODE,this);
new_node.m_imp = phase;
begin_node = new_node;
end_node = new_node;
// The phase_done objection is only required
// for task-based nodes
if ($cast(tp, phase)) begin
if (new_node.get_name() == "run") begin
new_node.phase_done = uvm_test_done_objection::get();
end
else begin
new_node.phase_done = uvm_objection::type_id::create({phase.get_name(), "_objection"});
end
end
end
// We are inserting an existing schedule
else begin
begin_node = phase;
end_node = phase.m_end_node;
phase.m_parent = this;
end
// If 'with_phase' is us, then insert node in parallel
/*
if (with_phase == this) begin
after_phase = this;
before_phase = m_end_node;
end
*/
// If no before/after/with specified, insert at end of this schedule
if (with_phase == null && after_phase == null && before_phase == null) begin
before_phase = m_end_node;
end
if (m_phase_trace) begin
uvm_phase_type typ = phase.get_phase_type();
`uvm_info("PH/TRC/ADD_PH",
{get_name()," (",m_phase_type.name(),") ADD_PHASE: phase=",phase.get_full_name()," (",
typ.name(),", inst_id=",$sformatf("%0d",phase.get_inst_id()),")",
" with_phase=", (with_phase == null) ? "null" : with_phase.get_name(),
" after_phase=", (after_phase == null) ? "null" : after_phase.get_name(),
" before_phase=", (before_phase == null) ? "null" : before_phase.get_name(),
" new_node=", (new_node == null) ? "null" : {new_node.get_name(),
" inst_id=",
$sformatf("%0d",new_node.get_inst_id())},
" begin_node=", (begin_node == null) ? "null" : begin_node.get_name(),
" end_node=", (end_node == null) ? "null" : end_node.get_name()},UVM_DEBUG)
end
// INSERT IN PARALLEL WITH 'WITH' PHASE
if (with_phase != null) begin
begin_node.m_predecessors = with_phase.m_predecessors;
end_node.m_successors = with_phase.m_successors;
foreach (with_phase.m_predecessors[pred])
pred.m_successors[begin_node] = 1;
foreach (with_phase.m_successors[succ])
succ.m_predecessors[end_node] = 1;
end
// INSERT BEFORE PHASE
else if (before_phase != null && after_phase == null) begin
begin_node.m_predecessors = before_phase.m_predecessors;
end_node.m_successors[before_phase] = 1;
foreach (before_phase.m_predecessors[pred]) begin
pred.m_successors.delete(before_phase);
pred.m_successors[begin_node] = 1;
end
before_phase.m_predecessors.delete();
before_phase.m_predecessors[end_node] = 1;
end
// INSERT AFTER PHASE
else if (before_phase == null && after_phase != null) begin
end_node.m_successors = after_phase.m_successors;
begin_node.m_predecessors[after_phase] = 1;
foreach (after_phase.m_successors[succ]) begin
succ.m_predecessors.delete(after_phase);
succ.m_predecessors[end_node] = 1;
end
after_phase.m_successors.delete();
after_phase.m_successors[begin_node] = 1;
end
// IN BETWEEN 'BEFORE' and 'AFTER' PHASES
else if (before_phase != null && after_phase != null) begin
if (!after_phase.is_before(before_phase)) begin
`uvm_fatal("PH_ADD_PHASE",{"Phase '",before_phase.get_name(),
"' is not before phase '",after_phase.get_name(),"'"})
end
// before and after? add 1 pred and 1 succ
begin_node.m_predecessors[after_phase] = 1;
end_node.m_successors[before_phase] = 1;
after_phase.m_successors[begin_node] = 1;
before_phase.m_predecessors[end_node] = 1;
if (after_phase.m_successors.exists(before_phase)) begin
after_phase.m_successors.delete(before_phase);
before_phase.m_successors.delete(after_phase);
end
end // if (before_phase != null && after_phase != null)
// Transition nodes to DORMANT state
if (new_node == null)
tmp_node = phase;
else
tmp_node = new_node;
state_chg = uvm_phase_state_change::type_id::create(tmp_node.get_name());
state_chg.m_phase = tmp_node;
state_chg.m_jump_to = null;
state_chg.m_prev_state = tmp_node.m_state;
tmp_node.m_state = UVM_PHASE_DORMANT;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(tmp_node, state_chg))
endfunction
// get_parent
// ----------
function uvm_phase uvm_phase::get_parent();
return m_parent;
endfunction
// get_imp
// -------
function uvm_phase uvm_phase::get_imp();
return m_imp;
endfunction
// get_schedule
// ------------
function uvm_phase uvm_phase::get_schedule(bit hier=0);
uvm_phase sched;
sched = this;
if (hier)
while (sched.m_parent != null && (sched.m_parent.get_phase_type() == UVM_PHASE_SCHEDULE))
sched = sched.m_parent;
if (sched.m_phase_type == UVM_PHASE_SCHEDULE)
return sched;
if (sched.m_phase_type == UVM_PHASE_NODE)
if (m_parent != null && m_parent.m_phase_type != UVM_PHASE_DOMAIN)
return m_parent;
return null;
endfunction
// get_domain
// ----------
function uvm_domain uvm_phase::get_domain();
uvm_phase phase;
phase = this;
while (phase != null && phase.m_phase_type != UVM_PHASE_DOMAIN)
phase = phase.m_parent;
if (phase == null) // no parent domain
return null;
if(!$cast(get_domain,phase))
`uvm_fatal("PH/INTERNAL", "get_domain: m_phase_type is DOMAIN but $cast to uvm_domain fails")
endfunction
// get_domain_name
// ---------------
function string uvm_phase::get_domain_name();
uvm_domain domain;
domain = get_domain();
if (domain == null)
return "unknown";
return domain.get_name();
endfunction
// get_schedule_name
// -----------------
function string uvm_phase::get_schedule_name(bit hier=0);
uvm_phase sched;
string s;
sched = get_schedule(hier);
if (sched == null)
return "";
s = sched.get_name();
while (sched.m_parent != null && sched.m_parent != sched &&
(sched.m_parent.get_phase_type() == UVM_PHASE_SCHEDULE)) begin
sched = sched.m_parent;
s = {sched.get_name(),(s.len()>0?".":""),s};
end
return s;
endfunction
// get_full_name
// -------------
function string uvm_phase::get_full_name();
string dom, sch;
if (m_phase_type == UVM_PHASE_IMP)
return get_name();
get_full_name = get_domain_name();
sch = get_schedule_name();
if (sch != "")
get_full_name = {get_full_name, ".", sch};
if (m_phase_type != UVM_PHASE_DOMAIN && m_phase_type != UVM_PHASE_SCHEDULE)
get_full_name = {get_full_name, ".", get_name()};
endfunction
// get_phase_type
// --------------
function uvm_phase_type uvm_phase::get_phase_type();
return m_phase_type;
endfunction
//-----------------------
// Implementation - State
//-----------------------
// get_state
// ---------
function uvm_phase_state uvm_phase::get_state();
return m_state;
endfunction
// get_run_count
// -------------
function int uvm_phase::get_run_count();
return m_run_count;
endfunction
// m_print_successors
// ------------------
function void uvm_phase::m_print_successors();
uvm_phase found;
static string spaces = " ";
static int level;
if (m_phase_type == UVM_PHASE_DOMAIN)
level = 0;
`uvm_info("UVM/PHASE/SUCC",$sformatf("%s%s (%s) id=%0d",spaces.substr(0,level*2),get_name(), m_phase_type.name(),get_inst_id()),UVM_NONE)
level++;
foreach (m_successors[succ]) begin
succ.m_print_successors();
end
level--;
endfunction
// m_find_predecessor
// ------------------
function uvm_phase uvm_phase::m_find_predecessor(uvm_phase phase, bit stay_in_scope=1, uvm_phase orig_phase=null);
uvm_phase found;
//$display(" FIND PRED node '",phase.get_name(),"' (id=",$sformatf("%0d",phase.get_inst_id()),") - checking against ",get_name()," (",m_phase_type.name()," id=",$sformatf("%0d",get_inst_id()),(m_imp==null)?"":{"/",$sformatf("%0d",m_imp.get_inst_id())},")");
if (phase == null) begin
return null ;
end
if (phase == m_imp || phase == this)
return this;
foreach (m_predecessors[pred]) begin
uvm_phase orig;
orig = (orig_phase==null) ? this : orig_phase;
if (!stay_in_scope ||
(pred.get_schedule() == orig.get_schedule()) ||
(pred.get_domain() == orig.get_domain())) begin
found = pred.m_find_predecessor(phase,stay_in_scope,orig);
if (found != null)
return found;
end
end
return null;
endfunction
// m_find_predecessor_by_name
// --------------------------
function uvm_phase uvm_phase::m_find_predecessor_by_name(string name, bit stay_in_scope=1, uvm_phase orig_phase=null);
uvm_phase found;
//$display(" FIND PRED node '",name,"' - checking against ",get_name()," (",m_phase_type.name()," id=",$sformatf("%0d",get_inst_id()),(m_imp==null)?"":{"/",$sformatf("%0d",m_imp.get_inst_id())},")");
if (get_name() == name)
return this;
foreach (m_predecessors[pred]) begin
uvm_phase orig;
orig = (orig_phase==null) ? this : orig_phase;
if (!stay_in_scope ||
(pred.get_schedule() == orig.get_schedule()) ||
(pred.get_domain() == orig.get_domain())) begin
found = pred.m_find_predecessor_by_name(name,stay_in_scope,orig);
if (found != null)
return found;
end
end
return null;
endfunction
// m_find_successor
// ----------------
function uvm_phase uvm_phase::m_find_successor(uvm_phase phase, bit stay_in_scope=1, uvm_phase orig_phase=null);
uvm_phase found;
//$display(" FIND SUCC node '",phase.get_name(),"' (id=",$sformatf("%0d",phase.get_inst_id()),") - checking against ",get_name()," (",m_phase_type.name()," id=",$sformatf("%0d",get_inst_id()),(m_imp==null)?"":{"/",$sformatf("%0d",m_imp.get_inst_id())},")");
if (phase == null) begin
return null ;
end
if (phase == m_imp || phase == this) begin
return this;
end
foreach (m_successors[succ]) begin
uvm_phase orig;
orig = (orig_phase==null) ? this : orig_phase;
if (!stay_in_scope ||
(succ.get_schedule() == orig.get_schedule()) ||
(succ.get_domain() == orig.get_domain())) begin
found = succ.m_find_successor(phase,stay_in_scope,orig);
if (found != null) begin
return found;
end
end
end
return null;
endfunction
// m_find_successor_by_name
// ------------------------
function uvm_phase uvm_phase::m_find_successor_by_name(string name, bit stay_in_scope=1, uvm_phase orig_phase=null);
uvm_phase found;
//$display(" FIND SUCC node '",name,"' - checking against ",get_name()," (",m_phase_type.name()," id=",$sformatf("%0d",get_inst_id()),(m_imp==null)?"":{"/",$sformatf("%0d",m_imp.get_inst_id())},")");
if (get_name() == name)
return this;
foreach (m_successors[succ]) begin
uvm_phase orig;
orig = (orig_phase==null) ? this : orig_phase;
if (!stay_in_scope ||
(succ.get_schedule() == orig.get_schedule()) ||
(succ.get_domain() == orig.get_domain())) begin
found = succ.m_find_successor_by_name(name,stay_in_scope,orig);
if (found != null)
return found;
end
end
return null;
endfunction
// find
// ----
function uvm_phase uvm_phase::find(uvm_phase phase, bit stay_in_scope=1);
// TBD full search
//$display({"\nFIND node '",phase.get_name(),"' within ",get_name()," (scope ",m_phase_type.name(),")", (stay_in_scope) ? " staying within scope" : ""});
if (phase == m_imp || phase == this)
return phase;
find = m_find_predecessor(phase,stay_in_scope,this);
if (find == null)
find = m_find_successor(phase,stay_in_scope,this);
endfunction
// find_by_name
// ------------
function uvm_phase uvm_phase::find_by_name(string name, bit stay_in_scope=1);
// TBD full search
//$display({"\nFIND node named '",name,"' within ",get_name()," (scope ",m_phase_type.name(),")", (stay_in_scope) ? " staying within scope" : ""});
if (get_name() == name)
return this;
find_by_name = m_find_predecessor_by_name(name,stay_in_scope,this);
if (find_by_name == null)
find_by_name = m_find_successor_by_name(name,stay_in_scope,this);
endfunction
// is
// --
function bit uvm_phase::is(uvm_phase phase);
return (m_imp == phase || this == phase);
endfunction
// is_before
// ---------
function bit uvm_phase::is_before(uvm_phase phase);
//$display("this=%s is before phase=%s?",get_name(),phase.get_name());
// TODO: add support for 'stay_in_scope=1' functionality
return (!is(phase) && m_find_successor(phase,0,this) != null);
endfunction
// is_after
// --------
function bit uvm_phase::is_after(uvm_phase phase);
//$display("this=%s is after phase=%s?",get_name(),phase.get_name());
// TODO: add support for 'stay_in_scope=1' functionality
return (!is(phase) && m_find_predecessor(phase,0,this) != null);
endfunction
// execute_phase
// -------------
task uvm_phase::execute_phase();
uvm_task_phase task_phase;
uvm_root top;
uvm_phase_state_change state_chg;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
// If we got here by jumping forward, we must wait for
// all its predecessor nodes to be marked DONE.
// (the next conditional speeds this up)
// Also, this helps us fast-forward through terminal (end) nodes
foreach (m_predecessors[pred])
wait (pred.m_state == UVM_PHASE_DONE);
// If DONE (by, say, a forward jump), return immed
if (m_state == UVM_PHASE_DONE)
return;
state_chg = uvm_phase_state_change::type_id::create(get_name());
state_chg.m_phase = this;
state_chg.m_jump_to = null;
//---------
// SYNCING:
//---------
// Wait for phases with which we have a sync()
// relationship to be ready. Sync can be 2-way -
// this additional state avoids deadlock.
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_SYNCING;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
#0;
if (m_sync.size()) begin
foreach (m_sync[i]) begin
wait (m_sync[i].m_state >= UVM_PHASE_SYNCING);
end
end
m_run_count++;
if (m_phase_trace) begin
`UVM_PH_TRACE("PH/TRC/STRT","Starting phase",this,UVM_LOW)
end
// If we're a schedule or domain, then "fake" execution
if (m_phase_type != UVM_PHASE_NODE) begin
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_STARTED;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
#0;
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_EXECUTING;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
#0;
end
else begin // PHASE NODE
//---------
// STARTED:
//---------
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_STARTED;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
m_imp.traverse(top,this,UVM_PHASE_STARTED);
m_ready_to_end_count = 0 ; // reset the ready_to_end count when phase starts
#0; // LET ANY WAITERS WAKE UP
//if (m_imp.get_phase_type() != UVM_PHASE_TASK) begin
if (!$cast(task_phase,m_imp)) begin
//-----------
// EXECUTING: (function phases)
//-----------
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_EXECUTING;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
#0; // LET ANY WAITERS WAKE UP
m_imp.traverse(top,this,UVM_PHASE_EXECUTING);
end
else begin
m_executing_phases[this] = 1;
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_EXECUTING;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
fork : master_phase_process
begin
m_phase_proc = process::self();
//-----------
// EXECUTING: (task phases)
//-----------
task_phase.traverse(top,this,UVM_PHASE_EXECUTING);
wait(0); // stay alive for later kill
end
join_none
uvm_wait_for_nba_region(); //Give sequences, etc. a chance to object
// Now wait for one of three criterion for end-of-phase.
fork
begin // guard
fork
// JUMP
begin
wait (m_premature_end);
`UVM_PH_TRACE("PH/TRC/EXE/JUMP","PHASE EXIT ON JUMP REQUEST",this,UVM_DEBUG)
end
// WAIT_FOR_ALL_DROPPED
begin
bit do_ready_to_end ; // bit used for ready_to_end iterations
// OVM semantic: don't end until objection raised or stop request
if (phase_done.get_objection_total(top) ||
m_use_ovm_run_semantic && m_imp.get_name() == "run") begin
if (!phase_done.m_top_all_dropped)
phase_done.wait_for(UVM_ALL_DROPPED, top);
`UVM_PH_TRACE("PH/TRC/EXE/ALLDROP","PHASE EXIT ALL_DROPPED",this,UVM_DEBUG)
end
else begin
if (m_phase_trace) `UVM_PH_TRACE("PH/TRC/SKIP","No objections raised, skipping phase",this,UVM_LOW)
end
wait_for_self_and_siblings_to_drop() ;
do_ready_to_end = 1;
//--------------
// READY_TO_END:
//--------------
while (do_ready_to_end) begin
uvm_wait_for_nba_region(); // Let all siblings see no objections before traverse might raise another
`UVM_PH_TRACE("PH_READY_TO_END","PHASE READY TO END",this,UVM_DEBUG)
m_ready_to_end_count++;
if (m_phase_trace)
`UVM_PH_TRACE("PH_READY_TO_END_CB","CALLING READY_TO_END CB",this,UVM_HIGH)
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_READY_TO_END;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
if (m_imp != null)
m_imp.traverse(top,this,UVM_PHASE_READY_TO_END);
uvm_wait_for_nba_region(); // Give traverse targets a chance to object
wait_for_self_and_siblings_to_drop();
do_ready_to_end = (m_state == UVM_PHASE_EXECUTING) && (m_ready_to_end_count < max_ready_to_end_iter) ; //when we don't wait in task above, we drop out of while loop
end
end
// TIMEOUT
begin
if (this.get_name() == "run") begin
if (top.phase_timeout == 0)
wait(top.phase_timeout != 0);
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TO_WAIT", $sformatf("STARTING PHASE TIMEOUT WATCHDOG (timeout == %t)", top.phase_timeout), this, UVM_HIGH)
`uvm_delay(top.phase_timeout)
if ($time == `UVM_DEFAULT_TIMEOUT) begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
foreach (m_executing_phases[p]) begin
if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
$sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
this,
UVM_LOW)
end
end
`uvm_fatal("PH_TIMEOUT",
$sformatf("Default timeout of %0t hit, indicating a probable testbench issue",
`UVM_DEFAULT_TIMEOUT))
end
else begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
foreach (m_executing_phases[p]) begin
if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
$sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
this,
UVM_LOW)
end
end
`uvm_fatal("PH_TIMEOUT",
$sformatf("Explicit timeout of %0t hit, indicating a probable testbench issue",
top.phase_timeout))
end
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/EXE/3","PHASE EXIT TIMEOUT",this,UVM_DEBUG)
end // if (this.get_name() == "run")
else begin
wait (0); // never unblock for non-run phase
end
end // if (m_phase_trace)
join_any
disable fork;
end
join // guard
end
end
m_executing_phases.delete(this);
//---------
// JUMPING:
//---------
// If jump_to() was called then we need to kill all the successor
// phases which may still be running and then initiate the new
// phase. The return is necessary so we don't start new successor
// phases. If we are doing a forward jump then we want to set the
// state of this phase's successors to UVM_PHASE_DONE. This
// will let us pretend that all the phases between here and there
// were executed and completed. Thus any dependencies will be
// satisfied preventing deadlocks.
// GSA TBD insert new jump support
if (m_phase_type == UVM_PHASE_NODE) begin
if(m_premature_end) begin
if(m_jump_phase != null) begin
state_chg.m_jump_to = m_jump_phase;
`uvm_info("PH_JUMP",
$sformatf("phase %s (schedule %s, domain %s) is jumping to phase %s",
get_name(), get_schedule_name(), get_domain_name(), m_jump_phase.get_name()),
UVM_MEDIUM);
end
else begin
`uvm_info("PH_JUMP",
$sformatf("phase %s (schedule %s, domain %s) is ending prematurely",
get_name(), get_schedule_name(), get_domain_name()),
UVM_MEDIUM);
end
#0; // LET ANY WAITERS ON READY_TO_END TO WAKE UP
if (m_phase_trace)
`UVM_PH_TRACE("PH_END","ENDING PHASE PREMATURELY",this,UVM_HIGH)
end
else begin
// WAIT FOR PREDECESSORS: // WAIT FOR PREDECESSORS:
// function phases only
if (task_phase == null)
m_wait_for_pred();
end
//-------
// ENDED:
//-------
// execute 'phase_ended' callbacks
if (m_phase_trace)
`UVM_PH_TRACE("PH_END","ENDING PHASE",this,UVM_HIGH)
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_ENDED;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
if (m_imp != null)
m_imp.traverse(top,this,UVM_PHASE_ENDED);
#0; // LET ANY WAITERS WAKE UP
//---------
// CLEANUP:
//---------
// kill this phase's threads
state_chg.m_prev_state = m_state;
if(m_premature_end) m_state = UVM_PHASE_JUMPING;
else m_state = UVM_PHASE_CLEANUP ;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
if (m_phase_proc != null) begin
m_phase_proc.kill();
m_phase_proc = null;
end
#0; // LET ANY WAITERS WAKE UP
if (phase_done != null)
phase_done.clear();
end
//------
// DONE:
//------
m_premature_end = 0 ;
if(m_jump_fwd || m_jump_bkwd) begin
if(m_jump_fwd) begin
clear_successors(UVM_PHASE_DONE,m_jump_phase);
end
m_jump_phase.clear_successors();
end
else begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/DONE","Completed phase",this,UVM_LOW)
state_chg.m_prev_state = m_state;
m_state = UVM_PHASE_DONE;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(this, state_chg))
m_phase_proc = null;
#0; // LET ANY WAITERS WAKE UP
end
#0; // LET ANY WAITERS WAKE UP
if (phase_done != null)
phase_done.clear();
//-----------
// SCHEDULED:
//-----------
if(m_jump_fwd || m_jump_bkwd) begin
void'(m_phase_hopper.try_put(m_jump_phase));
m_jump_phase = null;
m_jump_fwd = 0;
m_jump_bkwd = 0;
end
// If more successors, schedule them to run now
else if (m_successors.size() == 0) begin
top.m_phase_all_done=1;
end
else begin
// execute all the successors
foreach (m_successors[succ]) begin
if(succ.m_state < UVM_PHASE_SCHEDULED) begin
state_chg.m_prev_state = succ.m_state;
state_chg.m_phase = succ;
succ.m_state = UVM_PHASE_SCHEDULED;
`uvm_do_callbacks(uvm_phase, uvm_phase_cb, phase_state_change(succ, state_chg))
#0; // LET ANY WAITERS WAKE UP
void'(m_phase_hopper.try_put(succ));
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/SCHEDULED",{"Scheduled from phase ",get_full_name()},succ,UVM_LOW)
end
end
end
endtask
function void uvm_phase::get_adjacent_predecessor_nodes(ref uvm_phase pred[]);
bit done;
bit predecessors[uvm_phase];
int idx;
// Get all predecessors (including TERMINALS, SCHEDULES, etc.)
foreach (m_predecessors[p])
predecessors[p] = 1;
// Replace any terminal / schedule nodes with their predecessors,
// recursively.
do begin
done = 1;
foreach (predecessors[p]) begin
if (p.get_phase_type() != UVM_PHASE_NODE) begin
predecessors.delete(p);
foreach (p.m_predecessors[next_p])
predecessors[next_p] = 1;
done = 0;
end
end
end while (!done);
pred = new [predecessors.size()];
foreach (predecessors[p]) begin
pred[idx++] = p;
end
endfunction : get_adjacent_predecessor_nodes
function void uvm_phase::get_adjacent_successor_nodes(ref uvm_phase succ[]);
bit done;
bit successors[uvm_phase];
int idx;
// Get all successors (including TERMINALS, SCHEDULES, etc.)
foreach (m_successors[s])
successors[s] = 1;
// Replace any terminal / schedule nodes with their successors,
// recursively.
do begin
done = 1;
foreach (successors[s]) begin
if (s.get_phase_type() != UVM_PHASE_NODE) begin
successors.delete(s);
foreach (s.m_successors[next_s])
successors[next_s] = 1;
done = 0;
end
end
end while (!done);
succ = new [successors.size()];
foreach (successors[s]) begin
succ[idx++] = s;
end
endfunction : get_adjacent_successor_nodes
// Internal implementation, more efficient than calling get_predessor_nodes on all
// of the successors returned by get_adjacent_successor_nodes
function void uvm_phase::get_predecessors_for_successors(output bit pred_of_succ[uvm_phase]);
bit done;
uvm_phase successors[];
get_adjacent_successor_nodes(successors);
// get all predecessors to these successors
foreach (successors[s])
foreach (successors[s].m_predecessors[pred])
pred_of_succ[pred] = 1;
// replace any terminal nodes with their predecessors, recursively.
// we are only interested in "real" phase nodes
do begin
done=1;
foreach (pred_of_succ[pred]) begin
if (pred.get_phase_type() != UVM_PHASE_NODE) begin
pred_of_succ.delete(pred);
foreach (pred.m_predecessors[next_pred])
pred_of_succ[next_pred] = 1;
done =0;
end
end
end while (!done);
// remove ourselves from the list
pred_of_succ.delete(this);
endfunction
// m_wait_for_pred
// ---------------
task uvm_phase::m_wait_for_pred();
bit pred_of_succ[uvm_phase];
get_predecessors_for_successors(pred_of_succ);
// wait for predecessors to successors (real phase nodes, not terminals)
// mostly debug msgs
foreach (pred_of_succ[sibling]) begin
if (m_phase_trace) begin
string s;
s = $sformatf("Waiting for phase '%s' (%0d) to be READY_TO_END. Current state is %s",
sibling.get_name(),sibling.get_inst_id(),sibling.m_state.name());
`UVM_PH_TRACE("PH/TRC/WAIT_PRED_OF_SUCC",s,this,UVM_HIGH)
end
sibling.wait_for_state(UVM_PHASE_READY_TO_END, UVM_GTE);
if (m_phase_trace) begin
string s;
s = $sformatf("Phase '%s' (%0d) is now READY_TO_END. Releasing phase",
sibling.get_name(),sibling.get_inst_id());
`UVM_PH_TRACE("PH/TRC/WAIT_PRED_OF_SUCC",s,this,UVM_HIGH)
end
end
if (m_phase_trace) begin
if (pred_of_succ.num()) begin
string s = "( ";
foreach (pred_of_succ[pred])
s = {s, pred.get_full_name()," "};
s = {s, ")"};
`UVM_PH_TRACE("PH/TRC/WAIT_PRED_OF_SUCC",
{"*** All pred to succ ",s," in READY_TO_END state, so ending phase ***"},this,UVM_HIGH)
end
else begin
`UVM_PH_TRACE("PH/TRC/WAIT_PRED_OF_SUCC",
"*** No pred to succ other than myself, so ending phase ***",this,UVM_HIGH)
end
end
#0; // LET ANY WAITERS WAKE UP
endtask
//---------------------------------
// Implementation - Synchronization
//---------------------------------
function void uvm_phase::m_report_null_objection(uvm_object obj,
string description,
int count,
string action);
string m_action;
string m_addon;
string m_obj_name = (obj == null) ? "uvm_top" : obj.get_full_name();
if ((action == "raise") || (action == "drop")) begin
if (count != 1)
m_action = $sformatf("%s %0d objections", action, count);
else
m_action = $sformatf("%s an objection", action);
end
else if (action == "get_objection_count") begin
m_action = "call get_objection_count";
end
if (this.get_phase_type() == UVM_PHASE_IMP) begin
m_addon = " (This is a UVM_PHASE_IMP, you have to query the schedule to find the UVM_PHASE_NODE)";
end
`uvm_error("UVM/PH/NULL_OBJECTION",
$sformatf("'%s' attempted to %s on '%s', however '%s' is not a task-based phase node! %s",
m_obj_name,
m_action,
get_name(),
get_name(),
m_addon))
endfunction : m_report_null_objection
// raise_objection
// ---------------
function void uvm_phase::raise_objection (uvm_object obj,
string description="",
int count=1);
if (phase_done != null)
phase_done.raise_objection(obj,description,count);
else
m_report_null_objection(obj, description, count, "raise");
endfunction
// drop_objection
// --------------
function void uvm_phase::drop_objection (uvm_object obj,
string description="",
int count=1);
if (phase_done != null)
phase_done.drop_objection(obj,description,count);
else
m_report_null_objection(obj, description, count, "drop");
endfunction
// get_objection_count
// -------------------
function int uvm_phase::get_objection_count (uvm_object obj=null);
if (phase_done != null)
return phase_done.get_objection_count(obj);
else begin
m_report_null_objection(obj, "" , 0, "get_objection_count");
return 0;
end
endfunction : get_objection_count
// sync
// ----
function void uvm_phase::sync(uvm_domain target,
uvm_phase phase=null,
uvm_phase with_phase=null);
if (!this.is_domain()) begin
`uvm_fatal("PH_BADSYNC","sync() called from a non-domain phase schedule node");
end
else if (target == null) begin
`uvm_fatal("PH_BADSYNC","sync() called with a null target domain");
end
else if (!target.is_domain()) begin
`uvm_fatal("PH_BADSYNC","sync() called with a non-domain phase schedule node as target");
end
else if (phase == null && with_phase != null) begin
`uvm_fatal("PH_BADSYNC","sync() called with null phase and non-null with phase");
end
else if (phase == null) begin
// whole domain sync - traverse this domain schedule from begin to end node and sync each node
int visited[uvm_phase];
uvm_phase queue[$];
queue.push_back(this);
visited[this] = 1;
while (queue.size()) begin
uvm_phase node;
node = queue.pop_front();
if (node.m_imp != null) begin
sync(target, node.m_imp);
end
foreach (node.m_successors[succ]) begin
if (!visited.exists(succ)) begin
queue.push_back(succ);
visited[succ] = 1;
end
end
end
end else begin
// single phase sync
// this is a 2-way ('with') sync and we check first in case it is already there
uvm_phase from_node, to_node;
int found_to[$], found_from[$];
if(with_phase == null) with_phase = phase;
from_node = find(phase);
to_node = target.find(with_phase);
if(from_node == null || to_node == null) return;
found_to = from_node.m_sync.find_index(node) with (node == to_node);
found_from = to_node.m_sync.find_index(node) with (node == from_node);
if (found_to.size() == 0) from_node.m_sync.push_back(to_node);
if (found_from.size() == 0) to_node.m_sync.push_back(from_node);
end
endfunction
// unsync
// ------
function void uvm_phase::unsync(uvm_domain target,
uvm_phase phase=null,
uvm_phase with_phase=null);
if (!this.is_domain()) begin
`uvm_fatal("PH_BADSYNC","unsync() called from a non-domain phase schedule node");
end else if (target == null) begin
`uvm_fatal("PH_BADSYNC","unsync() called with a null target domain");
end else if (!target.is_domain()) begin
`uvm_fatal("PH_BADSYNC","unsync() called with a non-domain phase schedule node as target");
end else if (phase == null && with_phase != null) begin
`uvm_fatal("PH_BADSYNC","unsync() called with null phase and non-null with phase");
end else if (phase == null) begin
// whole domain unsync - traverse this domain schedule from begin to end node and unsync each node
int visited[uvm_phase];
uvm_phase queue[$];
queue.push_back(this);
visited[this] = 1;
while (queue.size()) begin
uvm_phase node;
node = queue.pop_front();
if (node.m_imp != null) unsync(target,node.m_imp);
foreach (node.m_successors[succ]) begin
if (!visited.exists(succ)) begin
queue.push_back(succ);
visited[succ] = 1;
end
end
end
end else begin
// single phase unsync
// this is a 2-way ('with') sync and we check first in case it is already there
uvm_phase from_node, to_node;
int found_to[$], found_from[$];
if(with_phase == null) with_phase = phase;
from_node = find(phase);
to_node = target.find(with_phase);
if(from_node == null || to_node == null) return;
found_to = from_node.m_sync.find_index(node) with (node == to_node);
found_from = to_node.m_sync.find_index(node) with (node == from_node);
if (found_to.size()) from_node.m_sync.delete(found_to[0]);
if (found_from.size()) to_node.m_sync.delete(found_from[0]);
end
endfunction
// wait_for_state
//---------------
task uvm_phase::wait_for_state(uvm_phase_state state, uvm_wait_op op=UVM_EQ);
case (op)
UVM_EQ: wait((state&m_state) != 0);
UVM_NE: wait((state&m_state) == 0);
UVM_LT: wait(m_state < state);
UVM_LTE: wait(m_state <= state);
UVM_GT: wait(m_state > state);
UVM_GTE: wait(m_state >= state);
endcase
endtask
//-------------------------
// Implementation - Jumping
//-------------------------
// set_jump_phase
// ----
//
// Specify a phase to transition to when phase is complete.
function void uvm_phase::set_jump_phase(uvm_phase phase) ;
uvm_phase d;
if ((m_state < UVM_PHASE_STARTED) ||
(m_state > UVM_PHASE_ENDED) )
begin
`uvm_error("JMPPHIDL", { "Attempting to jump from phase \"",
get_name(), "\" which is not currently active (current state is ",
m_state.name(), "). The jump will not happen until the phase becomes ",
"active."})
end
// A jump can be either forward or backwards in the phase graph.
// If the specified phase (name) is found in the set of predecessors
// then we are jumping backwards. If, on the other hand, the phase is in the set
// of successors then we are jumping forwards. If neither, then we
// have an error.
//
// If the phase is non-existant and thus we don't know where to jump
// we have a situation where the only thing to do is to uvm_report_fatal
// and terminate_phase. By calling this function the intent was to
// jump to some other phase. So, continuing in the current phase doesn't
// make any sense. And we don't have a valid phase to jump to. So we're done.
d = m_find_predecessor(phase,0);
if (d == null) begin
d = m_find_successor(phase,0);
if (d == null) begin
string msg;
$sformat(msg,{"phase %s is neither a predecessor or successor of ",
"phase %s or is non-existant, so we cannot jump to it. ",
"Phase control flow is now undefined so the simulation ",
"must terminate"}, phase.get_name(), get_name());
`uvm_fatal("PH_BADJUMP", msg);
end
else begin
m_jump_fwd = 1;
`uvm_info("PH_JUMPF",$sformatf("jumping forward to phase %s", phase.get_name()),
UVM_DEBUG);
end
end
else begin
m_jump_bkwd = 1;
`uvm_info("PH_JUMPB",$sformatf("jumping backward to phase %s", phase.get_name()),
UVM_DEBUG);
end
m_jump_phase = d;
endfunction
// end_prematurely
// ----
//
// Set a flag to cause the phase to end prematurely.
function void uvm_phase::end_prematurely() ;
m_premature_end = 1 ;
endfunction
// jump
// ----
//
// Note that this function does not directly alter flow of control.
// That is, the new phase is not initiated in this function.
// Rather, flags are set which execute_phase() uses to determine
// that a jump has been requested and performs the jump.
function void uvm_phase::jump(uvm_phase phase);
set_jump_phase(phase) ;
end_prematurely() ;
endfunction
// jump_all
// --------
function void uvm_phase::jump_all(uvm_phase phase);
`uvm_warning("NOTIMPL","uvm_phase::jump_all is not implemented and has been replaced by uvm_domain::jump_all")
endfunction
// get_jump_target
// ---------------
function uvm_phase uvm_phase::get_jump_target();
return m_jump_phase;
endfunction
// clear
// -----
// for internal graph maintenance after a forward jump
function void uvm_phase::clear(uvm_phase_state state = UVM_PHASE_DORMANT);
m_state = state;
m_phase_proc = null;
if (phase_done != null)
phase_done.clear(this);
endfunction
// clear_successors
// ----------------
// for internal graph maintenance after a forward jump
// - called only by execute_phase()
// - depth-first traversal of the DAG, calliing clear() on each node
// - do not clear the end phase or beyond
function void uvm_phase::clear_successors(uvm_phase_state state = UVM_PHASE_DORMANT,
uvm_phase end_state=null);
if(this == end_state)
return;
clear(state);
foreach(m_successors[succ]) begin
succ.clear_successors(state, end_state);
end
endfunction
//---------------------------------
// Implementation - Overall Control
//---------------------------------
// wait_for_self_and_siblings_to_drop
// -----------------------------
// This task loops until this phase instance and all its siblings, either
// sync'd or sharing a common successor, have all objections dropped.
task uvm_phase::wait_for_self_and_siblings_to_drop() ;
bit need_to_check_all = 1 ;
uvm_root top;
uvm_coreservice_t cs;
bit siblings[uvm_phase];
cs = uvm_coreservice_t::get();
top = cs.get_root();
get_predecessors_for_successors(siblings);
foreach (m_sync[i]) begin
siblings[m_sync[i]] = 1;
end
while (need_to_check_all) begin
need_to_check_all = 0 ; //if all are dropped, we won't need to do this again
// wait for own objections to drop
if ((phase_done != null) && (phase_done.get_objection_total(top) != 0)) begin
m_state = UVM_PHASE_EXECUTING ;
phase_done.wait_for(UVM_ALL_DROPPED, top);
need_to_check_all = 1 ;
end
// now wait for siblings to drop
foreach(siblings[sib]) begin
sib.wait_for_state(UVM_PHASE_EXECUTING, UVM_GTE); // sibling must be at least executing
if ((sib.phase_done != null) && (sib.phase_done.get_objection_total(top) != 0)) begin
m_state = UVM_PHASE_EXECUTING ;
sib.phase_done.wait_for(UVM_ALL_DROPPED, top); // sibling must drop any objection
need_to_check_all = 1 ;
end
end
end
endtask
// kill
// ----
function void uvm_phase::kill();
`uvm_info("PH_KILL", {"killing phase '", get_name(),"'"}, UVM_DEBUG);
if (m_phase_proc != null) begin
m_phase_proc.kill();
m_phase_proc = null;
end
endfunction
// kill_successors
// ---------------
// Using a depth-first traversal, kill all the successor phases of the
// current phase.
function void uvm_phase::kill_successors();
foreach (m_successors[succ])
succ.kill_successors();
kill();
endfunction
// m_run_phases
// ------------
// This task contains the top-level process that owns all the phase
// processes. By hosting the phase processes here we avoid problems
// associated with phase processes related as parents/children
task uvm_phase::m_run_phases();
uvm_root top;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
// initiate by starting first phase in common domain
begin
uvm_phase ph = uvm_domain::get_common_domain();
void'(m_phase_hopper.try_put(ph));
end
forever begin
uvm_phase phase;
m_phase_hopper.get(phase);
fork
begin
phase.execute_phase();
end
join_none
#0; // let the process start running
end
endtask
// terminate_phase
// ---------------
function void uvm_phase::m_terminate_phase();
if (phase_done != null)
phase_done.clear(this);
endfunction
// print_termination_state
// -----------------------
function void uvm_phase::m_print_termination_state();
uvm_root top;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
if (phase_done != null) begin
`uvm_info("PH_TERMSTATE",
$sformatf("phase %s outstanding objections = %0d",
get_name(), phase_done.get_objection_total(top)),
UVM_DEBUG)
end
else begin
`uvm_info("PH_TERMSTATE",
$sformatf("phase %s has no outstanding objections",
get_name()),
UVM_DEBUG)
end
endfunction