# Copyright (c) 2018 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall # not be construed as granting a license to any other intellectual # property including but not limited to intellectual property relating # to a hardware implementation of the functionality of the software # licensed hereunder. You may use the software subject to the license # terms below provided that you ensure that this notice is replicated # unmodified and in its entirety in all distributions of the software, # modified or unmodified, in source code or in binary form. # # Copyright (c) 2004-2006 The Regents of The University of Michigan # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer; # redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution; # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Authors: Steve Reinhardt # Nathan Binkert ##################################################################### # # Proxy object support. # ##################################################################### import copy import params class BaseProxy(object): def __init__(self, search_self, search_up): self._search_self = search_self self._search_up = search_up self._multipliers = [] def __str__(self): if self._search_self and not self._search_up: s = 'Self' elif not self._search_self and self._search_up: s = 'Parent' else: s = 'ConfusedProxy' return s + '.' + self.path() def __setattr__(self, attr, value): if not attr.startswith('_'): raise AttributeError( "cannot set attribute '%s' on proxy object" % attr) super(BaseProxy, self).__setattr__(attr, value) # support for multiplying proxies by constants or other proxies to # other params def __mul__(self, other): if not (isinstance(other, (int, long, float)) or isproxy(other)): raise TypeError( "Proxy multiplier must be a constant or a proxy to a param") self._multipliers.append(other) return self __rmul__ = __mul__ def _mulcheck(self, result, base): for multiplier in self._multipliers: if isproxy(multiplier): multiplier = multiplier.unproxy(base) # assert that we are multiplying with a compatible # param if not isinstance(multiplier, params.NumericParamValue): raise TypeError( "Proxy multiplier must be a numerical param") multiplier = multiplier.getValue() result *= multiplier return result def unproxy(self, base): obj = base done = False if self._search_self: result, done = self.find(obj) if self._search_up: # Search up the tree but mark ourself # as visited to avoid a self-reference self._visited = True obj._visited = True while not done: obj = obj._parent if not obj: break result, done = self.find(obj) self._visited = False base._visited = False if not done: raise AttributeError( "Can't resolve proxy '%s' of type '%s' from '%s'" % \ (self.path(), self._pdesc.ptype_str, base.path())) if isinstance(result, BaseProxy): if result == self: raise RuntimeError("Cycle in unproxy") result = result.unproxy(obj) return self._mulcheck(result, base) def getindex(obj, index): if index == None: return obj try: obj = obj[index] except TypeError: if index != 0: raise # if index is 0 and item is not subscriptable, just # use item itself (so cpu[0] works on uniprocessors) return obj getindex = staticmethod(getindex) # This method should be called once the proxy is assigned to a # particular parameter or port to set the expected type of the # resolved proxy def set_param_desc(self, pdesc): self._pdesc = pdesc class AttrProxy(BaseProxy): def __init__(self, search_self, search_up, attr): super(AttrProxy, self).__init__(search_self, search_up) self._attr = attr self._modifiers = [] def __getattr__(self, attr): # python uses __bases__ internally for inheritance if attr.startswith('_'): return super(AttrProxy, self).__getattr__(self, attr) if hasattr(self, '_pdesc'): raise AttributeError("Attribute reference on bound proxy") # Return a copy of self rather than modifying self in place # since self could be an indirect reference via a variable or # parameter new_self = copy.deepcopy(self) new_self._modifiers.append(attr) return new_self # support indexing on proxies (e.g., Self.cpu[0]) def __getitem__(self, key): if not isinstance(key, int): raise TypeError("Proxy object requires integer index") if hasattr(self, '_pdesc'): raise AttributeError("Index operation on bound proxy") new_self = copy.deepcopy(self) new_self._modifiers.append(key) return new_self def find(self, obj): try: val = getattr(obj, self._attr) visited = False if hasattr(val, '_visited'): visited = getattr(val, '_visited') if not visited: # for any additional unproxying to be done, pass the # current, rather than the original object so that proxy # has the right context obj = val else: return None, False except: return None, False while isproxy(val): val = val.unproxy(obj) for m in self._modifiers: if isinstance(m, str): val = getattr(val, m) elif isinstance(m, int): val = val[m] else: assert("Item must be string or integer") while isproxy(val): val = val.unproxy(obj) return val, True def path(self): p = self._attr for m in self._modifiers: if isinstance(m, str): p += '.%s' % m elif isinstance(m, int): p += '[%d]' % m else: assert("Item must be string or integer") return p class AnyProxy(BaseProxy): def find(self, obj): return obj.find_any(self._pdesc.ptype) def path(self): return 'any' # The AllProxy traverses the entire sub-tree (not only the children) # and adds all objects of a specific type class AllProxy(BaseProxy): def find(self, obj): return obj.find_all(self._pdesc.ptype) def path(self): return 'all' def isproxy(obj): if isinstance(obj, (BaseProxy, params.EthernetAddr)): return True elif isinstance(obj, (list, tuple)): for v in obj: if isproxy(v): return True return False class ProxyFactory(object): def __init__(self, search_self, search_up): self.search_self = search_self self.search_up = search_up def __getattr__(self, attr): if attr == 'any': return AnyProxy(self.search_self, self.search_up) elif attr == 'all': if self.search_up: assert("Parant.all is not supported") return AllProxy(self.search_self, self.search_up) else: return AttrProxy(self.search_self, self.search_up, attr) # global objects for handling proxies Parent = ProxyFactory(search_self = False, search_up = True) Self = ProxyFactory(search_self = True, search_up = False) # limit exports on 'from proxy import *' __all__ = ['Parent', 'Self'] # see comment on imports at end of __init__.py. import params # for EthernetAddr