misc: Replaced master/slave terminology
[gem5.git] / src / python / m5 / SimObject.py
index c45867c85b6f7eff6888e81d13a56de8997994b1..9c9a9ed0fe2b9a18a59648076e0b9b82248c018f 100644 (file)
@@ -1,5 +1,18 @@
+# Copyright (c) 2017-2020 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.
+# Copyright (c) 2010-20013 Advanced Micro Devices, Inc.
+# Copyright (c) 2013 Mark D. Hill and David A. Wood
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # 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
+
+from __future__ import print_function
+from __future__ import absolute_import
+from six import add_metaclass
+import six
+if six.PY3:
+    long = int
 
 import sys
 from types import FunctionType, MethodType, ModuleType
-
-try:
-    import pydot
-except:
-    pydot = False
+from functools import wraps
+import inspect
 
 import m5
 from m5.util import *
+from m5.util.pybind import *
+# Use the pyfdt and not the helper class, because the fdthelper
+# relies on the SimObject definition
+from m5.ext.pyfdt import pyfdt
 
 # Have to import params up top since Param is referenced on initial
 # load (when SimObject class references Param to create a class
@@ -46,7 +64,7 @@ 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 m5.params import ParamDesc, VectorParamDesc, \
-     isNullPointer, SimObjectVector
+     isNullPointer, SimObjectVector, Port
 
 from m5.proxy import *
 from m5.proxy import isproxy
@@ -97,21 +115,300 @@ 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))
 
+def createCxxConfigDirectoryEntryFile(code, name, simobj, is_header):
+    entry_class = 'CxxConfigDirectoryEntry_%s' % name
+    param_class = '%sCxxConfigParams' % name
+
+    code('#include "params/%s.hh"' % name)
+
+    if not is_header:
+        for param in simobj._params.values():
+            if isSimObjectClass(param.ptype):
+                code('#include "%s"' % param.ptype._value_dict['cxx_header'])
+                code('#include "params/%s.hh"' % param.ptype.__name__)
+            else:
+                param.ptype.cxx_ini_predecls(code)
+
+    if is_header:
+        member_prefix = ''
+        end_of_decl = ';'
+        code('#include "sim/cxx_config.hh"')
+        code()
+        code('class ${param_class} : public CxxConfigParams,'
+            ' public ${name}Params')
+        code('{')
+        code('  private:')
+        code.indent()
+        code('class DirectoryEntry : public CxxConfigDirectoryEntry')
+        code('{')
+        code('  public:')
+        code.indent()
+        code('DirectoryEntry();');
+        code()
+        code('CxxConfigParams *makeParamsObject() const')
+        code('{ return new ${param_class}; }')
+        code.dedent()
+        code('};')
+        code()
+        code.dedent()
+        code('  public:')
+        code.indent()
+    else:
+        member_prefix = '%s::' % param_class
+        end_of_decl = ''
+        code('#include "%s"' % simobj._value_dict['cxx_header'])
+        code('#include "base/str.hh"')
+        code('#include "cxx_config/${name}.hh"')
+
+        code()
+        code('${member_prefix}DirectoryEntry::DirectoryEntry()');
+        code('{')
+
+        def cxx_bool(b):
+            return 'true' if b else 'false'
+
+        code.indent()
+        for param in simobj._params.values():
+            is_vector = isinstance(param, m5.params.VectorParamDesc)
+            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+            code('parameters["%s"] = new ParamDesc("%s", %s, %s);' %
+                (param.name, param.name, cxx_bool(is_vector),
+                cxx_bool(is_simobj)));
+
+        for port in simobj._ports.values():
+            is_vector = isinstance(port, m5.params.VectorPort)
+            is_requestor = port.role == 'GEM5 REQUESTOR'
+
+            code('ports["%s"] = new PortDesc("%s", %s, %s);' %
+                (port.name, port.name, cxx_bool(is_vector),
+                cxx_bool(is_requestor)))
+
+        code.dedent()
+        code('}')
+        code()
+
+    code('bool ${member_prefix}setSimObject(const std::string &name,')
+    code('    SimObject *simObject)${end_of_decl}')
+
+    if not is_header:
+        code('{')
+        code.indent()
+        code('bool ret = true;')
+        code()
+        code('if (false) {')
+        for param in simobj._params.values():
+            is_vector = isinstance(param, m5.params.VectorParamDesc)
+            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+            if is_simobj and not is_vector:
+                code('} else if (name == "${{param.name}}") {')
+                code.indent()
+                code('this->${{param.name}} = '
+                    'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);')
+                code('if (simObject && !this->${{param.name}})')
+                code('   ret = false;')
+                code.dedent()
+        code('} else {')
+        code('    ret = false;')
+        code('}')
+        code()
+        code('return ret;')
+        code.dedent()
+        code('}')
+
+    code()
+    code('bool ${member_prefix}setSimObjectVector('
+        'const std::string &name,')
+    code('    const std::vector<SimObject *> &simObjects)${end_of_decl}')
+
+    if not is_header:
+        code('{')
+        code.indent()
+        code('bool ret = true;')
+        code()
+        code('if (false) {')
+        for param in simobj._params.values():
+            is_vector = isinstance(param, m5.params.VectorParamDesc)
+            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+            if is_simobj and is_vector:
+                code('} else if (name == "${{param.name}}") {')
+                code.indent()
+                code('this->${{param.name}}.clear();')
+                code('for (auto i = simObjects.begin(); '
+                    'ret && i != simObjects.end(); i ++)')
+                code('{')
+                code.indent()
+                code('${{param.ptype.cxx_type}} object = '
+                    'dynamic_cast<${{param.ptype.cxx_type}}>(*i);')
+                code('if (*i && !object)')
+                code('    ret = false;')
+                code('else')
+                code('    this->${{param.name}}.push_back(object);')
+                code.dedent()
+                code('}')
+                code.dedent()
+        code('} else {')
+        code('    ret = false;')
+        code('}')
+        code()
+        code('return ret;')
+        code.dedent()
+        code('}')
+
+    code()
+    code('void ${member_prefix}setName(const std::string &name_)'
+        '${end_of_decl}')
+
+    if not is_header:
+        code('{')
+        code.indent()
+        code('this->name = name_;')
+        code.dedent()
+        code('}')
+
+    if is_header:
+        code('const std::string &${member_prefix}getName()')
+        code('{ return this->name; }')
+
+    code()
+    code('bool ${member_prefix}setParam(const std::string &name,')
+    code('    const std::string &value, const Flags flags)${end_of_decl}')
+
+    if not is_header:
+        code('{')
+        code.indent()
+        code('bool ret = true;')
+        code()
+        code('if (false) {')
+        for param in simobj._params.values():
+            is_vector = isinstance(param, m5.params.VectorParamDesc)
+            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+            if not is_simobj and not is_vector:
+                code('} else if (name == "${{param.name}}") {')
+                code.indent()
+                param.ptype.cxx_ini_parse(code,
+                    'value', 'this->%s' % param.name, 'ret =')
+                code.dedent()
+        code('} else {')
+        code('    ret = false;')
+        code('}')
+        code()
+        code('return ret;')
+        code.dedent()
+        code('}')
+
+    code()
+    code('bool ${member_prefix}setParamVector('
+        'const std::string &name,')
+    code('    const std::vector<std::string> &values,')
+    code('    const Flags flags)${end_of_decl}')
+
+    if not is_header:
+        code('{')
+        code.indent()
+        code('bool ret = true;')
+        code()
+        code('if (false) {')
+        for param in simobj._params.values():
+            is_vector = isinstance(param, m5.params.VectorParamDesc)
+            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+            if not is_simobj and is_vector:
+                code('} else if (name == "${{param.name}}") {')
+                code.indent()
+                code('${{param.name}}.clear();')
+                code('for (auto i = values.begin(); '
+                    'ret && i != values.end(); i ++)')
+                code('{')
+                code.indent()
+                code('${{param.ptype.cxx_type}} elem;')
+                param.ptype.cxx_ini_parse(code,
+                    '*i', 'elem', 'ret =')
+                code('if (ret)')
+                code('    this->${{param.name}}.push_back(elem);')
+                code.dedent()
+                code('}')
+                code.dedent()
+        code('} else {')
+        code('    ret = false;')
+        code('}')
+        code()
+        code('return ret;')
+        code.dedent()
+        code('}')
+
+    code()
+    code('bool ${member_prefix}setPortConnectionCount('
+        'const std::string &name,')
+    code('    unsigned int count)${end_of_decl}')
+
+    if not is_header:
+        code('{')
+        code.indent()
+        code('bool ret = true;')
+        code()
+        code('if (false)')
+        code('    ;')
+        for port in simobj._ports.values():
+            code('else if (name == "${{port.name}}")')
+            code('    this->port_${{port.name}}_connection_count = count;')
+        code('else')
+        code('    ret = false;')
+        code()
+        code('return ret;')
+        code.dedent()
+        code('}')
+
+    code()
+    code('SimObject *${member_prefix}simObjectCreate()${end_of_decl}')
+
+    if not is_header:
+        code('{')
+        if hasattr(simobj, 'abstract') and simobj.abstract:
+            code('    return NULL;')
+        else:
+            code('    return this->create();')
+        code('}')
+
+    if is_header:
+        code()
+        code('static CxxConfigDirectoryEntry'
+            ' *${member_prefix}makeDirectoryEntry()')
+        code('{ return new DirectoryEntry; }')
+
+    if is_header:
+        code.dedent()
+        code('};')
+
 # 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' : bool,
