URL
https://opencores.org/ocsvn/uart2bus_testbench/uart2bus_testbench/trunk
Subversion Repositories uart2bus_testbench
[/] [uart2bus_testbench/] [trunk/] [tb/] [uvm_src/] [base/] [uvm_phase.svh] - Rev 16
Compare with Previous | Blame | View Log
////----------------------------------------------------------------------// 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 uvm_test_done_objection;typedef class uvm_sequencer_base;typedef class uvm_domain;typedef class uvm_task_phase;typedef class 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 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 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 get_phase_type();//-------------// Group: State//-------------// Function: get_state//// Accessor to return current state of this phase//extern function uvm_phase_state get_state();// Function: get_run_count//// Accessor to return the integer number of times this phase has executed//extern function int 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 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 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 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 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 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 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 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 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 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 get_full_name();// Function: get_schedule//// Returns the topmost parent schedule node, if any, for hierarchical graph traversal//extern function uvm_phase get_schedule(bit hier = 0);// Function: get_schedule_name//// Returns the schedule name associated with this phase node//extern function string get_schedule_name(bit hier = 0);// Function: get_domain//// Returns the enclosing domain//extern function uvm_domain 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 get_imp();// Function: get_domain_name//// Returns the domain name associated with this phase node//extern function string 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 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 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 errorsextern 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 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 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 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 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 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 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 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 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 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 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 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 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 executedlocal 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 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 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 get_begin_node(); if (m_imp != null) return this; return null; endfunctionfunction uvm_phase get_end_node(); return m_end_node; endfunction// Implementation - Synchronization//---------------------------------local uvm_phase m_sync[$]; // schedule instance to which we are synceduvm_objection phase_done; // phase done objectionlocal int unsigned m_ready_to_end_count;function int unsigned get_ready_to_end_count();return m_ready_to_end_count;endfunctionextern 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 clear(uvm_phase_state state = UVM_PHASE_DORMANT);extern function void 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 kill();extern function void kill_successors();// TBD add more useful debug//---------------------------------protected static bit m_phase_trace;local static bit m_use_ovm_run_semantic;function string 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;endfunctionlocal function string m_aa2string(bit aa[uvm_phase]); // TBD tidystring s;int i;s = "'{ ";foreach (aa[ph]) beginuvm_phase n = ph;s = {s, (n == null) ? "null" : n.get_name(),(i == aa.num()-1) ? "" : ", "};i++;ends = {s, " }"};return s;endfunctionfunction bit is_domain();return (m_phase_type == UVM_PHASE_DOMAIN);endfunctionvirtual function void m_get_transitive_children(ref uvm_phase phases[$]);foreach (m_successors[succ])beginphases.push_back(succ);succ.m_get_transitive_children(phases);endendfunctionendclass//------------------------------------------------------------------------------//// 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 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 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 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 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 jump_to();return m_jump_to;endfunctionendclass//------------------------------------------------------------------------------//// 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 uvm_phase_cb extends uvm_callback;// Function: new// Constructorfunction 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 phase_state_change(uvm_phase phase,uvm_phase_state_change change);endfunctionendclass//------------------------------------------------------------------------------//// 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) uvm_phase_cb_pool;//------------------------------------------------------------------------------// IMPLEMENTATION//------------------------------------------------------------------------------typedef class 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//-----------------------------// newfunction 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;beginuvm_cmdline_processor clp = uvm_cmdline_processor::get_inst();string val;if (clp.get_arg_value("+UVM_PHASE_TRACE", val))m_phase_trace = 1;elsem_phase_trace = 0;if (clp.get_arg_value("+UVM_USE_OVM_RUN_SEMANTIC", val))m_use_ovm_run_semantic = 1;elsem_use_ovm_run_semantic = 0;endif (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;endendfunction// add// ---// TBD error checks if param nodes are actually in this schedule or notfunction 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) beginstring 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(),"'"})endif (before_phase != null && before_phase.get_phase_type() == UVM_PHASE_IMP) beginstring 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(),"'"})endif (after_phase != null && after_phase.get_phase_type() == UVM_PHASE_IMP) beginstring 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(),"'"})endif (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) beginuvm_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 nodesif ($cast(tp, phase)) beginif (new_node.get_name() == "run") beginnew_node.phase_done = uvm_test_done_objection::get();endelse beginnew_node.phase_done = uvm_objection::type_id::create({phase.get_name(), "_objection"});endendend// We are inserting an existing scheduleelse beginbegin_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) beginafter_phase = this;before_phase = m_end_node;end*/// If no before/after/with specified, insert at end of this scheduleif (with_phase == null && after_phase == null && before_phase == null) beginbefore_phase = m_end_node;endif (m_phase_trace) beginuvm_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' PHASEif (with_phase != null) beginbegin_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 PHASEelse if (before_phase != null && after_phase == null) beginbegin_node.m_predecessors = before_phase.m_predecessors;end_node.m_successors[before_phase] = 1;foreach (before_phase.m_predecessors[pred]) beginpred.m_successors.delete(before_phase);pred.m_successors[begin_node] = 1;endbefore_phase.m_predecessors.delete();before_phase.m_predecessors[end_node] = 1;end// INSERT AFTER PHASEelse if (before_phase == null && after_phase != null) beginend_node.m_successors = after_phase.m_successors;begin_node.m_predecessors[after_phase] = 1;foreach (after_phase.m_successors[succ]) beginsucc.m_predecessors.delete(after_phase);succ.m_predecessors[end_node] = 1;endafter_phase.m_successors.delete();after_phase.m_successors[begin_node] = 1;end// IN BETWEEN 'BEFORE' and 'AFTER' PHASESelse if (before_phase != null && after_phase != null) beginif (!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 succbegin_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)) beginafter_phase.m_successors.delete(before_phase);before_phase.m_successors.delete(after_phase);endend // if (before_phase != null && after_phase != null)// Transition nodes to DORMANT stateif (new_node == null)tmp_node = phase;elsetmp_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 domainreturn 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)) beginsched = sched.m_parent;s = {sched.get_name(),(s.len()>0?".":""),s};endreturn 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]) beginsucc.m_print_successors();endlevel--;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) beginreturn null ;endif (phase == m_imp || phase == this)return this;foreach (m_predecessors[pred]) beginuvm_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())) beginfound = pred.m_find_predecessor(phase,stay_in_scope,orig);if (found != null)return found;endendreturn 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]) beginuvm_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())) beginfound = pred.m_find_predecessor_by_name(name,stay_in_scope,orig);if (found != null)return found;endendreturn 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) beginreturn null ;endif (phase == m_imp || phase == this) beginreturn this;endforeach (m_successors[succ]) beginuvm_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())) beginfound = succ.m_find_successor(phase,stay_in_scope,orig);if (found != null) beginreturn found;endendendreturn 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]) beginuvm_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())) beginfound = succ.m_find_successor_by_name(name,stay_in_scope,orig);if (found != null)return found;endendreturn 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' functionalityreturn (!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' functionalityreturn (!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) nodesforeach (m_predecessors[pred])wait (pred.m_state == UVM_PHASE_DONE);// If DONE (by, say, a forward jump), return immedif (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()) beginforeach (m_sync[i]) beginwait (m_sync[i].m_state >= UVM_PHASE_SYNCING);endendm_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" executionif (m_phase_type != UVM_PHASE_NODE) beginstate_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;endelse 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) beginif (!$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 UPm_imp.traverse(top,this,UVM_PHASE_EXECUTING);endelse beginm_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_processbeginm_phase_proc = process::self();//-----------// EXECUTING: (task phases)//-----------task_phase.traverse(top,this,UVM_PHASE_EXECUTING);wait(0); // stay alive for later killendjoin_noneuvm_wait_for_nba_region(); //Give sequences, etc. a chance to object// Now wait for one of three criterion for end-of-phase.forkbegin // guardfork// JUMPbeginwait (m_premature_end);`UVM_PH_TRACE("PH/TRC/EXE/JUMP","PHASE EXIT ON JUMP REQUEST",this,UVM_DEBUG)end// WAIT_FOR_ALL_DROPPEDbeginbit do_ready_to_end ; // bit used for ready_to_end iterations// OVM semantic: don't end until objection raised or stop requestif (phase_done.get_objection_total(top) ||m_use_ovm_run_semantic && m_imp.get_name() == "run") beginif (!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)endelse beginif (m_phase_trace) `UVM_PH_TRACE("PH/TRC/SKIP","No objections raised, skipping phase",this,UVM_LOW)endwait_for_self_and_siblings_to_drop() ;do_ready_to_end = 1;//--------------// READY_TO_END://--------------while (do_ready_to_end) beginuvm_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 objectwait_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 loopendend// TIMEOUTbeginif (this.get_name() == "run") beginif (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) beginif (m_phase_trace)`UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)foreach (m_executing_phases[p]) beginif ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) beginif (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)endend`uvm_fatal("PH_TIMEOUT",$sformatf("Default timeout of %0t hit, indicating a probable testbench issue",`UVM_DEFAULT_TIMEOUT))endelse beginif (m_phase_trace)`UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)foreach (m_executing_phases[p]) beginif ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) beginif (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)endend`uvm_fatal("PH_TIMEOUT",$sformatf("Explicit timeout of %0t hit, indicating a probable testbench issue",top.phase_timeout))endif (m_phase_trace)`UVM_PH_TRACE("PH/TRC/EXE/3","PHASE EXIT TIMEOUT",this,UVM_DEBUG)end // if (this.get_name() == "run")else beginwait (0); // never unblock for non-run phaseendend // if (m_phase_trace)join_anydisable fork;endjoin // guardendendm_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 supportif (m_phase_type == UVM_PHASE_NODE) beginif(m_premature_end) beginif(m_jump_phase != null) beginstate_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);endelse 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 UPif (m_phase_trace)`UVM_PH_TRACE("PH_END","ENDING PHASE PREMATURELY",this,UVM_HIGH)endelse begin// WAIT FOR PREDECESSORS: // WAIT FOR PREDECESSORS:// function phases onlyif (task_phase == null)m_wait_for_pred();end//-------// ENDED://-------// execute 'phase_ended' callbacksif (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 threadsstate_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) beginm_phase_proc.kill();m_phase_proc = null;end#0; // LET ANY WAITERS WAKE UPif (phase_done != null)phase_done.clear();end//------// DONE://------m_premature_end = 0 ;if(m_jump_fwd || m_jump_bkwd) beginif(m_jump_fwd) beginclear_successors(UVM_PHASE_DONE,m_jump_phase);endm_jump_phase.clear_successors();endelse beginif (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 UPend#0; // LET ANY WAITERS WAKE UPif (phase_done != null)phase_done.clear();//-----------// SCHEDULED://-----------if(m_jump_fwd || m_jump_bkwd) beginvoid'(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 nowelse if (m_successors.size() == 0) begintop.m_phase_all_done=1;endelse begin// execute all the successorsforeach (m_successors[succ]) beginif(succ.m_state < UVM_PHASE_SCHEDULED) beginstate_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 UPvoid'(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)endendendendtaskfunction 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 begindone = 1;foreach (predecessors[p]) beginif (p.get_phase_type() != UVM_PHASE_NODE) beginpredecessors.delete(p);foreach (p.m_predecessors[next_p])predecessors[next_p] = 1;done = 0;endendend while (!done);pred = new [predecessors.size()];foreach (predecessors[p]) beginpred[idx++] = p;endendfunction : get_adjacent_predecessor_nodesfunction 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 begindone = 1;foreach (successors[s]) beginif (s.get_phase_type() != UVM_PHASE_NODE) beginsuccessors.delete(s);foreach (s.m_successors[next_s])successors[next_s] = 1;done = 0;endendend while (!done);succ = new [successors.size()];foreach (successors[s]) beginsucc[idx++] = s;endendfunction : get_adjacent_successor_nodes// Internal implementation, more efficient than calling get_predessor_nodes on all// of the successors returned by get_adjacent_successor_nodesfunction 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 successorsforeach (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 nodesdo begindone=1;foreach (pred_of_succ[pred]) beginif (pred.get_phase_type() != UVM_PHASE_NODE) beginpred_of_succ.delete(pred);foreach (pred.m_predecessors[next_pred])pred_of_succ[next_pred] = 1;done =0;endendend while (!done);// remove ourselves from the listpred_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 msgsforeach (pred_of_succ[sibling]) beginif (m_phase_trace) beginstring 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)endsibling.wait_for_state(UVM_PHASE_READY_TO_END, UVM_GTE);if (m_phase_trace) beginstring 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)endendif (m_phase_trace) beginif (pred_of_succ.num()) beginstring 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)endelse begin`UVM_PH_TRACE("PH/TRC/WAIT_PRED_OF_SUCC","*** No pred to succ other than myself, so ending phase ***",this,UVM_HIGH)endend#0; // LET ANY WAITERS WAKE UPendtask//---------------------------------// 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")) beginif (count != 1)m_action = $sformatf("%s %0d objections", action, count);elsem_action = $sformatf("%s an objection", action);endelse if (action == "get_objection_count") beginm_action = "call get_objection_count";endif (this.get_phase_type() == UVM_PHASE_IMP) beginm_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);elsem_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);elsem_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 beginm_report_null_objection(obj, "" , 0, "get_objection_count");return 0;endendfunction : 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");endelse if (target == null) begin`uvm_fatal("PH_BADSYNC","sync() called with a null target domain");endelse if (!target.is_domain()) begin`uvm_fatal("PH_BADSYNC","sync() called with a non-domain phase schedule node as target");endelse if (phase == null && with_phase != null) begin`uvm_fatal("PH_BADSYNC","sync() called with null phase and non-null with phase");endelse if (phase == null) begin// whole domain sync - traverse this domain schedule from begin to end node and sync each nodeint visited[uvm_phase];uvm_phase queue[$];queue.push_back(this);visited[this] = 1;while (queue.size()) beginuvm_phase node;node = queue.pop_front();if (node.m_imp != null) beginsync(target, node.m_imp);endforeach (node.m_successors[succ]) beginif (!visited.exists(succ)) beginqueue.push_back(succ);visited[succ] = 1;endendendend else begin// single phase sync// this is a 2-way ('with') sync and we check first in case it is already thereuvm_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);endendfunction// 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 nodeint visited[uvm_phase];uvm_phase queue[$];queue.push_back(this);visited[this] = 1;while (queue.size()) beginuvm_phase node;node = queue.pop_front();if (node.m_imp != null) unsync(target,node.m_imp);foreach (node.m_successors[succ]) beginif (!visited.exists(succ)) beginqueue.push_back(succ);visited[succ] = 1;endendendend else begin// single phase unsync// this is a 2-way ('with') sync and we check first in case it is already thereuvm_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]);endendfunction// 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);endcaseendtask//-------------------------// 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) begind = m_find_successor(phase,0);if (d == null) beginstring 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);endelse beginm_jump_fwd = 1;`uvm_info("PH_JUMPF",$sformatf("jumping forward to phase %s", phase.get_name()),UVM_DEBUG);endendelse beginm_jump_bkwd = 1;`uvm_info("PH_JUMPB",$sformatf("jumping backward to phase %s", phase.get_name()),UVM_DEBUG);endm_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 jumpfunction 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 beyondfunction 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]) beginsucc.clear_successors(state, end_state);endendfunction//---------------------------------// 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]) beginsiblings[m_sync[i]] = 1;endwhile (need_to_check_all) beginneed_to_check_all = 0 ; //if all are dropped, we won't need to do this again// wait for own objections to dropif ((phase_done != null) && (phase_done.get_objection_total(top) != 0)) beginm_state = UVM_PHASE_EXECUTING ;phase_done.wait_for(UVM_ALL_DROPPED, top);need_to_check_all = 1 ;end// now wait for siblings to dropforeach(siblings[sib]) beginsib.wait_for_state(UVM_PHASE_EXECUTING, UVM_GTE); // sibling must be at least executingif ((sib.phase_done != null) && (sib.phase_done.get_objection_total(top) != 0)) beginm_state = UVM_PHASE_EXECUTING ;sib.phase_done.wait_for(UVM_ALL_DROPPED, top); // sibling must drop any objectionneed_to_check_all = 1 ;endendendendtask// kill// ----function void uvm_phase::kill();`uvm_info("PH_KILL", {"killing phase '", get_name(),"'"}, UVM_DEBUG);if (m_phase_proc != null) beginm_phase_proc.kill();m_phase_proc = null;endendfunction// 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/childrentask 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 domainbeginuvm_phase ph = uvm_domain::get_common_domain();void'(m_phase_hopper.try_put(ph));endforever beginuvm_phase phase;m_phase_hopper.get(phase);forkbeginphase.execute_phase();endjoin_none#0; // let the process start runningendendtask// 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)endelse begin`uvm_info("PH_TERMSTATE",$sformatf("phase %s has no outstanding objections",get_name()),UVM_DEBUG)endendfunction
