blob: 13fa8d893d52e76ecf8600b5faa4404c17fd9c40 [file] [log] [blame]
//----------------------------------------------------------------------
// Copyright 2007-2011 Mentor Graphics Corp.
// 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_OBJECTION_SVH
`define OVM_OBJECTION_SVH
//Assumes access to ovm via an import of ovm_pkg or `include of ovm.svh
typedef class ovm_objection;
typedef class ovm_sequence_base;
//------------------------------------------------------------------------------
//
// Class: ovm_objection
//
//------------------------------------------------------------------------------
// Objections provide a facility for coordinating status information between
// two or more participating components, objects, and even module-based IP.
// In particular, the ~ovm_test_done~ built-in objection provides a means for
// coordinating when to end a test, i.e. when to call <global_stop_request> to
// end the <ovm_component::run> phase. When all participating components have
// dropped their raised objections with ~ovm_test_done~, an implicit call to
// ~global_stop_request~ is issued.
//------------------------------------------------------------------------------
class ovm_objection extends ovm_report_object;
protected int m_source_count[ovm_object];
protected int m_total_count [ovm_object];
protected time m_drain_time [ovm_object];
protected bit m_draining [ovm_object];
protected bit m_hier_mode = 1;
ovm_root top = ovm_root::get();
// Function: new
//
// Creates a new objection instance.
function new(string name="");
super.new(name);
set_report_verbosity_level(top.get_report_verbosity_level());
endfunction
// Function- m_report
//
// Internal method for reporting count updates
function void m_report(ovm_object obj, ovm_object source_obj, int count, string action);
// workaround to array lookup in IUS?
int _count = m_source_count.exists(obj) ? m_source_count[obj] : 0;
int _total = m_total_count[obj];
if (source_obj == obj)
ovm_report_info("OBJTN_CNT",
$sformatf("Object %0s %0s %0d objection(s), count=%0d total=%0d",
obj.get_full_name()==""?"ovm_top":obj.get_full_name(), action, count, _count, _total), OVM_HIGH);
else begin
ovm_report_info("OBJTN_CNT",
$sformatf("Object %0s %0s %0d objection(s) from its total (%s from source object %s), count=%0d total=%0d",
obj.get_full_name()==""?"ovm_top":obj.get_full_name(), action=="raised"?"added":"subtracted",
count, action, source_obj.get_full_name(), _count, _total), OVM_HIGH);
end
endfunction
// Function- m_get_parent
//
// Internal method for getting the parent of the given ~object~.
// The ultimate parent is ovm_top, OVM's implicit top-level component.
function ovm_object m_get_parent(ovm_object obj);
ovm_component comp;
ovm_sequence_base seq;
if ($cast(comp, obj)) begin
obj = comp.get_parent();
end
else if ($cast(seq, obj)) begin
obj = seq.get_sequencer();
end
else
obj = top;
if (obj == null)
obj = top;
return obj;
endfunction
// Function- m_propagate
//
// Propagate the objection to the objects parent. If the object is a
// component, the parent is just the hierarchical parent. If the object is
// a sequence, the parent is the parent sequence if one exists, or
// it is the attached sequencer if there is no parent sequence.
//
// obj : the ovm_object on which the objection is being raised or lowered
// source_obj : the root object on which the end user raised/lowered the
// objection (as opposed to an anscestor of the end user object)a
// count : the number of objections associated with the action.
// raise : indicator of whether the objection is being raised or lowered. A
// 1 indicates the objection is being raised.
function void m_propagate (ovm_object obj, ovm_object source_obj, int count, bit raise);
if (obj != null && obj != top) begin
obj = m_get_parent(obj);
if(raise)
m_raise(obj, source_obj, count);
else
m_drop(obj, source_obj, count);
end
endfunction
// Group: Objection Control
// Hierarchical mode only needs to be set for intermediate components, not
// for ovm_root or a leaf component.
function void m_set_hier_mode (ovm_object obj);
ovm_component c;
if((m_hier_mode == 1) || (obj == top)) begin
// Don't set if already set or the object is ovm_top.
return;
end
if($cast(c,obj)) begin
// Don't set if object is a leaf.
if(c.get_num_children() == 0) begin
return;
end
end
else begin
// Don't set if object is a non-component.
return;
end
// restore counts on non-source nodes
m_total_count.delete();
foreach (m_source_count[obj]) begin
ovm_object theobj = obj;
int count = m_source_count[obj];
do begin
if (m_total_count.exists(theobj))
m_total_count[theobj] += count;
else
m_total_count[theobj] = count;
theobj = m_get_parent(theobj);
end
while (theobj != null);
end
m_hier_mode = 1;
endfunction
// Function: raise_objection
//
// Raises the number of objections for the source ~object~ by ~count~, which
// defaults to 1. The ~object~ is usually the ~this~ handle of the caller.
// If ~object~ is not specified or null, the implicit top-level component,
// ~ovm_top~, is chosen.
//
// Rasing an objection causes the following.
//
// - The source and total objection counts for ~object~ are increased by
// ~count~.
//
// - The objection's <raised> virtual method is called, which calls the
// <ovm_component::raised> method for all of the components up the
// hierarchy.
//
function void raise_objection (ovm_object obj=null, int count=1);
if (obj == null)
obj = top;
m_raise (obj, obj, count);
endfunction
// Function- m_raise
function void m_raise (ovm_object obj, ovm_object source_obj, int count=1);
if (m_total_count.exists(obj))
m_total_count[obj] += count;
else
m_total_count[obj] = count;
if (source_obj==obj) begin
if (m_source_count.exists(obj))
m_source_count[obj] += count;
else
m_source_count[obj] = count;
source_obj = obj;
end
if (ovm_report_enabled(OVM_FULL,OVM_INFO,"OBJTN_CNT"))
m_report(obj,source_obj,count,"raised");
raised(obj, source_obj, count);
// If this object is still draining from a previous drop, then
// raise the count and return. Any propagation will be handled
// by the drain process.
if (m_draining.exists(obj))
return;
if (!m_hier_mode && obj != top)
m_raise(top,source_obj,count);
else if (obj != top)
m_propagate(obj, source_obj, count, 1);
endfunction
// Function: drop_objection
//
// Drops the number of objections for the source ~object~ by ~count~, which
// defaults to 1. The ~object~ is usually the ~this~ handle of the caller.
// If ~object~ is not specified or null, the implicit top-level component,
// ~ovm_top~, is chosen.
//
// Dropping an objection causes the following.
//
// - The source and total objection counts for ~object~ are decreased by
// ~count~. It is an error to drop the objection count for ~object~ below
// zero.
//
// - The objection's <dropped> virtual method is called, which calls the
// <ovm_component::dropped> method for all of the components up the
// hierarchy.
//
// - If the total objection count has not reached zero for ~object~, then
// the drop is propagated up the object hierarchy as with
// <raise_objection>. Then, each object in the hierarchy will have updated
// their ~source~ counts--objections that they originated--and ~total~
// counts--the total number of objections by them and all their
// descendants.
//
// If the total objection count reaches zero, propagation up the hierarchy
// is deferred until a configurable drain-time has passed and the
// <ovm_component::all_dropped> callback for the current hierarchy level
// has returned. The following process occurs for each instance up
// the hierarchy from the source caller:
//
// A process is forked in a non-blocking fashion, allowing the ~drop~
// call to return. The forked process then does the following:
//
// - If a drain time was set for the given ~object~, the process waits for
// that amount of time.
//
// - The objection's <all_dropped> virtual method is called, which calls the
// <ovm_component::all_dropped> method (if ~object~ is a component).
//
// - The process then waits for the ~all_dropped~ callback to complete.
//
// - After the drain time has elapsed and all_dropped callback has
// completed, propagation of the dropped objection to the parent proceeds
// as described in <raise_objection>, except as described below.
//
// If a new objection for this ~object~ or any of its descendents is raised
// during the drain time or during execution of the all_dropped callback at
// any point, the hierarchical chain described above is terminated and the
// dropped callback does not go up the hierarchy. The raised objection will
// propagate up the hierarchy, but the number of raised propagated up is
// reduced by the number of drops that were pending waiting for the
// all_dropped/drain time completion. Thus, if exactly one objection
// caused the count to go to zero, and during the drain exactly one new
// objection comes in, no raises or drops are propagted up the hierarchy,
//
// As an optimization, if the ~object~ has no set drain-time and no
// registered callbacks, the forked process can be skipped and propagation
// proceeds immediately to the parent as described.
function void drop_objection (ovm_object obj=null, int count=1);
if (obj == null)
obj = top;
m_drop (obj, obj, count);
endfunction
// Function- m_drop
function void m_drop (ovm_object obj, ovm_object source_obj, int count=1);
if (!m_total_count.exists(obj) || (count > m_total_count[obj])) begin
ovm_report_fatal("OBJTN_ZERO", {"Object \"", obj.get_full_name(),
"\" attempted to drop objection count below zero."});
return;
end
if ((obj == source_obj) &&
(!m_source_count.exists(obj) || (count > m_source_count[obj]))) begin
ovm_report_fatal("OBJTN_ZERO", {"Object \"", obj.get_full_name(),
"\" attempted to drop objection count below zero."});
return;
end
m_total_count[obj] -= count;
if (source_obj==obj) begin
m_source_count[obj] -= count;
source_obj = obj;
end
if (ovm_report_enabled(OVM_FULL,OVM_INFO,"OBJTN_CNT"))
m_report(obj,source_obj,count,"dropped");
dropped(obj, source_obj, count);
// if count != 0, no reason to fork
if (m_total_count[obj] != 0) begin
if (!m_hier_mode && obj != top)
m_drop(top,source_obj,count);
else if (obj != top)
this.m_propagate(obj, source_obj, count, 0);
end
else begin
int diff_count;
bit reraise;
m_draining[obj] = 1;
fork begin
if (m_total_count[obj] == 0) begin
fork begin //wrapper thread for disable fork
fork
begin
if (m_drain_time.exists(obj))
#(m_drain_time[obj]);
all_dropped(obj,source_obj,count);
// wait for all_dropped cbs to complete
wait fork;
end
wait (m_total_count[obj] != 0);
join_any
disable fork;
end join
end
m_draining.delete(obj);
diff_count = m_total_count[obj] - count;
// no propagation if the re-raise cancels the drop
if (diff_count != 0) begin
reraise = diff_count > 0 ? 1 : 0;
if (diff_count < 0)
diff_count = -diff_count;
if (!m_hier_mode && obj != top) begin
if (reraise)
m_raise(top,source_obj,diff_count);
else
m_drop(top,source_obj,diff_count);
end
else
if (obj != top) begin
this.m_propagate(obj, source_obj, diff_count, reraise);
end
end
end
join_none
end
endfunction
// Function: set_drain_time
//
// Sets the drain time on the given ~object~ to ~drain~.
//
// The drain time is the amount of time to wait once all objections have
// been dropped before calling the all_dropped callback and propagating
// the objection to the parent.
//
// If a new objection for this ~object~ or any of its descendents is raised
// during the drain time or during execution of the all_dropped callbacks,
// the drain_time/all_dropped execution is terminated.
function void set_drain_time (ovm_object obj, time drain);
m_drain_time[obj] = drain;
m_set_hier_mode(obj);
endfunction
// Group: Callback Hooks
// Function: raised
//
// Objection callback that is called when a <raise_objection> has reached ~obj~.
// The default implementation calls <ovm_component::raised>.
virtual function void raised (ovm_object obj, ovm_object source_obj, int count);
ovm_component comp;
if ($cast(comp,obj))
comp.raised(this, source_obj, count);
endfunction
// Function: dropped
//
// Objection callback that is called when a <drop_objection> has reached ~obj~.
// The default implementation calls <ovm_component::dropped>.
virtual function void dropped (ovm_object obj, ovm_object source_obj, int count);
ovm_component comp;
if($cast(comp,obj))
comp.dropped(this, source_obj, count);
endfunction
// Function: all_dropped
//
// Objection callback that is called when a <drop_objection> has reached ~obj~,
// and the total count for ~obj~ goes to zero. This callback is executed
// after the drain time associated with ~obj~. The default implementation
// calls <ovm_component::all_dropped>.
virtual task all_dropped (ovm_object obj, ovm_object source_obj, int count);
ovm_component comp;
if($cast(comp,obj))
comp.all_dropped(this, source_obj, count);
endtask
// Group: Objection Status
// Function: get_objection_count
//
// Returns the current number of objections raised by the given ~object~.
function int get_objection_count (ovm_object obj);
if (!m_source_count.exists(obj))
return 0;
return m_source_count[obj];
endfunction
// Function: get_objection_total
//
// Returns the current number of objections raised by the given ~object~
// and all descendants.
function int get_objection_total (ovm_object obj=null);
ovm_component c;
string ch;
if (obj==null)
obj = top;
if (!m_total_count.exists(obj))
return 0;
if (m_hier_mode)
return m_total_count[obj];
else begin
if ($cast(c,obj)) begin
get_objection_total = m_source_count[obj];
if (c.get_first_child(ch))
do
get_objection_total += get_objection_total(c.get_child(ch));
while (c.get_next_child(ch));
end
else begin
return m_total_count[obj];
end
end
endfunction
// Function: get_drain_time
//
// Returns the current drain time set for the given ~object~ (default: 0 ns).
function time get_drain_time (ovm_object obj);
if (!m_drain_time.exists(obj))
return 0;
return m_drain_time[obj];
endfunction
// Function: display_objections
//
// Displays objection information about the given ~object~. If ~object~ is
// not specified or ~null~, the implicit top-level component, <ovm_top>, is
// chosen. The ~show_header~ argument allows control of whether a header is
// output.
protected function string m_display_objections(ovm_object obj=null, bit show_header=1);
static string blank=" ";
string s;
int total;
ovm_object list[string];
ovm_object curr_obj;
int depth;
string name;
string this_obj_name;
string curr_obj_name;
foreach (m_total_count[o]) begin
ovm_object theobj = o;
if ( m_total_count[o] > 0)
list[theobj.get_full_name()] = theobj;
end
if (obj==null)
obj = top;
total = get_objection_total(obj);
s = $sformatf("The total objection count is %0d\n",total);
if (total == 0)
return s;
s = {s,"---------------------------------------------------------\n"};
s = {s,"Source Total \n"};
s = {s,"Count Count Object\n"};
s = {s,"---------------------------------------------------------\n"};
this_obj_name = obj.get_full_name();
curr_obj_name = this_obj_name;
do begin
curr_obj = list[curr_obj_name];
// determine depth
depth=0;
foreach (curr_obj_name[i])
if (curr_obj_name[i] == ".")
depth++;
// determine leaf name
name = curr_obj_name;
for (int i=curr_obj_name.len()-1;i >= 0; i--)
if (curr_obj_name[i] == ".") begin
name = curr_obj_name.substr(i+1,curr_obj_name.len()-1);
break;
end
if (curr_obj_name == "")
name = "ovm_top";
else
depth++;
// print it
s = {s, $sformatf("%-6d %-6d %s%s\n",
m_source_count.exists(curr_obj) ? m_source_count[curr_obj] : 0,
m_total_count.exists(curr_obj) ? m_total_count[curr_obj] : 0,
blank.substr(0,2*depth), name)};
end while (list.next(curr_obj_name) &&
curr_obj_name.substr(0,this_obj_name.len()-1) == this_obj_name);
s = {s,"---------------------------------------------------------\n"};
return s;
endfunction
function string convert2string();
return m_display_objections(top,1);
endfunction
function void display_objections(ovm_object obj=null, bit show_header=1);
$display(m_display_objections(obj,show_header));
endfunction
// Below is all of the basic data stuff that is needed for an ovm_object
// for factory registration, printing, comparing, etc.
typedef ovm_object_registry#(ovm_objection,"ovm_objection") type_id;
static function type_id get_type();
return type_id::get();
endfunction
function ovm_object create (string name="");
ovm_objection tmp = new(name);
return tmp;
endfunction
virtual function string get_type_name ();
return "ovm_objection";
endfunction
function void do_copy (ovm_object rhs);
ovm_objection _rhs;
$cast(_rhs, rhs);
m_source_count = _rhs.m_source_count;
m_total_count = _rhs.m_total_count;
m_drain_time = _rhs.m_drain_time;
m_draining = _rhs.m_draining;
m_hier_mode = _rhs.m_hier_mode;
endfunction
endclass
//------------------------------------------------------------------------------
//
// Class: ovm_test_done_objection
//
// Built-in end-of-test coordination
//------------------------------------------------------------------------------
class ovm_test_done_objection extends ovm_objection;
protected static ovm_test_done_objection m_inst = null;
protected bit m_forced;
// local function new();
function new(string name="ovm_test_done");
super.new(name);
endfunction
// Function: qualify
//
// Checks that the given ~object~ is derived from either <ovm_component> or
// <ovm_sequence_base>.
virtual function void qualify(ovm_object obj=null, bit is_raise);
ovm_component c;
ovm_sequence_base s;
string nm = is_raise ? "raise_objection" : "drop_objection";
if(! ($cast(c,obj) || $cast(s,obj))) begin
ovm_report_error("TEST_DONE_NOHIER", {"A non-hierarchical object, '",
obj.get_full_name(), "' (", obj.get_type_name(),") was used in a call ",
"to ovm_test_done.", nm,"(). For this objection, a sequence ",
"or component is required." });
end
endfunction
// Task: all_dropped
//
// This callback is called when the given ~object's~ objection count reaches
// zero; if the ~object~ is the implicit top-level, <ovm_top> then it means
// there are no more objections raised for the ~ovm_test_done~ objection.
// Thus, after calling <ovm_objection::all_dropped>, this method will call
// <global_stop_request> to stop the current task-based phase (e.g. run).
virtual task all_dropped (ovm_object obj, ovm_object source_obj, int count);
super.all_dropped(obj,source_obj,count);
if (obj == top) begin
string msg = "", msg2 = "";
if (!m_forced)
msg = "All end_of_test objections have been dropped. ";
if (!top.m_in_stop_request) begin
msg = {msg, "Calling global_stop_request()"};
top.stop_request();
end
else
msg = {msg, "Previous call to global_stop_request() will now be honored."};
ovm_report_info("TEST_DONE", msg, OVM_LOW);
end
endtask
// Function: raise_objection
//
// Calls <ovm_objection::raise_objection> after calling <qualify>.
// If the ~object~ is not provided or is ~null~, then the implicit top-level
// component, ~ovm_top~, is chosen.
virtual function void raise_objection (ovm_object obj=null, int count=1);
if(obj==null)
obj=top;
else
qualify(obj, 1);
super.raise_objection(obj,count);
endfunction
// Function: drop
//
// Calls <ovm_objection::drop_objection> after calling <qualify>.
// If the ~object~ is not provided or is ~null~, then the implicit top-level
// component, ~ovm_top~, is chosen.
virtual function void drop_objection (ovm_object obj=null, int count=1);
if(obj==null)
obj=top;
else
qualify(obj, 0);
super.drop_objection(obj,count);
endfunction
// Function: force_stop
//
//
virtual task force_stop(ovm_object obj=null);
string name;
if (obj==null)
obj=top;
name = obj.get_full_name();
if (name == "")
name = "ovm_top";
m_forced = 1;
ovm_report_warning("FORCE_STOP",{"Object '",name,"' called force_stop. ",
"Ending run phase"});
all_dropped(ovm_top, obj, 0);
m_forced = 0;
endtask
// Below is all of the basic data stuff that is needed for an ovm_object
// for factory registration, printing, comparing, etc.
typedef ovm_object_registry#(ovm_test_done_objection,"ovm_test_done") type_id;
static function type_id get_type();
return type_id::get();
endfunction
function ovm_object create (string name="");
ovm_test_done_objection tmp = new(name);
return tmp;
endfunction
virtual function string get_type_name ();
return "ovm_test_done";
endfunction
static function ovm_test_done_objection get();
if(m_inst == null)
m_inst = ovm_test_done_objection::type_id::create("ovm_test_done");
return m_inst;
endfunction
endclass
`endif