+# 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
# 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
# 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 }
# 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:
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:
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]
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
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
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__)
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)
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, )
# 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
# 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
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):
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)
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':
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.
# 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}__
''')
- # 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:
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('};')
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
# 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)
# 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
# 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)
# 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):
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, )
# 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
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
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):
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
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
# 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()
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)
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
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
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)
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
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
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):
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',
+]