// //---------------------------------------------------------------------- // 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. ) 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 // 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 // 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 // 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 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 and operands, several 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 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 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; endfunction function uvm_phase 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 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 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; 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 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 // 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 . // 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; 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 uvm_phase_cb extends uvm_callback; // Function: new // Constructor function 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); 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) 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 //----------------------------- // 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