-                      'cxx_class' : str,
-                      'cxx_type' : str,
-                      'type' : str }
+    init_keywords = {
+        'abstract' : bool,
+        'cxx_class' : str,
+        'cxx_type' : str,
+        'cxx_header' : str,
+        'type' : str,
+        'cxx_base' : (str, type(None)),
+        'cxx_extra_bases' : list,
+        'cxx_exports' : list,
+        'cxx_param_exports' : list,
+        'cxx_template_params' : list,
+    }
     # Attributes that can be set any time
     keywords = { 'check' : FunctionType }
 
@@ -128,7 +425,13 @@ class MetaSimObject(type):
         # filtered in __init__.
         cls_dict = {}
         value_dict = {}
+        cxx_exports = []
         for key,val in dict.items():
+            try:
+                cxx_exports.append(getattr(val, "__pybind"))
+            except AttributeError:
+                pass
+
             if public_value(key, val):
                 cls_dict[key] = val
             else:
@@ -136,6 +439,16 @@ class MetaSimObject(type):
                 value_dict[key] = val
         if 'abstract' not in value_dict:
             value_dict['abstract'] = False
+        if 'cxx_extra_bases' not in value_dict:
+            value_dict['cxx_extra_bases'] = []
+        if 'cxx_exports' not in value_dict:
+            value_dict['cxx_exports'] = cxx_exports
+        else:
+            value_dict['cxx_exports'] += cxx_exports
+        if 'cxx_param_exports' not in value_dict:
+            value_dict['cxx_param_exports'] = []
+        if 'cxx_template_params' not in value_dict:
+            value_dict['cxx_template_params'] = []
         cls_dict['_value_dict'] = value_dict
         cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
         if 'type' in value_dict:
@@ -154,16 +467,29 @@ class MetaSimObject(type):
         cls._params = multidict() # param descriptions
         cls._ports = multidict()  # port descriptions
 
+        # Parameter names that are deprecated. Dict[str, DeprecatedParam]
+        # The key is the "old_name" so that when the old_name is used in
+        # python config files, we will use the DeprecatedParam object to
+        # translate to the new type.
+        cls._deprecated_params = multidict()
+
         # class or instance attributes
         cls._values = multidict()   # param values
+        cls._hr_values = multidict() # human readable 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]
 
@@ -175,7 +501,9 @@ class MetaSimObject(type):
             cls._base = base
             cls._params.parent = base._params
             cls._ports.parent = base._ports
+            cls._deprecated_params.parent = base._deprecated_params
             cls._values.parent = base._values
+            cls._hr_values.parent = base._hr_values
             cls._children.parent = base._children
             cls._port_refs.parent = base._port_refs
             # mark base as having been subclassed
@@ -190,19 +518,10 @@ class MetaSimObject(type):
 
             cls._value_dict['cxx_type'] = '%s *' % cls._value_dict['cxx_class']
 
-        # 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)
+            if 'cxx_header' not in cls._value_dict:
+                global noCxxHeader
+                noCxxHeader = True
+                warn("No header file specified for SimObject: %s", name)
 
         # Now process the _value_dict items.  They could be defining
         # new (or overriding existing) parameters or ports, setting
@@ -220,8 +539,17 @@ class MetaSimObject(type):
             elif isinstance(val, Port):
                 cls._new_port(key, val)
 
