|
|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
|
# Copyright (c) 2004-2005 The Regents of The University of Michigan
|
|
|
|
|
# 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
|
|
|
|
|
@@ -27,7 +27,7 @@
|
|
|
|
|
# Authors: Steve Reinhardt
|
|
|
|
|
# Nathan Binkert
|
|
|
|
|
|
|
|
|
|
import os, re, sys, types, inspect
|
|
|
|
|
import os, re, sys, types, inspect, copy
|
|
|
|
|
|
|
|
|
|
import m5
|
|
|
|
|
from m5 import panic
|
|
|
|
|
@@ -84,69 +84,22 @@ class Singleton(type):
|
|
|
|
|
#
|
|
|
|
|
# Once a set of Python objects have been instantiated in a hierarchy,
|
|
|
|
|
# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
|
|
|
|
|
# will generate a .ini file. See simple-4cpu.py for an example
|
|
|
|
|
# (corresponding to m5-test/simple-4cpu.ini).
|
|
|
|
|
# will generate a .ini file.
|
|
|
|
|
#
|
|
|
|
|
#####################################################################
|
|
|
|
|
|
|
|
|
|
#####################################################################
|
|
|
|
|
#
|
|
|
|
|
# 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.
|
|
|
|
|
#
|
|
|
|
|
#####################################################################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# dict to look up SimObjects based on path
|
|
|
|
|
instanceDict = {}
|
|
|
|
|
|
|
|
|
|
#############################
|
|
|
|
|
#
|
|
|
|
|
# Utility methods
|
|
|
|
|
#
|
|
|
|
|
#############################
|
|
|
|
|
|
|
|
|
|
def isSimObject(value):
|
|
|
|
|
return isinstance(value, SimObject)
|
|
|
|
|
|
|
|
|
|
def isSimObjectClass(value):
|
|
|
|
|
try:
|
|
|
|
|
return issubclass(value, SimObject)
|
|
|
|
|
except TypeError:
|
|
|
|
|
# happens if value is not a class at all
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def isSimObjectSequence(value):
|
|
|
|
|
if not isinstance(value, (list, tuple)) or len(value) == 0:
|
|
|
|
|
return False
|
|
|
|
|
@@ -157,22 +110,9 @@ def isSimObjectSequence(value):
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
def isSimObjectOrSequence(value):
|
|
|
|
|
return isSimObject(value) or isSimObjectSequence(value)
|
|
|
|
|
|
|
|
|
|
def isSimObjectClassOrSequence(value):
|
|
|
|
|
return isSimObjectClass(value) or isSimObjectClassSequence(value)
|
|
|
|
|
|
|
|
|
|
def isNullPointer(value):
|
|
|
|
|
return isinstance(value, NullSimObject)
|
|
|
|
|
|
|
|
|
|
@@ -192,41 +132,36 @@ def applyOrMap(objOrSeq, meth, *args, **kwargs):
|
|
|
|
|
return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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).
|
|
|
|
|
class MetaSimObject(type):
|
|
|
|
|
# Attributes that can be set only at initialization time
|
|
|
|
|
init_keywords = { 'abstract' : types.BooleanType,
|
|
|
|
|
'type' : types.StringType }
|
|
|
|
|
# Attributes that can be set any time
|
|
|
|
|
keywords = { 'check' : types.FunctionType,
|
|
|
|
|
'children' : types.ListType,
|
|
|
|
|
'ccObject' : types.ObjectType }
|
|
|
|
|
keywords = { 'check' : types.FunctionType }
|
|
|
|
|
|
|
|
|
|
# __new__ is called before __init__, and is where the statements
|
|
|
|
|
# in the body of the class definition get loaded into the class's
|
|
|
|
|
# __dict__. We intercept this to filter out parameter assignments
|
|
|
|
|
# __dict__. We intercept this to filter out parameter & port assignments
|
|
|
|
|
# and only allow "private" attributes to be passed to the base
|
|
|
|
|
# __new__ (starting with underscore).
|
|
|
|
|
def __new__(mcls, name, bases, dict):
|
|
|
|
|
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
|
|
|
|
|
return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
|
|
|
|
|
|
|
|
|
|
# subclass initialization
|
|
|
|
|
@@ -236,11 +171,15 @@ class MetaSimObject(type):
|
|
|
|
|
super(MetaSimObject, cls).__init__(name, bases, dict)
|
|
|
|
|
|
|
|
|
|
# initialize required attributes
|
|
|
|
|
cls._params = multidict()
|
|
|
|
|
cls._values = multidict()
|
|
|
|
|
cls._ports = multidict()
|
|
|
|
|
cls._instantiated = False # really instantiated or subclassed
|
|
|
|
|
cls._anon_subclass_counter = 0
|
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
|
# We don't support multiple inheritance. If you want to, you
|
|
|
|
|
# must fix multidict to deal with it properly.
|
|
|
|
|
@@ -249,21 +188,28 @@ class MetaSimObject(type):
|
|
|
|
|
|
|
|
|
|
base = bases[0]
|
|
|
|
|
|
|
|
|
|
# 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).
|
|
|
|
|
if isinstance(base, MetaSimObject):
|
|
|
|
|
cls._params.parent = base._params
|
|
|
|
|
cls._values.parent = base._values
|
|
|
|
|
cls._ports.parent = base._ports
|
|
|
|
|
cls._values.parent = base._values
|
|
|
|
|
cls._port_map.parent = base._port_map
|
|
|
|
|
# mark base as having been subclassed
|
|
|
|
|
base._instantiated = True
|
|
|
|
|
|
|
|
|
|
# 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():
|
|
|
|
|
# param descriptions
|
|
|
|
|
elif isinstance(val, ParamDesc):
|
|
|
|
|
if isinstance(val, ParamDesc):
|
|
|
|
|
cls._new_param(key, val)
|
|
|
|
|
|
|
|
|
|
# port objects
|
|
|
|
|
@@ -278,27 +224,6 @@ class MetaSimObject(type):
|
|
|
|
|
else:
|
|
|
|
|
setattr(cls, key, val)
|
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _set_keyword(cls, keyword, val, kwtype):
|
|
|
|
|
if not isinstance(val, kwtype):
|
|
|
|
|
raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
|
|
|
|
|
@@ -328,15 +253,15 @@ class MetaSimObject(type):
|
|
|
|
|
self._ports[attr].connect(self, attr, value)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 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" \
|
|
|
|
|
" class %s has been instantiated or subclassed" \
|
|
|
|
|
% (attr, cls.__name__)
|
|
|
|
|
|
|
|
|
|
# check for param
|
|
|
|
|
param = cls._params.get(attr, None)
|
|
|
|
|
if param:
|
|
|
|
|
try:
|
|
|
|
|
cls._values[attr] = param.convert(value)
|
|
|
|
|
except Exception, e:
|
|
|
|
|
@@ -344,9 +269,9 @@ class MetaSimObject(type):
|
|
|
|
|
(e, cls.__name__, attr, value)
|
|
|
|
|
e.args = (msg, )
|
|
|
|
|
raise
|
|
|
|
|
# I would love to get rid of this
|
|
|
|
|
elif isSimObjectOrSequence(value):
|
|
|
|
|
cls._values[attr] = value
|
|
|
|
|
# if RHS is a SimObject, it's an implicit child assignment
|
|
|
|
|
cls._values[attr] = value
|
|
|
|
|
else:
|
|
|
|
|
raise AttributeError, \
|
|
|
|
|
"Class %s has no parameter %s" % (cls.__name__, attr)
|
|
|
|
|
@@ -358,23 +283,7 @@ class MetaSimObject(type):
|
|
|
|
|
raise AttributeError, \
|
|
|
|
|
"object '%s' has no attribute '%s'" % (cls.__name__, attr)
|
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
# the code in this class deals with the configuration hierarchy itself
|
|
|
|
|
# (parent/child node relationships).
|
|
|
|
|
class SimObject(object):
|
|
|
|
|
@@ -382,83 +291,72 @@ class SimObject(object):
|
|
|
|
|
# get this metaclass.
|
|
|
|
|
__metaclass__ = MetaSimObject
|
|
|
|
|
|
|
|
|
|
# __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
|
|
|
|
|
|
|
|
|
|
# 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 = {}
|
|
|
|
|
|
|
|
|
|
self.__class__._instantiated = True
|
|
|
|
|
if not ancestor:
|
|
|
|
|
ancestor = self.__class__
|
|
|
|
|
ancestor._instantiated = True
|
|
|
|
|
|
|
|
|
|
# initialize required attributes
|
|
|
|
|
self._parent = None
|
|
|
|
|
self._children = {}
|
|
|
|
|
self._ccObject = None # pointer to C++ object
|
|
|
|
|
self._instantiated = False # really "cloned"
|
|
|
|
|
|
|
|
|
|
# Inherit parameter values from class using multidict so
|
|
|
|
|
# individual value settings can be overridden.
|
|
|
|
|
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)
|
|
|
|
|
# apply attribute assignments from keyword args, if any
|
|
|
|
|
for key,val in kwargs.iteritems():
|
|
|
|
|
setattr(self, key, val)
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
def __call__(self, **kwargs):
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
def __getattr__(self, attr):
|
|
|
|
|
if self._ports.has_key(attr):
|
|
|
|
|
@@ -485,10 +383,14 @@ class SimObject(object):
|
|
|
|
|
self._ports[attr].connect(self, attr, value)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if isSimObjectOrSequence(value) and self._instantiated:
|
|
|
|
|
raise RuntimeError, \
|
|
|
|
|
"cannot set SimObject parameter '%s' after\n" \
|
|
|
|
|
" instance been cloned %s" % (attr, `self`)
|
|
|
|
|
|
|
|
|
|
# must be SimObject param
|
|
|
|
|
param = self._params.get(attr, None)
|
|
|
|
|
if param:
|
|
|
|
|
# It's ok: set attribute by delegating to 'object' class.
|
|
|
|
|
try:
|
|
|
|
|
value = param.convert(value)
|
|
|
|
|
except Exception, e:
|
|
|
|
|
@@ -496,7 +398,6 @@ class SimObject(object):
|
|
|
|
|
(e, self.__class__.__name__, attr, value)
|
|
|
|
|
e.args = (msg, )
|
|
|
|
|
raise
|
|
|
|
|
# I would love to get rid of this
|
|
|
|
|
elif isSimObjectOrSequence(value):
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
@@ -535,13 +436,13 @@ class SimObject(object):
|
|
|
|
|
self._children[name] = value
|
|
|
|
|
|
|
|
|
|
def set_path(self, parent, name):
|
|
|
|
|
if not hasattr(self, '_parent'):
|
|
|
|
|
if not self._parent:
|
|
|
|
|
self._parent = parent
|
|
|
|
|
self._name = name
|
|
|
|
|
parent.add_child(name, self)
|
|
|
|
|
|
|
|
|
|
def path(self):
|
|
|
|
|
if not hasattr(self, '_parent'):
|
|
|
|
|
if not self._parent:
|
|
|
|
|
return 'root'
|
|
|
|
|
ppath = self._parent.path()
|
|
|
|
|
if ppath == 'root':
|
|
|
|
|
@@ -618,13 +519,22 @@ class SimObject(object):
|
|
|
|
|
# Call C++ to create C++ object corresponding to this object and
|
|
|
|
|
# (recursively) all its children
|
|
|
|
|
def createCCObject(self):
|
|
|
|
|
if self._ccObject:
|
|
|
|
|
return
|
|
|
|
|
self._ccObject = -1
|
|
|
|
|
self._ccObject = m5.main.createSimObject(self.path())
|
|
|
|
|
self.getCCObject() # force creation
|
|
|
|
|
for child in self._children.itervalues():
|
|
|
|
|
child.createCCObject()
|
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
|
# Create C++ port connections corresponding to the connections in
|
|
|
|
|
# _port_map (& recursively for all children)
|
|
|
|
|
def connectPorts(self):
|
|
|
|
|
@@ -723,9 +633,9 @@ class BaseProxy(object):
|
|
|
|
|
|
|
|
|
|
if self._search_up:
|
|
|
|
|
while not done:
|
|
|
|
|
try: obj = obj._parent
|
|
|
|
|
except: break
|
|
|
|
|
|
|
|
|
|
obj = obj._parent
|
|
|
|
|
if not obj:
|
|
|
|
|
break
|
|
|
|
|
result, done = self.find(obj)
|
|
|
|
|
|
|
|
|
|
if not done:
|
|
|
|
|
@@ -841,16 +751,16 @@ Self = ProxyFactory(search_self = True, search_up = False)
|
|
|
|
|
#
|
|
|
|
|
# Parameter description classes
|
|
|
|
|
#
|
|
|
|
|
# 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
|
|
|
|
|
# parameter description string, the parameter type, and the default
|
|
|
|
|
# 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.
|
|
|
|
|
#
|
|
|
|
|
# Note that the default values are loaded into the class's attribute
|
|
|
|
|
# space when the parameter dictionary is initialized (in
|
|
|
|
|
# MetaConfigNode._setparams()); after that point they aren't used.
|
|
|
|
|
# MetaSimObject._new_param()); after that point they aren't used.
|
|
|
|
|
#
|
|
|
|
|
#####################################################################
|
|
|
|
|
|
|
|
|
|
@@ -1480,6 +1390,7 @@ AllMemory = AddrRange(0, MaxAddr)
|
|
|
|
|
# particular SimObject.
|
|
|
|
|
class PortRef(object):
|
|
|
|
|
def __init__(self, simobj, name, isVec):
|
|
|
|
|
assert(isSimObject(simobj))
|
|
|
|
|
self.simobj = simobj
|
|
|
|
|
self.name = name
|
|
|
|
|
self.index = -1
|
|
|
|
|
@@ -1502,13 +1413,38 @@ class PortRef(object):
|
|
|
|
|
self.simobj._port_map[self.name] = curMap
|
|
|
|
|
self.peer = other
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
# Call C++ to create corresponding port connection between C++ objects
|
|
|
|
|
def ccConnect(self):
|
|
|
|
|
if self.ccConnected: # already done this
|
|
|
|
|
return
|
|
|
|
|
peer = self.peer
|
|
|
|
|
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)
|
|
|
|
|
self.ccConnected = True
|
|
|
|
|
peer.ccConnected = True
|
|
|
|
|
|
|
|
|
|
@@ -1528,6 +1464,9 @@ class Port(object):
|
|
|
|
|
# Connect an instance of this port (on the given SimObject with
|
|
|
|
|
# the given name) with the port described by the supplied PortRef
|
|
|
|
|
def connect(self, simobj, name, ref):
|
|
|
|
|
if not isinstance(ref, PortRef):
|
|
|
|
|
raise TypeError, \
|
|
|
|
|
"assigning non-port reference port '%s'" % name
|
|
|
|
|
myRef = self.makeRef(simobj, name)
|
|
|
|
|
myRef.setPeer(ref)
|
|
|
|
|
ref.setPeer(myRef)
|
|
|
|
|
|