1c1
< # Copyright (c) 2004-2005 The Regents of The University of Michigan
---
> # Copyright (c) 2004-2006 The Regents of The University of Michigan
30c30
< import os, re, sys, types, inspect
---
> import os, re, sys, types, inspect, copy
87,88c87
< # will generate a .ini file. See simple-4cpu.py for an example
< # (corresponding to m5-test/simple-4cpu.ini).
---
> # will generate a .ini file.
92,136d90
< #####################################################################
< #
< # ConfigNode/SimObject classes
< #
< # The Python class hierarchy rooted by ConfigNode (which is the base
< # class of SimObject, which in turn is the base class of all other M5
< # SimObject classes) has special attribute behavior. In general, an
< # object in this hierarchy has three categories of attribute-like
< # things:
< #
< # 1. Regular Python methods and variables. These must start with an
< # underscore to be treated normally.
< #
< # 2. SimObject parameters. These values are stored as normal Python
< # attributes, but all assignments to these attributes are checked
< # against the pre-defined set of parameters stored in the class's
< # _params dictionary. Assignments to attributes that do not
< # correspond to predefined parameters, or that are not of the correct
< # type, incur runtime errors.
< #
< # 3. Hierarchy children. The child nodes of a ConfigNode are stored
< # in the node's _children dictionary, but can be accessed using the
< # Python attribute dot-notation (just as they are printed out by the
< # simulator). Children cannot be created using attribute assigment;
< # they must be added by specifying the parent node in the child's
< # constructor or using the '+=' operator.
<
< # The SimObject parameters are the most complex, for a few reasons.
< # First, both parameter descriptions and parameter values are
< # inherited. Thus parameter description lookup must go up the
< # inheritance chain like normal attribute lookup, but this behavior
< # must be explicitly coded since the lookup occurs in each class's
< # _params attribute. Second, because parameter values can be set
< # on SimObject classes (to implement default values), the parameter
< # checking behavior must be enforced on class attribute assignments as
< # well as instance attribute assignments. Finally, because we allow
< # class specialization via inheritance (e.g., see the L1Cache class in
< # the simple-4cpu.py example), we must do parameter checking even on
< # class instantiation. To provide all these features, we use a
< # metaclass to define most of the SimObject parameter behavior for
< # this class hierarchy.
< #
< #####################################################################
<
<
139a94,99
> #############################
> #
> # Utility methods
> #
> #############################
>
143,149d102
< def isSimObjectClass(value):
< try:
< return issubclass(value, SimObject)
< except TypeError:
< # happens if value is not a class at all
< return False
<
160,169d112
< def isSimObjectClassSequence(value):
< if not isinstance(value, (list, tuple)) or len(value) == 0:
< return False
<
< for val in value:
< if not isNullPointer(val) and not isSimObjectClass(val):
< return False
<
< return True
<
173,175d115
< def isSimObjectClassOrSequence(value):
< return isSimObjectClass(value) or isSimObjectClassSequence(value)
<
195,200c135,138
< # The metaclass for ConfigNode (and thus for everything that derives
< # from ConfigNode, including SimObject). This class controls how new
< # classes that derive from ConfigNode are instantiated, and provides
< # inherited class behavior (just like a class controls how instances
< # of that class are instantiated, and provides inherited instance
< # behavior).
---
> # The metaclass for SimObject. This class controls how new classes
> # that derive from SimObject are instantiated, and provides inherited
> # class behavior (just like a class controls how instances of that
> # class are instantiated, and provides inherited instance behavior).
206,208c144
< keywords = { 'check' : types.FunctionType,
< 'children' : types.ListType,
< 'ccObject' : types.ObjectType }
---
> keywords = { 'check' : types.FunctionType }
212c148
< # __dict__. We intercept this to filter out parameter assignments
---
> # __dict__. We intercept this to filter out parameter & port assignments
216,229c152,164
< if dict.has_key('_init_dict'):
< # must have been called from makeSubclass() rather than
< # via Python class declaration; bypass filtering process.
< cls_dict = dict
< else:
< # Copy "private" attributes (including special methods
< # such as __new__) to the official dict. Everything else
< # goes in _init_dict to be filtered in __init__.
< cls_dict = {}
< for key,val in dict.items():
< if key.startswith('_'):
< cls_dict[key] = val
< del dict[key]
< cls_dict['_init_dict'] = dict
---
> # Copy "private" attributes, functions, and classes to the
> # official dict. Everything else goes in _init_dict to be
> # filtered in __init__.
> cls_dict = {}
> value_dict = {}
> for key,val in dict.items():
> if key.startswith('_') or isinstance(val, (types.FunctionType,
> types.TypeType)):
> cls_dict[key] = val
> else:
> # must be a param/port setting
> value_dict[key] = val
> cls_dict['_value_dict'] = value_dict
239,243d173
< cls._params = multidict()
< cls._values = multidict()
< cls._ports = multidict()
< cls._instantiated = False # really instantiated or subclassed
< cls._anon_subclass_counter = 0
244a175,183
> # class-only attributes
> cls._params = multidict() # param descriptions
> cls._ports = multidict() # port descriptions
>
> # class or instance attributes
> cls._values = multidict() # param values
> cls._port_map = multidict() # port bindings
> cls._instantiated = False # really instantiated, cloned, or subclassed
>
252,253c191,194
< # the only time the following is not true is when we define
< # the SimObject class itself
---
> # Set up general inheritance via multidicts. A subclass will
> # inherit all its settings from the base class. The only time
> # the following is not true is when we define the SimObject
> # class itself (in which case the multidicts have no parent).
256d196
< cls._values.parent = base._values
257a198,200
> cls._values.parent = base._values
> cls._port_map.parent = base._port_map
> # mark base as having been subclassed
260,264c203,210
< # now process the _init_dict items
< for key,val in cls._init_dict.items():
< if isinstance(val, (types.FunctionType, types.TypeType)):
< type.__setattr__(cls, key, val)
<
---
> # Now process the _value_dict items. They could be defining
> # new (or overriding existing) parameters or ports, setting
> # class keywords (e.g., 'abstract'), or setting parameter
> # values or port bindings. The first 3 can only be set when
> # the class is defined, so we handle them here. The others
> # can be set later too, so just emulate that by calling
> # setattr().
> for key,val in cls._value_dict.items():
266c212
< elif isinstance(val, ParamDesc):
---
> if isinstance(val, ParamDesc):
281,301d226
< # Pull the deep-copy memoization dict out of the class dict if
< # it's there...
< memo = cls.__dict__.get('_memo', {})
<
< # Handle SimObject values
< for key,val in cls._values.iteritems():
< # SimObject instances need to be promoted to classes.
< # Existing classes should not have any instance values, so
< # these can only occur at the lowest level dict (the
< # parameters just being set in this class definition).
< if isSimObjectOrSequence(val):
< assert(val == cls._values.local[key])
< cls._values[key] = applyOrMap(val, 'makeClass', memo)
< # SimObject classes need to be subclassed so that
< # parameters that get set at this level only affect this
< # level and derivatives.
< elif isSimObjectClassOrSequence(val):
< assert(not cls._values.local.has_key(key))
< cls._values[key] = applyOrMap(val, 'makeSubclass', {}, memo)
<
<
331,337c256,258
< # must be SimObject param
< param = cls._params.get(attr, None)
< if param:
< # It's ok: set attribute by delegating to 'object' class.
< if isSimObjectOrSequence(value) and cls._instantiated:
< raise AttributeError, \
< "Cannot set SimObject parameter '%s' after\n" \
---
> if isSimObjectOrSequence(value) and cls._instantiated:
> raise RuntimeError, \
> "cannot set SimObject parameter '%s' after\n" \
339a261,264
>
> # check for param
> param = cls._params.get(attr, None)
> if param:
347d271
< # I would love to get rid of this
349c273,274
< cls._values[attr] = value
---
> # if RHS is a SimObject, it's an implicit child assignment
> cls._values[attr] = value
361,377c286
< # Create a subclass of this class. Basically a function interface
< # to the standard Python class definition mechanism, primarily for
< # internal use. 'memo' dict param supports "deep copy" (really
< # "deep subclass") operations... within a given operation,
< # multiple references to a class should result in a single
< # subclass object with multiple references to it (as opposed to
< # mutiple unique subclasses).
< def makeSubclass(cls, init_dict, memo = {}):
< subcls = memo.get(cls)
< if not subcls:
< name = cls.__name__ + '_' + str(cls._anon_subclass_counter)
< cls._anon_subclass_counter += 1
< subcls = MetaSimObject(name, (cls,),
< { '_init_dict': init_dict, '_memo': memo })
< return subcls
<
< # The ConfigNode class is the root of the special hierarchy. Most of
---
> # The SimObject class is the root of the special hierarchy. Most of
385,402c294,309
< # __new__ operator allocates new instances of the class. We
< # override it here just to support "deep instantiation" operation
< # via the _memo dict. When recursively instantiating an object
< # hierarchy we want to make sure that each class is instantiated
< # only once, and that if there are multiple references to the same
< # original class, we end up with the corresponding instantiated
< # references all pointing to the same instance.
< def __new__(cls, _memo = None, **kwargs):
< if _memo is not None and _memo.has_key(cls):
< # return previously instantiated object
< assert(len(kwargs) == 0)
< return _memo[cls]
< else:
< # Need a new one... if it needs to be memoized, this will
< # happen in __init__. We defer the insertion until then
< # so __init__ can use the memo dict to tell whether or not
< # to perform the initialization.
< return super(SimObject, cls).__new__(cls, **kwargs)
---
> # Initialize new instance. For objects with SimObject-valued
> # children, we need to recursively clone the classes represented
> # by those param values as well in a consistent "deep copy"-style
> # fashion. That is, we want to make sure that each instance is
> # cloned only once, and that if there are multiple references to
> # the same original object, we end up with the corresponding
> # cloned references all pointing to the same cloned instance.
> def __init__(self, **kwargs):
> ancestor = kwargs.get('_ancestor')
> memo_dict = kwargs.get('_memo')
> if memo_dict is None:
> # prepare to memoize any recursively instantiated objects
> memo_dict = {}
> elif ancestor:
> # memoize me now to avoid problems with recursive calls
> memo_dict[ancestor] = self
404,425c311,313
< # Initialize new instance previously allocated by __new__. For
< # objects with SimObject-valued params, we need to recursively
< # instantiate the classes represented by those param values as
< # well (in a consistent "deep copy"-style fashion; see comment
< # above).
< def __init__(self, _memo = None, **kwargs):
< if _memo is not None:
< # We're inside a "deep instantiation"
< assert(isinstance(_memo, dict))
< assert(len(kwargs) == 0)
< if _memo.has_key(self.__class__):
< # __new__ returned an existing, already initialized
< # instance, so there's nothing to do here
< assert(_memo[self.__class__] == self)
< return
< # no pre-existing object, so remember this one here
< _memo[self.__class__] = self
< else:
< # This is a new top-level instantiation... don't memoize
< # this objcet, but prepare to memoize any recursively
< # instantiated objects.
< _memo = {}
---
> if not ancestor:
> ancestor = self.__class__
> ancestor._instantiated = True
427,428c315,316
< self.__class__._instantiated = True
<
---
> # initialize required attributes
> self._parent = None
429a318,320
> self._ccObject = None # pointer to C++ object
> self._instantiated = False # really "cloned"
>
432,441c323,334
< self._values = multidict(self.__class__._values)
< # For SimObject-valued parameters, the class should have
< # classes (not instances) for the values. We need to
< # instantiate these classes rather than just inheriting the
< # class object.
< for key,val in self.__class__._values.iteritems():
< if isSimObjectClass(val):
< setattr(self, key, val(_memo))
< elif isSimObjectClassSequence(val) and len(val):
< setattr(self, key, [ v(_memo) for v in val ])
---
> self._values = multidict(ancestor._values)
> # clone SimObject-valued parameters
> for key,val in ancestor._values.iteritems():
> if isSimObject(val):
> setattr(self, key, val(_memo=memo_dict))
> elif isSimObjectSequence(val) and len(val):
> setattr(self, key, [ v(_memo=memo_dict) for v in val ])
> # clone port references. no need to use a multidict here
> # since we will be creating new references for all ports.
> self._port_map = {}
> for key,val in ancestor._port_map.iteritems():
> self._port_map[key] = applyOrMap(val, 'clone', memo_dict)
446,458c339,343
< self._ccObject = None # pointer to C++ object
< self._port_map = {} # map of port connections
<
< # Use this instance as a template to create a new class.
< def makeClass(self, memo = {}):
< cls = memo.get(self)
< if not cls:
< cls = self.__class__.makeSubclass(self._values.local)
< memo[self] = cls
< return cls
<
< # Direct instantiation of instances (cloning) is no longer
< # allowed; must generate class from instance first.
---
> # "Clone" the current instance by creating another instance of
> # this instance's class, but that inherits its parameter values
> # and port mappings from the current instance. If we're in a
> # "deep copy" recursive clone, check the _memo dict to see if
> # we've already cloned this instance.
460,461c345,359
< raise TypeError, "cannot instantiate SimObject; "\
< "use makeClass() to make class first"
---
> memo_dict = kwargs.get('_memo')
> if memo_dict is None:
> # no memo_dict: must be top-level clone operation.
> # this is only allowed at the root of a hierarchy
> if self._parent:
> raise RuntimeError, "attempt to clone object %s " \
> "not at the root of a tree (parent = %s)" \
> % (self, self._parent)
> # create a new dict and use that.
> memo_dict = {}
> kwargs['_memo'] = memo_dict
> elif memo_dict.has_key(self):
> # clone already done & memoized
> return memo_dict[self]
> return self.__class__(_ancestor = self, **kwargs)
487a386,390
> if isSimObjectOrSequence(value) and self._instantiated:
> raise RuntimeError, \
> "cannot set SimObject parameter '%s' after\n" \
> " instance been cloned %s" % (attr, `self`)
>
491d393
< # It's ok: set attribute by delegating to 'object' class.
499d400
< # I would love to get rid of this
538c439
< if not hasattr(self, '_parent'):
---
> if not self._parent:
544c445
< if not hasattr(self, '_parent'):
---
> if not self._parent:
621,624c522
< if self._ccObject:
< return
< self._ccObject = -1
< self._ccObject = m5.main.createSimObject(self.path())
---
> self.getCCObject() # force creation
627a526,537
> # Get C++ object corresponding to this object, calling C++ if
> # necessary to construct it. Does *not* recursively create
> # children.
> def getCCObject(self):
> if not self._ccObject:
> self._ccObject = -1 # flag to catch cycles in recursion
> self._ccObject = m5.main.createSimObject(self.path())
> elif self._ccObject == -1:
> raise RuntimeError, "%s: recursive call to getCCObject()" \
> % self.path()
> return self._ccObject
>
726,728c636,638
< try: obj = obj._parent
< except: break
<
---
> obj = obj._parent
> if not obj:
> break
844,845c754,755
< # The _params dictionary in each class maps parameter names to
< # either a Param or a VectorParam object. These objects contain the
---
> # The _params dictionary in each class maps parameter names to either
> # a Param or a VectorParam object. These objects contain the
847,849c757,759
< # value (loaded from the PARAM section of the .odesc files). The
< # _convert() method on these objects is used to force whatever value
< # is assigned to the parameter to the appropriate type.
---
> # value (if any). The convert() method on these objects is used to
> # force whatever value is assigned to the parameter to the appropriate
> # type.
853c763
< # MetaConfigNode._setparams()); after that point they aren't used.
---
> # MetaSimObject._new_param()); after that point they aren't used.
1482a1393
> assert(isSimObject(simobj))
1504a1416,1440
> def clone(self, memo):
> newRef = copy.copy(self)
> assert(isSimObject(newRef.simobj))
> newRef.simobj = newRef.simobj(_memo=memo)
> # Tricky: if I'm the *second* PortRef in the pair to be
> # cloned, then my peer is still in the middle of its clone
> # method, and thus hasn't returned to its owner's
> # SimObject.__init__ to get installed in _port_map. As a
> # result I have no way of finding the *new* peer object. So I
> # mark myself as "waiting" for my peer, and I let the *first*
> # PortRef clone call set up both peer pointers after I return.
> newPeer = newRef.simobj._port_map.get(self.name)
> if newPeer:
> if self.isVec:
> assert(self.index != -1)
> newPeer = newPeer[self.index]
> # other guy is all set up except for his peer pointer
> assert(newPeer.peer == -1) # peer must be waiting for handshake
> newPeer.peer = newRef
> newRef.peer = newPeer
> else:
> # other guy is in clone; just wait for him to do the work
> newRef.peer = -1 # mark as waiting for handshake
> return newRef
>
1510,1511c1446,1447
< m5.main.connectPorts(self.simobj._ccObject, self.name, self.index,
< peer.simobj._ccObject, peer.name, peer.index)
---
> m5.main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
> peer.simobj.getCCObject(), peer.name, peer.index)
1530a1467,1469
> if not isinstance(ref, PortRef):
> raise TypeError, \
> "assigning non-port reference port '%s'" % name