+            # Deprecated variable names
+            elif isinstance(val, DeprecatedParam):
+                new_name, new_val = cls._get_param_by_value(val.newParam)
+                # Note: We don't know the (string) name of this variable until
+                # here, so now we can finish setting up the dep_param.
+                val.oldName = key
+                val.newName = new_name
+                cls._deprecated_params[key] = val
+
             # init-time-only keywords
-            elif cls.init_keywords.has_key(key):
+            elif key in cls.init_keywords:
                 cls._set_keyword(key, val, cls.init_keywords[key])
 
             # default: use normal path (ends up in __setattr__)
@@ -230,8 +558,8 @@ class MetaSimObject(type):
 
     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)
+            raise TypeError('keyword %s has bad type %s (expecting %s)' % \
+                  (keyword, type(val), kwtype))
         if isinstance(val, FunctionType):
             val = classmethod(val)
         type.__setattr__(cls, keyword, val)
@@ -247,8 +575,9 @@ class MetaSimObject(type):
     def _set_param(cls, name, value, param):
         assert(param.name == name)
         try:
+            hr_value = value
             value = param.convert(value)
-        except Exception, e:
+        except Exception as e:
             msg = "%s\nError setting param %s.%s to %s\n" % \
                   (e, cls.__name__, name, value)
             e.args = (msg, )
@@ -258,6 +587,11 @@ class MetaSimObject(type):
         # it gets cloned properly when the class is instantiated
         if isSimObjectOrVector(value) and not value.has_parent():
             cls._add_cls_child(name, value)
+        # update human-readable values of the param if it has a literal
+        # value and is not an object or proxy.
+        if not (isSimObjectOrVector(value) or\
+                isinstance(value, m5.proxy.BaseProxy)):
+            cls._hr_values[name] = hr_value
 
     def _add_cls_child(cls, name, child):
         # It's a little funky to have a class as a parent, but these
@@ -266,7 +600,8 @@ class MetaSimObject(type):
         # object is not an orphan and can provide better error
         # messages.
         child.set_parent(cls, name)
-        cls._children[name] = child
+        if not isNullPointer(child):
+            cls._children[name] = child
 
     def _new_port(cls, name, port):
         # each port should be uniquely assigned to one variable
@@ -285,6 +620,18 @@ class MetaSimObject(type):
             cls._port_refs[attr] = ref
         return ref
 
+    def _get_param_by_value(cls, value):
+        """Given an object, value, return the name and the value from the
+        internal list of parameter values. If this value can't be found, raise
+        a runtime error. This will search both the current object and its
+        parents.
+        """
+        for k,v in cls._value_dict.items():
+            if v == value:
+                return k,v
+        raise RuntimeError("Cannot find parameter {} in parameter list"
+                           .format(value))
+
     # Set attribute (called on foo.attr = value when foo is an
     # instance of class cls).
     def __setattr__(cls, attr, value):
@@ -293,19 +640,19 @@ class MetaSimObject(type):
             type.__setattr__(cls, attr, value)
             return
 
-        if cls.keywords.has_key(attr):
+        if attr in cls.keywords:
             cls._set_keyword(attr, value, cls.keywords[attr])
             return
 
-        if cls._ports.has_key(attr):
+        if attr in cls._ports:
             cls._cls_get_port_ref(attr).connect(value)
             return
 
         if isSimObjectOrSequence(value) and cls._instantiated:
-            raise RuntimeError, \
+            raise RuntimeError(
                   "cannot set SimObject parameter '%s' after\n" \
                   "    class %s has been instantiated or subclassed" \
-                  % (attr, cls.__name__)
+                  % (attr, cls.__name__))
 
         # check for param
         param = cls._params.get(attr)
@@ -319,8 +666,8 @@ class MetaSimObject(type):
             return
 
         # no valid assignment... raise exception
-        raise AttributeError, \
-              "Class %s has no parameter \'%s\'" % (cls.__name__, attr)
+        raise AttributeError(
+              "Class %s has no parameter \'%s\'" % (cls.__name__, attr))
 
     def __getattr__(cls, attr):
         if attr == 'cxx_class_path':
@@ -332,106 +679,130 @@ class MetaSimObject(type):
         if attr == 'cxx_namespaces':
             return cls.cxx_class_path[:-1]
 
-        if cls._values.has_key(attr):
+        if attr == 'pybind_class':
+            return  '_COLONS_'.join(cls.cxx_class_path)
+
+        if attr in cls._values:
             return cls._values[attr]
 
-        if cls._children.has_key(attr):
+        if attr in cls._children:
             return cls._children[attr]
 
-        raise AttributeError, \
-              "object '%s' has no attribute '%s'" % (cls.__name__, attr)
+        try:
+            return getattr(cls.getCCClass(), attr)
+        except AttributeError:
+            raise AttributeError(
+                "object '%s' has no attribute '%s'" % (cls.__name__, attr))
 
     def __str__(cls):
         return cls.__name__
 
+    def getCCClass(cls):
+        return getattr(m5.internal.params, cls.pybind_class)
+
     # 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
+    def pybind_predecls(cls, code):
+        code('#include "${{cls.cxx_header}}"')
 
-    # 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]
+    def pybind_decl(cls, code):
+        py_class_name = cls.pybind_class
 
         # 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()
+        # here). Sort the params based on their key
+        params = list(map(lambda k_v: k_v[1], sorted(cls._params.local.items())))
+        ports = cls._ports.local
 
