//----------------------------------------------------------------------
// Copyright 2007-2011 Mentor Graphics Corporation
// Copyright 2007-2009 Cadence Design Systems, Inc.
// Copyright 2010 Synopsys, Inc.
// Copyright 2013 NVIDIA Corporation
// 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 UVM_HEARTBEAT_SVH
`define UVM_HEARTBEAT_SVH
typedef enum {
UVM_ALL_ACTIVE,
UVM_ONE_ACTIVE,
UVM_ANY_ACTIVE,
UVM_NO_HB_MODE
} [docs]uvm_heartbeat_modes;
typedef class [docs]uvm_heartbeat_callback;
typedef uvm_callbacks #(uvm_objection,uvm_heartbeat_callback) [docs]uvm_heartbeat_cbs_t;
//------------------------------------------------------------------------------
//
// Class: uvm_heartbeat
//
//------------------------------------------------------------------------------
// Heartbeats provide a way for environments to easily ensure that their
// descendants are alive. A uvm_heartbeat is associated with a specific
// objection object. A component that is being tracked by the heartbeat
// object must raise (or drop) the synchronizing objection during
// the heartbeat window.
//
// The uvm_heartbeat object has a list of participating objects. The heartbeat
// can be configured so that all components (UVM_ALL_ACTIVE), exactly one
// (UVM_ONE_ACTIVE), or any component (UVM_ANY_ACTIVE) must trigger the
// objection in order to satisfy the heartbeat condition.
//------------------------------------------------------------------------------
typedef class [docs]uvm_objection_callback;
class [docs]uvm_heartbeat extends uvm_object;
protected uvm_objection m_objection;
protected uvm_heartbeat_callback m_cb;
protected uvm_component m_cntxt;
protected uvm_heartbeat_modes m_mode;
protected uvm_component m_hblist[$];
protected uvm_event#(uvm_object) m_event;
protected bit m_started;
protected event m_stop_event;
// Function: new
//
// Creates a new heartbeat instance associated with ~cntxt~. The context
// is the hierarchical location that the heartbeat objections will flow
// through and be monitored at. The ~objection~ associated with the heartbeat
// is optional, if it is left ~null~ but it must be set before the heartbeat
// monitor will activate.
//
//| uvm_objection myobjection = new("myobjection"); //some shared objection
//| class myenv extends uvm_env;
//| uvm_heartbeat hb = new("hb", this, myobjection);
//| ...
//| endclass
function [docs]new(string name, uvm_component cntxt, uvm_objection objection=null);
uvm_coreservice_t cs;
super.new(name);
m_objection = objection;
cs = uvm_coreservice_t::get();
//if a cntxt is given it will be used for reporting.
if(cntxt != null) m_cntxt = cntxt;
else m_cntxt = cs.get_root();
m_cb = new({name,"_cb"},m_cntxt);
endfunction
// Function: set_mode
//
// Sets or retrieves the heartbeat mode. The current value for the heartbeat
// mode is returned. If an argument is specified to change the mode then the
// mode is changed to the new value.
function uvm_heartbeat_modes [docs]set_mode (uvm_heartbeat_modes mode = UVM_NO_HB_MODE);
set_mode = m_mode;
if(mode == UVM_ANY_ACTIVE || mode == UVM_ONE_ACTIVE || mode == UVM_ALL_ACTIVE)
m_mode = mode;
endfunction
// Function: set_heartbeat
//
// Sets up the heartbeat event and assigns a list of objects to watch. The
// monitoring is started as soon as this method is called. Once the
// monitoring has been started with a specific event, providing a new
// monitor event results in an error. To change trigger events, you
// must first <stop> the monitor and then <start> with a new event trigger.
//
// If the trigger event ~e~ is ~null~ and there was no previously set
// trigger event, then the monitoring is not started. Monitoring can be
// started by explicitly calling <start>.
function void [docs]set_heartbeat (uvm_event#(uvm_object) e, ref uvm_component comps[$]);
uvm_object c;
foreach(comps[i]) begin
c = comps[i];
if(!m_cb.cnt.exists(c))
m_cb.cnt[c]=0;
if(!m_cb.last_trigger.exists(c))
m_cb.last_trigger[c]=0;
end
if(e==null && m_event==null) return;
start(e);
endfunction
// Function: add
//
// Add a single component to the set of components to be monitored.
// This does not cause monitoring to be started. If monitoring is
// currently active then this component will be immediately added
// to the list of components and will be expected to participate
// in the currently active event window.
function void [docs]add (uvm_component comp);
uvm_object c = comp;
if(m_cb.cnt.exists(c)) return;
m_cb.cnt[c]=0;
m_cb.last_trigger[c]=0;
endfunction
// Function: remove
//
// Remove a single component to the set of components being monitored.
// Monitoring is not stopped, even if the last component has been
// removed (an explicit stop is required).
function void [docs]remove (uvm_component comp);
uvm_object c = comp;
if(m_cb.cnt.exists(c)) m_cb.cnt.delete(c);
if(m_cb.last_trigger.exists(c)) m_cb.last_trigger.delete(c);
endfunction
// Function: start
//
// Starts the heartbeat monitor. If ~e~ is ~null~ then whatever event
// was previously set is used. If no event was previously set then
// a warning is issued. It is an error if the monitor is currently
// running and ~e~ is specifying a different trigger event from the
// current event.
function void [docs]start (uvm_event#(uvm_object) e=null);
if(m_event == null && e == null) begin
m_cntxt.uvm_report_warning("NOEVNT", { "start() was called for: ",
get_name(), " with a null trigger and no currently set trigger" },
UVM_NONE);
return;
end
if((m_event != null) && (e != m_event) && m_started) begin
m_cntxt.uvm_report_error("ILHBVNT", { "start() was called for: ",
get_name(), " with trigger ", e.get_name(), " which is different ",
"from the original trigger ", m_event.get_name() }, UVM_NONE);
return;
end
if(e != null) m_event = e;
m_enable_cb();
m_start_hb_process();
endfunction
// Function: stop
//
// Stops the heartbeat monitor. Current state information is reset so
// that if <start> is called again the process will wait for the first
// event trigger to start the monitoring.
function void [docs]stop ();
m_started = 0;
->m_stop_event;
m_disable_cb();
endfunction
function void m_start_hb_process();
if(m_started) return;
m_started = 1;
fork
m_hb_process;
join_none
endfunction
protected bit m_added;
function void m_enable_cb;
void'(m_cb.callback_mode(1));
if(m_objection == null) return;
if(!m_added)
uvm_heartbeat_cbs_t::add(m_objection, m_cb);
m_added = 1;
endfunction
function void m_disable_cb;
void'(m_cb.callback_mode(0));
endfunction
task m_hb_process;
uvm_object obj;
bit triggered;
time last_trigger=0;
fork
begin
// The process waits for the event trigger. The first trigger is
// ignored, but sets the first start window. On susequent triggers
// the monitor tests that the mode criteria was full-filled.
while(1) begin
m_event.wait_trigger();
if(triggered) begin
case (m_mode)
UVM_ALL_ACTIVE:
begin
foreach(m_cb.cnt[idx]) begin
obj = idx;
if(!m_cb.cnt[obj]) begin
m_cntxt.uvm_report_fatal("HBFAIL", $sformatf("Did not recieve an update of %s for component %s since last event trigger at time %0t : last update time was %0t",
m_objection.get_name(), obj.get_full_name(),
last_trigger, m_cb.last_trigger[obj]), UVM_NONE);
end
end
end
UVM_ANY_ACTIVE:
begin
if(m_cb.cnt.num() && !m_cb.objects_triggered()) begin
string s;
foreach(m_cb.cnt[idx]) begin
obj = idx;
s={s,"\n ",obj.get_full_name()};
end
m_cntxt.uvm_report_fatal("HBFAIL", $sformatf("Did not recieve an update of %s on any component since last event trigger at time %0t. The list of registered components is:%s",
m_objection.get_name(), last_trigger, s), UVM_NONE);
end
end
UVM_ONE_ACTIVE:
begin
if(m_cb.objects_triggered() > 1) begin
string s;
foreach(m_cb.cnt[idx]) begin
obj = idx;
if(m_cb.cnt[obj]) $swrite(s,"%s\n %s (updated: %0t)",
s, obj.get_full_name(), m_cb.last_trigger[obj]);
end
m_cntxt.uvm_report_fatal("HBFAIL", $sformatf("Recieved update of %s from more than one component since last event trigger at time %0t. The list of triggered components is:%s",
m_objection.get_name(), last_trigger, s), UVM_NONE);
end
if(m_cb.cnt.num() && !m_cb.objects_triggered()) begin
string s;
foreach(m_cb.cnt[idx]) begin
obj = idx;
s={s,"\n ",obj.get_full_name()};
end
m_cntxt.uvm_report_fatal("HBFAIL", $sformatf("Did not recieve an update of %s on any component since last event trigger at time %0t. The list of registered components is:%s",
m_objection.get_name(), last_trigger, s), UVM_NONE);
end
end
endcase
end
m_cb.reset_counts();
last_trigger = $realtime;
triggered = 1;
end
end
@(m_stop_event);
join_any
disable fork;
endtask
endclass
class [docs]uvm_heartbeat_callback extends uvm_objection_callback;
int cnt [uvm_object];
time last_trigger [uvm_object];
uvm_object target;
uvm_coreservice_t cs = uvm_coreservice_t::get();
function [docs]new(string name, uvm_object target);
super.new(name);
if (target != null)
this.target = target;
else
this.target = cs.get_root();
endfunction
virtual function void [docs]raised (uvm_objection objection,
uvm_object obj,
uvm_object source_obj,
string description,
int count);
if(obj == target) begin
if(!cnt.exists(source_obj))
cnt[source_obj] = 0;
cnt[source_obj] = cnt[source_obj]+1;
last_trigger[source_obj] = $realtime;
end
endfunction
virtual function void [docs]dropped (uvm_objection objection,
uvm_object obj,
uvm_object source_obj,
string description,
int count);
raised(objection,obj,source_obj,description,count);
endfunction
function void [docs]reset_counts;
foreach(cnt[i]) cnt[i] = 0;
endfunction
function int [docs]objects_triggered;
objects_triggered = 0;
foreach(cnt[i])
if (cnt[i] != 0)
objects_triggered++;
endfunction
endclass
`endif