SimObject.py revision 13764:1647bbdc9444
12623SN/A# Copyright (c) 2017-2018 ARM Limited
28926Sandreas.hansson@arm.com# All rights reserved.
38926Sandreas.hansson@arm.com#
48926Sandreas.hansson@arm.com# The license below extends only to copyright in the software and shall
58926Sandreas.hansson@arm.com# not be construed as granting a license to any other intellectual
68926Sandreas.hansson@arm.com# property including but not limited to intellectual property relating
78926Sandreas.hansson@arm.com# to a hardware implementation of the functionality of the software
88926Sandreas.hansson@arm.com# licensed hereunder.  You may use the software subject to the license
98926Sandreas.hansson@arm.com# terms below provided that you ensure that this notice is replicated
108926Sandreas.hansson@arm.com# unmodified and in its entirety in all distributions of the software,
118926Sandreas.hansson@arm.com# modified or unmodified, in source code or in binary form.
128926Sandreas.hansson@arm.com#
138926Sandreas.hansson@arm.com# Copyright (c) 2004-2006 The Regents of The University of Michigan
142623SN/A# Copyright (c) 2010-20013 Advanced Micro Devices, Inc.
152623SN/A# Copyright (c) 2013 Mark D. Hill and David A. Wood
162623SN/A# All rights reserved.
172623SN/A#
182623SN/A# Redistribution and use in source and binary forms, with or without
192623SN/A# modification, are permitted provided that the following conditions are
202623SN/A# met: redistributions of source code must retain the above copyright
212623SN/A# notice, this list of conditions and the following disclaimer;
222623SN/A# redistributions in binary form must reproduce the above copyright
232623SN/A# notice, this list of conditions and the following disclaimer in the
242623SN/A# documentation and/or other materials provided with the distribution;
252623SN/A# neither the name of the copyright holders nor the names of its
262623SN/A# contributors may be used to endorse or promote products derived from
272623SN/A# this software without specific prior written permission.
282623SN/A#
292623SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
302623SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
312623SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
322623SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
332623SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
342623SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
352623SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
362623SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
372623SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
382623SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
392665Ssaidi@eecs.umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
402665Ssaidi@eecs.umich.edu#
412623SN/A# Authors: Steve Reinhardt
422623SN/A#          Nathan Binkert
433170Sstever@eecs.umich.edu#          Andreas Hansson
448105Sgblack@eecs.umich.edu#          Andreas Sandberg
452623SN/A
464040Ssaidi@eecs.umich.edufrom __future__ import print_function
476658Snate@binkert.orgfrom __future__ import absolute_import
488229Snate@binkert.orgimport six
492623SN/Aif six.PY3:
509443SAndreas.Sandberg@ARM.com    long = int
518232Snate@binkert.org
528232Snate@binkert.orgimport sys
533348Sbinkertn@umich.edufrom types import FunctionType, MethodType, ModuleType
543348Sbinkertn@umich.edufrom functools import wraps
558926Sandreas.hansson@arm.comimport inspect
564762Snate@binkert.org
577678Sgblack@eecs.umich.eduimport m5
582901Ssaidi@eecs.umich.edufrom m5.util import *
598779Sgblack@eecs.umich.edufrom m5.util.pybind import *
602623SN/A# Use the pyfdt and not the helper class, because the fdthelper
612623SN/A# relies on the SimObject definition
622623SN/Afrom m5.ext.pyfdt import pyfdt
632623SN/A
642623SN/A# Have to import params up top since Param is referenced on initial
655606Snate@binkert.org# load (when SimObject class references Param to create a class
662623SN/A# variable, the 'name' param)...
672623SN/Afrom m5.params import *
682623SN/A# There are a few things we need that aren't in params.__all__ since
692623SN/A# normal users don't need them
702623SN/Afrom m5.params import ParamDesc, VectorParamDesc, \
712623SN/A     isNullPointer, SimObjectVector, Port
722623SN/A
732623SN/Afrom m5.proxy import *
742623SN/Afrom m5.proxy import isproxy
752623SN/A
762623SN/A#####################################################################
775336Shines@cs.fsu.edu#
782623SN/A# M5 Python Configuration Utility
794873Sstever@eecs.umich.edu#
802623SN/A# The basic idea is to write simple Python programs that build Python
812623SN/A# objects corresponding to M5 SimObjects for the desired simulation
822623SN/A# configuration.  For now, the Python emits a .ini file that can be
832623SN/A# parsed by M5.  In the future, some tighter integration between M5
842623SN/A# and the Python interpreter may allow bypassing the .ini file.
852623SN/A#
868921Sandreas.hansson@arm.com# Each SimObject class in M5 is represented by a Python class with the
879433SAndreas.Sandberg@ARM.com# same name.  The Python inheritance tree mirrors the M5 C++ tree
889424SAndreas.Sandberg@ARM.com# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
899424SAndreas.Sandberg@ARM.com# SimObjects inherit from a single SimObject base class).  To specify
909424SAndreas.Sandberg@ARM.com# an instance of an M5 SimObject in a configuration, the user simply
919424SAndreas.Sandberg@ARM.com# instantiates the corresponding Python object.  The parameters for
929424SAndreas.Sandberg@ARM.com# that SimObject are given by assigning to attributes of the Python
938921Sandreas.hansson@arm.com# object, either using keyword assignment in the constructor or in
948921Sandreas.hansson@arm.com# separate assignment statements.  For example:
958921Sandreas.hansson@arm.com#
969433SAndreas.Sandberg@ARM.com# cache = BaseCache(size='64KB')
978779Sgblack@eecs.umich.edu# cache.hit_latency = 3
988779Sgblack@eecs.umich.edu# cache.assoc = 8
998779Sgblack@eecs.umich.edu#
1008779Sgblack@eecs.umich.edu# The magic lies in the mapping of the Python attributes for SimObject
1018779Sgblack@eecs.umich.edu# classes to the actual SimObject parameter specifications.  This
1028779Sgblack@eecs.umich.edu# allows parameter validity checking in the Python code.  Continuing
1032623SN/A# the example above, the statements "cache.blurfl=3" or
1048706Sandreas.hansson@arm.com# "cache.assoc='hello'" would both result in runtime errors in Python,
1055714Shsul@eecs.umich.edu# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
1065712Shsul@eecs.umich.edu# parameter requires an integer, respectively.  This magic is done
1075712Shsul@eecs.umich.edu# primarily by overriding the special __setattr__ method that controls
1085712Shsul@eecs.umich.edu# assignment to object attributes.
1092623SN/A#
1102623SN/A# Once a set of Python objects have been instantiated in a hierarchy,
1115529Snate@binkert.org# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
1126078Sgblack@eecs.umich.edu# will generate a .ini file.
1135487Snate@binkert.org#
1145487Snate@binkert.org#####################################################################
1159443SAndreas.Sandberg@ARM.com
1169095Sandreas.hansson@arm.com# list of all SimObject classes
1179095Sandreas.hansson@arm.comallClasses = {}
1188926Sandreas.hansson@arm.com
1192623SN/A# dict to look up SimObjects based on path
1202623SN/AinstanceDict = {}
1212623SN/A
1222623SN/A# Did any of the SimObjects lack a header file?
1232623SN/AnoCxxHeader = False
1242623SN/A
1252623SN/Adef public_value(key, value):
1266775SBrad.Beckmann@amd.com    return key.startswith('_') or \
1276775SBrad.Beckmann@amd.com               isinstance(value, (FunctionType, MethodType, ModuleType,
1286775SBrad.Beckmann@amd.com                                  classmethod, type))
1292623SN/A
1302623SN/Adef createCxxConfigDirectoryEntryFile(code, name, simobj, is_header):
1319443SAndreas.Sandberg@ARM.com    entry_class = 'CxxConfigDirectoryEntry_%s' % name
1329443SAndreas.Sandberg@ARM.com    param_class = '%sCxxConfigParams' % name
1332623SN/A
1349443SAndreas.Sandberg@ARM.com    code('#include "params/%s.hh"' % name)
1359443SAndreas.Sandberg@ARM.com
1369443SAndreas.Sandberg@ARM.com    if not is_header:
1372623SN/A        for param in simobj._params.values():
1389443SAndreas.Sandberg@ARM.com            if isSimObjectClass(param.ptype):
1399443SAndreas.Sandberg@ARM.com                code('#include "%s"' % param.ptype._value_dict['cxx_header'])
1409443SAndreas.Sandberg@ARM.com                code('#include "params/%s.hh"' % param.ptype.__name__)
1419443SAndreas.Sandberg@ARM.com            else:
1429443SAndreas.Sandberg@ARM.com                param.ptype.cxx_ini_predecls(code)
1439443SAndreas.Sandberg@ARM.com
1449443SAndreas.Sandberg@ARM.com    if is_header:
1452915Sktlim@umich.edu        member_prefix = ''
1469443SAndreas.Sandberg@ARM.com        end_of_decl = ';'
1479443SAndreas.Sandberg@ARM.com        code('#include "sim/cxx_config.hh"')
1489443SAndreas.Sandberg@ARM.com        code()
1499342SAndreas.Sandberg@arm.com        code('class ${param_class} : public CxxConfigParams,'
1509342SAndreas.Sandberg@arm.com            ' public ${name}Params')
1512915Sktlim@umich.edu        code('{')
1529342SAndreas.Sandberg@arm.com        code('  private:')
1532915Sktlim@umich.edu        code.indent()
1549443SAndreas.Sandberg@ARM.com        code('class DirectoryEntry : public CxxConfigDirectoryEntry')
1555220Ssaidi@eecs.umich.edu        code('{')
1565220Ssaidi@eecs.umich.edu        code('  public:')
1575220Ssaidi@eecs.umich.edu        code.indent()
1584940Snate@binkert.org        code('DirectoryEntry();');
1599424SAndreas.Sandberg@ARM.com        code()
1609424SAndreas.Sandberg@ARM.com        code('CxxConfigParams *makeParamsObject() const')
1619424SAndreas.Sandberg@ARM.com        code('{ return new ${param_class}; }')
1629424SAndreas.Sandberg@ARM.com        code.dedent()
1633324Shsul@eecs.umich.edu        code('};')
1649443SAndreas.Sandberg@ARM.com        code()
1659443SAndreas.Sandberg@ARM.com        code.dedent()
1669443SAndreas.Sandberg@ARM.com        code('  public:')
1679443SAndreas.Sandberg@ARM.com        code.indent()
1687897Shestness@cs.utexas.edu    else:
1692623SN/A        member_prefix = '%s::' % param_class
1702623SN/A        end_of_decl = ''
1719443SAndreas.Sandberg@ARM.com        code('#include "%s"' % simobj._value_dict['cxx_header'])
1729443SAndreas.Sandberg@ARM.com        code('#include "base/str.hh"')
1739443SAndreas.Sandberg@ARM.com        code('#include "cxx_config/${name}.hh"')
1749443SAndreas.Sandberg@ARM.com
1759443SAndreas.Sandberg@ARM.com        if simobj._ports:
1769443SAndreas.Sandberg@ARM.com            code('#include "mem/mem_object.hh"')
1779443SAndreas.Sandberg@ARM.com            code('#include "mem/port.hh"')
1789443SAndreas.Sandberg@ARM.com
1799443SAndreas.Sandberg@ARM.com        code()
1809443SAndreas.Sandberg@ARM.com        code('${member_prefix}DirectoryEntry::DirectoryEntry()');
1819443SAndreas.Sandberg@ARM.com        code('{')
1829443SAndreas.Sandberg@ARM.com
1839443SAndreas.Sandberg@ARM.com        def cxx_bool(b):
1849443SAndreas.Sandberg@ARM.com            return 'true' if b else 'false'
1859443SAndreas.Sandberg@ARM.com
1869443SAndreas.Sandberg@ARM.com        code.indent()
1879443SAndreas.Sandberg@ARM.com        for param in simobj._params.values():
1889443SAndreas.Sandberg@ARM.com            is_vector = isinstance(param, m5.params.VectorParamDesc)
1892623SN/A            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
1902798Sktlim@umich.edu
1912623SN/A            code('parameters["%s"] = new ParamDesc("%s", %s, %s);' %
1929429SAndreas.Sandberg@ARM.com                (param.name, param.name, cxx_bool(is_vector),
1939429SAndreas.Sandberg@ARM.com                cxx_bool(is_simobj)));
1949443SAndreas.Sandberg@ARM.com
1959342SAndreas.Sandberg@arm.com        for port in simobj._ports.values():
1969443SAndreas.Sandberg@ARM.com            is_vector = isinstance(port, m5.params.VectorPort)
1979443SAndreas.Sandberg@ARM.com            is_master = port.role == 'MASTER'
1982798Sktlim@umich.edu
1992623SN/A            code('ports["%s"] = new PortDesc("%s", %s, %s);' %
2002623SN/A                (port.name, port.name, cxx_bool(is_vector),
2012623SN/A                cxx_bool(is_master)))
2022623SN/A
2032623SN/A        code.dedent()
2042623SN/A        code('}')
2059429SAndreas.Sandberg@ARM.com        code()
2062623SN/A
2079443SAndreas.Sandberg@ARM.com    code('bool ${member_prefix}setSimObject(const std::string &name,')
2082623SN/A    code('    SimObject *simObject)${end_of_decl}')
2092623SN/A
2109443SAndreas.Sandberg@ARM.com    if not is_header:
2119443SAndreas.Sandberg@ARM.com        code('{')
2129443SAndreas.Sandberg@ARM.com        code.indent()
2139443SAndreas.Sandberg@ARM.com        code('bool ret = true;')
2149443SAndreas.Sandberg@ARM.com        code()
2159443SAndreas.Sandberg@ARM.com        code('if (false) {')
2169443SAndreas.Sandberg@ARM.com        for param in simobj._params.values():
2179443SAndreas.Sandberg@ARM.com            is_vector = isinstance(param, m5.params.VectorParamDesc)
2183512Sktlim@umich.edu            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
2199443SAndreas.Sandberg@ARM.com
2205712Shsul@eecs.umich.edu            if is_simobj and not is_vector:
2215712Shsul@eecs.umich.edu                code('} else if (name == "${{param.name}}") {')
2225712Shsul@eecs.umich.edu                code.indent()
2232623SN/A                code('this->${{param.name}} = '
2242623SN/A                    'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);')
2252623SN/A                code('if (simObject && !this->${{param.name}})')
2262623SN/A                code('   ret = false;')
2279180Sandreas.hansson@arm.com                code.dedent()
2282623SN/A        code('} else {')
2294940Snate@binkert.org        code('    ret = false;')
2304940Snate@binkert.org        code('}')
2312623SN/A        code()
2322683Sktlim@umich.edu        code('return ret;')
2332623SN/A        code.dedent()
2342623SN/A        code('}')
2352623SN/A
2362623SN/A    code()
2372623SN/A    code('bool ${member_prefix}setSimObjectVector('
2389180Sandreas.hansson@arm.com        'const std::string &name,')
2393686Sktlim@umich.edu    code('    const std::vector<SimObject *> &simObjects)${end_of_decl}')
2403430Sgblack@eecs.umich.edu
2419179Sandreas.hansson@arm.com    if not is_header:
2429342SAndreas.Sandberg@arm.com        code('{')
2432623SN/A        code.indent()
2442623SN/A        code('bool ret = true;')
2452623SN/A        code()
2462623SN/A        code('if (false) {')
2478737Skoansin.tan@gmail.com        for param in simobj._params.values():
2482623SN/A            is_vector = isinstance(param, m5.params.VectorParamDesc)
2494940Snate@binkert.org            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
2504940Snate@binkert.org
2512623SN/A            if is_simobj and is_vector:
2522683Sktlim@umich.edu                code('} else if (name == "${{param.name}}") {')
2532623SN/A                code.indent()
2546043Sgblack@eecs.umich.edu                code('this->${{param.name}}.clear();')
2556043Sgblack@eecs.umich.edu                code('for (auto i = simObjects.begin(); '
2566043Sgblack@eecs.umich.edu                    'ret && i != simObjects.end(); i ++)')
2579342SAndreas.Sandberg@arm.com                code('{')
2582626SN/A                code.indent()
2592626SN/A                code('${{param.ptype.cxx_type}} object = '
2602626SN/A                    'dynamic_cast<${{param.ptype.cxx_type}}>(*i);')
2612626SN/A                code('if (*i && !object)')
2625606Snate@binkert.org                code('    ret = false;')
2632623SN/A                code('else')
2642623SN/A                code('    this->${{param.name}}.push_back(object);')
2652623SN/A                code.dedent()
2662623SN/A                code('}')
2672623SN/A                code.dedent()
2682623SN/A        code('} else {')
2692623SN/A        code('    ret = false;')
2708444Sgblack@eecs.umich.edu        code('}')
2718444Sgblack@eecs.umich.edu        code()
2722623SN/A        code('return ret;')
2733169Sstever@eecs.umich.edu        code.dedent()
2744870Sstever@eecs.umich.edu        code('}')
2752623SN/A
2762623SN/A    code()
2772623SN/A    code('void ${member_prefix}setName(const std::string &name_)'
2782623SN/A        '${end_of_decl}')
2792623SN/A
2804999Sgblack@eecs.umich.edu    if not is_header:
2816227Snate@binkert.org        code('{')
2824999Sgblack@eecs.umich.edu        code.indent()
2837520Sgblack@eecs.umich.edu        code('this->name = name_;')
2842623SN/A        code.dedent()
2854999Sgblack@eecs.umich.edu        code('}')
2864999Sgblack@eecs.umich.edu
2877520Sgblack@eecs.umich.edu    if is_header:
2884999Sgblack@eecs.umich.edu        code('const std::string &${member_prefix}getName()')
2897520Sgblack@eecs.umich.edu        code('{ return this->name; }')
2907520Sgblack@eecs.umich.edu
2914999Sgblack@eecs.umich.edu    code()
2924999Sgblack@eecs.umich.edu    code('bool ${member_prefix}setParam(const std::string &name,')
2934999Sgblack@eecs.umich.edu    code('    const std::string &value, const Flags flags)${end_of_decl}')
2947520Sgblack@eecs.umich.edu
2958832SAli.Saidi@ARM.com    if not is_header:
2964999Sgblack@eecs.umich.edu        code('{')
2974999Sgblack@eecs.umich.edu        code.indent()
2986023Snate@binkert.org        code('bool ret = true;')
2994999Sgblack@eecs.umich.edu        code()
3004999Sgblack@eecs.umich.edu        code('if (false) {')
3016623Sgblack@eecs.umich.edu        for param in simobj._params.values():
3024999Sgblack@eecs.umich.edu            is_vector = isinstance(param, m5.params.VectorParamDesc)
3038949Sandreas.hansson@arm.com            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
3048949Sandreas.hansson@arm.com
3057520Sgblack@eecs.umich.edu            if not is_simobj and not is_vector:
3064999Sgblack@eecs.umich.edu                code('} else if (name == "${{param.name}}") {')
3078105Sgblack@eecs.umich.edu                code.indent()
3084999Sgblack@eecs.umich.edu                param.ptype.cxx_ini_parse(code,
3094999Sgblack@eecs.umich.edu                    'value', 'this->%s' % param.name, 'ret =')
3108931Sandreas.hansson@arm.com                code.dedent()
3118931Sandreas.hansson@arm.com        code('} else {')
3124999Sgblack@eecs.umich.edu        code('    ret = false;')
3134999Sgblack@eecs.umich.edu        code('}')
3144999Sgblack@eecs.umich.edu        code()
3154999Sgblack@eecs.umich.edu        code('return ret;')
3165012Sgblack@eecs.umich.edu        code.dedent()
3174999Sgblack@eecs.umich.edu        code('}')
3184999Sgblack@eecs.umich.edu
3196102Sgblack@eecs.umich.edu    code()
3204999Sgblack@eecs.umich.edu    code('bool ${member_prefix}setParamVector('
3214999Sgblack@eecs.umich.edu        'const std::string &name,')
3224968Sacolyte@umich.edu    code('    const std::vector<std::string> &values,')
3234986Ssaidi@eecs.umich.edu    code('    const Flags flags)${end_of_decl}')
3244999Sgblack@eecs.umich.edu
3256739Sgblack@eecs.umich.edu    if not is_header:
3266739Sgblack@eecs.umich.edu        code('{')
3276739Sgblack@eecs.umich.edu        code.indent()
3286739Sgblack@eecs.umich.edu        code('bool ret = true;')
3296739Sgblack@eecs.umich.edu        code()
3306739Sgblack@eecs.umich.edu        code('if (false) {')
3316739Sgblack@eecs.umich.edu        for param in simobj._params.values():
3326739Sgblack@eecs.umich.edu            is_vector = isinstance(param, m5.params.VectorParamDesc)
3334999Sgblack@eecs.umich.edu            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
3344999Sgblack@eecs.umich.edu
3354999Sgblack@eecs.umich.edu            if not is_simobj and is_vector:
3366078Sgblack@eecs.umich.edu                code('} else if (name == "${{param.name}}") {')
3376078Sgblack@eecs.umich.edu                code.indent()
3386078Sgblack@eecs.umich.edu                code('${{param.name}}.clear();')
3396078Sgblack@eecs.umich.edu                code('for (auto i = values.begin(); '
3404999Sgblack@eecs.umich.edu                    'ret && i != values.end(); i ++)')
3414968Sacolyte@umich.edu                code('{')
3423170Sstever@eecs.umich.edu                code.indent()
3434999Sgblack@eecs.umich.edu                code('${{param.ptype.cxx_type}} elem;')
3444999Sgblack@eecs.umich.edu                param.ptype.cxx_ini_parse(code,
3454999Sgblack@eecs.umich.edu                    '*i', 'elem', 'ret =')
3464999Sgblack@eecs.umich.edu                code('if (ret)')
3474999Sgblack@eecs.umich.edu                code('    this->${{param.name}}.push_back(elem);')
3487520Sgblack@eecs.umich.edu                code.dedent()
3494999Sgblack@eecs.umich.edu                code('}')
3507520Sgblack@eecs.umich.edu                code.dedent()
3514999Sgblack@eecs.umich.edu        code('} else {')
3524999Sgblack@eecs.umich.edu        code('    ret = false;')
3532623SN/A        code('}')
3542623SN/A        code()
3552623SN/A        code('return ret;')
3567520Sgblack@eecs.umich.edu        code.dedent()
3572623SN/A        code('}')
3588444Sgblack@eecs.umich.edu
3598444Sgblack@eecs.umich.edu    code()
3602623SN/A    code('bool ${member_prefix}setPortConnectionCount('
3613169Sstever@eecs.umich.edu        'const std::string &name,')
3624870Sstever@eecs.umich.edu    code('    unsigned int count)${end_of_decl}')
3632623SN/A
3642623SN/A    if not is_header:
3652623SN/A        code('{')
3662623SN/A        code.indent()
3672623SN/A        code('bool ret = true;')
3684999Sgblack@eecs.umich.edu        code()
3696227Snate@binkert.org        code('if (false)')
3704999Sgblack@eecs.umich.edu        code('    ;')
3717520Sgblack@eecs.umich.edu        for port in simobj._ports.values():
3722623SN/A            code('else if (name == "${{port.name}}")')
3734999Sgblack@eecs.umich.edu            code('    this->port_${{port.name}}_connection_count = count;')
3744999Sgblack@eecs.umich.edu        code('else')
3757520Sgblack@eecs.umich.edu        code('    ret = false;')
3764999Sgblack@eecs.umich.edu        code()
3774999Sgblack@eecs.umich.edu        code('return ret;')
3787520Sgblack@eecs.umich.edu        code.dedent()
3794999Sgblack@eecs.umich.edu        code('}')
3804999Sgblack@eecs.umich.edu
3814999Sgblack@eecs.umich.edu    code()
3824999Sgblack@eecs.umich.edu    code('SimObject *${member_prefix}simObjectCreate()${end_of_decl}')
3838832SAli.Saidi@ARM.com
3844999Sgblack@eecs.umich.edu    if not is_header:
3854999Sgblack@eecs.umich.edu        code('{')
3866023Snate@binkert.org        if hasattr(simobj, 'abstract') and simobj.abstract:
3874999Sgblack@eecs.umich.edu            code('    return NULL;')
3884999Sgblack@eecs.umich.edu        else:
3894999Sgblack@eecs.umich.edu            code('    return this->create();')
3904999Sgblack@eecs.umich.edu        code('}')
3914999Sgblack@eecs.umich.edu
3924999Sgblack@eecs.umich.edu    if is_header:
3936102Sgblack@eecs.umich.edu        code()
3944999Sgblack@eecs.umich.edu        code('static CxxConfigDirectoryEntry'
3954999Sgblack@eecs.umich.edu            ' *${member_prefix}makeDirectoryEntry()')
3964999Sgblack@eecs.umich.edu        code('{ return new DirectoryEntry; }')
3974999Sgblack@eecs.umich.edu
3984999Sgblack@eecs.umich.edu    if is_header:
3994999Sgblack@eecs.umich.edu        code.dedent()
4004999Sgblack@eecs.umich.edu        code('};')
4014999Sgblack@eecs.umich.edu
4024999Sgblack@eecs.umich.edu# The metaclass for SimObject.  This class controls how new classes
4034999Sgblack@eecs.umich.edu# that derive from SimObject are instantiated, and provides inherited
4046623Sgblack@eecs.umich.edu# class behavior (just like a class controls how instances of that
4058949Sandreas.hansson@arm.com# class are instantiated, and provides inherited instance behavior).
4067520Sgblack@eecs.umich.educlass MetaSimObject(type):
4074999Sgblack@eecs.umich.edu    # Attributes that can be set only at initialization time
4088105Sgblack@eecs.umich.edu    init_keywords = {
4094999Sgblack@eecs.umich.edu        'abstract' : bool,
4104999Sgblack@eecs.umich.edu        'cxx_class' : str,
4114999Sgblack@eecs.umich.edu        'cxx_type' : str,
4128931Sandreas.hansson@arm.com        'cxx_header' : str,
4138931Sandreas.hansson@arm.com        'type' : str,
4144999Sgblack@eecs.umich.edu        'cxx_base' : (str, type(None)),
4154999Sgblack@eecs.umich.edu        'cxx_extra_bases' : list,
4164999Sgblack@eecs.umich.edu        'cxx_exports' : list,
4174999Sgblack@eecs.umich.edu        'cxx_param_exports' : list,
4184999Sgblack@eecs.umich.edu    }
4194999Sgblack@eecs.umich.edu    # Attributes that can be set any time
4204999Sgblack@eecs.umich.edu    keywords = { 'check' : FunctionType }
4214999Sgblack@eecs.umich.edu
4227520Sgblack@eecs.umich.edu    # __new__ is called before __init__, and is where the statements
4234999Sgblack@eecs.umich.edu    # in the body of the class definition get loaded into the class's
4244999Sgblack@eecs.umich.edu    # __dict__.  We intercept this to filter out parameter & port assignments
4254999Sgblack@eecs.umich.edu    # and only allow "private" attributes to be passed to the base
4264999Sgblack@eecs.umich.edu    # __new__ (starting with underscore).
4274999Sgblack@eecs.umich.edu    def __new__(mcls, name, bases, dict):
4284878Sstever@eecs.umich.edu        assert name not in allClasses, "SimObject %s already present" % name
4294040Ssaidi@eecs.umich.edu
4304040Ssaidi@eecs.umich.edu        # Copy "private" attributes, functions, and classes to the
4314999Sgblack@eecs.umich.edu        # official dict.  Everything else goes in _init_dict to be
4324999Sgblack@eecs.umich.edu        # filtered in __init__.
4334999Sgblack@eecs.umich.edu        cls_dict = {}
4344999Sgblack@eecs.umich.edu        value_dict = {}
4356078Sgblack@eecs.umich.edu        cxx_exports = []
4366078Sgblack@eecs.umich.edu        for key,val in dict.items():
4376078Sgblack@eecs.umich.edu            try:
4386078Sgblack@eecs.umich.edu                cxx_exports.append(getattr(val, "__pybind"))
4396739Sgblack@eecs.umich.edu            except AttributeError:
4406739Sgblack@eecs.umich.edu                pass
4416739Sgblack@eecs.umich.edu
4426739Sgblack@eecs.umich.edu            if public_value(key, val):
4436739Sgblack@eecs.umich.edu                cls_dict[key] = val
4443170Sstever@eecs.umich.edu            else:
4453170Sstever@eecs.umich.edu                # must be a param/port setting
4464999Sgblack@eecs.umich.edu                value_dict[key] = val
4474999Sgblack@eecs.umich.edu        if 'abstract' not in value_dict:
4484999Sgblack@eecs.umich.edu            value_dict['abstract'] = False
4494999Sgblack@eecs.umich.edu        if 'cxx_extra_bases' not in value_dict:
4504999Sgblack@eecs.umich.edu            value_dict['cxx_extra_bases'] = []
4517520Sgblack@eecs.umich.edu        if 'cxx_exports' not in value_dict:
4524999Sgblack@eecs.umich.edu            value_dict['cxx_exports'] = cxx_exports
4537520Sgblack@eecs.umich.edu        else:
4544999Sgblack@eecs.umich.edu            value_dict['cxx_exports'] += cxx_exports
4554999Sgblack@eecs.umich.edu        if 'cxx_param_exports' not in value_dict:
4562623SN/A            value_dict['cxx_param_exports'] = []
4572623SN/A        cls_dict['_value_dict'] = value_dict
4582623SN/A        cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
4592623SN/A        if 'type' in value_dict:
4602623SN/A            allClasses[name] = cls
4612623SN/A        return cls
4622623SN/A
4634940Snate@binkert.org    # subclass initialization
4644940Snate@binkert.org    def __init__(cls, name, bases, dict):
4655487Snate@binkert.org        # calls type.__init__()... I think that's a no-op, but leave
4662623SN/A        # it here just in case it's not.
4676078Sgblack@eecs.umich.edu        super(MetaSimObject, cls).__init__(name, bases, dict)
4682623SN/A
4692623SN/A        # initialize required attributes
4703387Sgblack@eecs.umich.edu
4713387Sgblack@eecs.umich.edu        # class-only attributes
4722626SN/A        cls._params = multidict() # param descriptions
4735348Ssaidi@eecs.umich.edu        cls._ports = multidict()  # port descriptions
4748143SAli.Saidi@ARM.com
4759443SAndreas.Sandberg@ARM.com        # class or instance attributes
4769443SAndreas.Sandberg@ARM.com        cls._values = multidict()   # param values
4778143SAli.Saidi@ARM.com        cls._hr_values = multidict() # human readable param values
4789443SAndreas.Sandberg@ARM.com        cls._children = multidict() # SimObject children
4795348Ssaidi@eecs.umich.edu        cls._port_refs = multidict() # port ref objects
4805669Sgblack@eecs.umich.edu        cls._instantiated = False # really instantiated, cloned, or subclassed
4815669Sgblack@eecs.umich.edu
4827720Sgblack@eecs.umich.edu        # We don't support multiple inheritance of sim objects.  If you want
4837720Sgblack@eecs.umich.edu        # to, you must fix multidict to deal with it properly. Non sim-objects
4847720Sgblack@eecs.umich.edu        # are ok, though
4857720Sgblack@eecs.umich.edu        bTotal = 0
4867720Sgblack@eecs.umich.edu        for c in bases:
4875894Sgblack@eecs.umich.edu            if isinstance(c, MetaSimObject):
4886023Snate@binkert.org                bTotal += 1
4896023Snate@binkert.org            if bTotal > 1:
4905894Sgblack@eecs.umich.edu                raise TypeError(
4912623SN/A                      "SimObjects do not support multiple inheritance")
4922623SN/A
4934182Sgblack@eecs.umich.edu        base = bases[0]
4944182Sgblack@eecs.umich.edu
4954182Sgblack@eecs.umich.edu        # Set up general inheritance via multidicts.  A subclass will
4962662Sstever@eecs.umich.edu        # inherit all its settings from the base class.  The only time
4977720Sgblack@eecs.umich.edu        # the following is not true is when we define the SimObject
4989023Sgblack@eecs.umich.edu        # class itself (in which case the multidicts have no parent).
4995694Sgblack@eecs.umich.edu        if isinstance(base, MetaSimObject):
5005694Sgblack@eecs.umich.edu            cls._base = base
5015694Sgblack@eecs.umich.edu            cls._params.parent = base._params
5025669Sgblack@eecs.umich.edu            cls._ports.parent = base._ports
5039023Sgblack@eecs.umich.edu            cls._values.parent = base._values
5045669Sgblack@eecs.umich.edu            cls._hr_values.parent = base._hr_values
5055669Sgblack@eecs.umich.edu            cls._children.parent = base._children
5068949Sandreas.hansson@arm.com            cls._port_refs.parent = base._port_refs
5075669Sgblack@eecs.umich.edu            # mark base as having been subclassed
5082623SN/A            base._instantiated = True
5098931Sandreas.hansson@arm.com        else:
5108931Sandreas.hansson@arm.com            cls._base = None
5115669Sgblack@eecs.umich.edu
5125669Sgblack@eecs.umich.edu        # default keyword values
5134968Sacolyte@umich.edu        if 'type' in cls._value_dict:
5145669Sgblack@eecs.umich.edu            if 'cxx_class' not in cls._value_dict:
5154968Sacolyte@umich.edu                cls._value_dict['cxx_class'] = cls._value_dict['type']
5165669Sgblack@eecs.umich.edu
5175669Sgblack@eecs.umich.edu            cls._value_dict['cxx_type'] = '%s *' % cls._value_dict['cxx_class']
5185669Sgblack@eecs.umich.edu
5195669Sgblack@eecs.umich.edu            if 'cxx_header' not in cls._value_dict:
5204182Sgblack@eecs.umich.edu                global noCxxHeader
5212623SN/A                noCxxHeader = True
5223814Ssaidi@eecs.umich.edu                warn("No header file specified for SimObject: %s", name)
5235001Sgblack@eecs.umich.edu
5244182Sgblack@eecs.umich.edu        # Now process the _value_dict items.  They could be defining
5254998Sgblack@eecs.umich.edu        # new (or overriding existing) parameters or ports, setting
5264998Sgblack@eecs.umich.edu        # class keywords (e.g., 'abstract'), or setting parameter
5274998Sgblack@eecs.umich.edu        # values or port bindings.  The first 3 can only be set when
5284998Sgblack@eecs.umich.edu        # the class is defined, so we handle them here.  The others
5297655Sali.saidi@arm.com        # can be set later too, so just emulate that by calling
5305001Sgblack@eecs.umich.edu        # setattr().
5315001Sgblack@eecs.umich.edu        for key,val in cls._value_dict.items():
5325001Sgblack@eecs.umich.edu            # param descriptions
5334998Sgblack@eecs.umich.edu            if isinstance(val, ParamDesc):
5344182Sgblack@eecs.umich.edu                cls._new_param(key, val)
5354182Sgblack@eecs.umich.edu
5362623SN/A            # port objects
5373814Ssaidi@eecs.umich.edu            elif isinstance(val, Port):
5384539Sgblack@eecs.umich.edu                cls._new_port(key, val)
5394539Sgblack@eecs.umich.edu
5403814Ssaidi@eecs.umich.edu            # init-time-only keywords
5413814Ssaidi@eecs.umich.edu            elif key in cls.init_keywords:
5425487Snate@binkert.org                cls._set_keyword(key, val, cls.init_keywords[key])
5435487Snate@binkert.org
5445487Snate@binkert.org            # default: use normal path (ends up in __setattr__)
5455487Snate@binkert.org            else:
5465487Snate@binkert.org                setattr(cls, key, val)
5475487Snate@binkert.org
5485487Snate@binkert.org    def _set_keyword(cls, keyword, val, kwtype):
5495487Snate@binkert.org        if not isinstance(val, kwtype):
5509180Sandreas.hansson@arm.com            raise TypeError('keyword %s has bad type %s (expecting %s)' % \
5519180Sandreas.hansson@arm.com                  (keyword, type(val), kwtype))
5529180Sandreas.hansson@arm.com        if isinstance(val, FunctionType):
5539180Sandreas.hansson@arm.com            val = classmethod(val)
5549180Sandreas.hansson@arm.com        type.__setattr__(cls, keyword, val)
5552623SN/A
5562623SN/A    def _new_param(cls, name, pdesc):
5572623SN/A        # each param desc should be uniquely assigned to one variable
5584377Sgblack@eecs.umich.edu        assert(not hasattr(pdesc, 'name'))
5594182Sgblack@eecs.umich.edu        pdesc.name = name
5602623SN/A        cls._params[name] = pdesc
5612623SN/A        if hasattr(pdesc, 'default'):
5629443SAndreas.Sandberg@ARM.com            cls._set_param(name, pdesc.default, pdesc)
5639443SAndreas.Sandberg@ARM.com
5649443SAndreas.Sandberg@ARM.com    def _set_param(cls, name, value, param):
5655487Snate@binkert.org        assert(param.name == name)
5669179Sandreas.hansson@arm.com        try:
5679179Sandreas.hansson@arm.com            hr_value = value
5685487Snate@binkert.org            value = param.convert(value)
5692626SN/A        except Exception as e:
5707823Ssteve.reinhardt@amd.com            msg = "%s\nError setting param %s.%s to %s\n" % \
5712623SN/A                  (e, cls.__name__, name, value)
5722623SN/A            e.args = (msg, )
5732623SN/A            raise
5745315Sstever@gmail.com        cls._values[name] = value
5755315Sstever@gmail.com        # if param value is a SimObject, make it a child too, so that
5765315Sstever@gmail.com        # it gets cloned properly when the class is instantiated
5775315Sstever@gmail.com        if isSimObjectOrVector(value) and not value.has_parent():
5785315Sstever@gmail.com            cls._add_cls_child(name, value)
5795315Sstever@gmail.com        # update human-readable values of the param if it has a literal
5805315Sstever@gmail.com        # value and is not an object or proxy.
5812623SN/A        if not (isSimObjectOrVector(value) or\
5822623SN/A                isinstance(value, m5.proxy.BaseProxy)):
5832623SN/A            cls._hr_values[name] = hr_value
5842623SN/A
5854762Snate@binkert.org    def _add_cls_child(cls, name, child):
5864762Snate@binkert.org        # It's a little funky to have a class as a parent, but these
5872623SN/A        # objects should never be instantiated (only cloned, which
5885529Snate@binkert.org        # clears the parent pointer), and this makes it clear that the
5898779Sgblack@eecs.umich.edu        # object is not an orphan and can provide better error
5904762Snate@binkert.org        # messages.
5915529Snate@binkert.org        child.set_parent(cls, name)
5922623SN/A        if not isNullPointer(child):
593            cls._children[name] = child
594
595    def _new_port(cls, name, port):
596        # each port should be uniquely assigned to one variable
597        assert(not hasattr(port, 'name'))
598        port.name = name
599        cls._ports[name] = port
600
601    # same as _get_port_ref, effectively, but for classes
602    def _cls_get_port_ref(cls, attr):
603        # Return reference that can be assigned to another port
604        # via __setattr__.  There is only ever one reference
605        # object per port, but we create them lazily here.
606        ref = cls._port_refs.get(attr)
607        if not ref:
608            ref = cls._ports[attr].makeRef(cls)
609            cls._port_refs[attr] = ref
610        return ref
611
612    # Set attribute (called on foo.attr = value when foo is an
613    # instance of class cls).
614    def __setattr__(cls, attr, value):
615        # normal processing for private attributes
616        if public_value(attr, value):
617            type.__setattr__(cls, attr, value)
618            return
619
620        if attr in cls.keywords:
621            cls._set_keyword(attr, value, cls.keywords[attr])
622            return
623
624        if attr in cls._ports:
625            cls._cls_get_port_ref(attr).connect(value)
626            return
627
628        if isSimObjectOrSequence(value) and cls._instantiated:
629            raise RuntimeError(
630                  "cannot set SimObject parameter '%s' after\n" \
631                  "    class %s has been instantiated or subclassed" \
632                  % (attr, cls.__name__))
633
634        # check for param
635        param = cls._params.get(attr)
636        if param:
637            cls._set_param(attr, value, param)
638            return
639
640        if isSimObjectOrSequence(value):
641            # If RHS is a SimObject, it's an implicit child assignment.
642            cls._add_cls_child(attr, coerceSimObjectOrVector(value))
643            return
644
645        # no valid assignment... raise exception
646        raise AttributeError(
647              "Class %s has no parameter \'%s\'" % (cls.__name__, attr))
648
649    def __getattr__(cls, attr):
650        if attr == 'cxx_class_path':
651            return cls.cxx_class.split('::')
652
653        if attr == 'cxx_class_name':
654            return cls.cxx_class_path[-1]
655
656        if attr == 'cxx_namespaces':
657            return cls.cxx_class_path[:-1]
658
659        if attr in cls._values:
660            return cls._values[attr]
661
662        if attr in cls._children:
663            return cls._children[attr]
664
665        raise AttributeError(
666              "object '%s' has no attribute '%s'" % (cls.__name__, attr))
667
668    def __str__(cls):
669        return cls.__name__
670
671    # See ParamValue.cxx_predecls for description.
672    def cxx_predecls(cls, code):
673        code('#include "params/$cls.hh"')
674
675    def pybind_predecls(cls, code):
676        code('#include "${{cls.cxx_header}}"')
677
678    def pybind_decl(cls, code):
679        class_path = cls.cxx_class.split('::')
680        namespaces, classname = class_path[:-1], class_path[-1]
681        py_class_name = '_COLONS_'.join(class_path) if namespaces else \
682                        classname;
683
684        # The 'local' attribute restricts us to the params declared in
685        # the object itself, not including inherited params (which
686        # will also be inherited from the base class's param struct
687        # here). Sort the params based on their key
688        params = map(lambda k_v: k_v[1], sorted(cls._params.local.items()))
689        ports = cls._ports.local
690
691        code('''#include "pybind11/pybind11.h"
692#include "pybind11/stl.h"
693
694#include "params/$cls.hh"
695#include "python/pybind11/core.hh"
696#include "sim/init.hh"
697#include "sim/sim_object.hh"
698
699#include "${{cls.cxx_header}}"
700
701''')
702
703        for param in params:
704            param.pybind_predecls(code)
705
706        code('''namespace py = pybind11;
707
708static void
709module_init(py::module &m_internal)
710{
711    py::module m = m_internal.def_submodule("param_${cls}");
712''')
713        code.indent()
714        if cls._base:
715            code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
716                 'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
717                 'm, "${cls}Params")')
718        else:
719            code('py::class_<${cls}Params, ' \
720                 'std::unique_ptr<${cls}Params, py::nodelete>>(' \
721                 'm, "${cls}Params")')
722
723        code.indent()
724        if not hasattr(cls, 'abstract') or not cls.abstract:
725            code('.def(py::init<>())')
726            code('.def("create", &${cls}Params::create)')
727
728        param_exports = cls.cxx_param_exports + [
729            PyBindProperty(k)
730            for k, v in sorted(cls._params.local.items())
731        ] + [
732            PyBindProperty("port_%s_connection_count" % port.name)
733            for port in ports.values()
734        ]
735        for exp in param_exports:
736            exp.export(code, "%sParams" % cls)
737
738        code(';')
739        code()
740        code.dedent()
741
742        bases = []
743        if 'cxx_base' in cls._value_dict:
744            # If the c++ base class implied by python inheritance was
745            # overridden, use that value.
746            if cls.cxx_base:
747                bases.append(cls.cxx_base)
748        elif cls._base:
749            # If not and if there was a SimObject base, use its c++ class
750            # as this class' base.
751            bases.append(cls._base.cxx_class)
752        # Add in any extra bases that were requested.
753        bases.extend(cls.cxx_extra_bases)
754
755        if bases:
756            base_str = ", ".join(bases)
757            code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
758                 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
759                 'm, "${py_class_name}")')
760        else:
761            code('py::class_<${{cls.cxx_class}}, ' \
762                 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
763                 'm, "${py_class_name}")')
764        code.indent()
765        for exp in cls.cxx_exports:
766            exp.export(code, cls.cxx_class)
767        code(';')
768        code.dedent()
769        code()
770        code.dedent()
771        code('}')
772        code()
773        code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
774             cls, cls._base.type if cls._base else "")
775
776
777    # Generate the C++ declaration (.hh file) for this SimObject's
778    # param struct.  Called from src/SConscript.
779    def cxx_param_decl(cls, code):
780        # The 'local' attribute restricts us to the params declared in
781        # the object itself, not including inherited params (which
782        # will also be inherited from the base class's param struct
783        # here). Sort the params based on their key
784        params = map(lambda k_v: k_v[1], sorted(cls._params.local.items()))
785        ports = cls._ports.local
786        try:
787            ptypes = [p.ptype for p in params]
788        except:
789            print(cls, p, p.ptype_str)
790            print(params)
791            raise
792
793        class_path = cls._value_dict['cxx_class'].split('::')
794
795        code('''\
796#ifndef __PARAMS__${cls}__
797#define __PARAMS__${cls}__
798
799''')
800
801
802        # The base SimObject has a couple of params that get
803        # automatically set from Python without being declared through
804        # the normal Param mechanism; we slip them in here (needed
805        # predecls now, actual declarations below)
806        if cls == SimObject:
807            code('''#include <string>''')
808
809        # A forward class declaration is sufficient since we are just
810        # declaring a pointer.
811        for ns in class_path[:-1]:
812            code('namespace $ns {')
813        code('class $0;', class_path[-1])
814        for ns in reversed(class_path[:-1]):
815            code('} // namespace $ns')
816        code()
817
818        for param in params:
819            param.cxx_predecls(code)
820        for port in ports.values():
821            port.cxx_predecls(code)
822        code()
823
824        if cls._base:
825            code('#include "params/${{cls._base.type}}.hh"')
826            code()
827
828        for ptype in ptypes:
829            if issubclass(ptype, Enum):
830                code('#include "enums/${{ptype.__name__}}.hh"')
831                code()
832
833        # now generate the actual param struct
834        code("struct ${cls}Params")
835        if cls._base:
836            code("    : public ${{cls._base.type}}Params")
837        code("{")
838        if not hasattr(cls, 'abstract') or not cls.abstract:
839            if 'type' in cls.__dict__:
840                code("    ${{cls.cxx_type}} create();")
841
842        code.indent()
843        if cls == SimObject:
844            code('''
845    SimObjectParams() {}
846    virtual ~SimObjectParams() {}
847
848    std::string name;
849            ''')
850
851        for param in params:
852            param.cxx_decl(code)
853        for port in ports.values():
854            port.cxx_decl(code)
855
856        code.dedent()
857        code('};')
858
859        code()
860        code('#endif // __PARAMS__${cls}__')
861        return code
862
863    # Generate the C++ declaration/definition files for this SimObject's
864    # param struct to allow C++ initialisation
865    def cxx_config_param_file(cls, code, is_header):
866        createCxxConfigDirectoryEntryFile(code, cls.__name__, cls, is_header)
867        return code
868
869# This *temporary* definition is required to support calls from the
870# SimObject class definition to the MetaSimObject methods (in
871# particular _set_param, which gets called for parameters with default
872# values defined on the SimObject class itself).  It will get
873# overridden by the permanent definition (which requires that
874# SimObject be defined) lower in this file.
875def isSimObjectOrVector(value):
876    return False
877
878def cxxMethod(*args, **kwargs):
879    """Decorator to export C++ functions to Python"""
880
881    def decorate(func):
882        name = func.__name__
883        override = kwargs.get("override", False)
884        cxx_name = kwargs.get("cxx_name", name)
885        return_value_policy = kwargs.get("return_value_policy", None)
886
887        args, varargs, keywords, defaults = inspect.getargspec(func)
888        if varargs or keywords:
889            raise ValueError("Wrapped methods must not contain variable " \
890                             "arguments")
891
892        # Create tuples of (argument, default)
893        if defaults:
894            args = args[:-len(defaults)] + \
895                   list(zip(args[-len(defaults):], defaults))
896        # Don't include self in the argument list to PyBind
897        args = args[1:]
898
899
900        @wraps(func)
901        def cxx_call(self, *args, **kwargs):
902            ccobj = self.getCCObject()
903            return getattr(ccobj, name)(*args, **kwargs)
904
905        @wraps(func)
906        def py_call(self, *args, **kwargs):
907            return func(self, *args, **kwargs)
908
909        f = py_call if override else cxx_call
910        f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args,
911                                  return_value_policy=return_value_policy)
912
913        return f
914
915    if len(args) == 0:
916        return decorate
917    elif len(args) == 1 and len(kwargs) == 0:
918        return decorate(*args)
919    else:
920        raise TypeError("One argument and no kwargs, or only kwargs expected")
921
922# This class holds information about each simobject parameter
923# that should be displayed on the command line for use in the
924# configuration system.
925class ParamInfo(object):
926  def __init__(self, type, desc, type_str, example, default_val, access_str):
927    self.type = type
928    self.desc = desc
929    self.type_str = type_str
930    self.example_str = example
931    self.default_val = default_val
932    # The string representation used to access this param through python.
933    # The method to access this parameter presented on the command line may
934    # be different, so this needs to be stored for later use.
935    self.access_str = access_str
936    self.created = True
937
938  # Make it so we can only set attributes at initialization time
939  # and effectively make this a const object.
940  def __setattr__(self, name, value):
941    if not "created" in self.__dict__:
942      self.__dict__[name] = value
943
944class SimObjectCliWrapperException(Exception):
945    def __init__(self, message):
946        super(Exception, self).__init__(message)
947
948class SimObjectCliWrapper(object):
949    """
950    Wrapper class to restrict operations that may be done
951    from the command line on SimObjects.
952
953    Only parameters may be set, and only children may be accessed.
954
955    Slicing allows for multiple simultaneous assignment of items in
956    one statement.
957    """
958
959    def __init__(self, sim_objects):
960        self.__dict__['_sim_objects'] = list(sim_objects)
961
962    def __getattr__(self, key):
963        return SimObjectCliWrapper(sim_object._children[key]
964                for sim_object in self._sim_objects)
965
966    def __setattr__(self, key, val):
967        for sim_object in self._sim_objects:
968            if key in sim_object._params:
969                if sim_object._params[key].isCmdLineSettable():
970                    setattr(sim_object, key, val)
971                else:
972                    raise SimObjectCliWrapperException(
973                            'tried to set or unsettable' \
974                            'object parameter: ' + key)
975            else:
976                raise SimObjectCliWrapperException(
977                            'tried to set or access non-existent' \
978                            'object parameter: ' + key)
979
980    def __getitem__(self, idx):
981        """
982        Extends the list() semantics to also allow tuples,
983        for example object[1, 3] selects items 1 and 3.
984        """
985        out = []
986        if isinstance(idx, tuple):
987            for t in idx:
988                out.extend(self[t]._sim_objects)
989        else:
990            if isinstance(idx, int):
991                _range = range(idx, idx + 1)
992            elif not isinstance(idx, slice):
993                raise SimObjectCliWrapperException( \
994                        'invalid index type: ' + repr(idx))
995            for sim_object in self._sim_objects:
996                if isinstance(idx, slice):
997                    _range = range(*idx.indices(len(sim_object)))
998                out.extend(sim_object[i] for i in _range)
999        return SimObjectCliWrapper(out)
1000
1001# The SimObject class is the root of the special hierarchy.  Most of
1002# the code in this class deals with the configuration hierarchy itself
1003# (parent/child node relationships).
1004class SimObject(object):
1005    # Specify metaclass.  Any class inheriting from SimObject will
1006    # get this metaclass.
1007    __metaclass__ = MetaSimObject
1008    type = 'SimObject'
1009    abstract = True
1010
1011    cxx_header = "sim/sim_object.hh"
1012    cxx_extra_bases = [ "Drainable", "Serializable" ]
1013    eventq_index = Param.UInt32(Parent.eventq_index, "Event Queue Index")
1014
1015    cxx_exports = [
1016        PyBindMethod("init"),
1017        PyBindMethod("initState"),
1018        PyBindMethod("memInvalidate"),
1019        PyBindMethod("memWriteback"),
1020        PyBindMethod("regStats"),
1021        PyBindMethod("resetStats"),
1022        PyBindMethod("regProbePoints"),
1023        PyBindMethod("regProbeListeners"),
1024        PyBindMethod("startup"),
1025    ]
1026
1027    cxx_param_exports = [
1028        PyBindProperty("name"),
1029    ]
1030
1031    @cxxMethod
1032    def loadState(self, cp):
1033        """Load SimObject state from a checkpoint"""
1034        pass
1035
1036    # Returns a dict of all the option strings that can be
1037    # generated as command line options for this simobject instance
1038    # by tracing all reachable params in the top level instance and
1039    # any children it contains.
1040    def enumerateParams(self, flags_dict = {},
1041                        cmd_line_str = "", access_str = ""):
1042        if hasattr(self, "_paramEnumed"):
1043            print("Cycle detected enumerating params")
1044        else:
1045            self._paramEnumed = True
1046            # Scan the children first to pick up all the objects in this SimObj
1047            for keys in self._children:
1048                child = self._children[keys]
1049                next_cmdline_str = cmd_line_str + keys
1050                next_access_str = access_str + keys
1051                if not isSimObjectVector(child):
1052                    next_cmdline_str = next_cmdline_str + "."
1053                    next_access_str = next_access_str + "."
1054                flags_dict = child.enumerateParams(flags_dict,
1055                                                   next_cmdline_str,
1056                                                   next_access_str)
1057
1058            # Go through the simple params in the simobject in this level
1059            # of the simobject hierarchy and save information about the
1060            # parameter to be used for generating and processing command line
1061            # options to the simulator to set these parameters.
1062            for keys,values in self._params.items():
1063                if values.isCmdLineSettable():
1064                    type_str = ''
1065                    ex_str = values.example_str()
1066                    ptype = None
1067                    if isinstance(values, VectorParamDesc):
1068                        type_str = 'Vector_%s' % values.ptype_str
1069                        ptype = values
1070                    else:
1071                        type_str = '%s' % values.ptype_str
1072                        ptype = values.ptype
1073
1074                    if keys in self._hr_values\
1075                       and keys in self._values\
1076                       and not isinstance(self._values[keys],
1077                                          m5.proxy.BaseProxy):
1078                        cmd_str = cmd_line_str + keys
1079                        acc_str = access_str + keys
1080                        flags_dict[cmd_str] = ParamInfo(ptype,
1081                                    self._params[keys].desc, type_str, ex_str,
1082                                    values.pretty_print(self._hr_values[keys]),
1083                                    acc_str)
1084                    elif not keys in self._hr_values\
1085                         and not keys in self._values:
1086                        # Empty param
1087                        cmd_str = cmd_line_str + keys
1088                        acc_str = access_str + keys
1089                        flags_dict[cmd_str] = ParamInfo(ptype,
1090                                    self._params[keys].desc,
1091                                    type_str, ex_str, '', acc_str)
1092
1093        return flags_dict
1094
1095    # Initialize new instance.  For objects with SimObject-valued
1096    # children, we need to recursively clone the classes represented
1097    # by those param values as well in a consistent "deep copy"-style
1098    # fashion.  That is, we want to make sure that each instance is
1099    # cloned only once, and that if there are multiple references to
1100    # the same original object, we end up with the corresponding
1101    # cloned references all pointing to the same cloned instance.
1102    def __init__(self, **kwargs):
1103        ancestor = kwargs.get('_ancestor')
1104        memo_dict = kwargs.get('_memo')
1105        if memo_dict is None:
1106            # prepare to memoize any recursively instantiated objects
1107            memo_dict = {}
1108        elif ancestor:
1109            # memoize me now to avoid problems with recursive calls
1110            memo_dict[ancestor] = self
1111
1112        if not ancestor:
1113            ancestor = self.__class__
1114        ancestor._instantiated = True
1115
1116        # initialize required attributes
1117        self._parent = None
1118        self._name = None
1119        self._ccObject = None  # pointer to C++ object
1120        self._ccParams = None
1121        self._instantiated = False # really "cloned"
1122
1123        # Clone children specified at class level.  No need for a
1124        # multidict here since we will be cloning everything.
1125        # Do children before parameter values so that children that
1126        # are also param values get cloned properly.
1127        self._children = {}
1128        for key,val in ancestor._children.items():
1129            self.add_child(key, val(_memo=memo_dict))
1130
1131        # Inherit parameter values from class using multidict so
1132        # individual value settings can be overridden but we still
1133        # inherit late changes to non-overridden class values.
1134        self._values = multidict(ancestor._values)
1135        self._hr_values = multidict(ancestor._hr_values)
1136        # clone SimObject-valued parameters
1137        for key,val in ancestor._values.items():
1138            val = tryAsSimObjectOrVector(val)
1139            if val is not None:
1140                self._values[key] = val(_memo=memo_dict)
1141
1142        # clone port references.  no need to use a multidict here
1143        # since we will be creating new references for all ports.
1144        self._port_refs = {}
1145        for key,val in ancestor._port_refs.items():
1146            self._port_refs[key] = val.clone(self, memo_dict)
1147        # apply attribute assignments from keyword args, if any
1148        for key,val in kwargs.items():
1149            setattr(self, key, val)
1150
1151    # "Clone" the current instance by creating another instance of
1152    # this instance's class, but that inherits its parameter values
1153    # and port mappings from the current instance.  If we're in a
1154    # "deep copy" recursive clone, check the _memo dict to see if
1155    # we've already cloned this instance.
1156    def __call__(self, **kwargs):
1157        memo_dict = kwargs.get('_memo')
1158        if memo_dict is None:
1159            # no memo_dict: must be top-level clone operation.
1160            # this is only allowed at the root of a hierarchy
1161            if self._parent:
1162                raise RuntimeError("attempt to clone object %s " \
1163                      "not at the root of a tree (parent = %s)" \
1164                      % (self, self._parent))
1165            # create a new dict and use that.
1166            memo_dict = {}
1167            kwargs['_memo'] = memo_dict
1168        elif self in memo_dict:
1169            # clone already done & memoized
1170            return memo_dict[self]
1171        return self.__class__(_ancestor = self, **kwargs)
1172
1173    def _get_port_ref(self, attr):
1174        # Return reference that can be assigned to another port
1175        # via __setattr__.  There is only ever one reference
1176        # object per port, but we create them lazily here.
1177        ref = self._port_refs.get(attr)
1178        if ref == None:
1179            ref = self._ports[attr].makeRef(self)
1180            self._port_refs[attr] = ref
1181        return ref
1182
1183    def __getattr__(self, attr):
1184        if attr in self._ports:
1185            return self._get_port_ref(attr)
1186
1187        if attr in self._values:
1188            return self._values[attr]
1189
1190        if attr in self._children:
1191            return self._children[attr]
1192
1193        # If the attribute exists on the C++ object, transparently
1194        # forward the reference there.  This is typically used for
1195        # methods exported to Python (e.g., init(), and startup())
1196        if self._ccObject and hasattr(self._ccObject, attr):
1197            return getattr(self._ccObject, attr)
1198
1199        err_string = "object '%s' has no attribute '%s'" \
1200              % (self.__class__.__name__, attr)
1201
1202        if not self._ccObject:
1203            err_string += "\n  (C++ object is not yet constructed," \
1204                          " so wrapped C++ methods are unavailable.)"
1205
1206        raise AttributeError(err_string)
1207
1208    # Set attribute (called on foo.attr = value when foo is an
1209    # instance of class cls).
1210    def __setattr__(self, attr, value):
1211        # normal processing for private attributes
1212        if attr.startswith('_'):
1213            object.__setattr__(self, attr, value)
1214            return
1215
1216        if attr in self._ports:
1217            # set up port connection
1218            self._get_port_ref(attr).connect(value)
1219            return
1220
1221        param = self._params.get(attr)
1222        if param:
1223            try:
1224                hr_value = value
1225                value = param.convert(value)
1226            except Exception as e:
1227                msg = "%s\nError setting param %s.%s to %s\n" % \
1228                      (e, self.__class__.__name__, attr, value)
1229                e.args = (msg, )
1230                raise
1231            self._values[attr] = value
1232            # implicitly parent unparented objects assigned as params
1233            if isSimObjectOrVector(value) and not value.has_parent():
1234                self.add_child(attr, value)
1235            # set the human-readable value dict if this is a param
1236            # with a literal value and is not being set as an object
1237            # or proxy.
1238            if not (isSimObjectOrVector(value) or\
1239                    isinstance(value, m5.proxy.BaseProxy)):
1240                self._hr_values[attr] = hr_value
1241
1242            return
1243
1244        # if RHS is a SimObject, it's an implicit child assignment
1245        if isSimObjectOrSequence(value):
1246            self.add_child(attr, value)
1247            return
1248
1249        # no valid assignment... raise exception
1250        raise AttributeError("Class %s has no parameter %s" \
1251              % (self.__class__.__name__, attr))
1252
1253
1254    # this hack allows tacking a '[0]' onto parameters that may or may
1255    # not be vectors, and always getting the first element (e.g. cpus)
1256    def __getitem__(self, key):
1257        if key == 0:
1258            return self
1259        raise IndexError("Non-zero index '%s' to SimObject" % key)
1260
1261    # this hack allows us to iterate over a SimObject that may
1262    # not be a vector, so we can call a loop over it and get just one
1263    # element.
1264    def __len__(self):
1265        return 1
1266
1267    # Also implemented by SimObjectVector
1268    def clear_parent(self, old_parent):
1269        assert self._parent is old_parent
1270        self._parent = None
1271
1272    # Also implemented by SimObjectVector
1273    def set_parent(self, parent, name):
1274        self._parent = parent
1275        self._name = name
1276
1277    # Return parent object of this SimObject, not implemented by
1278    # SimObjectVector because the elements in a SimObjectVector may not share
1279    # the same parent
1280    def get_parent(self):
1281        return self._parent
1282
1283    # Also implemented by SimObjectVector
1284    def get_name(self):
1285        return self._name
1286
1287    # Also implemented by SimObjectVector
1288    def has_parent(self):
1289        return self._parent is not None
1290
1291    # clear out child with given name. This code is not likely to be exercised.
1292    # See comment in add_child.
1293    def clear_child(self, name):
1294        child = self._children[name]
1295        child.clear_parent(self)
1296        del self._children[name]
1297
1298    # Add a new child to this object.
1299    def add_child(self, name, child):
1300        child = coerceSimObjectOrVector(child)
1301        if child.has_parent():
1302            warn("add_child('%s'): child '%s' already has parent", name,
1303                child.get_name())
1304        if name in self._children:
1305            # This code path had an undiscovered bug that would make it fail
1306            # at runtime. It had been here for a long time and was only
1307            # exposed by a buggy script. Changes here will probably not be
1308            # exercised without specialized testing.
1309            self.clear_child(name)
1310        child.set_parent(self, name)
1311        if not isNullPointer(child):
1312            self._children[name] = child
1313
1314    # Take SimObject-valued parameters that haven't been explicitly
1315    # assigned as children and make them children of the object that
1316    # they were assigned to as a parameter value.  This guarantees
1317    # that when we instantiate all the parameter objects we're still
1318    # inside the configuration hierarchy.
1319    def adoptOrphanParams(self):
1320        for key,val in self._values.items():
1321            if not isSimObjectVector(val) and isSimObjectSequence(val):
1322                # need to convert raw SimObject sequences to
1323                # SimObjectVector class so we can call has_parent()
1324                val = SimObjectVector(val)
1325                self._values[key] = val
1326            if isSimObjectOrVector(val) and not val.has_parent():
1327                warn("%s adopting orphan SimObject param '%s'", self, key)
1328                self.add_child(key, val)
1329
1330    def path(self):
1331        if not self._parent:
1332            return '<orphan %s>' % self.__class__
1333        elif isinstance(self._parent, MetaSimObject):
1334            return str(self.__class__)
1335
1336        ppath = self._parent.path()
1337        if ppath == 'root':
1338            return self._name
1339        return ppath + "." + self._name
1340
1341    def __str__(self):
1342        return self.path()
1343
1344    def config_value(self):
1345        return self.path()
1346
1347    def ini_str(self):
1348        return self.path()
1349
1350    def find_any(self, ptype):
1351        if isinstance(self, ptype):
1352            return self, True
1353
1354        found_obj = None
1355        for child in self._children.values():
1356            visited = False
1357            if hasattr(child, '_visited'):
1358              visited = getattr(child, '_visited')
1359
1360            if isinstance(child, ptype) and not visited:
1361                if found_obj != None and child != found_obj:
1362                    raise AttributeError(
1363                          'parent.any matched more than one: %s %s' % \
1364                          (found_obj.path, child.path))
1365                found_obj = child
1366        # search param space
1367        for pname,pdesc in self._params.items():
1368            if issubclass(pdesc.ptype, ptype):
1369                match_obj = self._values[pname]
1370                if found_obj != None and found_obj != match_obj:
1371                    raise AttributeError(
1372                          'parent.any matched more than one: %s and %s' % \
1373                          (found_obj.path, match_obj.path))
1374                found_obj = match_obj
1375        return found_obj, found_obj != None
1376
1377    def find_all(self, ptype):
1378        all = {}
1379        # search children
1380        for child in self._children.values():
1381            # a child could be a list, so ensure we visit each item
1382            if isinstance(child, list):
1383                children = child
1384            else:
1385                children = [child]
1386
1387            for child in children:
1388                if isinstance(child, ptype) and not isproxy(child) and \
1389                        not isNullPointer(child):
1390                    all[child] = True
1391                if isSimObject(child):
1392                    # also add results from the child itself
1393                    child_all, done = child.find_all(ptype)
1394                    all.update(dict(zip(child_all, [done] * len(child_all))))
1395        # search param space
1396        for pname,pdesc in self._params.items():
1397            if issubclass(pdesc.ptype, ptype):
1398                match_obj = self._values[pname]
1399                if not isproxy(match_obj) and not isNullPointer(match_obj):
1400                    all[match_obj] = True
1401        # Also make sure to sort the keys based on the objects' path to
1402        # ensure that the order is the same on all hosts
1403        return sorted(all.keys(), key = lambda o: o.path()), True
1404
1405    def unproxy(self, base):
1406        return self
1407
1408    def unproxyParams(self):
1409        for param in self._params.keys():
1410            value = self._values.get(param)
1411            if value != None and isproxy(value):
1412                try:
1413                    value = value.unproxy(self)
1414                except:
1415                    print("Error in unproxying param '%s' of %s" %
1416                          (param, self.path()))
1417                    raise
1418                setattr(self, param, value)
1419
1420        # Unproxy ports in sorted order so that 'append' operations on
1421        # vector ports are done in a deterministic fashion.
1422        port_names = list(self._ports.keys())
1423        port_names.sort()
1424        for port_name in port_names:
1425            port = self._port_refs.get(port_name)
1426            if port != None:
1427                port.unproxy(self)
1428
1429    def print_ini(self, ini_file):
1430        print('[' + self.path() + ']', file=ini_file)    # .ini section header
1431
1432        instanceDict[self.path()] = self
1433
1434        if hasattr(self, 'type'):
1435            print('type=%s' % self.type, file=ini_file)
1436
1437        if len(self._children.keys()):
1438            print('children=%s' %
1439                  ' '.join(self._children[n].get_name()
1440                           for n in sorted(self._children.keys())),
1441                  file=ini_file)
1442
1443        for param in sorted(self._params.keys()):
1444            value = self._values.get(param)
1445            if value != None:
1446                print('%s=%s' % (param, self._values[param].ini_str()),
1447                      file=ini_file)
1448
1449        for port_name in sorted(self._ports.keys()):
1450            port = self._port_refs.get(port_name, None)
1451            if port != None:
1452                print('%s=%s' % (port_name, port.ini_str()), file=ini_file)
1453
1454        print(file=ini_file)        # blank line between objects
1455
1456    # generate a tree of dictionaries expressing all the parameters in the
1457    # instantiated system for use by scripts that want to do power, thermal
1458    # visualization, and other similar tasks
1459    def get_config_as_dict(self):
1460        d = attrdict()
1461        if hasattr(self, 'type'):
1462            d.type = self.type
1463        if hasattr(self, 'cxx_class'):
1464            d.cxx_class = self.cxx_class
1465        # Add the name and path of this object to be able to link to
1466        # the stats
1467        d.name = self.get_name()
1468        d.path = self.path()
1469
1470        for param in sorted(self._params.keys()):
1471            value = self._values.get(param)
1472            if value != None:
1473                d[param] = value.config_value()
1474
1475        for n in sorted(self._children.keys()):
1476            child = self._children[n]
1477            # Use the name of the attribute (and not get_name()) as
1478            # the key in the JSON dictionary to capture the hierarchy
1479            # in the Python code that assembled this system
1480            d[n] = child.get_config_as_dict()
1481
1482        for port_name in sorted(self._ports.keys()):
1483            port = self._port_refs.get(port_name, None)
1484            if port != None:
1485                # Represent each port with a dictionary containing the
1486                # prominent attributes
1487                d[port_name] = port.get_config_as_dict()
1488
1489        return d
1490
1491    def getCCParams(self):
1492        if self._ccParams:
1493            return self._ccParams
1494
1495        cc_params_struct = getattr(m5.internal.params, '%sParams' % self.type)
1496        cc_params = cc_params_struct()
1497        cc_params.name = str(self)
1498
1499        param_names = list(self._params.keys())
1500        param_names.sort()
1501        for param in param_names:
1502            value = self._values.get(param)
1503            if value is None:
1504                fatal("%s.%s without default or user set value",
1505                      self.path(), param)
1506
1507            value = value.getValue()
1508            if isinstance(self._params[param], VectorParamDesc):
1509                assert isinstance(value, list)
1510                vec = getattr(cc_params, param)
1511                assert not len(vec)
1512                # Some types are exposed as opaque types. They support
1513                # the append operation unlike the automatically
1514                # wrapped types.
1515                if isinstance(vec, list):
1516                    setattr(cc_params, param, list(value))
1517                else:
1518                    for v in value:
1519                        getattr(cc_params, param).append(v)
1520            else:
1521                setattr(cc_params, param, value)
1522
1523        port_names = list(self._ports.keys())
1524        port_names.sort()
1525        for port_name in port_names:
1526            port = self._port_refs.get(port_name, None)
1527            if port != None:
1528                port_count = len(port)
1529            else:
1530                port_count = 0
1531            setattr(cc_params, 'port_' + port_name + '_connection_count',
1532                    port_count)
1533        self._ccParams = cc_params
1534        return self._ccParams
1535
1536    # Get C++ object corresponding to this object, calling C++ if
1537    # necessary to construct it.  Does *not* recursively create
1538    # children.
1539    def getCCObject(self):
1540        if not self._ccObject:
1541            # Make sure this object is in the configuration hierarchy
1542            if not self._parent and not isRoot(self):
1543                raise RuntimeError("Attempt to instantiate orphan node")
1544            # Cycles in the configuration hierarchy are not supported. This
1545            # will catch the resulting recursion and stop.
1546            self._ccObject = -1
1547            if not self.abstract:
1548                params = self.getCCParams()
1549                self._ccObject = params.create()
1550        elif self._ccObject == -1:
1551            raise RuntimeError("%s: Cycle found in configuration hierarchy." \
1552                  % self.path())
1553        return self._ccObject
1554
1555    def descendants(self):
1556        yield self
1557        # The order of the dict is implementation dependent, so sort
1558        # it based on the key (name) to ensure the order is the same
1559        # on all hosts
1560        for (name, child) in sorted(self._children.items()):
1561            for obj in child.descendants():
1562                yield obj
1563
1564    # Call C++ to create C++ object corresponding to this object
1565    def createCCObject(self):
1566        self.getCCParams()
1567        self.getCCObject() # force creation
1568
1569    def getValue(self):
1570        return self.getCCObject()
1571
1572    # Create C++ port connections corresponding to the connections in
1573    # _port_refs
1574    def connectPorts(self):
1575        # Sort the ports based on their attribute name to ensure the
1576        # order is the same on all hosts
1577        for (attr, portRef) in sorted(self._port_refs.items()):
1578            portRef.ccConnect()
1579
1580    # Default function for generating the device structure.
1581    # Can be overloaded by the inheriting class
1582    def generateDeviceTree(self, state):
1583        return # return without yielding anything
1584        yield  # make this function a (null) generator
1585
1586    def recurseDeviceTree(self, state):
1587        for child in self._children.values():
1588            for item in child: # For looping over SimObjectVectors
1589                for dt in item.generateDeviceTree(state):
1590                    yield dt
1591
1592    # On a separate method otherwise certain buggy Python versions
1593    # would fail with: SyntaxError: unqualified exec is not allowed
1594    # in function 'apply_config'
1595    def _apply_config_get_dict(self):
1596        return {
1597            child_name: SimObjectCliWrapper(
1598                iter(self._children[child_name]))
1599            for child_name in self._children
1600        }
1601
1602    def apply_config(self, params):
1603        """
1604        exec a list of Python code strings contained in params.
1605
1606        The only exposed globals to those strings are the child
1607        SimObjects of this node.
1608
1609        This function is intended to allow users to modify SimObject
1610        parameters from the command line with Python statements.
1611        """
1612        d = self._apply_config_get_dict()
1613        for param in params:
1614            exec(param, d)
1615
1616# Function to provide to C++ so it can look up instances based on paths
1617def resolveSimObject(name):
1618    obj = instanceDict[name]
1619    return obj.getCCObject()
1620
1621def isSimObject(value):
1622    return isinstance(value, SimObject)
1623
1624def isSimObjectClass(value):
1625    return issubclass(value, SimObject)
1626
1627def isSimObjectVector(value):
1628    return isinstance(value, SimObjectVector)
1629
1630def isSimObjectSequence(value):
1631    if not isinstance(value, (list, tuple)) or len(value) == 0:
1632        return False
1633
1634    for val in value:
1635        if not isNullPointer(val) and not isSimObject(val):
1636            return False
1637
1638    return True
1639
1640def isSimObjectOrSequence(value):
1641    return isSimObject(value) or isSimObjectSequence(value)
1642
1643def isRoot(obj):
1644    from m5.objects import Root
1645    return obj and obj is Root.getInstance()
1646
1647def isSimObjectOrVector(value):
1648    return isSimObject(value) or isSimObjectVector(value)
1649
1650def tryAsSimObjectOrVector(value):
1651    if isSimObjectOrVector(value):
1652        return value
1653    if isSimObjectSequence(value):
1654        return SimObjectVector(value)
1655    return None
1656
1657def coerceSimObjectOrVector(value):
1658    value = tryAsSimObjectOrVector(value)
1659    if value is None:
1660        raise TypeError("SimObject or SimObjectVector expected")
1661    return value
1662
1663baseClasses = allClasses.copy()
1664baseInstances = instanceDict.copy()
1665
1666def clear():
1667    global allClasses, instanceDict, noCxxHeader
1668
1669    allClasses = baseClasses.copy()
1670    instanceDict = baseInstances.copy()
1671    noCxxHeader = False
1672
1673# __all__ defines the list of symbols that get exported when
1674# 'from config import *' is invoked.  Try to keep this reasonably
1675# short to avoid polluting other namespaces.
1676__all__ = [
1677    'SimObject',
1678    'cxxMethod',
1679    'PyBindMethod',
1680    'PyBindProperty',
1681]
1682