-        code('%module(package="m5.internal") param_$cls')
-        code()
-        code('%{')
-        code('#include "params/$cls.hh"')
-        for param in params:
-            param.cxx_predecls(code)
-        cls.export_method_cxx_predecls(code)
-        code('%}')
-        code()
+        code('''#include "pybind11/pybind11.h"
+#include "pybind11/stl.h"
 
-        for param in params:
-            param.swig_predecls(code)
-        cls.export_method_swig_predecls(code)
+#include "params/$cls.hh"
+#include "python/pybind11/core.hh"
+#include "sim/init.hh"
+#include "sim/sim_object.hh"
 
-        code()
-        if cls._base:
-            code('%import "python/m5/internal/param_${{cls._base}}.i"')
-        code()
+#include "${{cls.cxx_header}}"
 
-        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;')
+        for param in params:
+            param.pybind_predecls(code)
 
-        code()
-        code('// stop swig from creating/wrapping default ctor/dtor')
-        code('%nodefault $classname;')
-        code('class $classname')
+        code('''namespace py = pybind11;
+
+static void
+module_init(py::module &m_internal)
+{
+    py::module m = m_internal.def_submodule("param_${cls}");
+''')
+        code.indent()
         if cls._base:
-            code('    : public ${{cls._base.cxx_class}}')
-        code('{')
-        code('  public:')
-        cls.export_methods(code)
-        code('};')
+            code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
+                 'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
+                 'm, "${cls}Params")')
+        else:
+            code('py::class_<${cls}Params, ' \
+                 'std::unique_ptr<${cls}Params, py::nodelete>>(' \
+                 'm, "${cls}Params")')
 
-        for ns in reversed(namespaces):
-            code('} // namespace $ns')
+        code.indent()
+        if not hasattr(cls, 'abstract') or not cls.abstract:
+            code('.def(py::init<>())')
+            code('.def("create", &${cls}Params::create)')
+
+        param_exports = cls.cxx_param_exports + [
+            PyBindProperty(k)
+            for k, v in sorted(cls._params.local.items())
+        ] + [
+            PyBindProperty("port_%s_connection_count" % port.name)
+            for port in ports.values()
+        ]
+        for exp in param_exports:
+            exp.export(code, "%sParams" % cls)
+
+        code(';')
+        code()
+        code.dedent()
 
+        bases = []
+        if 'cxx_base' in cls._value_dict:
+            # If the c++ base class implied by python inheritance was
+            # overridden, use that value.
+            if cls.cxx_base:
+                bases.append(cls.cxx_base)
+        elif cls._base:
+            # If not and if there was a SimObject base, use its c++ class
+            # as this class' base.
+            bases.append(cls._base.cxx_class)
+        # Add in any extra bases that were requested.
+        bases.extend(cls.cxx_extra_bases)
+
+        if bases:
+            base_str = ", ".join(bases)
+            code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
+                 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
+                 'm, "${py_class_name}")')
+        else:
+            code('py::class_<${{cls.cxx_class}}, ' \
+                 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
+                 'm, "${py_class_name}")')
+        code.indent()
+        for exp in cls.cxx_exports:
+            exp.export(code, cls.cxx_class)
+        code(';')
+        code.dedent()
+        code()
+        code.dedent()
+        code('}')
         code()
-        code('%include "params/$cls.hh"')
+        code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
+             cls, cls._base.type if cls._base else "")
 
+    _warned_about_nested_templates = False
 
     # Generate the C++ declaration (.hh file) for this SimObject's
     # param struct.  Called from src/SConscript.
@@ -439,16 +810,88 @@ class MetaSimObject(type):
         # 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()
+        # here). Sort the params based on their key
+        params = list(map(lambda k_v: k_v[1], sorted(cls._params.local.items())))
+        ports = cls._ports.local
         try:
             ptypes = [p.ptype for p in params]
         except:
-            print cls, p, p.ptype_str
-            print params
+            print(cls, p, p.ptype_str)
+            print(params)
             raise
 
-        class_path = cls._value_dict['cxx_class'].split('::')
+        class CxxClass(object):
+            def __init__(self, sig, template_params=[]):
+                # Split the signature into its constituent parts. This could
+                # potentially be done with regular expressions, but
+                # it's simple enough to pick appart a class signature
+                # manually.
+                parts = sig.split('<', 1)
+                base = parts[0]
+                t_args = []
+                if len(parts) > 1:
+                    # The signature had template arguments.
+                    text = parts[1].rstrip(' \t\n>')
+                    arg = ''
+                    # Keep track of nesting to avoid splitting on ","s embedded
+                    # in the arguments themselves.
+                    depth = 0
+                    for c in text:
+                        if c == '<':
+                            depth = depth + 1
+                            if depth > 0 and not \
+                                    self._warned_about_nested_templates:
+                                self._warned_about_nested_templates = True
+                                print('Nested template argument in cxx_class.'
+                                      ' This feature is largely untested and '
+                                      ' may not work.')
+                        elif c == '>':
+                            depth = depth - 1
+                        elif c == ',' and depth == 0:
+                            t_args.append(arg.strip())
+                            arg = ''
+                        else:
+                            arg = arg + c
+                    if arg:
+                        t_args.append(arg.strip())
+                # Split the non-template part on :: boundaries.
+                class_path = base.split('::')
+
+                # The namespaces are everything except the last part of the
+                # class path.
+                self.namespaces = class_path[:-1]
+                # And the class name is the last part.
+                self.name = class_path[-1]
+
+                self.template_params = template_params
+                self.template_arguments = []
+                # Iterate through the template arguments and their values. This
+                # will likely break if parameter packs are used.
+                for arg, param in zip(t_args, template_params):
+                    type_keys = ('class', 'typename')
+                    # If a parameter is a type, parse it recursively. Otherwise
+                    # assume it's a constant, and store it verbatim.
+                    if any(param.strip().startswith(kw) for kw in type_keys):
+                        self.template_arguments.append(CxxClass(arg))
+                    else:
+                        self.template_arguments.append(arg)
+
+            def declare(self, code):
+                # First declare any template argument types.
+                for arg in self.template_arguments:
+                    if isinstance(arg, CxxClass):
+                        arg.declare(code)
+                # Re-open the target namespace.
+                for ns in self.namespaces:
+                    code('namespace $ns {')
+                # If this is a class template...
+                if self.template_params:
+                    code('template <${{", ".join(self.template_params)}}>')
+                # The actual class declaration.
+                code('class ${{self.name}};')
+                # Close the target namespaces.
+                for ns in reversed(self.namespaces):
+                    code('} // namespace $ns')
 
         code('''\
 #ifndef __PARAMS__${cls}__
@@ -456,31 +899,25 @@ class MetaSimObject(type):
 
 ''')
 
-        # 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
+            code('''#include <string>''')
 
-#include <string>
+        cxx_class = CxxClass(cls._value_dict['cxx_class'],
+                             cls._value_dict['cxx_template_params'])
+
+        # A forward class declaration is sufficient since we are just
+        # declaring a pointer.
+        cxx_class.declare(code)
 
-class EventQueue;
-''')
         for param in params:
             param.cxx_predecls(code)
+        for port in ports.values():
+            port.cxx_predecls(code)
         code()
 
         if cls._base:
@@ -504,19 +941,17 @@ class EventQueue;
         code.indent()
         if cls == SimObject:
             code('''
-    SimObjectParams()
-    {
-        extern EventQueue mainEventQueue;
-        eventq = &mainEventQueue;
-    }
+    SimObjectParams() {}
     virtual ~SimObjectParams() {}
 
     std::string name;
-    PyObject *pyobj;
-    EventQueue *eventq;
             ''')
+
         for param in params:
             param.cxx_decl(code)
+        for port in ports.values():
+            port.cxx_decl(code)
+
         code.dedent()
         code('};')
 
@@ -524,55 +959,240 @@ class EventQueue;
         code('#endif // __PARAMS__${cls}__')
         return code
 
+    # Generate the C++ declaration/definition files for this SimObject's
+    # param struct to allow C++ initialisation
+    def cxx_config_param_file(cls, code, is_header):
+        createCxxConfigDirectoryEntryFile(code, cls.__name__, cls, is_header)
+        return code
 
+# This *temporary* definition is required to support calls from the
+# SimObject class definition to the MetaSimObject methods (in
+# particular _set_param, which gets called for parameters with default
+# values defined on the SimObject class itself).  It will get
+# overridden by the permanent definition (which requires that
+# SimObject be defined) lower in this file.
+def isSimObjectOrVector(value):
+    return False
+
+def cxxMethod(*args, **kwargs):
+    """Decorator to export C++ functions to Python"""
+
+    def decorate(func):
+        name = func.__name__
+        override = kwargs.get("override", False)
+        cxx_name = kwargs.get("cxx_name", name)
+        return_value_policy = kwargs.get("return_value_policy", None)
+        static = kwargs.get("static", False)
+
+        args, varargs, keywords, defaults = inspect.getargspec(func)
+        if varargs or keywords:
+            raise ValueError("Wrapped methods must not contain variable " \
+                             "arguments")
+
+        # Create tuples of (argument, default)
+        if defaults:
+            args = args[:-len(defaults)] + \
+                   list(zip(args[-len(defaults):], defaults))
+        # Don't include self in the argument list to PyBind
+        args = args[1:]
+
+
+        @wraps(func)
+        def cxx_call(self, *args, **kwargs):
+            ccobj = self.getCCClass() if static else self.getCCObject()
+            return getattr(ccobj, name)(*args, **kwargs)
+
+        @wraps(func)
+        def py_call(self, *args, **kwargs):
+            return func(self, *args, **kwargs)
+
+        f = py_call if override else cxx_call
+        f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args,
+                                  return_value_policy=return_value_policy,
+                                  static=static)
+
+        return f
+
+    if len(args) == 0:
+        return decorate
+    elif len(args) == 1 and len(kwargs) == 0:
+        return decorate(*args)
+    else:
+        raise TypeError("One argument and no kwargs, or only kwargs expected")
+
+# This class holds information about each simobject parameter
+# that should be displayed on the command line for use in the
+# configuration system.
+class ParamInfo(object):
+  def __init__(self, type, desc, type_str, example, default_val, access_str):
+    self.type = type
+    self.desc = desc
+    self.type_str = type_str
+    self.example_str = example
+    self.default_val = default_val
+    # The string representation used to access this param through python.
+    # The method to access this parameter presented on the command line may
+    # be different, so this needs to be stored for later use.
+    self.access_str = access_str
+    self.created = True
+
+  # Make it so we can only set attributes at initialization time
+  # and effectively make this a const object.
+  def __setattr__(self, name, value):
+    if not "created" in self.__dict__:
+      self.__dict__[name] = value
+
+class SimObjectCliWrapperException(Exception):
+    def __init__(self, message):
+        super(Exception, self).__init__(message)
+
+class SimObjectCliWrapper(object):
+    """
+    Wrapper class to restrict operations that may be done
+    from the command line on SimObjects.
+
+    Only parameters may be set, and only children may be accessed.
+
+    Slicing allows for multiple simultaneous assignment of items in
+    one statement.
+    """
+
+    def __init__(self, sim_objects):
+        self.__dict__['_sim_objects'] = list(sim_objects)
+
+    def __getattr__(self, key):
+        return SimObjectCliWrapper(sim_object._children[key]
+                for sim_object in self._sim_objects)
+
+    def __setattr__(self, key, val):
+        for sim_object in self._sim_objects:
+            if key in sim_object._params:
+                if sim_object._params[key].isCmdLineSettable():
+                    setattr(sim_object, key, val)
+                else:
+                    raise SimObjectCliWrapperException(
+                            'tried to set or unsettable' \
+                            'object parameter: ' + key)
+            else:
+                raise SimObjectCliWrapperException(
+                            'tried to set or access non-existent' \
+                            'object parameter: ' + key)
+
+    def __getitem__(self, idx):
+        """
+        Extends the list() semantics to also allow tuples,
+        for example object[1, 3] selects items 1 and 3.
+        """
+        out = []
+        if isinstance(idx, tuple):
+            for t in idx:
+                out.extend(self[t]._sim_objects)
+        else:
+            if isinstance(idx, int):
+                _range = range(idx, idx + 1)
+            elif not isinstance(idx, slice):
+                raise SimObjectCliWrapperException( \
+                        'invalid index type: ' + repr(idx))
+            for sim_object in self._sim_objects:
+                if isinstance(idx, slice):
+                    _range = range(*idx.indices(len(sim_object)))
+                out.extend(sim_object[i] for i in _range)
+        return SimObjectCliWrapper(out)
+
+    def __iter__(self):
+        return iter(self._sim_objects)
 
 # 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).
+@add_metaclass(MetaSimObject)
 class SimObject(object):
     # Specify metaclass.  Any class inheriting from SimObject will
     # get this metaclass.
-    __metaclass__ = MetaSimObject
     type = 'SimObject'
     abstract = True
 
-    @classmethod
-    def export_method_cxx_predecls(cls, code):
-        code('''
-#include <Python.h>
-
-#include "sim/serialize.hh"
-#include "sim/sim_object.hh"
-''')
-
-    @classmethod
-    def export_method_swig_predecls(cls, code):
-        code('''
-%include <std_string.i>
-''')
+    cxx_header = "sim/sim_object.hh"
+    cxx_extra_bases = [ "Drainable", "Serializable", "Stats::Group" ]
+    eventq_index = Param.UInt32(Parent.eventq_index, "Event Queue Index")
+
+    cxx_exports = [
+        PyBindMethod("init"),
+        PyBindMethod("initState"),
+        PyBindMethod("memInvalidate"),
+        PyBindMethod("memWriteback"),
+        PyBindMethod("regProbePoints"),
+        PyBindMethod("regProbeListeners"),
+        PyBindMethod("startup"),
+    ]
+
+    cxx_param_exports = [
+        PyBindProperty("name"),
+    ]
+
+    @cxxMethod
+    def loadState(self, cp):
+        """Load SimObject state from a checkpoint"""
+        pass
 
-    @classmethod
-    def export_methods(cls, code):
-        code('''
-    enum State {
-      Running,
-      Draining,
-      Drained
-    };
-
-    void init();
-    void loadState(Checkpoint *cp);
-    void initState();
-    void regStats();
-    void regFormulas();
-    void resetStats();
-    void startup();
-
-    unsigned int drain(Event *drain_event);
-    void resume();
-    void switchOut();
-    void takeOverFrom(BaseCPU *cpu);
-''')
+    # Returns a dict of all the option strings that can be
+    # generated as command line options for this simobject instance
+    # by tracing all reachable params in the top level instance and
+    # any children it contains.
+    def enumerateParams(self, flags_dict = {},
+                        cmd_line_str = "", access_str = ""):
+        if hasattr(self, "_paramEnumed"):
+            print("Cycle detected enumerating params")
+        else:
+            self._paramEnumed = True
+            # Scan the children first to pick up all the objects in this SimObj
+            for keys in self._children:
+                child = self._children[keys]
+                next_cmdline_str = cmd_line_str + keys
+                next_access_str = access_str + keys
+                if not isSimObjectVector(child):
+                    next_cmdline_str = next_cmdline_str + "."
+                    next_access_str = next_access_str + "."
+                flags_dict = child.enumerateParams(flags_dict,
+                                                   next_cmdline_str,
+                                                   next_access_str)
+
+            # Go through the simple params in the simobject in this level
+            # of the simobject hierarchy and save information about the
+            # parameter to be used for generating and processing command line
+            # options to the simulator to set these parameters.
+            for keys,values in self._params.items():
+                if values.isCmdLineSettable():
+                    type_str = ''
+                    ex_str = values.example_str()
+                    ptype = None
+                    if isinstance(values, VectorParamDesc):
+                        type_str = 'Vector_%s' % values.ptype_str
+                        ptype = values
+                    else:
+                        type_str = '%s' % values.ptype_str
+                        ptype = values.ptype
+
+                    if keys in self._hr_values\
+                       and keys in self._values\
+                       and not isinstance(self._values[keys],
+                                          m5.proxy.BaseProxy):
+                        cmd_str = cmd_line_str + keys
+                        acc_str = access_str + keys
+                        flags_dict[cmd_str] = ParamInfo(ptype,
+                                    self._params[keys].desc, type_str, ex_str,
+                                    values.pretty_print(self._hr_values[keys]),
+                                    acc_str)
+                    elif not keys in self._hr_values\
+                         and not keys in self._values:
+                        # Empty param
+                        cmd_str = cmd_line_str + keys
+                        acc_str = access_str + keys
+                        flags_dict[cmd_str] = ParamInfo(ptype,
+                                    self._params[keys].desc,
+                                    type_str, ex_str, '', acc_str)
+
+        return flags_dict
 
     # Initialize new instance.  For objects with SimObject-valued
     # children, we need to recursively clone the classes represented
@@ -607,15 +1227,16 @@ class SimObject(object):
         # 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():
+        for key,val in ancestor._children.items():
             self.add_child(key, val(_memo=memo_dict))
 
         # Inherit parameter values from class using multidict so
         # individual value settings can be overridden but we still
         # inherit late changes to non-overridden class values.
         self._values = multidict(ancestor._values)
+        self._hr_values = multidict(ancestor._hr_values)
         # clone SimObject-valued parameters
-        for key,val in ancestor._values.iteritems():
+        for key,val in ancestor._values.items():
             val = tryAsSimObjectOrVector(val)
             if val is not None:
                 self._values[key] = val(_memo=memo_dict)
@@ -623,10 +1244,10 @@ class SimObject(object):
         # clone port references.  no need to use a multidict here
         # since we will be creating new references for all ports.
         self._port_refs = {}
-        for key,val in ancestor._port_refs.iteritems():
+        for key,val in ancestor._port_refs.items():
             self._port_refs[key] = val.clone(self, memo_dict)
         # apply attribute assignments from keyword args, if any
-        for key,val in kwargs.iteritems():
+        for key,val in kwargs.items():
             setattr(self, key, val)
 
     # "Clone" the current instance by creating another instance of
@@ -640,13 +1261,13 @@ class SimObject(object):
             # 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 " \
+                raise RuntimeError("attempt to clone object %s " \
                       "not at the root of a tree (parent = %s)" \
-                      % (self, self._parent)
+                      % (self, self._parent))
             # create a new dict and use that.
             memo_dict = {}
             kwargs['_memo'] = memo_dict
-        elif memo_dict.has_key(self):
+        elif self in memo_dict:
             # clone already done & memoized
             return memo_dict[self]
         return self.__class__(_ancestor = self, **kwargs)
@@ -656,32 +1277,41 @@ class SimObject(object):
         # via __setattr__.  There is only ever one reference
         # object per port, but we create them lazily here.
         ref = self._port_refs.get(attr)
-        if not ref:
+        if ref == None:
             ref = self._ports[attr].makeRef(self)
             self._port_refs[attr] = ref
         return ref
 
     def __getattr__(self, attr):
-        if self._ports.has_key(attr):
+        if attr in self._deprecated_params:
+            dep_param = self._deprecated_params[attr]
+            dep_param.printWarning(self._name, self.__class__.__name__)
+            return getattr(self, self._deprecated_params[attr].newName)
+
+        if attr in self._ports:
             return self._get_port_ref(attr)
 
-        if self._values.has_key(attr):
+        if attr in self._values:
             return self._values[attr]
 
-        if self._children.has_key(attr):
+        if attr in self._children:
             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(),
-        # regFormulas(), resetStats(), startup(), drain(), and
-        # resume().
+        # methods exported to Python (e.g., init(), and startup())
         if self._ccObject and hasattr(self._ccObject, attr):
             return getattr(self._ccObject, attr)
 
-        raise AttributeError, "object '%s' has no attribute '%s'" \
+        err_string = "object '%s' has no attribute '%s'" \
               % (self.__class__.__name__, attr)
 
+        if not self._ccObject:
+            err_string += "\n  (C++ object is not yet constructed," \
+                          " so wrapped C++ methods are unavailable.)"
+
+        raise AttributeError(err_string)
+
     # Set attribute (called on foo.attr = value when foo is an
     # instance of class cls).
     def __setattr__(self, attr, value):
@@ -690,21 +1320,22 @@ class SimObject(object):
             object.__setattr__(self, attr, value)
             return
 
-        if self._ports.has_key(attr):
+        if attr in self._deprecated_params:
+            dep_param = self._deprecated_params[attr]
+            dep_param.printWarning(self._name, self.__class__.__name__)
+            return setattr(self, self._deprecated_params[attr].newName, value)
+
+        if attr in self._ports:
             # set up port connection
             self._get_port_ref(attr).connect(value)
             return
 
-        if isSimObjectOrSequence(value) and self._instantiated:
-            raise RuntimeError, \
-                  "cannot set SimObject parameter '%s' after\n" \
-                  "    instance been cloned %s" % (attr, `self`)
-
         param = self._params.get(attr)
         if param:
             try:
+                hr_value = value
                 value = param.convert(value)
-            except Exception, e:
+            except Exception as e:
                 msg = "%s\nError setting param %s.%s to %s\n" % \
                       (e, self.__class__.__name__, attr, value)
                 e.args = (msg, )
@@ -713,6 +1344,13 @@ class SimObject(object):
             # implicitly parent unparented objects assigned as params
             if isSimObjectOrVector(value) and not value.has_parent():
                 self.add_child(attr, value)
+            # set the human-readable value dict if this is a param
+            # with a literal value and is not being set as an object
+            # or proxy.
+            if not (isSimObjectOrVector(value) or\
+                    isinstance(value, m5.proxy.BaseProxy)):
+                self._hr_values[attr] = hr_value
+
             return
 
         # if RHS is a SimObject, it's an implicit child assignment
@@ -721,8 +1359,8 @@ class SimObject(object):
             return
 
         # no valid assignment... raise exception
-        raise AttributeError"Class %s has no parameter %s" \
-              % (self.__class__.__name__, attr)
+        raise AttributeError("Class %s has no parameter %s" \
+              % (self.__class__.__name__, attr))
 
 
     # this hack allows tacking a '[0]' onto parameters that may or may
@@ -730,7 +1368,13 @@ class SimObject(object):
     def __getitem__(self, key):
         if key == 0:
             return self
-        raise TypeError, "Non-zero index '%s' to SimObject" % key
+        raise IndexError("Non-zero index '%s' to SimObject" % key)
+
+    # this hack allows us to iterate over a SimObject that may
+    # not be a vector, so we can call a loop over it and get just one
+    # element.
+    def __len__(self):
+        return 1
 
     # Also implemented by SimObjectVector
     def clear_parent(self, old_parent):
@@ -742,6 +1386,12 @@ class SimObject(object):
         self._parent = parent
         self._name = name
 
+    # Return parent object of this SimObject, not implemented by
+    # SimObjectVector because the elements in a SimObjectVector may not share
+    # the same parent
+    def get_parent(self):
+        return self._parent
+
     # Also implemented by SimObjectVector
     def get_name(self):
         return self._name
@@ -761,16 +1411,17 @@ class SimObject(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):
+            warn("add_child('%s'): child '%s' already has parent", name,
+                child.get_name())
+        if name in self._children:
             # 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
+        if not isNullPointer(child):
+            self._children[name] = child
 
     # Take SimObject-valued parameters that haven't been explicitly
     # assigned as children and make them children of the object that
@@ -778,28 +1429,40 @@ class SimObject(object):
     # 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():
+        for key,val in self._values.items():
             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)
+                warn("%s adopting orphan SimObject param '%s'", self, key)
                 self.add_child(key, val)
 
     def path(self):
         if not self._parent:
             return '<orphan %s>' % self.__class__
+        elif isinstance(self._parent, MetaSimObject):
+            return str(self.__class__)
+
         ppath = self._parent.path()
         if ppath == 'root':
             return self._name
         return ppath + "." + self._name
 
+    def path_list(self):
+        if self._parent:
+            return self._parent.path_list() + [ self._name, ]
+        else:
+            # Don't include the root node
+            return []
+
     def __str__(self):
         return self.path()
 
+    def config_value(self):
+        return self.path()
+
     def ini_str(self):
         return self.path()
 
@@ -808,56 +1471,74 @@ class SimObject(object):
             return self, True
 
         found_obj = None
-        for child in self._children.itervalues():
-            if isinstance(child, ptype):
+        for child in self._children.values():
+            visited = False
+            if hasattr(child, '_visited'):
+              visited = getattr(child, '_visited')
+
+            if isinstance(child, ptype) and not visited:
                 if found_obj != None and child != found_obj:
-                    raise AttributeError, \
+                    raise AttributeError(
                           'parent.any matched more than one: %s %s' % \
-                          (found_obj.path, child.path)
+                          (found_obj.path, child.path))
                 found_obj = child
         # search param space
-        for pname,pdesc in self._params.iteritems():
+        for pname,pdesc in self._params.items():
             if issubclass(pdesc.ptype, ptype):
                 match_obj = self._values[pname]
                 if found_obj != None and found_obj != match_obj:
-                    raise AttributeError, \
-                          'parent.any matched more than one: %s and %s' % (found_obj.path, match_obj.path)
+                    raise AttributeError(
+                          '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():
-            if isinstance(child, ptype) and not isproxy(child) and \
-               not isNullPointer(child):
-                all[child] = True
+        for child in self._children.values():
+            # 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():
+        for pname,pdesc in self._params.items():
             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
+        # Also make sure to sort the keys based on the objects' path to
+        # ensure that the order is the same on all hosts
+        return sorted(all.keys(), key = lambda o: o.path()), True
 
     def unproxy(self, base):
         return self
 
     def unproxyParams(self):
-        for param in self._params.iterkeys():
+        for param in self._params.keys():
             value = self._values.get(param)
             if value != None and isproxy(value):
                 try:
                     value = value.unproxy(self)
                 except:
-                    print "Error in unproxying param '%s' of %s" % \
-                          (param, self.path())
+                    print("Error in unproxying param '%s' of %s" %
+                          (param, self.path()))
                     raise
                 setattr(self, param, value)
 
         # Unproxy ports in sorted order so that 'append' operations on
         # vector ports are done in a deterministic fashion.
-        port_names = self._ports.keys()
+        port_names = list(self._ports.keys())
         port_names.sort()
         for port_name in port_names:
             port = self._port_refs.get(port_name)
@@ -865,30 +1546,31 @@ class SimObject(object):
                 port.unproxy(self)
 
     def print_ini(self, ini_file):
-        print >>ini_file, '[' + self.path() + ']'       # .ini section header
+        print('[' + self.path() + ']', file=ini_file)    # .ini section header
 
         instanceDict[self.path()] = self
 
         if hasattr(self, 'type'):
-            print >>ini_file, 'type=%s' % self.type
+            print('type=%s' % self.type, file=ini_file)
 
         if len(self._children.keys()):
-            print >>ini_file, 'children=%s' % \
-                  ' '.join(self._children[n].get_name() \
-                  for n in sorted(self._children.keys()))
+            print('children=%s' %
+                  ' '.join(self._children[n].get_name()
+                           for n in sorted(self._children.keys())),
+                  file=ini_file)
 
         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())
+                print('%s=%s' % (param, self._values[param].ini_str()),
+                      file=ini_file)
 
         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('%s=%s' % (port_name, port.ini_str()), file=ini_file)
 
-        print >>ini_file        # blank line between objects
+        print(file=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
@@ -899,32 +1581,29 @@ class SimObject(object):
             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)
-            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
+            if value != None:
+                d[param] = value.config_value()
 
         for n in sorted(self._children.keys()):
-            d[self._children[n].get_name()] =  self._children[n].get_config_as_dict()
+            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:
-                # Might want to actually make this reference the object
-                # in the future, although execing the string problem would
-                # get some of the way there
-                d[port_name] = port.ini_str()
+                # Represent each port with a dictionary containing the
+                # prominent attributes
+                d[port_name] = port.get_config_as_dict()
 
         return d
 
@@ -934,10 +1613,9 @@ class SimObject(object):
 
         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 = list(self._params.keys())
         param_names.sort()
         for param in param_names:
             value = self._values.get(param)
@@ -950,17 +1628,27 @@ class SimObject(object):
                 assert isinstance(value, list)
                 vec = getattr(cc_params, param)
                 assert not len(vec)
-                for v in value:
-                    vec.append(v)
+                # Some types are exposed as opaque types. They support
+                # the append operation unlike the automatically
+                # wrapped types.
+                if isinstance(vec, list):
+                    setattr(cc_params, param, list(value))
+                else:
+                    for v in value:
+                        getattr(cc_params, param).append(v)
             else:
                 setattr(cc_params, param, value)
 
-        port_names = self._ports.keys()
+        port_names = list(self._ports.keys())
         port_names.sort()
         for port_name in port_names:
             port = self._port_refs.get(port_name, None)
             if port != None:
-                setattr(cc_params, port_name, port)
+                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
 
@@ -971,20 +1659,24 @@ class SimObject(object):
         if not self._ccObject:
             # Make sure this object is in the configuration hierarchy
             if not self._parent and not isRoot(self):
-                raise RuntimeError, "Attempt to instantiate orphan node"
+                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()
+            if not self.abstract:
+                params = self.getCCParams()
+                self._ccObject = params.create()
         elif self._ccObject == -1:
-            raise RuntimeError"%s: Cycle found in configuration hierarchy." \
-                  % self.path()
+            raise RuntimeError("%s: Cycle found in configuration hierarchy." \
+                  % self.path())
         return self._ccObject
 
     def descendants(self):
         yield self
-        for child in self._children.itervalues():
+        # The order of the dict is implementation dependent, so sort
+        # it based on the key (name) to ensure the order is the same
+        # on all hosts
+        for (name, child) in sorted(self._children.items()):
             for obj in child.descendants():
                 yield obj
 
@@ -996,70 +1688,65 @@ class SimObject(object):
     def getValue(self):
         return self.getCCObject()
 
+    @cxxMethod(return_value_policy="reference")
+    def getPort(self, if_name, idx):
+        pass
+
     # Create C++ port connections corresponding to the connections in
     # _port_refs
     def connectPorts(self):
-        for portRef in self._port_refs.itervalues():
+        # Sort the ports based on their attribute name to ensure the
+        # order is the same on all hosts
+        for (attr, portRef) in sorted(self._port_refs.items()):
             portRef.ccConnect()
 
-    def getMemoryMode(self):
-        if not isinstance(self, m5.objects.System):
-            return None
-
-        return self._ccObject.getMemoryMode()
-
-    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
-            self._ccObject.setMemoryMode(mode)
-
-    def takeOverFrom(self, old_cpu):
-        self._ccObject.takeOverFrom(old_cpu._ccObject)
-
-    # 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
+    # Default function for generating the device structure.
+    # Can be overloaded by the inheriting class
+    def generateDeviceTree(self, state):
+        return # return without yielding anything
+        yield  # make this function a (null) generator
+
+    def recurseDeviceTree(self, state):
+        for child in self._children.values():
+            for item in child: # For looping over SimObjectVectors
+                for dt in item.generateDeviceTree(state):
+                    yield dt
+
+    # On a separate method otherwise certain buggy Python versions
+    # would fail with: SyntaxError: unqualified exec is not allowed
+    # in function 'apply_config'
+    def _apply_config_get_dict(self):
+        return {
+            child_name: SimObjectCliWrapper(
+                iter(self._children[child_name]))
+            for child_name in self._children
+        }
+
+    def apply_config(self, params):
+        """
+        exec a list of Python code strings contained in params.
+
+        The only exposed globals to those strings are the child
+        SimObjects of this node.
+
+        This function is intended to allow users to modify SimObject
+        parameters from the command line with Python statements.
+        """
+        d = self._apply_config_get_dict()
+        for param in params:
+            exec(param, d)
 
-            if isSimObject(param.ptype) and string != "Null":
-                simobjs.append(string)
-            else:
-                label += '%s = %s\\n' % (param.name, string)
+    def get_simobj(self, simobj_path):
+        """
+        Get all sim objects that match a given string.
 
-        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))
+        The format is the same as that supported by SimObjectCliWrapper.
 
-        # recursively dump out children
-        for c in self.children:
-            c.outputDot(dot)
+        :param simobj_path: Current state to be in.
+        :type simobj_path: str
+        """
+        d = self._apply_config_get_dict()
+        return eval(simobj_path, d)
 
 # Function to provide to C++ so it can look up instances based on paths
 def resolveSimObject(name):
@@ -1105,19 +1792,25 @@ def tryAsSimObjectOrVector(value):
 def coerceSimObjectOrVector(value):
     value = tryAsSimObjectOrVector(value)
     if value is None:
-        raise TypeError, "SimObject or SimObjectVector expected"
+        raise TypeError("SimObject or SimObjectVector expected")
     return value
 
 baseClasses = allClasses.copy()
 baseInstances = instanceDict.copy()
 
 def clear():
-    global allClasses, instanceDict
+    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' ]
+__all__ = [
+    'SimObject',
+    'cxxMethod',
+    'PyBindMethod',
+    'PyBindProperty',
+]