blob: 6262ac9ddd9f91ced3dbc9ff6386881643f3d658 [file] [log] [blame]
//
//------------------------------------------------------------------------------
// Copyright 2007-2011 Mentor Graphics Corporation
// Copyright 2007-2009 Cadence Design 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.
//------------------------------------------------------------------------------
`ifndef OVM_ROOT_SVH
`define OVM_ROOT_SVH
`define OVM_DEFAULT_TIMEOUT 9200s
//------------------------------------------------------------------------------
//
// CLASS: ovm_root
//
// The ~ovm_root~ class serves as the implicit top-level and phase controller for
// all OVM components. Users do not directly instantiate ~ovm_root~. The OVM
// automatically creates a single instance of <ovm_root> that users can
// access via the global (ovm_pkg-scope) variable, ~ovm_top~.
//
// (see ovm_ref_root.gif)
//
// The ~ovm_top~ instance of ~ovm_root~ plays several key roles in the OVM.
//
// Implicit top-level - The ~ovm_top~ serves as an implicit top-level component.
// Any component whose parent is specified as NULL becomes a child of ~ovm_top~.
// Thus, all OVM components in simulation are descendants of ~ovm_top~.
//
// Phase control - ~ovm_top~ manages the phasing for all components.
// There are eight phases predefined in every component: build, connect,
// end_of_elaboration, start_of_simulation, run, extract, check, and
// report. Of these, only the run phase is a task. All others are
// functions. OVM's flexible phasing mechanism allows users to insert
// any number of custom function and task-based phases.
// See <run_test>, <insert_phase>, and <stop_request>, and others.
//
// Search - Use ~ovm_top~ to search for components based on their
// hierarchical name. See <find> and <find_all>.
//
// Report configuration - Use ~ovm_top~ to globally configure
// report verbosity, log files, and actions. For example,
// ~ovm_top.set_report_verbosity_level_hier(OVM_FULL)~ would set
// full verbosity for all components in simulation.
//
// Global reporter - Because ~ovm_top~ is globally accessible (in ovm_pkg
// scope), OVM's reporting mechanism is accessible from anywhere
// outside ~ovm_component~, such as in modules and sequences.
// See <ovm_report_error>, <ovm_report_warning>, and other global
// methods.
//
//------------------------------------------------------------------------------
class ovm_root extends ovm_component;
extern static function ovm_root get();
// Task: run_test
//
// Phases all components through all registered phases. If the optional
// test_name argument is provided, or if a command-line plusarg,
// +OVM_TESTNAME=TEST_NAME, is found, then the specified component is created
// just prior to phasing. The test may contain new verification components or
// the entire testbench, in which case the test and testbench can be chosen from
// the command line without forcing recompilation. If the global (package)
// variable, finish_on_completion, is set, then $finish is called after
// phasing completes.
extern virtual task run_test (string test_name="");
// Function- run_global_phase
//
// Note: all phasing should be started via run_test. This method is used to
// run through (~upto~=0) or up to (~upto~=1) the given ~phase~. If null, then
// all remaining phases will be run, effectively completing simulation.
extern task run_global_phase (ovm_phase phase=null, bit upto=0);
// Function- run_global_func_phase
//
// Note: all phasing should be started via run_test. This method is used to
// run through (~upto~=0) or up to (~upto~=1) the given ~phase~. If null, then
// all remaining phases will be run, effectively completing simulation.
extern function void run_global_func_phase (ovm_phase phase=null, bit upto=0);
// Function: stop_request
//
// Calling this function triggers the process of shutting down the currently
// running task-based phase. This process involves calling all components'
// stop tasks for those components whose enable_stop_interrupt bit is set.
// Once all stop tasks return, or once the optional global_stop_timeout
// expires, all components' kill method is called, effectively ending the
// current phase. The ovm_top will then begin execution of the next phase,
// if any.
extern function void stop_request();
// Function: in_stop_request
//
// This function returns 1 if a stop request is currently active, and 0
// otherwise.
extern function bit in_stop_request();
// Function: insert_phase
//
// Inserts a new phase given by new_phase _after_ the existing phase given by
// exist_phase. The ovm_top maintains a queue of phases executed in
// consecutive order. If exist_phase is null, then new_phase is inserted at
// the head of the queue, i.e., it becomes the first phase.
extern function void insert_phase (ovm_phase new_phase,
ovm_phase exist_phase);
// Function: find
extern function ovm_component find (string comp_match);
// Function: find_all
//
// Returns the component handle (find) or list of components handles
// (find_all) matching a given string. The string may contain the wildcards,
// * and ?. Strings beginning with '.' are absolute path names. If optional
// comp arg is provided, then search begins from that component down
// (default=all components).
extern function void find_all (string comp_match,
ref ovm_component comps[$],
input ovm_component comp=null);
// Function: get_current_phase
//
// Returns the handle of the currently executing phase.
extern function ovm_phase get_current_phase ();
// Function: get_phase_by_name
//
// Returns the handle of the phase having the given ~name~.
extern function ovm_phase get_phase_by_name (string name);
virtual function string get_type_name(); return "ovm_root"; endfunction
// Variable: phase_timeout
time phase_timeout = `OVM_DEFAULT_TIMEOUT;
// Variable: stop_timeout
//
// These set watchdog timers for task-based phases and stop tasks.
// The timeout is specified in absolute time.
// The default timeout is set by the OVM_DEFAULT_TIMEOUT define.
// A timeout at this value usually indicates a problem with your
// testbench, or failure to call <global_stop_request>.
// When set to 0, the timeout is disabled.
time stop_timeout = `OVM_DEFAULT_TIMEOUT;
// Variable: enable_print_topology
//
// If set, then the entire testbench topology is printed just after completion
// of the end_of_elaboration phase.
bit enable_print_topology = 0;
// Variable: finish_on_completion
//
// If set, then run_test will call $finish after all phases are executed.
bit finish_on_completion = 1;
// PRIVATE members
extern `_protected function new ();
extern function void check_verbosity();
extern local function void m_do_phase (ovm_component comp, ovm_phase phase);
extern local task m_stop_process ();
extern local task m_stop_request (time timeout=0);
extern local task m_do_stop_all (ovm_component comp);
extern local function void m_reset_phase(ovm_component comp,
ovm_phase phase=null);
extern local function ovm_phase m_get_phase_master(ovm_phase phase, bit set=0);
local ovm_phase m_phase_master[ovm_phase];
local ovm_phase m_phase_q[ovm_phase];
local ovm_phase m_first_phase = null;
local ovm_phase m_last_phase = null;
local event m_stop_request_e;
ovm_phase m_curr_phase = null;
static local ovm_root m_inst;
// For communicating all objections dropped.
bit m_objections_outstanding = 0;
bit m_in_stop_request = 0;
bit m_executing_stop_processes = 0;
extern virtual task all_dropped (ovm_objection objection,
ovm_object source_obj, int count);
extern virtual function void raised (ovm_objection objection,
ovm_object source_obj, int count);
/*** DEPRECATED - Do not use in new code. Convert code when appropriate ***/
extern function void print_unit_list (ovm_component comp=null);
extern function void print_unit (string name, ovm_printer printer=null);
extern function void print_units (ovm_printer printer=null);
extern function void print_topology (ovm_printer printer=null);
endclass
// Class- ovm_root_report_handler
//
class ovm_root_report_handler extends ovm_report_handler;
virtual function void report(
ovm_severity severity,
string name,
string id,
string message,
int verbosity_level,
string filename,
int line,
ovm_report_object client
);
if(name == "")
name = "reporter";
super.report(severity, name, id, message, verbosity_level, filename, line, client);
endfunction
endclass
//------------------------------------------------------------------------------
//
// Class - ovm_*_phase (predefined phases)
//
//------------------------------------------------------------------------------
`ovm_phase_func_decl(build,1)
`ovm_phase_func_decl(connect,0)
`ovm_phase_func_decl(end_of_elaboration,0)
`ovm_phase_func_decl(start_of_simulation,0)
`ovm_phase_task_decl(run,0)
`ovm_phase_func_decl(extract,0)
`ovm_phase_func_decl(check,0)
`ovm_phase_func_decl(report,0)
build_phase #(ovm_component) build_ph;
connect_phase #(ovm_component) connect_ph;
end_of_elaboration_phase #(ovm_component) end_of_elaboration_ph;
start_of_simulation_phase #(ovm_component) start_of_simulation_ph;
run_phase #(ovm_component) run_ph;
extract_phase #(ovm_component) extract_ph;
check_phase #(ovm_component) check_ph;
report_phase #(ovm_component) report_ph;
// DEPRECATED PHASES - DO NOT USE IN NEW CODE
`ovm_phase_func_decl(post_new,0)
`ovm_phase_func_decl(export_connections,0)
`ovm_phase_func_decl(import_connections,1)
`ovm_phase_func_decl(pre_run,0)
`ovm_phase_func_decl(configure,0)
post_new_phase #(ovm_component) post_new_ph;
export_connections_phase #(ovm_component) export_connections_ph;
import_connections_phase #(ovm_component) import_connections_ph;
pre_run_phase #(ovm_component) pre_run_ph;
configure_phase #(ovm_component) configure_ph;
//-----------------------------------------------------------------------------
//
// IMPLEMENTATION
//
//-----------------------------------------------------------------------------
// get
// ---
function ovm_root ovm_root::get();
if (m_inst == null)
m_inst = new();
return m_inst;
endfunction
// new
// ---
function ovm_root::new();
ovm_root_report_handler rh;
super.new("__top__", null);
rh = new;
set_report_handler(rh);
check_verbosity();
report_header();
print_enabled=0;
build_ph = new;
post_new_ph = new;
export_connections_ph = new;
connect_ph = new;
import_connections_ph = new;
configure_ph = new;
end_of_elaboration_ph = new;
start_of_simulation_ph = new;
pre_run_ph = new;
run_ph = new;
extract_ph = new;
check_ph = new;
report_ph = new;
insert_phase(build_ph, null);
insert_phase(post_new_ph, build_ph);
insert_phase(export_connections_ph, post_new_ph);
insert_phase(connect_ph, export_connections_ph);
insert_phase(import_connections_ph, connect_ph);
insert_phase(configure_ph, import_connections_ph);
insert_phase(end_of_elaboration_ph, configure_ph);
insert_phase(start_of_simulation_ph,end_of_elaboration_ph);
insert_phase(pre_run_ph, start_of_simulation_ph);
insert_phase(run_ph, pre_run_ph);
insert_phase(extract_ph, run_ph);
insert_phase(check_ph, extract_ph);
insert_phase(report_ph, check_ph);
endfunction
// check_verbosity
// ---------------
function void ovm_root::check_verbosity();
string s;
int plusarg;
string msg;
int verbosity= OVM_MEDIUM;
case(1)
$value$plusargs("OVM_VERBOSITY=%s", s) > 0 : plusarg = 1;
$value$plusargs("ovm_verbosity=%s", s) > 0 : plusarg = 1;
$value$plusargs("VERBOSITY=%s", s) > 0 : plusarg = 1;
$value$plusargs("verbosity=%s", s) > 0 : plusarg = 1;
default : plusarg = 0;
endcase
if(plusarg == 1) begin
case(s.toupper())
"OVM_NONE" : verbosity = OVM_NONE;
"NONE" : verbosity = OVM_NONE;
"OVM_LOW" : verbosity = OVM_LOW;
"LOW" : verbosity = OVM_LOW;
"LO" : verbosity = OVM_LOW;
"OVM_MEDIUM" : verbosity = OVM_MEDIUM;
"OVM_MED" : verbosity = OVM_MEDIUM;
"MEDIUM" : verbosity = OVM_MEDIUM;
"MED" : verbosity = OVM_MEDIUM;
"OVM_HIGH" : verbosity = OVM_HIGH;
"OVM_HI" : verbosity = OVM_HIGH;
"HIGH" : verbosity = OVM_HIGH;
"HI" : verbosity = OVM_HIGH;
"OVM_FULL" : verbosity = OVM_FULL;
"FULL" : verbosity = OVM_FULL;
"OVM_DEBUG" : verbosity = OVM_DEBUG;
"DEBUG" : verbosity = OVM_DEBUG;
default : begin
verbosity = s.atoi();
if(verbosity == 0) begin
verbosity = OVM_MEDIUM;
$sformat(msg, "illegal verbosity value, using default of %0d",
OVM_MEDIUM);
ovm_report_warning("verbosity", msg, OVM_NONE);
end
end
endcase
end
set_report_verbosity_level_hier(verbosity);
endfunction
//------------------------------------------------------------------------------
// Variable: ovm_top
//
// This is the top-level that governs phase execution and provides component
// search interface. See <ovm_root> for more information.
`const ovm_root ovm_top = ovm_root::get();
// for backward compatibility
`const ovm_root _global_reporter = ovm_root::get();
//------------------------------------------------------------------------------
/* deprecated */ ovm_component ovm_test_top;
/* deprecated */ bit ovm_enable_print_topology = 0;
//------------------------------------------------------------------------------
//
// Primary Simulation Entry Points
//
//------------------------------------------------------------------------------
// run_test
// --------
task ovm_root::run_test(string test_name="");
ovm_factory factory = ovm_factory::get();
bit testname_plusarg;
string msg;
testname_plusarg = 0;
// plusarg overrides argument
if ($value$plusargs("OVM_TESTNAME=%s", test_name))
testname_plusarg = 1;
if ($value$plusargs("TESTNAME=%s", test_name)) begin
ovm_report_warning("DPRFT",
"+TESTNAME is deprecated. Use +OVM_TESTNAME instead.", OVM_NONE);
testname_plusarg = 1;
end
// if test now defined, create it using common factory
if (test_name != "") begin
if(m_children.exists("ovm_test_top")) begin
ovm_report_fatal("TTINST",
"An ovm_test_top already exists via a previous call to run_test", OVM_NONE);
#0; // forces shutdown because $finish is forked
end
$cast(ovm_test_top, factory.create_component_by_name(test_name,
"ovm_test_top", "ovm_test_top", null));
if (ovm_test_top == null) begin
msg = testname_plusarg ? "command line +OVM_TESTNAME=": "call to run_test(";
ovm_report_fatal("INVTST",
{"Requested test from ",msg, test_name, ") not found." }, OVM_NONE);
end
end
if (m_children.num() == 0) begin
ovm_report_fatal("NOCOMP",
{"No components instantiated. You must instantiate",
" at least one component before calling run_test. To run",
" a test, use +OVM_TESTNAME or supply the test name in",
" the argument to run_test(). Exiting simulation."}, OVM_NONE);
return;
end
ovm_report_info("RNTST", {"Running test ",test_name, "..."}, OVM_LOW);
fork
// isolated from calling process
run_global_phase();
join_any
report_summarize();
if (finish_on_completion) begin
// forking allows current delta to complete
fork
$finish;
join_none
end
endtask
// m_reset_phase
// -------------
function void ovm_root::m_reset_phase(ovm_component comp, ovm_phase phase=null);
string name;
if (comp.get_first_child(name))
do
this.m_reset_phase(comp.get_child(name));
while (comp.get_next_child(name));
comp.m_curr_phase=phase;
endfunction
// m_get_phase_master
// ------------------
function ovm_phase ovm_root::m_get_phase_master(ovm_phase phase, bit set=0);
// Returns the master phase if one hase been initialized. Otherwise, finds
// a master by name. If none is found then the master is initialized
// to itself.
if(phase == null) return phase;
if(m_phase_master.exists(phase)) return m_phase_master[phase];
foreach(m_phase_master[i])
if(m_phase_master[i].get_name() == phase.get_name()) begin
if(set == 1) m_phase_master[phase] = m_phase_master[i];
return m_phase_master[i];
end
if(set == 1) m_phase_master[phase] = phase;
return phase;
endfunction
//------------------------------------------------------------------------------
// Phase control
//------------------------------------------------------------------------------
// run_global_func_phase
// ---------------------
// Limitations on usage:
//
// Given phase can not be ahead of any un-executed task-based phases.
//
// The #0 after triggering the phase's start and done events can not occur
// in a function. Any processes waiting on a function-based phase to start
// or finish will not resume until all phases up through the given phase
// have executed.
function void ovm_root::run_global_func_phase(ovm_phase phase=null, bit upto=0);
time timeout;
bit run_all_phases;
//Get the master phase in case the input phase is an alias.
phase = m_get_phase_master(phase);
if (phase != null) begin
if (!m_phase_q.exists(phase)) begin
ovm_report_fatal("PHNTFD", {"Phase %0s not registered.",phase.get_name()}, OVM_NONE);
return;
end
if (upto) begin
ovm_phase prev_ph;
if (phase == m_first_phase)
return;
prev_ph = m_first_phase;
while (phase != m_phase_q[prev_ph])
prev_ph = m_phase_q[prev_ph];
phase = prev_ph;
end
// make sure we've something to do
if (phase.is_done()) begin
ovm_report_warning("PHDONE", {"Phase ", phase.get_name()," already executed."}, OVM_NONE);
return;
end
end
else begin
run_all_phases = 1;
phase = m_last_phase;
end
while (m_curr_phase != phase) begin
if (m_curr_phase == null)
m_curr_phase = m_first_phase;
else
m_curr_phase = m_phase_q[m_curr_phase];
if (m_curr_phase.is_task()) begin
ovm_report_fatal("TASKPH",
{"Phase ", m_curr_phase.get_name(),
" is a time-consuming method. Cannot be run using",
" ovm_root::run_global_phase_func()"}, OVM_NONE);
return;
end
// Trigger phase's in_progress event.
m_curr_phase.m_set_in_progress();
// #0; can't call this in a func
ovm_report_info("STARTPH",
$psprintf("STARTING PHASE %0s",m_curr_phase.get_name()),int'(OVM_FULL)+1);
m_do_phase(this,m_curr_phase);
ovm_report_info("ENDPH",
$psprintf("ENDING PHASE %0s",m_curr_phase.get_name()),int'(OVM_FULL)+1);
// Trigger phase's done event.
m_curr_phase.m_set_done();
// #0; can't call this in a func
// If error occurred during elaboration, exit with FATAL.
if (m_curr_phase == end_of_elaboration_ph) begin
ovm_report_server srvr;
srvr = get_report_server();
if(srvr.get_severity_count(OVM_ERROR) > 0) begin
ovm_report_fatal("ovm", "elaboration errors", OVM_NONE);
//#0; // $finish is called in a forked process in ovm_report_object::die.
// this forces that process to start, preventing us from continuing
return;
end
if (enable_print_topology || ovm_enable_print_topology)
print_topology();
end
// if next phase is end_of_elab, the resolve all connections
if (m_phase_q[m_curr_phase] == end_of_elaboration_ph)
do_resolve_bindings();
if (run_all_phases)
phase = m_last_phase;
end
endfunction
// run_global_phase
// ----------------
task ovm_root::run_global_phase(ovm_phase phase=null, bit upto=0);
static semaphore sema=new(1);
time timeout;
bit run_all_phases;
sema.get();
//Get the master phase in case the input phase is an alias.
phase = m_get_phase_master(phase);
if (phase != null) begin
if (!m_phase_q.exists(phase)) begin
ovm_report_fatal("PHNTFD", {"Phase ", phase.get_name()," not registered."}, OVM_NONE);
return;
end
// if only running up to the given phase, run through previous phase
if (upto) begin
ovm_phase prev_ph;
if (phase == m_first_phase)
return;
prev_ph = m_first_phase;
while (phase != m_phase_q[prev_ph])
prev_ph = m_phase_q[prev_ph];
phase = prev_ph;
end
// make sure we've something to do
if (phase.is_done()) begin
ovm_report_warning("PHDONE", {"Phase ", phase.get_name()," already executed."}, OVM_NONE);
return;
end
end
else begin
run_all_phases = 1;
phase = m_last_phase;
end
// MAIN LOOP: Executes all phases from the current phase
// through the phase given in the argument. If 'phase' is null,
// will run through all phases, even those that may have been added in
// phases that have yet to run.
while (m_curr_phase != phase) begin
if (m_curr_phase == null)
m_curr_phase = m_first_phase;
else
m_curr_phase = m_phase_q[m_curr_phase];
// Trigger phase's in_progress event.
// The #0 allows any waiting processes to resume before we continue.
m_curr_phase.m_set_in_progress();
#0;
ovm_report_info("STARTPH",
$psprintf("STARTING PHASE %0s",m_curr_phase.get_name()),int'(OVM_FULL)+1);
// TASK-based phase
if (m_curr_phase.is_task()) begin
timeout = phase_timeout;
// timeout is absolute time
if ($time > 0)
if (timeout > $time)
timeout = timeout - $time;
else
timeout = 1;
`ifdef INCA
`define ALT_PHASING
`endif
`ifdef ALT_PHASING
// IUS does not support disabling named fork blocks, so we isolate the
// inner fork block so we can kill it using disable fork
fork // guard process
begin
fork
// Start an independent process that kills the phase, else the killing
// stops prematurely at the component that issued the request.
m_stop_process();
begin // guard process
fork
begin
#0; // ensures stop_process active before potential stop_request
m_do_phase(this,m_curr_phase);
wait fork;
end
begin
if (timeout == 0)
wait(timeout != 0);
#timeout ovm_report_error("TIMOUT",
$psprintf("Watchdog timeout of '%0t' expired.", timeout), OVM_NONE);
end
join_any
disable fork;
end // end guard process
join_any
disable fork;
end
join // end guard process
`else // QUESTA
fork : task_based_phase
m_stop_process();
begin
#0 m_do_phase(this,m_curr_phase);
wait fork;
end
begin
if (timeout == 0)
wait(timeout != 0);
#timeout ovm_report_error("TIMOUT",
$psprintf("Watchdog timeout of '%0t' expired.", timeout), OVM_NONE);
end
join_any
disable task_based_phase;
`endif // INCA-QUESTA
if(ovm_test_done.get_objection_total(ovm_root::get()) != 0) begin
ovm_test_done.ovm_report_warning("OBJOUT", $psprintf("%0d objection(s) are still outstanding", ovm_test_done.get_objection_total(ovm_root::get())));
ovm_report_info("SHOW_OBJECTIONS",ovm_test_done.convert2string());
end
end // if (is_task)
// FUNCTION-based phase
else begin
m_do_phase(this,m_curr_phase);
end
ovm_report_info("ENDPH",
$psprintf("ENDING PHASE %0s",m_curr_phase.get_name()),int'(OVM_FULL)+1);
// Trigger phase's done event.
// The #0 allows any waiting processes to resume before we continue.
m_curr_phase.m_set_done();
#0;
// If error occurred during elaboration, exit with FATAL.
if (m_curr_phase == end_of_elaboration_ph) begin
ovm_report_server srvr;
srvr = get_report_server();
if(srvr.get_severity_count(OVM_ERROR) > 0) begin
ovm_report_fatal("ovm", "elaboration errors", OVM_NONE);
#0; // $finish is called in a forked process in ovm_report_object::die.
// this forces that process to start, preventing us from continuing
end
if (enable_print_topology || ovm_enable_print_topology)
print_topology();
end
// if next phase is end_of_elab, the resolve all connections
if (m_phase_q[m_curr_phase] == end_of_elaboration_ph)
do_resolve_bindings();
if (m_curr_phase == report_ph)
check_config_usage();
if (run_all_phases)
phase = m_last_phase;
end
sema.put();
endtask
// m_do_phase
// --------
function void ovm_root::m_do_phase (ovm_component comp, ovm_phase phase);
// run_global_phase calls this private function for each phase in consecutive
// order. If the phase is a function, then all components' functions are
// called sequentially in top-down or bottom-up order. If the phase is a
// task, all components' tasks are forked and we return with no waiting.
// The caller can subsequently call 'wait fork' to wait for the forked
// tasks to complete.
ovm_phase curr_phase;
bit done[string];
phase = m_get_phase_master(phase);
curr_phase = comp.m_curr_phase;
// This while loop is needed in case new componenents are created
// several phases into a simulation.
while (curr_phase != phase) begin
ovm_phase ph;
done.delete();
if (curr_phase == null)
curr_phase = m_first_phase;
else
curr_phase = m_phase_q[curr_phase];
// bottom-up
if (!curr_phase.is_top_down()) begin
string name;
if (comp.get_first_child(name)) begin
do begin
this.m_do_phase(comp.get_child(name),curr_phase);
done[name] = 1;
end
while (comp.get_next_child(name));
end
end
ovm_report_info("COMPPH", $psprintf("*** comp %0s (%0s) curr_phase is %0s",
comp.get_full_name(),comp.get_type_name(),
curr_phase.get_name()),int'(OVM_FULL)+1);
if (curr_phase.is_task()) begin
// We fork here to ensure that do_task_phase, a user-overridable task,
// does not inadvertently block this process
fork
comp.do_task_phase(curr_phase);
join_none
end
else
comp.do_func_phase(curr_phase);
// bottom-up 2nd pass: phase newly created components, if any
if (!curr_phase.is_top_down()) begin
while (comp.get_num_children() != done.num()) begin
string name;
if (comp.get_first_child(name)) begin
do begin
if (!done.exists(name)) begin
this.m_do_phase(comp.get_child(name),curr_phase);
done[name] = 1;
end
end
while (comp.get_next_child(name));
end
end
end
// top-down
else begin
string name;
if (comp.get_first_child(name))
do begin
this.m_do_phase(comp.get_child(name),curr_phase);
end
while (comp.get_next_child(name));
end
end
endfunction
// get_current_phase
// -----------------
function ovm_phase ovm_root::get_current_phase();
return m_curr_phase;
endfunction
//------------------------------------------------------------------------------
// Stopping
//------------------------------------------------------------------------------
// stop_request
// ------------
function void ovm_root::stop_request();
->m_stop_request_e;
endfunction
// m_stop_process
// --------------
task ovm_root::m_stop_process();
@m_stop_request_e;
m_stop_request(stop_timeout);
endtask
// in_stop_request
// ---------------
function bit ovm_root::in_stop_request();
return m_in_stop_request;
endfunction
// m_stop_request
// --------------
task ovm_root::m_stop_request(time timeout=0);
// timeout is absolute time
if ($time > 0)
if (timeout > $time)
timeout = timeout - $time;
else
timeout = 1;
// stop request valid for running task-based phases only
if (m_curr_phase == null || !m_curr_phase.is_task()) begin
ovm_report_warning("STPNA",
$psprintf("Stop-request has no effect outside non-time-consuming phases%s%s",
"current phase is ",m_curr_phase==null?
"none (not started":m_curr_phase.get_name()), OVM_NONE);
return;
end
m_in_stop_request=1;
// All stop tasks are forked from a single thread so 'wait fork'
// can be used. We fork the single thread as well so that 'wait fork'
// does not wait for threads previously started by the caller's thread.
// IUS does not support disabling named fork blocks, so we isolate the
// inner fork block so we can kill it using disable fork
`ifdef INCA
fork begin // guard process
fork
begin
//If objections are outstanding, wait for them to finish first
wait(m_objections_outstanding==0);
m_executing_stop_processes = 1;
m_do_stop_all(this);
wait fork;
m_executing_stop_processes = 0;
end
begin
if (timeout == 0)
wait(timeout != 0);
#timeout ovm_report_warning("STPTO",
$psprintf("Stop-request timeout of %0t expired. Stopping phase '%0s'",
timeout, m_curr_phase.get_name()), OVM_NONE);
end
join_any
disable fork;
end
join
`else // QUESTA
fork : stop_tasks
begin
//If objections are outstanding, wait for them to finish first
wait(m_objections_outstanding==0);
m_executing_stop_processes = 1;
m_do_stop_all(this);
wait fork;
m_executing_stop_processes = 0;
end
begin
if (timeout == 0)
wait(timeout != 0);
#timeout ovm_report_warning("STPTO",
$psprintf("Stop-request timeout of %0t expired. Stopping phase '%0s'",
timeout, m_curr_phase.get_name()), OVM_NONE);
end
join_any
disable stop_tasks;
`endif // INCA
// all stop processes have completed, or a timeout has occured
this.do_kill_all();
m_in_stop_request=0;
endtask
// m_do_stop_all
// -------------
task ovm_root::m_do_stop_all(ovm_component comp);
string name;
// we use an external traversal to ensure all forks are
// made from a single threaad.
if (comp.get_first_child(name))
do begin
m_do_stop_all(comp.get_child(name));
end
while (comp.get_next_child(name));
if (comp.enable_stop_interrupt) begin
fork begin
comp.stop(m_curr_phase.get_name());
end
join_none
end
endtask
// This objection is used to communicate all objections dropped at the
// root level so that the ovm_top can start the shutdown.
// Function: raised
//
//
function void ovm_root::raised (ovm_objection objection, ovm_object source_obj,
int count);
if (m_executing_stop_processes)
ovm_report_warning("ILLRAISE", "An ovm_test_done objection was raised during the execution of component stop processes for the stop_request. The objection is ignored by the stop process.", OVM_NONE);
else
m_objections_outstanding = 1;
endfunction
// Task: all_dropped
//
//
task ovm_root::all_dropped (ovm_objection objection, ovm_object source_obj,
int count);
m_objections_outstanding = 0;
endtask
//------------------------------------------------------------------------------
// Phase Insertion
//------------------------------------------------------------------------------
// insert_phase
// ------------
function void ovm_root::insert_phase(ovm_phase new_phase,
ovm_phase exist_phase);
ovm_phase exist_ph;
ovm_phase master_ph;
string s;
// Get the phase object that is in charge of a given named phase. Since we
// are inserting the phase, set the master if not set.
master_ph = m_get_phase_master(new_phase, 1);
exist_phase = m_get_phase_master(exist_phase);
if (build_ph.is_done()) begin
ovm_report_fatal("PHINST", "Phase insertion after build phase prohibited.", OVM_NONE);
return;
end
if (exist_phase != null && exist_phase.is_done() ||
exist_phase == null && m_curr_phase != null) begin
ovm_report_fatal("PHINST", {"Can not insert a phase at a point that has ",
"already executed. Current phase is '", m_curr_phase.get_name(),"'."}, OVM_NONE);
return;
end
if (new_phase == null) begin
ovm_report_fatal("PHNULL", "Phase argument is null.", OVM_NONE);
return;
end
if (exist_phase != null && !m_phase_q.exists(exist_phase)) begin
//could be an aliased phase. The phase may not exist in the queue, but if
//the name matches one in the queue then it is a possible alias
if(get_phase_by_name(exist_phase.get_name()) == null) begin
ovm_report_fatal("PHNTFD", {"Phase '",exist_phase.get_name(),
"' is not registered."}, OVM_NONE);
return;
end
end
// If the new phase being added is an alias object, add the alias and
// return.
if(master_ph != new_phase) begin
master_ph.add_alias(new_phase, exist_phase);
return;
end
if (m_phase_q.exists(new_phase)) begin
if ((exist_phase == null && m_first_phase != new_phase) ||
(exist_phase != null && m_phase_q[exist_phase] != new_phase)) begin
ovm_report_error("PHDUPL", {"Phase '", new_phase.get_name(),
"' is already registered in a different order."}, OVM_NONE);
end
return;
end
new_phase.set_insertion_phase(exist_phase);
if (exist_phase == null) begin
m_phase_q[new_phase] = m_first_phase;
m_first_phase = new_phase;
end
else begin
m_phase_q[new_phase] = m_phase_q[exist_phase];
m_phase_q[exist_phase] = new_phase;
end
if (m_phase_q[new_phase] == null)
m_last_phase = new_phase;
endfunction
// get_phase_by_name
// -----------------
function ovm_phase ovm_root::get_phase_by_name (string name);
ovm_phase m_ph;
foreach (m_phase_q[ph]) begin
m_ph = ph;
if(m_ph.get_name() == name)
return ph;
end
return null;
endfunction
//------------------------------------------------------------------------------
// Component Search & Printing
//------------------------------------------------------------------------------
// find_all
// --------
function void ovm_root::find_all(string comp_match, ref ovm_component comps[$],
input ovm_component comp=null);
string name;
if (comp==null)
comp = this;
if (comp.get_first_child(name))
do begin
this.find_all(comp_match,comps,comp.get_child(name));
end
while (comp.get_next_child(name));
if (ovm_is_match(comp_match, comp.get_full_name()) &&
comp.get_name() != "") /* ovm_top */
comps.push_back(comp);
endfunction
// find
// ----
function ovm_component ovm_root::find (string comp_match);
ovm_component comp_list[$];
find_all(comp_match,comp_list);
if (comp_list.size() > 1)
ovm_report_warning("MMATCH",
$psprintf("Found %0d components matching '%s'. Returning first match, %0s.",
comp_list.size(),comp_match,comp_list[0].get_full_name()), OVM_NONE);
if (comp_list.size() == 0) begin
ovm_report_warning("CMPNFD",
{"Component matching '",comp_match,
"' was not found in the list of ovm_components"}, OVM_NONE);
return null;
end
return comp_list[0];
endfunction
// print_topology
// --------------
function void ovm_root::print_topology(ovm_printer printer=null);
string s;
ovm_report_info("OVMTOP", "OVM testbench topology:", OVM_LOW);
if (m_children.num()==0) begin
ovm_report_warning("EMTCOMP", "print_topology - No OVM components to print.", OVM_NONE);
return;
end
if (printer==null)
printer = ovm_default_printer;
if (printer.knobs.sprint)
s = printer.m_string;
foreach (m_children[c]) begin
if(m_children[c].print_enabled) begin
printer.print_object("", m_children[c]);
if(printer.knobs.sprint)
s = {s, printer.m_string};
end
end
printer.m_string = s;
endfunction
//------------------------------------------------------------------------------
//
// REVIEW FOR DEPRECATION OR REMOVAL
//
//------------------------------------------------------------------------------
// print_unit
// ----------
function void ovm_root::print_unit (string name, ovm_printer printer=null);
ovm_component comp;
static bit issued=0;
if (!issued) begin
issued=1;
ovm_report_warning("deprecated",
{"ovm_root::print_unit() is an internal method that has been deprecated.",
" It is replaced by comp=ovm_top.find(name); comp.print(printer);"}, OVM_NONE);
end
comp = find(name);
if (comp != null)
comp.print(printer);
endfunction
// print_units
// -----------
function void ovm_root::print_units (ovm_printer printer=null);
string s;
static bit issued=0;
if (!issued) begin
issued=1;
ovm_report_warning("deprecated",
{"ovm_root::print_units() is an internal method that ",
"has been deprecated. It can be replaced by ovm_top.print(printer);"}, OVM_NONE);
end
print_topology(printer);
endfunction
// print_unit_list
// ---------------
function void ovm_root::print_unit_list(ovm_component comp=null);
string name;
static bit issued=0;
if (!issued) begin
issued=1;
ovm_report_warning("deprecated",
{"ovm_root::print_unit_list() is an internal method that ",
"has been deprecated."}, OVM_NONE);
end
if (comp==null) begin
comp = this;
if (m_children.num()==0) begin
ovm_report_warning("NOUNIT","No OVM components to print. ", OVM_NONE);
return;
end
$display("List of ovm components");
end
else begin
$display("%s (%s)", comp.get_full_name(), comp.get_type_name());
end
if (comp.get_first_child(name))
do begin
this.print_unit_list(comp.get_child(name));
end
while (comp.get_next_child(name));
endfunction
`endif //OVM_ROOT_SVH