Source code for uvm.base.uvm_callback

#----------------------------------------------------------------------
#   Copyright 2007-2011 Mentor Graphics Corporation
#   Copyright 2007-2010 Cadence Design Systems, Inc.
#   Copyright 2010-2011 Synopsys, Inc.
#   Copyright 2019 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.
#
#   uvm-python NOTE: All code ported from SystemVerilog UVM 1.2 to
#   python. Original code structures (including comments)
#   preserved where possible.
#----------------------------------------------------------------------

from typing import Dict, List, Optional

from .sv import sv
from .uvm_object import UVMObject
from .uvm_report_object import UVMReportObject
from .uvm_pool import UVMPool
from .uvm_queue import UVMQueue
from .uvm_misc import UVM_APPEND
from ..macros import (uvm_cb_trace_noobj, uvm_warning, uvm_info)
from ..uvm_macros import UVM_STRING_QUEUE_STREAMING_PACK
from .uvm_object_globals import UVM_NONE
from .uvm_globals import (uvm_report_warning, uvm_report_error,
    uvm_report_fatal)

ALL_TYPES = None

"""
Callbacks Classes
=================

This section defines the classes used for callback registration, management,
and user-defined callbacks.
"""


[docs]def o2str(ordering): if ordering == UVM_APPEND: return "UVM_APPEND" return "<NO_ORDER_ADDED>"
[docs]class UVMTypeIDBase: """ Simple typeid interface. Need this to set up the base-super mapping. This is similar to the factory, but much simpler. The idea of this interface is that each object type T has a typeid that can be used for mapping type relationships. This is not a user visible class. """ typename = "" # UVMTypeIDBase -> uvm_callbacks typeid_map: Dict['UVMTypeIDBase', 'UVMCallbacks'] = {} # uvm_callbacks_base -> UVMTypeIDBase type_map: Dict['UVMCallbacksBase', 'UVMTypeIDBase'] = {}
[docs]class UVMTypeID(UVMTypeIDBase): m_b_inst = None
[docs] @classmethod def get(cls): if UVMTypeID.m_b_inst is None: UVMTypeID.m_b_inst = UVMTypeID() return UVMTypeID.m_b_inst
[docs]class UVMCallbacksBase(UVMObject): """ Base class singleton that holds generic queues for all instance specific objects. This is an internal class. This class contains a global pool that has all of the instance specific callback queues in it. All of the typewide callback queues live in the derivative class UVMTypedCallbacks#(T). This is not a user visible class. This class holds the class inheritance hierarchy information (super types and derivative types). Note, all derivative uvm_callbacks#() class singletons access this global m_pool object in order to get access to their specific instance queue. """ m_b_inst = None m_pool = UVMPool() # uvm_object -> uvm_queue#(uvm_callback) m_tracing = True def __init__(self, name): super().__init__(name) #Type checking interface self.m_this_type = [] # one to many T->T/CB self.m_super_type = None # one to one relation self.m_derived_types = [] # one to many relation, UVMTypeIDBase
[docs] @classmethod def m_initialize(cls): if cls.m_b_inst is None: cls.m_b_inst = UVMCallbacksBase("callbacks_base") cls.m_pool = UVMPool() return UVMCallbacksBase.m_b_inst
[docs] def m_am_i_a(self, obj): return 0
[docs] def m_is_for_me(self, cb: 'UVMCallback'): return 0
[docs] def m_is_registered(self, obj, cb): return True
[docs] def m_get_tw_cb_q(self, obj, CB=None): return None
[docs] def m_add_tw_cbs(self, cb, ordering): raise Exception('m_add_tw_cbs is pure virtual function!')
[docs] def m_delete_tw_cbs(self, cb): return 0
[docs] def check_registration(self, obj, cb): """ Check registration. To test registration, start at this class and work down the class hierarchy. If any class returns true then the pair is legal. Args: obj (UVMObject): cb (UVMCallback): Returns: bool: True if obj is registered, False otherwise """ # st = None dt = None if self.m_is_registered(obj,cb): return True # Need to look at all possible T/CB pairs of this type for item in self.m_this_type: if (UVMCallbacksBase.m_b_inst != item and item.m_is_registered(obj,cb)): return True if obj is None: for item in self.m_derived_types: dt = UVMTypeIDBase.typeid_map[item] if dt is not None and dt.check_registration(None,cb): return True return False
[docs] @classmethod def get_first(cls, itr, obj, CB=None) -> Optional['UVMCallback']: """ Returns the first enabled callback of type CB which resides in the queue for `obj`. If `obj` is `None` then the typewide queue for T is searched. `itr` is the iterator it will be updated with a value that can be supplied to `get_next` to get the next callback object. If the queue is empty then `None` is returned. The iterator class `uvm_callback_iter` may be used as an alternative, simplified, iterator interface. Args: itr: obj: CB: Type of the callback Returns: UVMCallback: First found callback. """ cb = None cls.get() q = cls.m_get_q(obj, CB) if q is not None: for i in range(len(q)): itr.m_i = i cb = q[i] if cb is not None and cb.callback_mode(): return cb return None
#------------------------------------------------------------------------------ # # Class - UVMTypedCallbacks#(T) # #------------------------------------------------------------------------------ # Another internal class. This contains the queue of typewide # callbacks. It also contains some of the public interface methods, # but those methods are accessed via the uvm_callbacks#() class # so they are documented in that class even though the implementation # is in this class. # # The <add>, <delete>, and <display> methods are implemented in this class.
[docs]class UVMTypedCallbacks(UVMCallbacksBase): # (type T=uvm_object) extends uvm_callbacks_base # static uvm_queue#(uvm_callback) m_tw_cb_q m_tw_cb_q: List['UVMCallback'] = [] # static string m_typename m_typename = "" # #The actual global object from the derivative class. Note that this is # #just a reference to the object that is generated in the derived class. # static this_type m_t_inst m_t_inst = None def __init__(self, name, T=ALL_TYPES): super().__init__(name) self.T = T
[docs] @classmethod def m_initialize(cls): if UVMTypedCallbacks.m_t_inst is None: super().m_initialize() UVMTypedCallbacks.m_t_inst = UVMTypedCallbacks("typed_callbacks") UVMTypedCallbacks.m_t_inst.m_tw_cb_q = [] return UVMTypedCallbacks.m_t_inst
[docs] def m_am_i_a(self, obj): """ Type checking interface: is given `obj` of type T? Args: obj: Returns: """ if obj is None: return True arr = [] if self.T is not ALL_TYPES: return sv.cast(arr, obj, self.T) return True
[docs] def m_get_tw_cb_q(self, obj, CB=None): """ Getting the typewide queue Args: obj: CB: Returns: """ if self.m_am_i_a(obj): for i in range(len(self.m_derived_types)): #super_type dt dt = UVMTypeIDBase.typeid_map[self.m_derived_types[i]] if dt is not None and dt != self: m_get_tw_cb_q = dt.m_get_tw_cb_q(obj, CB) if m_get_tw_cb_q is not None: return m_get_tw_cb_q return UVMTypedCallbacks.m_t_inst.m_tw_cb_q else: return None
[docs] @classmethod def m_cb_find(cls, q, cb): for i in range(len(q)): if q[i] == cb: return i return -1
[docs] @classmethod def m_cb_find_name(cls, q, name, where): """ static function int m_cb_find_name(uvm_queue#(uvm_callback) q, string name, string where) Args: cls: q: name: where: Returns: """ cb = None for i in range(len(q)): cb = q[i] if cb.get_name() == name: uvm_report_warning("UVM/CB/NAM/SAM", "A callback named \"" + name + "\" is already registered with " + where) return 1 return 0
# endfunction
[docs] def m_add_tw_cbs(self, cb, ordering): """ #For a typewide callback, need to add to derivative types as well. virtual function void m_add_tw_cbs(uvm_callback cb, uvm_apprepend ordering) Args: cb: ordering: """ cb_pair = None # super_type obj = None # uvm_object me = None # T warned = False # tpoikela: Which one is correct? #cls = UVMCallbacks cls = UVMTypedCallbacks q = [] # uvm_queue#(uvm_callback) if self.m_cb_find(cls.m_t_inst.m_tw_cb_q, cb) == -1: warned = cls.m_cb_find_name(cls.m_t_inst.m_tw_cb_q, cb.get_name(), "type") if ordering == UVM_APPEND: cls.m_t_inst.m_tw_cb_q.append(cb) else: cls.m_t_inst.m_tw_cb_q.insert(0, cb) if cls.m_t_inst.m_pool.has_first(): obj = cls.m_t_inst.m_pool.get_first() while True: #if($cast(me,obj)): me = obj q = cls.m_t_inst.m_pool.get(obj) if q is None: q = [] cls.m_t_inst.m_pool.add(obj,q) if self.m_cb_find(q,cb) == -1: if not warned: cls.m_cb_find_name(q, cb.get_name(), "object instance " + me.get_full_name()) if ordering == UVM_APPEND: q.append(cb) else: q.insert(0, cb) if cls.m_t_inst.m_pool.has_next() is False: break #end while(m_t_inst.m_pool.next(obj)) for i in range(len(self.m_derived_types)): cb_pair = UVMTypeIDBase.typeid_map[self.m_derived_types[i]] if cb_pair != self: cb_pair.m_add_tw_cbs(cb,ordering)
# endfunction # #For a typewide callback, need to remove from derivative types as well. # virtual function bit m_delete_tw_cbs(uvm_callback cb) # super_type cb_pair # uvm_object obj # uvm_queue#(uvm_callback) q # int pos = m_cb_find(m_t_inst.m_tw_cb_q,cb) # # if(pos != -1): # m_t_inst.m_tw_cb_q.delete(pos) # m_delete_tw_cbs = 1 # end # # if(m_t_inst.m_pool.first(obj)): # do begin # q = m_t_inst.m_pool.get(obj) # if(qis None): # q=new # m_t_inst.m_pool.add(obj,q) # end # pos = m_cb_find(q,cb) # if(pos != -1): # q.delete(pos) # m_delete_tw_cbs = 1 # end # end while(m_t_inst.m_pool.next(obj)) # end # foreach(m_derived_types[i]): # cb_pair = UVMTypeIDBase::typeid_map[m_derived_types[i] ] # if(cb_pair != this) # m_delete_tw_cbs |= cb_pair.m_delete_tw_cbs(cb) # end # endfunction
[docs] @classmethod def display(cls, obj=None): """ static function void display(T obj=None) Args: cls: obj: """ # T me # super_type ib = m_t_inst cbq = [] # string [$] inst_q = [] # string [$] mode_q = [] # string [$] cb = None # uvm_callback blanks = " " bobj = obj qs = [] # string[$] q = None # uvm_queue#(uvm_callback) tname = "" strr = "" max_cb_name = 0 max_inst_name = 0 prev_trace = cls.m_tracing cls.m_tracing = False # don't allow tracing during display if cls.m_typename != "": tname = cls.m_typename elif (obj is not None): tname = obj.get_type_name() else: tname = "*" q = UVMTypedCallbacks.m_t_inst.m_tw_cb_q for i in range(len(q)): cb = q[i] cbq.append(cb.get_name()) inst_q.append("(*)") if (cb.is_enabled()): mode_q.append("ON") else: mode_q.append("OFF") strr = cb.get_name() if max_cb_name <= len(strr): max_cb_name = len(strr) strr = "(*)" if max_inst_name <= len(strr): max_inst_name = len(strr) # tpoikela: TODO finalize this # if(obj is None): # if(m_t_inst.m_pool.first(bobj)): # do # if($cast(me,bobj)) break # while(m_t_inst.m_pool.next(bobj)) # end # if(me is not None || m_t_inst.m_tw_cb_q.size()): # qs.push_back($sformatf("Registered callbacks for all instances of %s\n", tname)) # qs.push_back("---------------------------------------------------------------\n") # end # if(me is not None): # do begin # if($cast(me,bobj)): # q = m_t_inst.m_pool.get(bobj) # if (qis None): # q=new # m_t_inst.m_pool.add(bobj,q) # end # for(int i=0; i<q.size(); ++i): # cb = q.get(i) # cbq.push_back(cb.get_name()) # inst_q.push_back(bobj.get_full_name()) # if(cb.is_enabled()) mode_q.push_back("ON") # else mode_q.push_back("OFF") # # strr = cb.get_name() # max_cb_name = max_cb_name > strr.len() ? max_cb_name : strr.len() # strr = bobj.get_full_name() # max_inst_name = max_inst_name > strr.len() ? max_inst_name : strr.len() # end # end # end while (m_t_inst.m_pool.next(bobj)) # end # else begin # qs.push_back($sformatf("No callbacks registered for any instances of type %s\n", tname)) # end # end # else begin # if(m_t_inst.m_pool.exists(bobj) || m_t_inst.m_tw_cb_q.size()): # qs.push_back($sformatf("Registered callbacks for instance %s of %s\n", obj.get_full_name(), tname)) # qs.push_back("---------------------------------------------------------------\n") # end # if(m_t_inst.m_pool.exists(bobj)): # q = m_t_inst.m_pool.get(bobj) # if(qis None): # q=new # m_t_inst.m_pool.add(bobj,q) # end # for(int i=0; i<q.size(); ++i): # cb = q.get(i) # cbq.push_back(cb.get_name()) # inst_q.push_back(bobj.get_full_name()) # if(cb.is_enabled()) mode_q.push_back("ON") # else mode_q.push_back("OFF") # # str = cb.get_name() # max_cb_name = max_cb_name > str.len() ? max_cb_name : str.len() # str = bobj.get_full_name() # max_inst_name = max_inst_name > str.len() ? max_inst_name : str.len() # end # end # end # if(!cbq.size()): # if(obj is None) str = "*" # else str = obj.get_full_name() # qs.push_back($sformatf("No callbacks registered for instance %s of type %s\n", str, tname)) # end # for i in range(len(cbq)): padding = blanks[0:max_cb_name - len(cbq[i])] qs.append(sv.sformatf("%s %s %s on %s %s\n", cbq[i], padding, inst_q[i], blanks[0:max_inst_name - len(inst_q[i])-1], mode_q[i])) uvm_info("UVM/CB/DISPLAY", UVM_STRING_QUEUE_STREAMING_PACK(qs), UVM_NONE) cls.m_tracing = prev_trace # allow tracing to be resumed
[docs]class UVMCallbacks(UVMTypedCallbacks): """ The `UVMCallbacks` class provides a base class for implementing callbacks, which are typically used to modify or augment component behavior without changing the component class. To work effectively, the developer of the component class defines a set of "hook" methods that enable users to customize certain behaviors of the component in a manner that is controlled by the component developer. The integrity of the component's overall behavior is intact, while still allowing certain customizable actions by the user. To enable compile-time type-safety, the class is parameterized on both the user-defined callback interface implementation as well as the object type associated with the callback. The object type-callback type pair are associated together using the `UVMRegisterCb` macro to define a valid pairing; valid pairings are checked when a user attempts to add a callback to an object. To provide the most flexibility for end-user customization and reuse, it is recommended that the component developer also define a corresponding set of virtual method hooks in the component itself. This affords users the ability to customize via inheritance/factory overrides as well as callback object registration. The implementation of each virtual method would provide the default traversal algorithm for the particular callback being called. Being virtual, users can define subtypes that override the default algorithm, perform tasks before and/or after calling super.<method> to execute any registered callbacks, or to not call the base implementation, effectively disabling that particular hook. A demonstration of this methodology is provided in an example included in the kit. """ # Singleton instance is used for type checking m_inst = None # typeinfo m_typeid = None # type: UVMTypeIDBase m_cb_typeid = None # type: UVMTypeIDBase m_typename = '' m_cb_typename = '' reporter = UVMReportObject("cb_tracer") m_base_inst = None # uvm_callbacks#(T,uvm_callback) # tpoikela: Added for containing callbacks for each class _m_cb_table: Dict[str, 'UVMCallbacks'] = {} # UVMCallbacks[str] def __init__(self, name='uvm_callbacks', T=ALL_TYPES, CB=ALL_TYPES): super().__init__(name) self.m_registered = False # These are type parameters self.T = T self.CB = CB
[docs] @classmethod def get(cls) -> 'UVMCallbacks': """ Get the singleton instance of the class Returns: UVMCallbacks: The singleton instance of the class """ if cls.m_inst is None: cb_base_type = UVMTypeIDBase() super().m_initialize() cb_base_type = UVMTypeID.get() cls.m_cb_typeid = UVMTypeID.get() cls.m_typeid = UVMTypeID.get() cls.m_inst = UVMCallbacks() if cb_base_type == cls.m_cb_typeid: #cast(m_base_inst, m_inst) cls.m_base_inst = cls.m_inst # The base inst in the super class gets set to this base inst cls.m_t_inst = cls.m_base_inst UVMTypeIDBase.typeid_map[cls.m_typeid] = cls.m_inst UVMTypeIDBase.type_map[cls.m_b_inst] = cls.m_typeid else: cls.m_base_inst = cls.get() cls.m_base_inst.m_this_type.append(cls.m_inst) if cls.m_inst is None: uvm_report_fatal("CB/INTERNAL","get(): m_inst is None") return cls.m_inst
[docs] @classmethod def m_register_pair(cls, tname="", cbname="", T=ALL_TYPES): """ m_register_pair Register valid callback type Args: tname (str): cbname (str): T: ALL_TYPES: Returns: """ inst = cls.get() # tpoikela: mimics typed callbacks by creating one cbs-object # per registered pair cbs = UVMCallbacks(tname + "__" + cbname, T) cbs.m_typename = tname # TODO super_type.m_typename = tname cbs.m_typeid.typename = tname cbs.m_cb_typename = cbname cbs.m_cb_typeid.typename = cbname cbs.m_registered = True if cbs.get_name() not in inst._m_cb_table: inst._m_cb_table[cbs.get_name()] = cbs else: uvm_warning("CB_EXISTS", "Callback for " + cbs.get_name() + " already register") return True
@classmethod def _get_typed_cbs(cls, obj, CB): cb_table_key = cls._get_cb_table_key(obj, CB) if cb_table_key in cls._m_cb_table: return cls._m_cb_table[cb_table_key] return cls @classmethod def _get_cb_table_key(cls, obj, CB): if obj is not None: if CB is not None: return type(obj).__name__ + '__' + CB.__name__ else: return type(obj).__name__ elif CB is not None: return '__' + CB.__name__ else: return '__<unknown_key>__' # virtual function bit m_is_registered(uvm_object obj, uvm_callback cb) # if(m_is_for_me(cb) && m_am_i_a(obj)): # return m_registered # end # endfunction # #Does type check to see if the callback is valid for this type # virtual function bit m_is_for_me(uvm_callback cb) # CB this_cb # return($cast(this_cb,cb)) # endfunction # Group: Add/delete interface # Function: add # # Registers the given callback object, ~cb~, with the given # ~obj~ handle. The ~obj~ handle can be ~None~, which allows # registration of callbacks without an object context. If # ~ordering~ is UVM_APPEND (default), the callback will be executed # after previously added callbacks, else the callback # will be executed ahead of previously added callbacks. The ~cb~ # is the callback handle; it must be non-~None~, and if the callback # has already been added to the object instance then a warning is # issued. Note that the CB parameter is optional. For example, the # following are equivalent: # #| uvm_callbacks#(my_comp)::add(comp_a, cb) #| uvm_callbacks#(my_comp, my_callback)::add(comp_a,cb)
[docs] @classmethod def add(cls, obj, cb, ordering=UVM_APPEND): q = [] # uvm_queue#(uvm_callback) q nm = "" tnm = "" cls.get() if cb is None: nm, tnm = cls.get_obj_and_typename(obj) uvm_report_error("CBUNREG", "None callback object cannot be registered with object " + nm + " (" + tnm + ")", UVM_NONE) return if not cls.m_base_inst.check_registration(obj,cb): nm, tnm = cls.get_obj_and_typename(obj) uvm_report_warning("CBUNREG", "Callback " + cb.get_name() + " cannot be registered with object " + nm + " because callback type " + cb.get_type_name() + " is not registered with object type " + tnm, UVM_NONE) if obj is None: if cls.m_cb_find(cls.m_t_inst.m_tw_cb_q, cb) != -1: if cls.m_base_inst.m_typename != "": tnm = cls.m_base_inst.m_typename else: tnm = "uvm_object" uvm_report_warning("CBPREG", "Callback object " + cb.get_name() + " is already registered with type " + tnm, UVM_NONE) else: uvm_cb_trace_noobj(cb,sv.sformatf("Add (%s) typewide callback %0s for type %s", o2str(ordering), cb.get_name(), cls.m_base_inst.m_typename)) cls.m_t_inst.m_add_tw_cbs(cb, ordering) else: uvm_cb_trace_noobj(cb, sv.sformatf("Add (%s) callback %0s to object %0s ", o2str(ordering), cb.get_name(), obj.get_full_name())) q = cls.m_base_inst.m_pool.get(obj) if q is None: q = [] cls.m_base_inst.m_pool.add(obj,q) if len(q) == 0: # Need to make sure that registered report catchers are added. This # way users don't need to set up uvm_report_object as a super type. o = [] # uvm_report_object if (sv.cast(o,obj, UVMReportObject)): # uvm_queue#(uvm_callback) qr UVMCallbacks.get() qr = UVMCallbacks.m_t_inst.m_tw_cb_q for i in range(len(qr)): q.append(qr[i]) # for(int i=0; i<m_t_inst.m_tw_cb_q.size(); ++i) for i in range(len(cls.m_t_inst.m_tw_cb_q)): q.append(cls.m_t_inst.m_tw_cb_q[i]) #check if already exists in the queue if cls.m_cb_find(q,cb) != -1: uvm_report_warning("CBPREG", "Callback object " + cb.get_name() + " is already registered with object " + obj.get_full_name(), UVM_NONE) else: cls.m_cb_find_name(q, cb.get_name(), "object instance " + obj.get_full_name()) if ordering == UVM_APPEND: q.append(cb) else: q.insert(0, cb)
# endfunction
[docs] @classmethod def get_obj_and_typename(cls, obj): nm = "" tnm = "" if obj is None: nm = "(*)" else: nm = obj.get_full_name() if cls.m_base_inst.m_typename != "": tnm = cls.m_base_inst.m_typename elif obj is not None: tnm = obj.get_type_name() else: tnm = "uvm_object" return nm, tnm
# # Function: add_by_name # # # # Registers the given callback object, ~cb~, with one or more uvm_components. # # The components must already exist and must be type T or a derivative. As # # with <add> the CB parameter is optional. ~root~ specifies the location in # # the component hierarchy to start the search for ~name~. See <uvm_root::find_all> # # for more details on searching by name. # # static function void add_by_name(string name, # uvm_callback cb, # uvm_component root, # uvm_apprepend ordering=UVM_APPEND) # uvm_component cq[$] # uvm_root top # uvm_coreservice_t cs # T t # void'(get()) # cs = uvm_coreservice_t::get() # top = cs.get_root() # # if(cbis None): # uvm_report_error("CBUNREG", { "None callback object cannot be registered with object(s) ", # name }, UVM_NONE) # return # end # `uvm_cb_trace_noobj(cb,$sformatf("Add (%s) callback %0s by name to object(s) %0s ", # ordering.name(), cb.get_name(), name)) # top.find_all(name,cq,root) # if(cq.size() == 0): # uvm_report_warning("CBNOMTC", { "add_by_name failed to find any components matching the name ", # name, ", callback ", cb.get_name(), " will not be registered." }, UVM_NONE) # end # foreach(cq[i]): # if($cast(t,cq[i])): # add(t,cb,ordering) # end # end # endfunction # # # # Function: delete # # # # Deletes the given callback object, ~cb~, from the queue associated with # # the given ~obj~ handle. The ~obj~ handle can be ~None~, which allows # # de-registration of callbacks without an object context. # # The ~cb~ is the callback handle; it must be non-~None~, and if the callback # # has already been removed from the object instance then a warning is # # issued. Note that the CB parameter is optional. For example, the # # following are equivalent: # # # #| uvm_callbacks#(my_comp)::delete(comp_a, cb) # #| uvm_callbacks#(my_comp, my_callback)::delete(comp_a,cb) # # static function void delete(T obj, uvm_callback cb) # uvm_object b_obj = obj # uvm_queue#(uvm_callback) q # bit found # int pos # void'(get()) # # if(obj is None): # `uvm_cb_trace_noobj(cb,$sformatf("Delete typewide callback %0s for type %s", # cb.get_name(), m_base_inst.m_typename)) # found = m_t_inst.m_delete_tw_cbs(cb) # end # else begin # `uvm_cb_trace_noobj(cb,$sformatf("Delete callback %0s from object %0s ", # cb.get_name(), obj.get_full_name())) # q = m_base_inst.m_pool.get(b_obj) # pos = m_cb_find(q,cb) # if(pos != -1): # q.delete(pos) # found = 1 # end # end # if(!found): # string nm # if(objis None) nm = "(*)"; else nm = obj.get_full_name() # uvm_report_warning("CBUNREG", { "Callback ", cb.get_name(), " cannot be removed from object ", # nm, " because it is not currently registered to that object." }, UVM_NONE) # end # endfunction # # # # Function: delete_by_name # # # # Removes the given callback object, ~cb~, associated with one or more # # uvm_component callback queues. As with <delete> the CB parameter is # # optional. ~root~ specifies the location in the component hierarchy to start # # the search for ~name~. See <uvm_root::find_all> for more details on searching # # by name. # # static function void delete_by_name(string name, uvm_callback cb, # uvm_component root) # uvm_component cq[$] # uvm_root top # T t # uvm_coreservice_t cs # void'(get()) # cs = uvm_coreservice_t::get() # top = cs.get_root() # # `uvm_cb_trace_noobj(cb,$sformatf("Delete callback %0s by name from object(s) %0s ", # cb.get_name(), name)) # top.find_all(name,cq,root) # if(cq.size() == 0): # uvm_report_warning("CBNOMTC", { "delete_by_name failed to find any components matching the name ", # name, ", callback ", cb.get_name(), " will not be unregistered." }, UVM_NONE) # end # foreach(cq[i]): # if($cast(t,cq[i])): # delete(t,cb) # end # end # endfunction
[docs] @classmethod def m_get_q(cls, obj, CB=None): """ #-------------------------- Group: Iterator Interface #-------------------------- This set of functions provide an iterator interface for callback queues. A facade class, `uvm_callback_iter` is also available, and is the generally preferred way to iterate over callback queues. Args: cls: obj: CB: Returns: """ q = None if not cls.m_base_inst.m_pool.exists(obj): # no instance specific if obj is None: q = cls.m_t_inst.m_tw_cb_q else: q = cls.m_t_inst.m_get_tw_cb_q(obj, CB) else: q = cls.m_base_inst.m_pool.get(obj) if q is None: q = UVMQueue() cls.m_base_inst.m_pool.add(obj, q) return q
[docs] @classmethod def get_first(cls, itr, obj, CB=None): """ Function: get_first Returns the first enabled callback of type CB which resides in the queue for `obj`. If `obj` is `None` then the typewide queue for T is searched. `itr` is the iterator it will be updated with a value that can be supplied to `get_next` to get the next callback object. If the queue is empty then `None` is returned. The iterator class `uvm_callback_iter` may be used as an alternative, simplified, iterator interface. Args: cls: itr: obj: CB: Returns: """ cls.get() # tpoikela: added to mimic typed cbs typed_cls = cls._get_typed_cbs(obj, CB) cls = typed_cls q = cls.m_get_q(obj, CB) for i in range(len(q)): itr.m_i = i cb = [] if CB is not None: if sv.cast(cb, q[itr.m_i], CB) and cb[0].callback_mode(): return cb[0] else: return q[itr.m_i] # Just return 1st index without type match return None
# endfunction
[docs] @classmethod def get_last(cls, itr, obj, CB=None): """ Function: get_last Returns the last enabled callback of type CB which resides in the queue for `obj`. If `obj` is `None` then the typewide queue for T is searched. `itr` is the iterator it will be updated with a value that can be supplied to `get_prev` to get the previous callback object. If the queue is empty then `None` is returned. The iterator class `uvm_callback_iter` may be used as an alternative, simplified, iterator interface. static function CB get_last (ref int itr, input T obj) Args: cls: itr: obj: CB: Returns: """ cls.get() # tpoikela: added to mimic typed cbs typed_cls = cls._get_typed_cbs(obj, CB) cls = typed_cls q = cls.m_get_q(obj, CB) for i in range(len(q)-1, -1, -1): itr.m_i = i cb = [] if CB is not None: if sv.cast(cb, q[itr.m_i], CB) and cb[0].callback_mode(): return cb[0] else: return q[itr.m_i] # Just return 1st index without type match return None
[docs] @classmethod def get_next(cls, itr, obj, CB=None): """ Function: get_next Returns the next enabled callback of type CB which resides in the queue for `obj`, using `itr` as the starting point. If `obj` is `None` then the typewide queue for T is searched. `itr` is the iterator; it will be updated with a value that can be supplied to `get_next` to get the next callback object. If no more callbacks exist in the queue, then `None` is returned. `get_next` will continue to return `None` in this case until `get_first` or `get_last` has been used to reset the iterator. The iterator class `uvm_callback_iter` may be used as an alternative, simplified, iterator interface. static function CB get_next (ref int itr, input T obj) Args: cls: itr: obj: CB: Returns: """ cls.get() # tpoikela: added to mimic typed cbs typed_cls = cls._get_typed_cbs(obj, CB) cls = typed_cls q = cls.m_get_q(obj) for i in range(itr.m_i + 1, len(q)): itr.m_i = i cb = [] if CB is not None: if sv.cast(cb, q[itr.m_i], CB) and cb[0].callback_mode(): return cb[0] else: return q[itr.m_i] return None
# endfunction
[docs] @classmethod def get_prev(cls, itr, obj, CB=None): """ Function: get_prev Returns the previous enabled callback of type CB which resides in the queue for `obj`, using `itr` as the starting point. If `obj` is `None` then the typewide queue for T is searched. `itr` is the iterator; it will be updated with a value that can be supplied to `get_prev` to get the previous callback object. If no more callbacks exist in the queue, then `None` is returned. `get_prev` will continue to return `None` in this case until `get_first` or `get_last` has been used to reset the iterator. The iterator class `uvm_callback_iter` may be used as an alternative, simplified, iterator interface. static function CB get_prev (ref int itr, input T obj) Args: cls: itr: obj: CB: Returns: """ cls.get() # tpoikela: added to mimic typed cbs typed_cls = cls._get_typed_cbs(obj, CB) cls = typed_cls q = cls.m_get_q(obj) # for(itr = itr-1; itr>= 0; --itr) for i in range(itr.m_i - 1, -1, -1): itr.m_i = i cb = [] if CB is not None: if sv.cast(cb, q[itr.m_i], CB) and cb[0].callback_mode(): return cb[0] else: return q[itr.m_i] return None
[docs] @classmethod def display(cls, obj=None): """ #------------- Group: Debug #------------- Function: display This function displays callback information for `obj`. If `obj` is `None`, then it displays callback information for all objects of type `T`, including typewide callbacks. static function void display(T obj=None) Args: cls: obj: """ # For documentation purposes, need a function wrapper here. cls.get() super().display(obj)
#endclass #------------------------------------------------------------------------------ # # Class- uvm_derived_callbacks #(T,ST,CB) # #------------------------------------------------------------------------------ # This type is not really expected to be used directly by the user, instead they are # expected to use the macro `uvm_set_super_type. The sole purpose of this type is to # allow for setting up of the derived_type/super_type mapping. #------------------------------------------------------------------------------ #class uvm_derived_callbacks#(type T=uvm_object, type ST=uvm_object, type CB=uvm_callback) # extends uvm_callbacks#(T,CB) # # typedef uvm_derived_callbacks#(T,ST,CB) this_type # typedef uvm_callbacks#(T) this_user_type # typedef uvm_callbacks#(ST) this_super_type # # # Singleton instance is used for type checking # static this_type m_d_inst # static this_user_type m_user_inst # static this_super_type m_super_inst # # # typeinfo # static UVMTypeIDBase m_s_typeid # # static function this_type get() # m_user_inst = this_user_type::get() # m_super_inst = this_super_type::get() # m_s_typeid = uvm_typeid#(ST)::get() # if(m_d_inst is None): # m_d_inst = new # end # return m_d_inst # endfunction # # static function bit register_super_type(string tname="", sname="") # this_user_type u_inst = this_user_type::get() # this_type inst = this_type::get() # uvm_callbacks_base s_obj # # this_user_type::m_t_inst.m_typename = tname # # if(sname != "") m_s_typeid.typename = sname # # if(u_inst.m_super_type is not None): # if(u_inst.m_super_type == m_s_typeid) return 1 # uvm_report_warning("CBTPREG", { "Type ", tname, " is already registered to super type ", # this_super_type::m_t_inst.m_typename, ". Ignoring attempt to register to super type ", # sname}, UVM_NONE) # return 1 # end # if(this_super_type::m_t_inst.m_typename == "") # this_super_type::m_t_inst.m_typename = sname # u_inst.m_super_type = m_s_typeid # u_inst.m_base_inst.m_super_type = m_s_typeid # s_obj = UVMTypeIDBase::typeid_map[m_s_typeid] # s_obj.m_derived_types.push_back(m_typeid) # return 1 # endfunction # #endclass
[docs]class UVMCallbackIter: # (type T = uvm_object, type CB = uvm_callback) """ The ~uvm_callback_iter~ class is an iterator class for iterating over callback queues of a specific callback type. The typical usage of the class is: .. code-block:: python uvm_callback_iter#(mycomp,mycb) iter = new(this) for(mycb cb = iter.first(); cb is not None; cb = iter.next()) cb.dosomething() The callback iteration macros, <`uvm_do_callbacks> and <`uvm_do_callbacks_exit_on> provide a simple method for iterating callbacks and executing the callback methods. """ def __init__(self, obj, CB=None): """ Function: new Creates a new callback iterator object. It is required that the object context be provided. Args: obj: CB: """ self.m_i = 0 self.m_cb: Optional[UVMCallback] = None # UVMCallback self.m_obj = obj self.CB = CB
[docs] def first(self) -> Optional['UVMCallback']: """ Function: first Returns the first valid (enabled) callback of the callback type (or a derivative) that is in the queue of the context object. If the queue is empty then `None` is returned. Returns: """ self.m_cb = UVMCallbacks.get_first(self, self.m_obj, self.CB) return self.m_cb
[docs] def last(self) -> Optional['UVMCallback']: """ Function: last Returns the last valid (enabled) callback of the callback type (or a derivative) that is in the queue of the context object. If the queue is empty then `None` is returned. Returns: """ self.m_cb = UVMCallbacks.get_last(self, self.m_obj, self.CB) return self.m_cb
[docs] def next(self) -> Optional['UVMCallback']: """ Returns the next valid (enabled) callback of the callback type (or a derivative) that is in the queue of the context object. If there are no more valid callbacks in the queue, then `None` is returned. Returns: """ self.m_cb = UVMCallbacks.get_next(self, self.m_obj, self.CB) return self.m_cb
[docs] def prev(self) -> Optional['UVMCallback']: """ Returns the previous valid (enabled) callback of the callback type (or a derivative) that is in the queue of the context object. If there are no more valid callbacks in the queue, then `None` is returned. Returns: """ self.m_cb = UVMCallbacks.get_prev(self, self.m_obj, self.CB) return self.m_cb
[docs] def get_cb(self) -> Optional['UVMCallback']: """ Returns the last callback accessed via a first() or next() call. Returns: """ return self.m_cb
#function void trace(uvm_object obj = None) # if (m_cb is not None && T::cbs::get_debug_flags() & UVM_CALLBACK_TRACE): # uvm_report_object reporter = None # string who = "Executing " # void'($cast(reporter, obj)) # if (reporter is None) void'($cast(reporter, m_obj)) # if (reporter is None) reporter = uvm_top # if (obj is not None) who = {obj.get_full_name(), " is executing "} # elif (m_obj is not None) who = {m_obj.get_full_name(), " is executing "} # reporter.uvm_report_info("CLLBK_TRC", {who, "callback ", m_cb.get_name()}, UVM_LOW) # end #endfunction #endclass
[docs]class UVMCallback(UVMObject): """ The `UVMCallback` class is the base class for user-defined callback classes. Typically, the component developer defines an application-specific callback class that extends from this class. In it, he defines one or more virtual methods, called a ~callback interface~, that represent the hooks available for user override. Methods intended for optional override should not be declared ~pure.~ Usually, all the callback methods are defined with empty implementations so users have the option of overriding any or all of them. The prototypes for each hook method are completely application specific with no restrictions. """ reporter = UVMReportObject("cb_tracer") def __init__(self, name="uvm_callback"): """ Creates a new uvm_callback object, giving it an optional `name`. Args: name (str): Name of the callback """ super().__init__(name) self.m_enabled = True
[docs] def callback_mode(self, on=-1): """ Enable/disable callbacks (modeled like rand_mode and constraint_mode). Args: on: Returns: """ if on == 0 or on == 1: is_en = "DISABLED" if on == 1: is_en = "ENABLED" uvm_cb_trace_noobj(self,sv.sformatf("Setting callback mode for %s to %s", self.get_name(), is_en)) else: is_en = "DISABLED" if self.m_enabled is True: is_en = "ENABLED" uvm_cb_trace_noobj(self, sv.sformatf("Callback mode for %s is %s", self.get_name(), is_en)) callback_mode = self.m_enabled if on == 0: self.m_enabled = False if on == 1: self.m_enabled = True return callback_mode
[docs] def is_enabled(self): """ Returns 1 if the callback is enabled, 0 otherwise. Returns: bool: 1 if the callback is enabled, 0 otherwise. """ return self.callback_mode()
type_name = "uvm_callback"
[docs] def get_type_name(self): """ Returns the type name of this callback object. Returns: str: Type name of this callback object. """ return UVMCallback.type_name