#
#//
#//----------------------------------------------------------------------
#// Copyright 2007-2011 Mentor Graphics Corporation
#// Copyright 2007-2011 Cadence Design Systems, Inc.
#// Copyright 2010-2011 Synopsys, Inc.
#// Copyright 2013 NVIDIA Corporation
#// Copyright 2019-2021 Tuomas Poikela
#// 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.
#//----------------------------------------------------------------------
"""
Title: Objection Mechanism
The following classes define the objection mechanism and end-of-test
functionality, which is based on <uvm_objection>.
"""
import cocotb
from cocotb.triggers import Event, Timer
from .uvm_report_object import UVMReportObject
from .uvm_debug import uvm_debug
from .uvm_globals import *
from .uvm_object_globals import (UVM_RAISED, UVM_DROPPED, UVM_ALL_DROPPED)
from .sv import sv
from ..macros import (uvm_info, uvm_do_callbacks,
uvm_do_callbacks_async)
from typing import List, Dict, Any
from .uvm_pool import UVMPool
from .uvm_callback import UVMCallback
UVM_USE_PROCESS_CONTAINER = 1
[docs]def classmethod_named(func):
new_func = classmethod(func)
setattr(new_func, '__name__', func.__name__)
return new_func
[docs]class UVMObjectionEvents():
def __init__(self):
self.waiters = 0
self.raised = Event('raised')
self.dropped = Event('dropped')
self.all_dropped = Event('all_dropped')
ObjContextDict = Dict[Any, 'UVMObjectionContextObject']
ObjContextList = List['UVMObjectionContextObject']
[docs]def get_name_depth(curr_obj_name: str) -> int:
""" Returns the hier depth of the name """
depth = 0
for i in curr_obj_name:
if i == ".":
depth += 1
return depth
[docs]def get_leaf_name(curr_obj_name: str) -> str:
name = curr_obj_name
rr = range(len(curr_obj_name)-1, -1, -1)
for i in rr:
if curr_obj_name[i] == ".":
name = curr_obj_name[i+1:len(curr_obj_name)]
break
return name
[docs]class UVMObjectionCallback(UVMCallback):
"""
The UVMObjection is the callback type that defines the callback
implementations for an objection callback. A user uses the callback
type uvm_objection_cbs_t to add callbacks to specific objections.
.. code-block:: python
class my_objection_cb extends uvm_objection_callback
def __init__(self, name):
super().__init__(name)
def raised(objection, obj, source_obj, description, count):
uvm_info("RAISED","{}: Objection {}: Raised for {}".format(sv.time(),
objection.get_name(), obj.get_full_name())
...
@cocotb.test()
async def initial_begin(dut):
cb = my_objection_cb ("cb")
uvm_objection_cbs_t.add(None, cb) # typewide callback
"""
def __init__(self, name):
super().__init__(name)
# // Function: raised
# //
# // Objection raised callback function. Called by <uvm_objection::raised>.
#
# virtual function void raised (uvm_objection objection, uvm_object obj,
# uvm_object source_obj, string description, int count)
# endfunction
#
# // Function: dropped
# //
# // Objection dropped callback function. Called by <uvm_objection::dropped>.
#
# virtual function void dropped (uvm_objection objection, uvm_object obj,
# uvm_object source_obj, string description, int count)
# endfunction
#
# // Function: all_dropped
# //
# // Objection all_dropped callback function. Called by <uvm_objection::all_dropped>.
#
# virtual task all_dropped (uvm_objection objection, uvm_object obj,
# uvm_object source_obj, string description, int count)
# endtask
[docs]class UVMObjection(UVMReportObject):
"""
Class: UVMObjection
Objections provide a facility for coordinating status information between
two or more participating components, objects, and even module-based IP.
Tracing of objection activity can be turned on to follow the activity of
the objection mechanism. It may be turned on for a specific objection
instance with <uvm_objection::trace_mode>, or it can be set for all
objections from the command line using the option +UVM_OBJECTION_TRACE.
"""
m_objections = [] # static uvm_objection[$]
# `uvm_register_cb(uvm_objection, uvm_objection_callback)
#
# protected bit self.m_trace_mode
# protected int self.m_source_count[uvm_object]
# protected int self.m_total_count [uvm_object]
# protected time self.m_drain_time [uvm_object]
# protected uvm_objection_events self.m_events [uvm_object]
# /*protected*/ bit self.m_top_all_dropped
#
# protected uvm_root self.m_top
#
#
# //// Drain Logic
#
# // The context pool holds used context objects, so that
# // they're not constantly being recreated. The maximum
# // number of contexts in the pool is equal to the maximum
# // number of simultaneous drains you could have occuring,
# // both pre and post forks.
# //
# // There's the potential for a programmability within the
# // library to dictate the largest this pool should be allowed
# // to grow, but that seems like overkill for the time being.
# local static uvm_objection_context_object UVMObjection.m_context_pool[$]
m_context_pool = []
# Notified when m_scheduled_list is not empty
m_scheduled_list_not_empty_event = Event('m_scheduled_list_not_empty_event')
# // These are the active drain processes, which have been
# // forked off by the background process. A raise can
# // use this array to kill a drain.
#`ifndef UVM_USE_PROCESS_CONTAINER
# local process self.m_drain_proc[uvm_object]
#`else
# local process_container_c self.m_drain_proc[uvm_object]
#`endif
# // These are the contexts which have been scheduled for
# // retrieval by the background process, but which the
# // background process hasn't seen yet.
# local static uvm_objection_context_object m_scheduled_list[$]
m_scheduled_list: ObjContextList = []
# // Once a context is seen by the background process, it is
# // removed from the scheduled list, and placed in the forked
# // list. At the same time, it is placed in the scheduled
# // contexts array. A re-raise can use the scheduled contexts
# // array to detect (and cancel) the drain.
# local uvm_objection_context_object self.m_scheduled_contexts[uvm_object]
# // Once the forked drain has actually started (this occurs
# // ~1 delta AFTER the background process schedules it), the
# // context is removed from the above array and list, and placed
# // in the forked_contexts list.
# local uvm_objection_context_object self.m_forked_contexts[uvm_object]
#
# protected bit self.m_prop_mode = 1
# // Function: new
# //
# // Creates a new objection instance. Accesses the command line
# // argument +UVM_OBJECTION_TRACE to turn tracing on for
# // all objection objects.
#
def __init__(self, name=""):
#uvm_cmdline_processor clp
#uvm_coreservice_t cs_
trace_args = [] # string [$]
UVMReportObject.__init__(self, name)
from .uvm_coreservice import UVMCoreService
cs_ = UVMCoreService.get()
#cs_ = uvm_coreservice_t::get()
self.m_top = cs_.get_root()
self.m_cleared = 0 # protected bit /* for checking obj count<0 */
self.m_forked_list: ObjContextList = [] # uvm_objection_context_object[$]
self.m_scheduled_contexts: ObjContextDict = {} # uvm_objection_context_object[uvm_object]
self.m_forked_contexts: ObjContextDict = {} # uvm_objection_context_object[uvm_object]
self.m_source_count = {} # int[uvm_object]
self.m_total_count = {} # int[uvm_object]
self.m_drain_time = {} # time [uvm_object]
self.m_events: Dict[Any, UVMObjectionEvents] = {} # uvm_objection_events[uvm_object]
self.m_top_all_dropped = 0
self.m_drain_proc = {} # process_container_c [uvm_object]
self.set_report_verbosity_level(self.m_top.get_report_verbosity_level())
# Get the command line trace mode setting
#clp = uvm_cmdline_processor::get_inst()
from .uvm_cmdline_processor import UVMCmdlineProcessor
clp = UVMCmdlineProcessor.get_inst()
self.m_prop_mode = 1
self.m_trace_mode = 0
if clp.get_arg_matches("+UVM_OBJECTION_TRACE", trace_args):
self.m_trace_mode = 1
UVMObjection.m_objections.append(self)
[docs] def trace_mode(self, mode=-1):
"""
Set or get the trace mode for the objection object. If no
argument is specified (or an argument other than 0 or 1)
the current trace mode is unaffected. A trace_mode of
0 turns tracing off. A trace mode of 1 turns tracing on.
The return value is the mode prior to being reset.
Args:
mode (int): 0=disable, 1=enable
Returns int:
"""
trace_mode = self.m_trace_mode
if mode == 0:
self.m_trace_mode = 0
elif mode == 1:
self.m_trace_mode = 1
return trace_mode
# // Function- m_report
# //
# // Internal method for reporting count updates
#
[docs] def m_report(self, obj, source_obj, description: str, count: int, action: str) -> None:
_count = 0
if obj in self.m_source_count:
_count = self.m_source_count[obj]
_total = 0
if obj in self.m_total_count:
_total = self.m_total_count[obj]
if (not uvm_report_enabled(UVM_NONE, UVM_INFO,"OBJTN_TRC") or (not self.m_trace_mode)):
return
descr = ""
if description != "":
descr = " (" + description + ")"
if source_obj is obj:
name = obj.get_full_name()
if name == "":
name = "uvm_top"
uvm_report_info("OBJTN_TRC",
sv.sformatf("Object %0s %0s %0d objection(s)%s: count=%0d total=%0d",
name, action, count, descr, _count, _total), UVM_NONE)
else:
cpath = 0
last_dot = 0
sname = source_obj.get_full_name()
nm = obj.get_full_name()
_max = sname.len()
if sname.len() > nm.len():
_max = nm.len()
# For readability, only print the part of the source obj hierarchy underneath
# the current object.
while ((sname[cpath] == nm[cpath]) and (cpath < _max)):
if (sname[cpath] == "."):
last_dot = cpath
cpath += 1
if last_dot:
sname = sname.substr(last_dot+1, sname.len())
name = obj.get_full_name()
if name == "":
name = "uvm_top"
act_type = "subtracted"
if action == "raised":
act_type = "added"
act_dir = "from"
if action == "raised":
act_dir = "to"
uvm_report_info("OBJTN_TRC",
sv.sformatf("Object %0s %0s %0d objection(s) %0s its total (%s from"
" source object %s%s): count=%0d total=%0d", name, act_type, count,
act_dir, action, sname, descr, _count, _total), UVM_NONE)
# // Function- m_get_parent
# //
# // Internal method for getting the parent of the given ~object~.
# // The ultimate parent is uvm_top, UVM's implicit top-level component.
#
[docs] def m_get_parent(self, obj):
comp = None
seq = None
if hasattr(obj, 'get_parent'):
comp = obj
obj = comp.get_parent()
elif hasattr(obj, 'get_sequencer'):
seq = obj
obj = seq.get_sequencer()
else:
obj = self.m_top
if obj is None:
obj = self.m_top
return obj
# // 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 uvm_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.
#
[docs] def m_propagate(self, obj, source_obj, description, count, raise_, in_top_thread):
if obj is not None and obj != self.m_top:
parent_obj = self.m_get_parent(obj)
if raise_:
self.m_raise(parent_obj, source_obj, description, count)
else:
self.m_drop(parent_obj, source_obj, description, count, in_top_thread)
# // Group: Objection Control
#
# // Function: set_propagate_mode
# // Sets the propagation mode for this objection.
# //
# // By default, objections support hierarchical propagation for
# // components. For example, if we have the following basic
# // component tree:
# //
# //| uvm_top.parent.child
# //
# // Any objections raised by 'child' would get propagated
# // down to parent, and then to uvm_test_top. Resulting in the
# // following counts and totals:
# //
# //| | count | total |
# //| uvm_top.parent.child | 1 | 1 |
# //| uvm_top.parent | 0 | 1 |
# //| uvm_top | 0 | 1 |
# //|
# //
# // While propagations such as these can be useful, if they are
# // unused by the testbench then they are simply an unnecessary
# // performance hit. If the testbench is not going to use this
# // functionality, then the performance can be improved by setting
# // the propagation mode to 0.
# //
# // When propagation mode is set to 0, all intermediate callbacks
# // between the ~source~ and ~top~ will be skipped. This would
# // result in the following counts and totals for the above objection:
# //
# //| | count | total |
# //| uvm_top.parent.child | 1 | 1 |
# //| uvm_top.parent | 0 | 0 |
# //| uvm_top | 0 | 1 |
# //|
# //
# // Since the propagation mode changes the behavior of the objection,
# // it can only be safely changed if there are no objections ~raised~
# // or ~draining~. Any attempts to change the mode while objections
# // are ~raised~ or ~draining~ will result in an error.
# //
# function void set_propagate_mode (bit prop_mode)
# if (!self.m_top_all_dropped && (get_objection_total() != 0)) begin
# `uvm_error("UVM/BASE/OBJTN/PROP_MODE",
# {"The propagation mode of '", this.get_full_name(),
# "' cannot be changed while the objection is raised ",
# "or draining!"})
# return
# end
#
# self.m_prop_mode = prop_mode
# endfunction : set_propagate_mode
# // Function: get_propagate_mode
# // Returns the propagation mode for this objection.
[docs] def get_propagate_mode(self) -> int:
return self.m_prop_mode
# // 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,
# // <uvm_root>, is chosen.
# //
# // Raising an objection causes the following.
# //
# // - The source and total objection counts for ~object~ are increased by
# // ~count~. ~description~ is a string that marks a specific objection
# // and is used in tracing/debug.
# //
# // - The objection's <raised> virtual method is called, which calls the
# // <uvm_component::raised> method for all of the components up the
# // hierarchy.
# //
[docs] def raise_objection(self, obj=None, description="", count=1):
if obj is None:
obj = self.m_top
self.m_cleared = 0
self.m_top_all_dropped = 0
uvm_debug(self, 'raise_objection', obj.get_name() + " Starting to raise objection")
self.m_raise(obj, obj, description, count)
# // Function- m_raise
[docs] def m_raise(self, obj, source_obj, description="", count=1):
idx = 0
ctxt = None # uvm_objection_context_object
# Ignore raise if count is 0
if count == 0:
return
if obj in self.m_total_count:
self.m_total_count[obj] += count
else:
self.m_total_count[obj] = count
if source_obj == obj:
if obj in self.m_source_count:
self.m_source_count[obj] += count
else:
self.m_source_count[obj] = count
if self.m_trace_mode:
self.m_report(obj,source_obj,description,count,"raised")
self.raised(obj, source_obj, description, count)
# Handle any outstanding drains...
# First go through the scheduled list
idx = 0
m_scheduled_list = UVMObjection.m_scheduled_list
# while idx < len(m_scheduled_list):
for entry in m_scheduled_list:
if ((entry.obj == obj) and
(entry.objection == self)):
# Caught it before the drain was forked
ctxt = entry
del m_scheduled_list[idx]
break
idx += 1
# If it's not there, go through the forked list
if ctxt is None:
idx = 0
while idx < len(self.m_forked_list):
if self.m_forked_list[idx].obj == obj:
# Caught it after the drain was forked,
# but before the fork started
ctxt = self.m_forked_list[idx]
del self.m_forked_list[idx]
del self.m_scheduled_contexts[ctxt.obj]
break
idx += 1
# If it's not there, go through the forked contexts
if ctxt is None:
if obj in self.m_forked_contexts:
# Caught it with the forked drain running
ctxt = self.m_forked_contexts[obj]
del self.m_forked_contexts[obj]
# Kill the drain
uvm_debug(self, 'm_raise', obj.get_name() + " ENDING FUNC")
# TODO
#if UVM_USE_PROCESS_CONTAINER:
# self.m_drain_proc[obj].kill()
# del self.m_drain_proc[obj]
#else:
# self.m_drain_proc[obj].p.kill()
# del self.m_drain_proc[obj]
uvm_debug(self, 'm_raise', obj.get_name() + " NEVER GETS HERE")
if ctxt is None:
# If there were no drains, just propagate as usual
if not self.m_prop_mode and obj != self.m_top:
uvm_debug(self, 'm_raise', obj.get_name() + " XXX NEVER GETS HERE")
self.m_raise(self.m_top,source_obj,description,count)
elif obj != self.m_top:
self.m_propagate(obj, source_obj, description, count, 1, 0)
else:
# Otherwise we need to determine what exactly happened
# Determine the diff count, if it's positive, then we're
# looking at a 'raise' total, if it's negative, then
# we're looking at a 'drop', but not down to 0. If it's
# a 0, that means that there is no change in the total.
diff_count = count - ctxt.count
if diff_count != 0:
# Something changed
if diff_count > 0:
# we're looking at an increase in the total
if not self.m_prop_mode and obj != self.m_top:
self.m_raise(self.m_top, source_obj, description, diff_count)
elif obj != self.m_top:
self.m_propagate(obj, source_obj, description, diff_count, 1, 0)
else:
# we're looking at a decrease in the total
# The count field is always positive...
diff_count = -diff_count
if not self.m_prop_mode and obj != self.m_top:
self.m_drop(self.m_top, source_obj, description, diff_count)
elif obj != self.m_top:
self.m_propagate(obj, source_obj, description, diff_count, 0, 0)
# Cleanup
ctxt.clear()
UVMObjection.m_context_pool.append(ctxt)
# 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,
# // <uvm_root>, 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
# // <uvm_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
# // <uvm_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
# // <uvm_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 descendants 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 propagated 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.
#
[docs] def drop_objection(self, obj=None, description="", count=1):
if obj is None:
obj = self.m_top
uvm_debug(self, 'drop_objection', obj.get_name() + " Starting to drop objection")
self.m_drop(obj, obj, description, count, 0)
# // Function- m_drop
#
[docs] def m_drop(self, obj, source_obj, description="", count=1,
in_top_thread=0):
# Ignore drops if the count is 0
if count == 0:
return
if obj not in self.m_total_count or (count > self.m_total_count[obj]):
if self.m_cleared:
return
uvm_report_fatal("OBJTN_ZERO", ("Object '" + obj.get_full_name()
+ "' attempted to drop total objection '" + self.get_name() + "' count below zero. " +
"Description: '" + description + "' source_obj: " +
source_obj.get_name()))
return
if obj == source_obj:
if obj not in self.m_source_count or (count > self.m_source_count[obj]):
if self.m_cleared:
return
uvm_report_fatal("OBJTN_ZERO", ("Object \"" + obj.get_full_name()
+ "\" attempted to drop source objection '" + self.get_name() + "' count below zero"))
return
self.m_source_count[obj] -= count
self.m_total_count[obj] -= count
if self.m_trace_mode:
self.m_report(obj,source_obj,description,count,"dropped")
self.dropped(obj, source_obj, description, count)
# if count != 0, no reason to fork
if self.m_total_count[obj] != 0:
if not self.m_prop_mode and obj != self.m_top:
self.m_drop(self.m_top,source_obj,description, count, in_top_thread)
elif obj != self.m_top:
self.m_propagate(obj, source_obj, description, count, 0, in_top_thread)
else:
ctxt = None # uvm_objection_context_object
if (len(UVMObjection.m_context_pool) > 0):
ctxt = UVMObjection.m_context_pool.pop(0)
else:
ctxt = UVMObjectionContextObject()
ctxt.obj = obj
ctxt.source_obj = source_obj
ctxt.description = description
ctxt.count = count
ctxt.objection = self
# Need to be thread-safe, let the background
# process handle it.
# Why don't we look at in_top_thread here? Because
# a re-raise will kill the drain at object that it's
# currently occuring at, and we need the leaf-level kills
# to not cause accidental kills at branch-levels in
# the propagation.
# Using the background process just allows us to
# separate the links of the chain.
UVMObjection.m_scheduled_list.append(ctxt)
UVMObjection.m_scheduled_list_not_empty_event.set()
#end // else: !if(self.m_total_count[obj] != 0)
# Function: clear
#
# Immediately clears the objection state. All counts are cleared and the
# any processes waiting on a call to wait_for(UVM_ALL_DROPPED, uvm_top)
# are released.
#
# The caller, if a uvm_object-based object, should pass its 'this' handle
# to the ~obj~ argument to document who cleared the objection.
# Any drain_times set by the user are not affected.
#
[docs] def clear(self, obj=None):
name = ""
ctxt = None # uvm_objection_context_object
idx = 0
uvm_debug(self, 'clear', 'START')
if obj is None:
obj = self.m_top
name = obj.get_full_name()
if name == "":
name = "uvm_top"
if (self.m_top_all_dropped is False and
self.get_objection_total(self.m_top) > 0):
uvm_report_warning("OBJTN_CLEAR",("Object '" + name
+ "' cleared objection counts for " + self.get_name()))
# Should there be a warning if there are outstanding objections
self.m_source_count = {}
self.m_total_count = {}
# Remove any scheduled drains from the static queue
idx = 0
while (idx < len(UVMObjection.m_scheduled_list)):
if (UVMObjection.m_scheduled_list[idx].objection == self):
UVMObjection.m_scheduled_list[idx].clear()
UVMObjection.m_context_pool.append(UVMObjection.m_scheduled_list[idx])
del UVMObjection.m_scheduled_list[idx]
else:
idx += 1
# Scheduled contexts and m_forked_lists have duplicate
# entries... clear out one, free the other.
self.m_scheduled_contexts = {}
while len(self.m_forked_list) > 0:
self.m_forked_list[0].clear()
UVMObjection.m_context_pool.append(self.m_forked_list[0])
front = self.m_forked_list[0]
self.m_forked_list[0].remove(front)
# running drains have a context and a process
for o in self.m_forked_contexts:
if UVM_USE_PROCESS_CONTAINER:
self.m_drain_proc[o].kill()
del self.m_drain_proc[o]
else:
self.m_drain_proc[o].p.kill()
del self.m_drain_proc[o]
self.m_forked_contexts[o].clear()
UVMObjection.m_context_pool.append(self.m_forked_contexts[o])
del self.m_forked_contexts[o]
self.m_top_all_dropped = False
self.m_cleared = 1
if self.m_top in self.m_events:
self.m_events[self.m_top].all_dropped.set()
# // m_execute_scheduled_forks
# // -------------------------
# // background process; when non
[docs] @classmethod
async def m_execute_scheduled_forks(cls):
while True:
#wait(UVMObjection.m_scheduled_list.size() != 0)
uvm_debug(cls, 'm_execute_scheduled_forks', 'waiting list to not be empty')
await UVMObjection.m_scheduled_list_not_empty_event.wait()
UVMObjection.m_scheduled_list_not_empty_event.clear()
if len(UVMObjection.m_scheduled_list) != 0:
# Save off the context before the fork
c = UVMObjection.m_scheduled_list.pop(0)
# A re-raise can use this to figure out props (if any)
objection = c.objection
#rm if objection is not None:
objection.m_scheduled_contexts[c.obj] = c
# The fork below pulls out from the forked list
objection.m_forked_list.append(c)
# The fork will guard the m_forked_drain call, but
# a re-raise can kill self.m_forked_list contexts in the delta
# before the fork executes.
pproc = cocotb.start_soon(cls.m_execute_scheduled_forks_fork_join_none(c))
#else:
# uvm_error("UVMObjection", "Null objection in objection context")
[docs] @classmethod
async def m_execute_scheduled_forks_fork_join_none(cls,
c: 'UVMObjectionContextObject'):
objection = c.objection # automatic uvm_objection
# Check to maike sure re-raise didn't empty the fifo
uvm_debug(cls, 'm_execute_scheduled_forks_fork_join_none', 'check list len')
if len(objection.m_forked_list) > 0:
#uvm_objection_context_object ctxt
ctxt = objection.m_forked_list.pop(0)
# Clear it out of scheduled
del objection.m_scheduled_contexts[ctxt.obj]
# Move it in to forked (so re-raise can figure out props)
objection.m_forked_contexts[ctxt.obj] = ctxt
# Save off our process handle, so a re-raise can kill it...
# TODO
#if UVM_USE_PROCESS_CONTAINER == 0:
# objection.m_drain_proc[ctxt.obj] = process::self()
#else:
# process_container_c c = new(process::self())
# objection.m_drain_proc[ctxt.obj]=c
# Execute the forked drain
await objection.m_forked_drain(ctxt.obj, ctxt.source_obj, ctxt.description,
ctxt.count, 1)
# Cleanup if we survived (no re-raises)
if ctxt.obj in objection.m_drain_proc:
del objection.m_drain_proc[ctxt.obj]
else:
pass
# tpoikela: Added check since ctxt.obj becomes None
#if ctxt.obj in objection.m_forked_contexts:
del objection.m_forked_contexts[ctxt.obj]
# Clear out the context object (prevent memory leaks)
ctxt.clear()
# Save the context in the pool for later reuse
UVMObjection.m_context_pool.append(ctxt)
#rm await uvm_zero_delay()
# // m_forked_drain
# // -------------
# task m_forked_drain (uvm_object obj,
# uvm_object source_obj,
# string description="",
# int count=1,
# int in_top_thread=0)
[docs] async def m_forked_drain(self, obj, source_obj, description="", count=1,
in_top_thread=0):
#rm diff_count = 0
if obj in self.m_drain_time:
# pass
# TODO `uvm_delay(self.m_drain_time[obj])
await Timer(self.m_drain_time[obj])
if self.m_trace_mode:
self.m_report(obj,source_obj,description,count,"all_dropped")
await self.all_dropped(obj,source_obj,description, count)
# wait for all_dropped cbs to complete
await uvm_zero_delay()
# TODO wait fork
# we are ready to delete the 0-count entries for the current
# object before propagating up the hierarchy.
if obj in self.m_source_count and self.m_source_count[obj] == 0:
del self.m_source_count[obj]
if obj in self.m_total_count and self.m_total_count[obj] == 0:
del self.m_total_count[obj]
if not self.m_prop_mode and obj != self.m_top:
self.m_drop(self.m_top,source_obj,description, count, 1)
elif obj != self.m_top:
self.m_propagate(obj, source_obj, description, count, 0, 1)
# // m_init_objections
# // -----------------
#
# // Forks off the single background process
[docs] @classmethod
async def m_init_objections(cls):
#uvm_debug(cls, 'm_init_objections', "Forking m_execute_scheduled_forks")
pproc = cocotb.start_soon(cls.m_execute_scheduled_forks())
#await uvm_zero_delay()
# // 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 descendants is raised
# // during the drain time or during execution of the all_dropped callbacks,
# // the drain_time/all_dropped execution is terminated.
#
# // AE: set_drain_time(drain,obj=null)?
[docs] def set_drain_time(self, obj=None, drain=0):
if obj is None:
obj = self.m_top
self.m_drain_time[obj] = drain
# //----------------------
# // Group: Callback Hooks
# //----------------------
# // Function: raised
# //
# // Objection callback that is called when a <raise_objection> has reached ~obj~.
# // The default implementation calls <uvm_component::raised>.
#
[docs] def raised(self, obj, source_obj, description, count):
if hasattr(obj, 'raised'):
obj.raised(self, source_obj, description, count)
# TODO `uvm_do_callbacks(uvm_objection,uvm_objection_callback,raised(this,obj,source_obj,description,count))
uvm_do_callbacks(self, UVMObjectionCallback, 'raised', self,obj,source_obj,description,count)
if obj in self.m_events:
self.m_events[obj].raised.set()
# // Function: dropped
# //
# // Objection callback that is called when a <drop_objection> has reached ~obj~.
# // The default implementation calls <uvm_component::dropped>.
#
# virtual function void dropped (uvm_object obj,
# uvm_object source_obj,
# string description,
# int count)
[docs] def dropped(self, obj, source_obj, description, count):
comp = obj
comp.dropped(self, source_obj, description, count)
# TODO `uvm_do_callbacks(uvm_objection,uvm_objection_callback,dropped(this,obj,source_obj,description,count))
uvm_do_callbacks(self,UVMObjectionCallback,'dropped', self,obj,source_obj,description,count)
if obj in self.m_events:
self.m_events[obj].dropped.set()
[docs] async def all_dropped(self, obj, source_obj, description, count):
"""
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 `UVMComponent.all_dropped`.
Args:
obj (UVMObject):
source_obj (UVMObject):
description (str):
count (int):
"""
comp = obj
await comp.all_dropped(self, source_obj, description, count)
# TODO `uvm_do_callbacks(uvm_objection,uvm_objection_callback,all_dropped(this,obj,source_obj,description,count))
await uvm_do_callbacks_async(self, UVMObjectionCallback, 'all_dropped', self,obj, source_obj, description, count)
if obj in self.m_events:
self.m_events[obj].all_dropped.set()
if obj == self.m_top:
self.m_top_all_dropped = 1
# endtask
# //------------------------
# // Group: Objection Status
# //------------------------
#
# // Function: get_objectors
# //
# // Returns the current list of objecting objects (objects that
# // raised an objection but have not dropped it).
#
# function void get_objectors(ref uvm_object list[$])
# list.delete()
# foreach (self.m_source_count[obj]) list.push_back(obj)
# endfunction
# // Task: wait_for
# //
# // Waits for the raised, dropped, or all_dropped ~event~ to occur in
# // the given ~obj~. The task returns after all corresponding callbacks
# // for that event have been executed.
# //
# task wait_for(uvm_objection_event objt_event, uvm_object obj=null)
[docs] async def wait_for(self, objt_event, obj=None):
if obj is None:
obj = self.m_top
if (obj in self.m_events) is False:
self.m_events[obj] = UVMObjectionEvents()
self.m_events[obj].waiters += 1
if objt_event == UVM_RAISED:
await self.m_events[obj].raised.wait()
elif objt_event == UVM_DROPPED:
await self.m_events[obj].dropped.wait()
elif objt_event == UVM_ALL_DROPPED:
await self.m_events[obj].all_dropped.wait()
self.m_events[obj].waiters -= 1
if self.m_events[obj].waiters == 0:
del self.m_events[obj]
#
# task wait_for_total_count(uvm_object obj=null, int count=0)
# if (obj==null)
# obj = self.m_top
#
# if(!self.m_total_count.exists(obj) && count == 0)
# return
# if (count == 0)
# wait (!self.m_total_count.exists(obj) && count == 0)
# else
# wait (self.m_total_count.exists(obj) && self.m_total_count[obj] == count)
# endtask
[docs] def get_objection_count(self, obj=None):
"""
Returns the current number of objections raised by the given ~object~.
"""
if obj is None:
obj = self.m_top
if obj not in self.m_source_count:
return 0
return self.m_source_count[obj]
[docs] def get_objection_total(self, obj=None):
"""
Returns the current number of objections raised by the given ~object~
and all descendants.
"""
uvm_debug(self, 'get_objection_total', 'START')
if obj is None:
obj = self.m_top
if obj not in self.m_total_count:
uvm_debug(self, 'get_objection_total', 'Returning 0')
return 0
else:
cnt = self.m_total_count[obj]
uvm_debug(self, 'get_objection_total', 'Returning cnt ' + str(cnt))
return self.m_total_count[obj]
# // Function: get_drain_time
# //
# // Returns the current drain time set for the given ~object~ (default: 0 ns).
#
# function time get_drain_time (uvm_object obj=null)
# if (obj==null)
# obj = self.m_top
#
# if (!self.m_drain_time.exists(obj))
# return 0
# return self.m_drain_time[obj]
# endfunction
#
#
# // m_display_objections
#
[docs] def m_display_objections(self, obj=None, show_header=1):
blank = " "
s = ""
total = 0
lst = UVMPool()
curr_obj = None
depth = 0
name = ""
this_obj_name = ""
curr_obj_name = ""
for o in self.m_total_count:
theobj = o
if self.m_total_count[o] > 0:
lst[theobj.get_full_name()] = theobj
if obj is None:
obj = self.m_top
total = self.get_objection_total(obj)
s = sv.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
while True:
curr_obj = lst[curr_obj_name]
# determine depth, count dots "." in the name
depth = get_name_depth(curr_obj_name)
# determine leaf name
name = get_leaf_name(curr_obj_name)
if curr_obj_name == "":
name = "uvm_top"
else:
depth += 1
# print it
src_count = 0
if curr_obj in self.m_source_count:
src_count = self.m_source_count[curr_obj]
total_count = 0
if curr_obj in self.m_total_count:
total_count = self.m_total_count[curr_obj]
s += sv.sformatf("%d %d %s'%s'\n", src_count, total_count,
blank[0:2*depth], name)
if not ((lst.has_next() and
curr_obj_name[0:len(this_obj_name)] == this_obj_name)):
break
curr_obj_name = lst.next()
s = s + "---------------------------------------------------------\n"
return s
[docs] def convert2string(self):
return self.m_display_objections(self.m_top,1)
#
# // Function: display_objections
# //
# // Displays objection information about the given ~object~. If ~object~ is
# // not specified or ~null~, the implicit top-level component, <uvm_root>, is
# // chosen. The ~show_header~ argument allows control of whether a header is
# // output.
[docs] def display_objections(self, obj=None, show_header=1):
m = self.m_display_objections(obj,show_header)
uvm_info("UVM/OBJ/DISPLAY", m, UVM_NONE)
#
# // Below is all of the basic data stuff that is needed for a uvm_object
# // for factory registration, printing, comparing, etc.
#
# typedef uvm_object_registry#(uvm_objection,"uvm_objection") type_id
# static function type_id get_type()
# return type_id::get()
# endfunction
#
# function uvm_object create (string name="")
# uvm_objection tmp = new(name)
# return tmp
# endfunction
#
# virtual function string get_type_name ()
# return "uvm_objection"
# endfunction
[docs] def do_copy(self, rhs):
_rhs = rhs
# $cast(_rhs, rhs)
self.m_source_count = _rhs.m_source_count
self.m_total_count = _rhs.m_total_count
self.m_drain_time = _rhs.m_drain_time
self.m_prop_mode = _rhs.self.m_prop_mode
#// TODO: change to plusarg
#//`define UVM_DEFAULT_TIMEOUT 9200s
#//------------------------------------------------------------------------------
#//
#// Class- uvm_test_done_objection DEPRECATED
#//
#// Provides built-in end-of-test coordination
#//------------------------------------------------------------------------------
#class uvm_test_done_objection extends uvm_objection
#
# protected static uvm_test_done_objection m_inst
# protected bit m_forced
#
# // For communicating all objections dropped and end of phasing
# local bit m_executing_stop_processes
# local int m_n_stop_threads
#
#
# // Function- new DEPRECATED
# //
# // Creates the singleton test_done objection. Users must not call
# // this method directly.
#
# function new(string name="uvm_test_done")
# super.new(name)
# endfunction
#
#
# // Function- qualify DEPRECATED
# //
# // Checks that the given ~object~ is derived from either <uvm_component> or
# // <uvm_sequence_base>.
#
# virtual function void qualify(uvm_object obj=null,
# bit is_raise,
# string description)
# uvm_component c
# uvm_sequence_base s
# string nm = is_raise ? "raise_objection" : "drop_objection"
# string desc = description == "" ? "" : {" (\"", description, "\")"}
# if(! ($cast(c,obj) || $cast(s,obj))) begin
# uvm_report_error("TEST_DONE_NOHIER", {"A non-hierarchical object, '",
# obj.get_full_name(), "' (", obj.get_type_name(),") was used in a call ",
# "to uvm_test_done.", nm,"(). For this objection, a sequence ",
# "or component is required.", desc })
# end
# endfunction
#
# // Below are basic data operations needed for all uvm_objects
# // for factory registration, printing, comparing, etc.
#
# typedef uvm_object_registry#(uvm_test_done_objection,"uvm_test_done") type_id
# static function type_id get_type()
# return type_id::get()
# endfunction
#
# function uvm_object create (string name="")
# uvm_test_done_objection tmp = new(name)
# return tmp
# endfunction
#
# virtual function string get_type_name ()
# return "uvm_test_done"
# endfunction
#
# static function uvm_test_done_objection get()
# if(m_inst == null)
# m_inst = uvm_test_done_objection::type_id::create("run")
# return m_inst
# endfunction
#
#endclass
#// Have a pool of context objects to use
#class uvm_objection_context_object
[docs]class UVMObjectionContextObject:
def __init__(self):
"""
uvm_object obj
uvm_object source_obj
string description
int count
uvm_objection objection
"""
self.obj = None
self.source_obj = None
self.description = ""
self.count = 0
#self.objection: Optional[UVMObjection] = None
self.objection = UVMObjection()
[docs] def clear(self):
"""
Clears the values stored within the object,
preventing memory leaks from reused objects
"""
self.obj = None
self.source_obj = None
self.description = ""
self.count = 0
self.objection = UVMObjection()
#// Typedef - Exists for backwards compat
#typedef uvm_objection uvm_callbacks_objection
#`endif
#