+# Copyright (c) 2012 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
+# Copyright (c) 2010 Advanced Micro Devices, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
#
# Authors: Steve Reinhardt
# Nathan Binkert
+# Andreas Hansson
-import sys, types
-
-from util import *
-from multidict import multidict
-
-# These utility functions have to come first because they're
-# referenced in params.py... otherwise they won't be defined when we
-# import params below, and the recursive import of this file from
-# params.py will not find these names.
-def isSimObject(value):
- return isinstance(value, SimObject)
-
-def isSimObjectClass(value):
- return issubclass(value, SimObject)
-
-def isSimObjectSequence(value):
- if not isinstance(value, (list, tuple)) or len(value) == 0:
- return False
-
- for val in value:
- if not isNullPointer(val) and not isSimObject(val):
- return False
-
- return True
+import sys
+from types import FunctionType, MethodType, ModuleType
-def isSimObjectOrSequence(value):
- return isSimObject(value) or isSimObjectSequence(value)
+import m5
+from m5.util import *
# Have to import params up top since Param is referenced on initial
# load (when SimObject class references Param to create a class
# variable, the 'name' param)...
-from params import *
+from m5.params import *
# There are a few things we need that aren't in params.__all__ since
# normal users don't need them
-from params import ParamDesc, isNullPointer, SimObjVector
+from m5.params import ParamDesc, VectorParamDesc, \
+ isNullPointer, SimObjectVector, Port
-noDot = False
-try:
- import pydot
-except:
- noDot = True
+from m5.proxy import *
+from m5.proxy import isproxy
#####################################################################
#
#
#####################################################################
+# list of all SimObject classes
+allClasses = {}
+
# dict to look up SimObjects based on path
instanceDict = {}
+# Did any of the SimObjects lack a header file?
+noCxxHeader = False
+
+def public_value(key, value):
+ return key.startswith('_') or \
+ isinstance(value, (FunctionType, MethodType, ModuleType,
+ classmethod, type))
+
# 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 }
+ init_keywords = { 'abstract' : bool,
+ 'cxx_class' : str,
+ 'cxx_type' : str,
+ 'cxx_header' : str,
+ 'type' : str,
+ 'cxx_bases' : list }
# Attributes that can be set any time
- keywords = { 'check' : types.FunctionType,
- 'cxx_type' : types.StringType,
- 'cxx_predecls' : types.ListType,
- 'swig_predecls' : types.ListType }
+ keywords = { 'check' : FunctionType }
# __new__ is called before __init__, and is where the statements
# in the body of the class definition get loaded into the class's
# and only allow "private" attributes to be passed to the base
# __new__ (starting with underscore).
def __new__(mcls, name, bases, dict):
+ assert name not in allClasses, "SimObject %s already present" % name
+
# 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)):
+ if public_value(key, val):
cls_dict[key] = val
else:
# must be a param/port setting
value_dict[key] = val
+ if 'abstract' not in value_dict:
+ value_dict['abstract'] = False
+ if 'cxx_bases' not in value_dict:
+ value_dict['cxx_bases'] = []
cls_dict['_value_dict'] = value_dict
- return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
+ cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
+ if 'type' in value_dict:
+ allClasses[name] = cls
+ return cls
# subclass initialization
def __init__(cls, name, bases, dict):
# class or instance attributes
cls._values = multidict() # param values
+ cls._children = multidict() # SimObject children
cls._port_refs = multidict() # port ref objects
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.
- if len(bases) > 1:
- raise TypeError, "SimObjects do not support multiple inheritance"
+ # We don't support multiple inheritance of sim objects. If you want
+ # to, you must fix multidict to deal with it properly. Non sim-objects
+ # are ok, though
+ bTotal = 0
+ for c in bases:
+ if isinstance(c, MetaSimObject):
+ bTotal += 1
+ if bTotal > 1:
+ raise TypeError, "SimObjects do not support multiple inheritance"
base = bases[0]
# 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._base = base
cls._params.parent = base._params
cls._ports.parent = base._ports
cls._values.parent = base._values
+ cls._children.parent = base._children
cls._port_refs.parent = base._port_refs
# mark base as having been subclassed
base._instantiated = True
+ else:
+ cls._base = None
+
+ # default keyword values
+ if 'type' in cls._value_dict:
+ if 'cxx_class' not in cls._value_dict:
+ cls._value_dict['cxx_class'] = cls._value_dict['type']
+
+ cls._value_dict['cxx_type'] = '%s *' % cls._value_dict['cxx_class']
+
+ if 'cxx_header' not in cls._value_dict:
+ global noCxxHeader
+ noCxxHeader = True
+ print >> sys.stderr, \
+ "warning: No header file specified for SimObject: %s" % name
+
+ # Export methods are automatically inherited via C++, so we
+ # don't want the method declarations to get inherited on the
+ # python side (and thus end up getting repeated in the wrapped
+ # versions of derived classes). The code below basicallly
+ # suppresses inheritance by substituting in the base (null)
+ # versions of these methods unless a different version is
+ # explicitly supplied.
+ for method_name in ('export_methods', 'export_method_cxx_predecls',
+ 'export_method_swig_predecls'):
+ if method_name not in cls.__dict__:
+ base_method = getattr(MetaSimObject, method_name)
+ m = MethodType(base_method, cls, MetaSimObject)
+ setattr(cls, method_name, m)
# Now process the _value_dict items. They could be defining
# new (or overriding existing) parameters or ports, setting
else:
setattr(cls, key, val)
- cls.cxx_type = cls.type + '*'
- # A forward class declaration is sufficient since we are just
- # declaring a pointer.
- cls.cxx_predecls = ['class %s;' % cls.type]
- cls.swig_predecls = cls.cxx_predecls
-
def _set_keyword(cls, keyword, val, kwtype):
if not isinstance(val, kwtype):
raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
(keyword, type(val), kwtype)
- if isinstance(val, types.FunctionType):
+ if isinstance(val, FunctionType):
val = classmethod(val)
type.__setattr__(cls, keyword, val)
def _set_param(cls, name, value, param):
assert(param.name == name)
try:
- cls._values[name] = param.convert(value)
+ value = param.convert(value)
except Exception, e:
msg = "%s\nError setting param %s.%s to %s\n" % \
(e, cls.__name__, name, value)
e.args = (msg, )
raise
+ cls._values[name] = value
+ # if param value is a SimObject, make it a child too, so that
+ # it gets cloned properly when the class is instantiated
+ if isSimObjectOrVector(value) and not value.has_parent():
+ cls._add_cls_child(name, value)
+
+ def _add_cls_child(cls, name, child):
+ # It's a little funky to have a class as a parent, but these
+ # objects should never be instantiated (only cloned, which
+ # clears the parent pointer), and this makes it clear that the
+ # object is not an orphan and can provide better error
+ # messages.
+ child.set_parent(cls, name)
+ cls._children[name] = child
def _new_port(cls, name, port):
# each port should be uniquely assigned to one variable
assert(not hasattr(port, 'name'))
port.name = name
cls._ports[name] = port
- if hasattr(port, 'default'):
- cls._cls_get_port_ref(name).connect(port.default)
# same as _get_port_ref, effectively, but for classes
def _cls_get_port_ref(cls, attr):
# instance of class cls).
def __setattr__(cls, attr, value):
# normal processing for private attributes
- if attr.startswith('_'):
+ if public_value(attr, value):
type.__setattr__(cls, attr, value)
return
if isSimObjectOrSequence(value):
# If RHS is a SimObject, it's an implicit child assignment.
- # Classes don't have children, so we just put this object
- # in _values; later, each instance will do a 'setattr(self,
- # attr, _values[attr])' in SimObject.__init__ which will
- # add this object as a child.
- cls._values[attr] = value
+ cls._add_cls_child(attr, coerceSimObjectOrVector(value))
return
# no valid assignment... raise exception
"Class %s has no parameter \'%s\'" % (cls.__name__, attr)
def __getattr__(cls, attr):
+ if attr == 'cxx_class_path':
+ return cls.cxx_class.split('::')
+
+ if attr == 'cxx_class_name':
+ return cls.cxx_class_path[-1]
+
+ if attr == 'cxx_namespaces':
+ return cls.cxx_class_path[:-1]
+
if cls._values.has_key(attr):
return cls._values[attr]
+ if cls._children.has_key(attr):
+ return cls._children[attr]
+
raise AttributeError, \
"object '%s' has no attribute '%s'" % (cls.__name__, attr)
def __str__(cls):
return cls.__name__
- def cxx_decl(cls):
- code = "#ifndef __PARAMS__%s\n#define __PARAMS__%s\n\n" % (cls, cls)
-
- if str(cls) != 'SimObject':
- base = cls.__bases__[0].type
+ # See ParamValue.cxx_predecls for description.
+ def cxx_predecls(cls, code):
+ code('#include "params/$cls.hh"')
+
+ # See ParamValue.swig_predecls for description.
+ def swig_predecls(cls, code):
+ code('%import "python/m5/internal/param_$cls.i"')
+
+ # Hook for exporting additional C++ methods to Python via SWIG.
+ # Default is none, override using @classmethod in class definition.
+ def export_methods(cls, code):
+ pass
+
+ # Generate the code needed as a prerequisite for the C++ methods
+ # exported via export_methods() to be compiled in the _wrap.cc
+ # file. Typically generates one or more #include statements. If
+ # any methods are exported, typically at least the C++ header
+ # declaring the relevant SimObject class must be included.
+ def export_method_cxx_predecls(cls, code):
+ pass
+
+ # Generate the code needed as a prerequisite for the C++ methods
+ # exported via export_methods() to be processed by SWIG.
+ # Typically generates one or more %include or %import statements.
+ # If any methods are exported, typically at least the C++ header
+ # declaring the relevant SimObject class must be included.
+ def export_method_swig_predecls(cls, code):
+ pass
+
+ # Generate the declaration for this object for wrapping with SWIG.
+ # Generates code that goes into a SWIG .i file. Called from
+ # src/SConscript.
+ def swig_decl(cls, code):
+ class_path = cls.cxx_class.split('::')
+ classname = class_path[-1]
+ namespaces = class_path[:-1]
+
+ # The 'local' attribute restricts us to the params declared in
+ # the object itself, not including inherited params (which
+ # will also be inherited from the base class's param struct
+ # here).
+ params = cls._params.local.values()
+ ports = cls._ports.local
+
+ code('%module(package="m5.internal") param_$cls')
+ code()
+ code('%{')
+ code('#include "sim/sim_object.hh"')
+ code('#include "params/$cls.hh"')
+ for param in params:
+ param.cxx_predecls(code)
+ code('#include "${{cls.cxx_header}}"')
+ cls.export_method_cxx_predecls(code)
+ code('''\
+/**
+ * This is a workaround for bug in swig. Prior to gcc 4.6.1 the STL
+ * headers like vector, string, etc. used to automatically pull in
+ * the cstddef header but starting with gcc 4.6.1 they no longer do.
+ * This leads to swig generated a file that does not compile so we
+ * explicitly include cstddef. Additionally, including version 2.0.4,
+ * swig uses ptrdiff_t without the std:: namespace prefix which is
+ * required with gcc 4.6.1. We explicitly provide access to it.
+ */
+#include <cstddef>
+using std::ptrdiff_t;
+''')
+ code('%}')
+ code()
+
+ for param in params:
+ param.swig_predecls(code)
+ cls.export_method_swig_predecls(code)
+
+ code()
+ if cls._base:
+ code('%import "python/m5/internal/param_${{cls._base}}.i"')
+ code()
+
+ for ns in namespaces:
+ code('namespace $ns {')
+
+ if namespaces:
+ code('// avoid name conflicts')
+ sep_string = '_COLONS_'
+ flat_name = sep_string.join(class_path)
+ code('%rename($flat_name) $classname;')
+
+ code()
+ code('// stop swig from creating/wrapping default ctor/dtor')
+ code('%nodefault $classname;')
+ code('class $classname')
+ if cls._base:
+ bases = [ cls._base.cxx_class ] + cls.cxx_bases
else:
- base = None
+ bases = cls.cxx_bases
+ base_first = True
+ for base in bases:
+ if base_first:
+ code(' : public ${{base}}')
+ base_first = False
+ else:
+ code(' , public ${{base}}')
+
+ code('{')
+ code(' public:')
+ cls.export_methods(code)
+ code('};')
+
+ for ns in reversed(namespaces):
+ code('} // namespace $ns')
- # The 'dict' attribute restricts us to the params declared in
+ code()
+ code('%include "params/$cls.hh"')
+
+
+ # Generate the C++ declaration (.hh file) for this SimObject's
+ # param struct. Called from src/SConscript.
+ def cxx_param_decl(cls, code):
+ # The 'local' attribute restricts us to the params declared in
# the object itself, not including inherited params (which
# will also be inherited from the base class's param struct
# here).
- params = cls._params.dict.values()
+ params = cls._params.local.values()
+ ports = cls._ports.local
try:
ptypes = [p.ptype for p in params]
except:
print params
raise
- # get a list of lists of predeclaration lines
- predecls = [p.cxx_predecls() for p in params]
- # flatten
- predecls = reduce(lambda x,y:x+y, predecls, [])
- # remove redundant lines
- predecls2 = []
- for pd in predecls:
- if pd not in predecls2:
- predecls2.append(pd)
- predecls2.sort()
- code += "\n".join(predecls2)
- code += "\n\n";
-
- if base:
- code += '#include "params/%s.hh"\n\n' % base
-
- # Generate declarations for locally defined enumerations.
- enum_ptypes = [t for t in ptypes if issubclass(t, Enum)]
- if enum_ptypes:
- code += "\n".join([t.cxx_decl() for t in enum_ptypes])
- code += "\n\n"
+ class_path = cls._value_dict['cxx_class'].split('::')
- # now generate the actual param struct
- code += "struct %sParams" % cls
- if base:
- code += " : public %sParams" % base
- code += " {\n"
- decls = [p.cxx_decl() for p in params]
- decls.sort()
- code += "".join([" %s\n" % d for d in decls])
- code += "};\n"
-
- # close #ifndef __PARAMS__* guard
- code += "\n#endif\n"
- return code
+ code('''\
+#ifndef __PARAMS__${cls}__
+#define __PARAMS__${cls}__
- def swig_decl(cls):
+''')
- code = '%%module %sParams\n' % cls
+ # A forward class declaration is sufficient since we are just
+ # declaring a pointer.
+ for ns in class_path[:-1]:
+ code('namespace $ns {')
+ code('class $0;', class_path[-1])
+ for ns in reversed(class_path[:-1]):
+ code('} // namespace $ns')
+ code()
+
+ # The base SimObject has a couple of params that get
+ # automatically set from Python without being declared through
+ # the normal Param mechanism; we slip them in here (needed
+ # predecls now, actual declarations below)
+ if cls == SimObject:
+ code('''
+#ifndef PY_VERSION
+struct PyObject;
+#endif
+
+#include <string>
+
+class EventQueue;
+''')
+ for param in params:
+ param.cxx_predecls(code)
+ for port in ports.itervalues():
+ port.cxx_predecls(code)
+ code()
+
+ if cls._base:
+ code('#include "params/${{cls._base.type}}.hh"')
+ code()
+
+ for ptype in ptypes:
+ if issubclass(ptype, Enum):
+ code('#include "enums/${{ptype.__name__}}.hh"')
+ code()
- if str(cls) != 'SimObject':
- base = cls.__bases__[0].type
- else:
- base = None
+ # now generate the actual param struct
+ code("struct ${cls}Params")
+ if cls._base:
+ code(" : public ${{cls._base.type}}Params")
+ code("{")
+ if not hasattr(cls, 'abstract') or not cls.abstract:
+ if 'type' in cls.__dict__:
+ code(" ${{cls.cxx_type}} create();")
+
+ code.indent()
+ if cls == SimObject:
+ code('''
+ SimObjectParams()
+ {
+ extern EventQueue mainEventQueue;
+ eventq = &mainEventQueue;
+ }
+ virtual ~SimObjectParams() {}
+
+ std::string name;
+ PyObject *pyobj;
+ EventQueue *eventq;
+ ''')
+ for param in params:
+ param.cxx_decl(code)
+ for port in ports.itervalues():
+ port.cxx_decl(code)
+
+ code.dedent()
+ code('};')
+
+ code()
+ code('#endif // __PARAMS__${cls}__')
+ return code
- # The 'dict' attribute restricts us to the params declared in
- # the object itself, not including inherited params (which
- # will also be inherited from the base class's param struct
- # here).
- params = cls._params.dict.values()
- ptypes = [p.ptype for p in params]
-
- # get a list of lists of predeclaration lines
- predecls = [p.swig_predecls() for p in params]
- # flatten
- predecls = reduce(lambda x,y:x+y, predecls, [])
- # remove redundant lines
- predecls2 = []
- for pd in predecls:
- if pd not in predecls2:
- predecls2.append(pd)
- predecls2.sort()
- code += "\n".join(predecls2)
- code += "\n\n";
-
- if base:
- code += '%%import "python/m5/swig/%sParams.i"\n\n' % base
-
- code += '%{\n'
- code += '#include "params/%s.hh"\n' % cls
- code += '%}\n\n'
- code += '%%include "params/%s.hh"\n\n' % cls
- return code
# The SimObject class is the root of the special hierarchy. Most of
# the code in this class deals with the configuration hierarchy itself
# get this metaclass.
__metaclass__ = MetaSimObject
type = 'SimObject'
-
- name = Param.String("Object name")
+ abstract = True
+ cxx_header = "sim/sim_object.hh"
+
+ cxx_bases = [ "Drainable", "Serializable" ]
+
+ @classmethod
+ def export_method_swig_predecls(cls, code):
+ code('''
+%include <std_string.i>
+
+%import "python/swig/drain.i"
+%import "python/swig/serialize.i"
+''')
+
+ @classmethod
+ def export_methods(cls, code):
+ code('''
+ void init();
+ void loadState(Checkpoint *cp);
+ void initState();
+ void regStats();
+ void resetStats();
+ void startup();
+''')
# Initialize new instance. For objects with SimObject-valued
# children, we need to recursively clone the classes represented
# initialize required attributes
self._parent = None
- self._children = {}
+ self._name = None
self._ccObject = None # pointer to C++ object
+ self._ccParams = None
self._instantiated = False # really "cloned"
+ # Clone children specified at class level. No need for a
+ # multidict here since we will be cloning everything.
+ # Do children before parameter values so that children that
+ # are also param values get cloned properly.
+ self._children = {}
+ for key,val in ancestor._children.iteritems():
+ self.add_child(key, val(_memo=memo_dict))
+
# Inherit parameter values from class using multidict so
- # individual value settings can be overridden.
+ # individual value settings can be overridden but we still
+ # inherit late changes to non-overridden class values.
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 ])
+ val = tryAsSimObjectOrVector(val)
+ if val is not None:
+ self._values[key] = val(_memo=memo_dict)
+
# clone port references. no need to use a multidict here
# since we will be creating new references for all ports.
self._port_refs = {}
if self._values.has_key(attr):
return self._values[attr]
+ if self._children.has_key(attr):
+ return self._children[attr]
+
+ # If the attribute exists on the C++ object, transparently
+ # forward the reference there. This is typically used for
+ # SWIG-wrapped methods such as init(), regStats(),
+ # resetStats(), startup(), drain(), and
+ # resume().
+ if self._ccObject and hasattr(self._ccObject, attr):
+ return getattr(self._ccObject, attr)
+
raise AttributeError, "object '%s' has no attribute '%s'" \
% (self.__class__.__name__, attr)
"cannot set SimObject parameter '%s' after\n" \
" instance been cloned %s" % (attr, `self`)
- # must be SimObject param
param = self._params.get(attr)
if param:
try:
(e, self.__class__.__name__, attr, value)
e.args = (msg, )
raise
- self._set_child(attr, value)
+ self._values[attr] = value
+ # implicitly parent unparented objects assigned as params
+ if isSimObjectOrVector(value) and not value.has_parent():
+ self.add_child(attr, value)
return
+ # if RHS is a SimObject, it's an implicit child assignment
if isSimObjectOrSequence(value):
- self._set_child(attr, value)
+ self.add_child(attr, value)
return
# no valid assignment... raise exception
return self
raise TypeError, "Non-zero index '%s' to SimObject" % key
- # clear out children with given name, even if it's a vector
- def clear_child(self, name):
- if not self._children.has_key(name):
- return
- child = self._children[name]
- if isinstance(child, SimObjVector):
- for i in xrange(len(child)):
- del self._children["s%d" % (name, i)]
- del self._children[name]
+ # Also implemented by SimObjectVector
+ def clear_parent(self, old_parent):
+ assert self._parent is old_parent
+ self._parent = None
- def add_child(self, name, value):
- self._children[name] = value
+ # Also implemented by SimObjectVector
+ def set_parent(self, parent, name):
+ self._parent = parent
+ self._name = name
- def _maybe_set_parent(self, parent, name):
- if not self._parent:
- self._parent = parent
- self._name = name
- parent.add_child(name, self)
+ # Also implemented by SimObjectVector
+ def get_name(self):
+ return self._name
- def _set_child(self, attr, value):
- # if RHS is a SimObject, it's an implicit child assignment
- # clear out old child with this name, if any
- self.clear_child(attr)
+ # Also implemented by SimObjectVector
+ def has_parent(self):
+ return self._parent is not None
- if isSimObject(value):
- value._maybe_set_parent(self, attr)
- elif isSimObjectSequence(value):
- value = SimObjVector(value)
- [v._maybe_set_parent(self, "%s%d" % (attr, i))
- for i,v in enumerate(value)]
+ # clear out child with given name. This code is not likely to be exercised.
+ # See comment in add_child.
+ def clear_child(self, name):
+ child = self._children[name]
+ child.clear_parent(self)
+ del self._children[name]
- self._values[attr] = value
+ # Add a new child to this object.
+ def add_child(self, name, child):
+ child = coerceSimObjectOrVector(child)
+ if child.has_parent():
+ print "warning: add_child('%s'): child '%s' already has parent" % \
+ (name, child.get_name())
+ if self._children.has_key(name):
+ # This code path had an undiscovered bug that would make it fail
+ # at runtime. It had been here for a long time and was only
+ # exposed by a buggy script. Changes here will probably not be
+ # exercised without specialized testing.
+ self.clear_child(name)
+ child.set_parent(self, name)
+ self._children[name] = child
+
+ # Take SimObject-valued parameters that haven't been explicitly
+ # assigned as children and make them children of the object that
+ # they were assigned to as a parameter value. This guarantees
+ # that when we instantiate all the parameter objects we're still
+ # inside the configuration hierarchy.
+ def adoptOrphanParams(self):
+ for key,val in self._values.iteritems():
+ if not isSimObjectVector(val) and isSimObjectSequence(val):
+ # need to convert raw SimObject sequences to
+ # SimObjectVector class so we can call has_parent()
+ val = SimObjectVector(val)
+ self._values[key] = val
+ if isSimObjectOrVector(val) and not val.has_parent():
+ print "warning: %s adopting orphan SimObject param '%s'" \
+ % (self, key)
+ self.add_child(key, val)
def path(self):
if not self._parent:
- return 'root'
+ return '<orphan %s>' % self.__class__
ppath = self._parent.path()
if ppath == 'root':
return self._name
match_obj = self._values[pname]
if found_obj != None and found_obj != match_obj:
raise AttributeError, \
- 'parent.any matched more than one: %s' % obj.path
+ 'parent.any matched more than one: %s and %s' % (found_obj.path, match_obj.path)
found_obj = match_obj
return found_obj, found_obj != None
+ def find_all(self, ptype):
+ all = {}
+ # search children
+ for child in self._children.itervalues():
+ # a child could be a list, so ensure we visit each item
+ if isinstance(child, list):
+ children = child
+ else:
+ children = [child]
+
+ for child in children:
+ if isinstance(child, ptype) and not isproxy(child) and \
+ not isNullPointer(child):
+ all[child] = True
+ if isSimObject(child):
+ # also add results from the child itself
+ child_all, done = child.find_all(ptype)
+ all.update(dict(zip(child_all, [done] * len(child_all))))
+ # search param space
+ for pname,pdesc in self._params.iteritems():
+ if issubclass(pdesc.ptype, ptype):
+ match_obj = self._values[pname]
+ if not isproxy(match_obj) and not isNullPointer(match_obj):
+ all[match_obj] = True
+ return all.keys(), True
+
def unproxy(self, base):
return self
- def unproxy_all(self):
+ def unproxyParams(self):
for param in self._params.iterkeys():
value = self._values.get(param)
- if value != None and proxy.isproxy(value):
+ if value != None and isproxy(value):
try:
value = value.unproxy(self)
except:
if port != None:
port.unproxy(self)
- # Unproxy children in sorted order for determinism also.
- child_names = self._children.keys()
- child_names.sort()
- for child in child_names:
- self._children[child].unproxy_all()
-
- def print_ini(self):
- print '[' + self.path() + ']' # .ini section header
+ def print_ini(self, ini_file):
+ print >>ini_file, '[' + self.path() + ']' # .ini section header
instanceDict[self.path()] = self
- if hasattr(self, 'type') and not isinstance(self, ParamContext):
- print 'type=%s' % self.type
+ if hasattr(self, 'type'):
+ print >>ini_file, 'type=%s' % self.type
+
+ if len(self._children.keys()):
+ print >>ini_file, 'children=%s' % \
+ ' '.join(self._children[n].get_name() \
+ for n in sorted(self._children.keys()))
+
+ for param in sorted(self._params.keys()):
+ value = self._values.get(param)
+ if value != None:
+ print >>ini_file, '%s=%s' % (param,
+ self._values[param].ini_str())
+
+ for port_name in sorted(self._ports.keys()):
+ port = self._port_refs.get(port_name, None)
+ if port != None:
+ print >>ini_file, '%s=%s' % (port_name, port.ini_str())
+
+ print >>ini_file # blank line between objects
+
+ # generate a tree of dictionaries expressing all the parameters in the
+ # instantiated system for use by scripts that want to do power, thermal
+ # visualization, and other similar tasks
+ def get_config_as_dict(self):
+ d = attrdict()
+ if hasattr(self, 'type'):
+ d.type = self.type
+ if hasattr(self, 'cxx_class'):
+ d.cxx_class = self.cxx_class
+ # Add the name and path of this object to be able to link to
+ # the stats
+ d.name = self.get_name()
+ d.path = self.path()
+
+ for param in sorted(self._params.keys()):
+ value = self._values.get(param)
+ if value != None:
+ try:
+ # Use native type for those supported by JSON and
+ # strings for everything else. skipkeys=True seems
+ # to not work as well as one would hope
+ if type(self._values[param].value) in \
+ [str, unicode, int, long, float, bool, None]:
+ d[param] = self._values[param].value
+ else:
+ d[param] = str(self._values[param])
+
+ except AttributeError:
+ pass
+
+ for n in sorted(self._children.keys()):
+ child = self._children[n]
+ # Use the name of the attribute (and not get_name()) as
+ # the key in the JSON dictionary to capture the hierarchy
+ # in the Python code that assembled this system
+ d[n] = child.get_config_as_dict()
+
+ for port_name in sorted(self._ports.keys()):
+ port = self._port_refs.get(port_name, None)
+ if port != None:
+ # Represent each port with a dictionary containing the
+ # prominent attributes
+ d[port_name] = port.get_config_as_dict()
+
+ return d
- child_names = self._children.keys()
- child_names.sort()
- np_child_names = [c for c in child_names \
- if not isinstance(self._children[c], ParamContext)]
- if len(np_child_names):
- print 'children=%s' % ' '.join(np_child_names)
+ def getCCParams(self):
+ if self._ccParams:
+ return self._ccParams
+
+ cc_params_struct = getattr(m5.internal.params, '%sParams' % self.type)
+ cc_params = cc_params_struct()
+ cc_params.pyobj = self
+ cc_params.name = str(self)
param_names = self._params.keys()
param_names.sort()
for param in param_names:
value = self._values.get(param)
- if value != None:
- print '%s=%s' % (param, self._values[param].ini_str())
+ if value is None:
+ fatal("%s.%s without default or user set value",
+ self.path(), param)
+
+ value = value.getValue()
+ if isinstance(self._params[param], VectorParamDesc):
+ assert isinstance(value, list)
+ vec = getattr(cc_params, param)
+ assert not len(vec)
+ for v in value:
+ vec.append(v)
+ else:
+ setattr(cc_params, param, value)
port_names = self._ports.keys()
port_names.sort()
for port_name in port_names:
port = self._port_refs.get(port_name, None)
if port != None:
- print '%s=%s' % (port_name, port.ini_str())
-
- print # blank line between objects
-
- for child in child_names:
- self._children[child].print_ini()
-
- # Call C++ to create C++ object corresponding to this object and
- # (recursively) all its children
- def createCCObject(self):
- self.getCCObject() # force creation
- for child in self._children.itervalues():
- child.createCCObject()
+ port_count = len(port)
+ else:
+ port_count = 0
+ setattr(cc_params, 'port_' + port_name + '_connection_count',
+ port_count)
+ self._ccParams = cc_params
+ return self._ccParams
# 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 = internal.main.createSimObject(self.path())
+ # Make sure this object is in the configuration hierarchy
+ if not self._parent and not isRoot(self):
+ raise RuntimeError, "Attempt to instantiate orphan node"
+ # Cycles in the configuration hierarchy are not supported. This
+ # will catch the resulting recursion and stop.
+ self._ccObject = -1
+ params = self.getCCParams()
+ self._ccObject = params.create()
elif self._ccObject == -1:
- raise RuntimeError, "%s: recursive call to getCCObject()" \
+ raise RuntimeError, "%s: Cycle found in configuration hierarchy." \
% self.path()
return self._ccObject
+ def descendants(self):
+ yield self
+ for child in self._children.itervalues():
+ for obj in child.descendants():
+ yield obj
+
+ # Call C++ to create C++ object corresponding to this object
+ def createCCObject(self):
+ self.getCCParams()
+ self.getCCObject() # force creation
+
+ def getValue(self):
+ return self.getCCObject()
+
# Create C++ port connections corresponding to the connections in
- # _port_refs (& recursively for all children)
+ # _port_refs
def connectPorts(self):
for portRef in self._port_refs.itervalues():
portRef.ccConnect()
- for child in self._children.itervalues():
- child.connectPorts()
-
- def startDrain(self, drain_event, recursive):
- count = 0
- # ParamContexts don't serialize
- if isinstance(self, SimObject) and not isinstance(self, ParamContext):
- count += self._ccObject.drain(drain_event)
- if recursive:
- for child in self._children.itervalues():
- count += child.startDrain(drain_event, True)
- return count
-
- def resume(self):
- if isinstance(self, SimObject) and not isinstance(self, ParamContext):
- self._ccObject.resume()
- for child in self._children.itervalues():
- child.resume()
-
- def changeTiming(self, mode):
- if isinstance(self, m5.objects.System):
- # i don't know if there's a better way to do this - calling
- # setMemoryMode directly from self._ccObject results in calling
- # SimObject::setMemoryMode, not the System::setMemoryMode
- system_ptr = internal.main.convertToSystemPtr(self._ccObject)
- system_ptr.setMemoryMode(mode)
- for child in self._children.itervalues():
- child.changeTiming(mode)
-
- def takeOverFrom(self, old_cpu):
- cpu_ptr = internal.main.convertToBaseCPUPtr(old_cpu._ccObject)
- self._ccObject.takeOverFrom(cpu_ptr)
-
- # generate output file for 'dot' to display as a pretty graph.
- # this code is currently broken.
- def outputDot(self, dot):
- label = "{%s|" % self.path
- if isSimObject(self.realtype):
- label += '%s|' % self.type
-
- if self.children:
- # instantiate children in same order they were added for
- # backward compatibility (else we can end up with cpu1
- # before cpu0).
- for c in self.children:
- dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
-
- simobjs = []
- for param in self.params:
- try:
- if param.value is None:
- raise AttributeError, 'Parameter with no value'
- value = param.value
- string = param.string(value)
- except Exception, e:
- msg = 'exception in %s:%s\n%s' % (self.name, param.name, e)
- e.args = (msg, )
- raise
+# Function to provide to C++ so it can look up instances based on paths
+def resolveSimObject(name):
+ obj = instanceDict[name]
+ return obj.getCCObject()
- if isSimObject(param.ptype) and string != "Null":
- simobjs.append(string)
- else:
- label += '%s = %s\\n' % (param.name, string)
+def isSimObject(value):
+ return isinstance(value, SimObject)
- for so in simobjs:
- label += "|<%s> %s" % (so, so)
- dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
- tailport="w"))
- label += '}'
- dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
+def isSimObjectClass(value):
+ return issubclass(value, SimObject)
- # recursively dump out children
- for c in self.children:
- c.outputDot(dot)
+def isSimObjectVector(value):
+ return isinstance(value, SimObjectVector)
-class ParamContext(SimObject):
- pass
+def isSimObjectSequence(value):
+ if not isinstance(value, (list, tuple)) or len(value) == 0:
+ return False
-# Function to provide to C++ so it can look up instances based on paths
-def resolveSimObject(name):
- obj = instanceDict[name]
- return obj.getCCObject()
+ for val in value:
+ if not isNullPointer(val) and not isSimObject(val):
+ return False
+
+ return True
+
+def isSimObjectOrSequence(value):
+ return isSimObject(value) or isSimObjectSequence(value)
+
+def isRoot(obj):
+ from m5.objects import Root
+ return obj and obj is Root.getInstance()
+
+def isSimObjectOrVector(value):
+ return isSimObject(value) or isSimObjectVector(value)
+
+def tryAsSimObjectOrVector(value):
+ if isSimObjectOrVector(value):
+ return value
+ if isSimObjectSequence(value):
+ return SimObjectVector(value)
+ return None
+
+def coerceSimObjectOrVector(value):
+ value = tryAsSimObjectOrVector(value)
+ if value is None:
+ raise TypeError, "SimObject or SimObjectVector expected"
+ return value
+
+baseClasses = allClasses.copy()
+baseInstances = instanceDict.copy()
+
+def clear():
+ global allClasses, instanceDict, noCxxHeader
+
+ allClasses = baseClasses.copy()
+ instanceDict = baseInstances.copy()
+ noCxxHeader = False
# __all__ defines the list of symbols that get exported when
# 'from config import *' is invoked. Try to keep this reasonably
# short to avoid polluting other namespaces.
-__all__ = ['SimObject', 'ParamContext']
-
-# see comment on imports at end of __init__.py.
-import proxy
-import internal
-import m5
+__all__ = [ 'SimObject' ]