SimObject.py revision 13719
1# Copyright (c) 2017-2018 ARM Limited
2# All rights reserved.
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Copyright (c) 2004-2006 The Regents of The University of Michigan
14# Copyright (c) 2010-20013 Advanced Micro Devices, Inc.
15# Copyright (c) 2013 Mark D. Hill and David A. Wood
16# All rights reserved.
17#
18# Redistribution and use in source and binary forms, with or without
19# modification, are permitted provided that the following conditions are
20# met: redistributions of source code must retain the above copyright
21# notice, this list of conditions and the following disclaimer;
22# redistributions in binary form must reproduce the above copyright
23# notice, this list of conditions and the following disclaimer in the
24# documentation and/or other materials provided with the distribution;
25# neither the name of the copyright holders nor the names of its
26# contributors may be used to endorse or promote products derived from
27# this software without specific prior written permission.
28#
29# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40#
41# Authors: Steve Reinhardt
42#          Nathan Binkert
43#          Andreas Hansson
44#          Andreas Sandberg
45
46from __future__ import print_function
47from __future__ import absolute_import
48import six
49if six.PY3:
50    long = int
51
52import sys
53from types import FunctionType, MethodType, ModuleType
54from functools import wraps
55import inspect
56
57import m5
58from m5.util import *
59from m5.util.pybind import *
60# Use the pyfdt and not the helper class, because the fdthelper
61# relies on the SimObject definition
62from m5.ext.pyfdt import pyfdt
63
64# Have to import params up top since Param is referenced on initial
65# load (when SimObject class references Param to create a class
66# variable, the 'name' param)...
67from m5.params import *
68# There are a few things we need that aren't in params.__all__ since
69# normal users don't need them
70from m5.params import ParamDesc, VectorParamDesc, \
71     isNullPointer, SimObjectVector, Port
72
73from m5.proxy import *
74from m5.proxy import isproxy
75
76#####################################################################
77#
78# M5 Python Configuration Utility
79#
80# The basic idea is to write simple Python programs that build Python
81# objects corresponding to M5 SimObjects for the desired simulation
82# configuration.  For now, the Python emits a .ini file that can be
83# parsed by M5.  In the future, some tighter integration between M5
84# and the Python interpreter may allow bypassing the .ini file.
85#
86# Each SimObject class in M5 is represented by a Python class with the
87# same name.  The Python inheritance tree mirrors the M5 C++ tree
88# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
89# SimObjects inherit from a single SimObject base class).  To specify
90# an instance of an M5 SimObject in a configuration, the user simply
91# instantiates the corresponding Python object.  The parameters for
92# that SimObject are given by assigning to attributes of the Python
93# object, either using keyword assignment in the constructor or in
94# separate assignment statements.  For example:
95#
96# cache = BaseCache(size='64KB')
97# cache.hit_latency = 3
98# cache.assoc = 8
99#
100# The magic lies in the mapping of the Python attributes for SimObject
101# classes to the actual SimObject parameter specifications.  This
102# allows parameter validity checking in the Python code.  Continuing
103# the example above, the statements "cache.blurfl=3" or
104# "cache.assoc='hello'" would both result in runtime errors in Python,
105# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
106# parameter requires an integer, respectively.  This magic is done
107# primarily by overriding the special __setattr__ method that controls
108# assignment to object attributes.
109#
110# Once a set of Python objects have been instantiated in a hierarchy,
111# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
112# will generate a .ini file.
113#
114#####################################################################
115
116# list of all SimObject classes
117allClasses = {}
118
119# dict to look up SimObjects based on path
120instanceDict = {}
121
122# Did any of the SimObjects lack a header file?
123noCxxHeader = False
124
125def public_value(key, value):
126    return key.startswith('_') or \
127               isinstance(value, (FunctionType, MethodType, ModuleType,
128                                  classmethod, type))
129
130def createCxxConfigDirectoryEntryFile(code, name, simobj, is_header):
131    entry_class = 'CxxConfigDirectoryEntry_%s' % name
132    param_class = '%sCxxConfigParams' % name
133
134    code('#include "params/%s.hh"' % name)
135
136    if not is_header:
137        for param in simobj._params.values():
138            if isSimObjectClass(param.ptype):
139                code('#include "%s"' % param.ptype._value_dict['cxx_header'])
140                code('#include "params/%s.hh"' % param.ptype.__name__)
141            else:
142                param.ptype.cxx_ini_predecls(code)
143
144    if is_header:
145        member_prefix = ''
146        end_of_decl = ';'
147        code('#include "sim/cxx_config.hh"')
148        code()
149        code('class ${param_class} : public CxxConfigParams,'
150            ' public ${name}Params')
151        code('{')
152        code('  private:')
153        code.indent()
154        code('class DirectoryEntry : public CxxConfigDirectoryEntry')
155        code('{')
156        code('  public:')
157        code.indent()
158        code('DirectoryEntry();');
159        code()
160        code('CxxConfigParams *makeParamsObject() const')
161        code('{ return new ${param_class}; }')
162        code.dedent()
163        code('};')
164        code()
165        code.dedent()
166        code('  public:')
167        code.indent()
168    else:
169        member_prefix = '%s::' % param_class
170        end_of_decl = ''
171        code('#include "%s"' % simobj._value_dict['cxx_header'])
172        code('#include "base/str.hh"')
173        code('#include "cxx_config/${name}.hh"')
174
175        if simobj._ports:
176            code('#include "mem/mem_object.hh"')
177            code('#include "mem/port.hh"')
178
179        code()
180        code('${member_prefix}DirectoryEntry::DirectoryEntry()');
181        code('{')
182
183        def cxx_bool(b):
184            return 'true' if b else 'false'
185
186        code.indent()
187        for param in simobj._params.values():
188            is_vector = isinstance(param, m5.params.VectorParamDesc)
189            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
190
191            code('parameters["%s"] = new ParamDesc("%s", %s, %s);' %
192                (param.name, param.name, cxx_bool(is_vector),
193                cxx_bool(is_simobj)));
194
195        for port in simobj._ports.values():
196            is_vector = isinstance(port, m5.params.VectorPort)
197            is_master = port.role == 'MASTER'
198
199            code('ports["%s"] = new PortDesc("%s", %s, %s);' %
200                (port.name, port.name, cxx_bool(is_vector),
201                cxx_bool(is_master)))
202
203        code.dedent()
204        code('}')
205        code()
206
207    code('bool ${member_prefix}setSimObject(const std::string &name,')
208    code('    SimObject *simObject)${end_of_decl}')
209
210    if not is_header:
211        code('{')
212        code.indent()
213        code('bool ret = true;')
214        code()
215        code('if (false) {')
216        for param in simobj._params.values():
217            is_vector = isinstance(param, m5.params.VectorParamDesc)
218            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
219
220            if is_simobj and not is_vector:
221                code('} else if (name == "${{param.name}}") {')
222                code.indent()
223                code('this->${{param.name}} = '
224                    'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);')
225                code('if (simObject && !this->${{param.name}})')
226                code('   ret = false;')
227                code.dedent()
228        code('} else {')
229        code('    ret = false;')
230        code('}')
231        code()
232        code('return ret;')
233        code.dedent()
234        code('}')
235
236    code()
237    code('bool ${member_prefix}setSimObjectVector('
238        'const std::string &name,')
239    code('    const std::vector<SimObject *> &simObjects)${end_of_decl}')
240
241    if not is_header:
242        code('{')
243        code.indent()
244        code('bool ret = true;')
245        code()
246        code('if (false) {')
247        for param in simobj._params.values():
248            is_vector = isinstance(param, m5.params.VectorParamDesc)
249            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
250
251            if is_simobj and is_vector:
252                code('} else if (name == "${{param.name}}") {')
253                code.indent()
254                code('this->${{param.name}}.clear();')
255                code('for (auto i = simObjects.begin(); '
256                    'ret && i != simObjects.end(); i ++)')
257                code('{')
258                code.indent()
259                code('${{param.ptype.cxx_type}} object = '
260                    'dynamic_cast<${{param.ptype.cxx_type}}>(*i);')
261                code('if (*i && !object)')
262                code('    ret = false;')
263                code('else')
264                code('    this->${{param.name}}.push_back(object);')
265                code.dedent()
266                code('}')
267                code.dedent()
268        code('} else {')
269        code('    ret = false;')
270        code('}')
271        code()
272        code('return ret;')
273        code.dedent()
274        code('}')
275
276    code()
277    code('void ${member_prefix}setName(const std::string &name_)'
278        '${end_of_decl}')
279
280    if not is_header:
281        code('{')
282        code.indent()
283        code('this->name = name_;')
284        code.dedent()
285        code('}')
286
287    if is_header:
288        code('const std::string &${member_prefix}getName()')
289        code('{ return this->name; }')
290
291    code()
292    code('bool ${member_prefix}setParam(const std::string &name,')
293    code('    const std::string &value, const Flags flags)${end_of_decl}')
294
295    if not is_header:
296        code('{')
297        code.indent()
298        code('bool ret = true;')
299        code()
300        code('if (false) {')
301        for param in simobj._params.values():
302            is_vector = isinstance(param, m5.params.VectorParamDesc)
303            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
304
305            if not is_simobj and not is_vector:
306                code('} else if (name == "${{param.name}}") {')
307                code.indent()
308                param.ptype.cxx_ini_parse(code,
309                    'value', 'this->%s' % param.name, 'ret =')
310                code.dedent()
311        code('} else {')
312        code('    ret = false;')
313        code('}')
314        code()
315        code('return ret;')
316        code.dedent()
317        code('}')
318
319    code()
320    code('bool ${member_prefix}setParamVector('
321        'const std::string &name,')
322    code('    const std::vector<std::string> &values,')
323    code('    const Flags flags)${end_of_decl}')
324
325    if not is_header:
326        code('{')
327        code.indent()
328        code('bool ret = true;')
329        code()
330        code('if (false) {')
331        for param in simobj._params.values():
332            is_vector = isinstance(param, m5.params.VectorParamDesc)
333            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
334
335            if not is_simobj and is_vector:
336                code('} else if (name == "${{param.name}}") {')
337                code.indent()
338                code('${{param.name}}.clear();')
339                code('for (auto i = values.begin(); '
340                    'ret && i != values.end(); i ++)')
341                code('{')
342                code.indent()
343                code('${{param.ptype.cxx_type}} elem;')
344                param.ptype.cxx_ini_parse(code,
345                    '*i', 'elem', 'ret =')
346                code('if (ret)')
347                code('    this->${{param.name}}.push_back(elem);')
348                code.dedent()
349                code('}')
350                code.dedent()
351        code('} else {')
352        code('    ret = false;')
353        code('}')
354        code()
355        code('return ret;')
356        code.dedent()
357        code('}')
358
359    code()
360    code('bool ${member_prefix}setPortConnectionCount('
361        'const std::string &name,')
362    code('    unsigned int count)${end_of_decl}')
363
364    if not is_header:
365        code('{')
366        code.indent()
367        code('bool ret = true;')
368        code()
369        code('if (false)')
370        code('    ;')
371        for port in simobj._ports.values():
372            code('else if (name == "${{port.name}}")')
373            code('    this->port_${{port.name}}_connection_count = count;')
374        code('else')
375        code('    ret = false;')
376        code()
377        code('return ret;')
378        code.dedent()
379        code('}')
380
381    code()
382    code('SimObject *${member_prefix}simObjectCreate()${end_of_decl}')
383
384    if not is_header:
385        code('{')
386        if hasattr(simobj, 'abstract') and simobj.abstract:
387            code('    return NULL;')
388        else:
389            code('    return this->create();')
390        code('}')
391
392    if is_header:
393        code()
394        code('static CxxConfigDirectoryEntry'
395            ' *${member_prefix}makeDirectoryEntry()')
396        code('{ return new DirectoryEntry; }')
397
398    if is_header:
399        code.dedent()
400        code('};')
401
402# The metaclass for SimObject.  This class controls how new classes
403# that derive from SimObject are instantiated, and provides inherited
404# class behavior (just like a class controls how instances of that
405# class are instantiated, and provides inherited instance behavior).
406class MetaSimObject(type):
407    # Attributes that can be set only at initialization time
408    init_keywords = {
409        'abstract' : bool,
410        'cxx_class' : str,
411        'cxx_type' : str,
412        'cxx_header' : str,
413        'type' : str,
414        'cxx_base' : (str, type(None)),
415        'cxx_extra_bases' : list,
416        'cxx_exports' : list,
417        'cxx_param_exports' : list,
418    }
419    # Attributes that can be set any time
420    keywords = { 'check' : FunctionType }
421
422    # __new__ is called before __init__, and is where the statements
423    # in the body of the class definition get loaded into the class's
424    # __dict__.  We intercept this to filter out parameter & port assignments
425    # and only allow "private" attributes to be passed to the base
426    # __new__ (starting with underscore).
427    def __new__(mcls, name, bases, dict):
428        assert name not in allClasses, "SimObject %s already present" % name
429
430        # Copy "private" attributes, functions, and classes to the
431        # official dict.  Everything else goes in _init_dict to be
432        # filtered in __init__.
433        cls_dict = {}
434        value_dict = {}
435        cxx_exports = []
436        for key,val in dict.items():
437            try:
438                cxx_exports.append(getattr(val, "__pybind"))
439            except AttributeError:
440                pass
441
442            if public_value(key, val):
443                cls_dict[key] = val
444            else:
445                # must be a param/port setting
446                value_dict[key] = val
447        if 'abstract' not in value_dict:
448            value_dict['abstract'] = False
449        if 'cxx_extra_bases' not in value_dict:
450            value_dict['cxx_extra_bases'] = []
451        if 'cxx_exports' not in value_dict:
452            value_dict['cxx_exports'] = cxx_exports
453        else:
454            value_dict['cxx_exports'] += cxx_exports
455        if 'cxx_param_exports' not in value_dict:
456            value_dict['cxx_param_exports'] = []
457        cls_dict['_value_dict'] = value_dict
458        cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
459        if 'type' in value_dict:
460            allClasses[name] = cls
461        return cls
462
463    # subclass initialization
464    def __init__(cls, name, bases, dict):
465        # calls type.__init__()... I think that's a no-op, but leave
466        # it here just in case it's not.
467        super(MetaSimObject, cls).__init__(name, bases, dict)
468
469        # initialize required attributes
470
471        # class-only attributes
472        cls._params = multidict() # param descriptions
473        cls._ports = multidict()  # port descriptions
474
475        # class or instance attributes
476        cls._values = multidict()   # param values
477        cls._hr_values = multidict() # human readable param values
478        cls._children = multidict() # SimObject children
479        cls._port_refs = multidict() # port ref objects
480        cls._instantiated = False # really instantiated, cloned, or subclassed
481
482        # We don't support multiple inheritance of sim objects.  If you want
483        # to, you must fix multidict to deal with it properly. Non sim-objects
484        # are ok, though
485        bTotal = 0
486        for c in bases:
487            if isinstance(c, MetaSimObject):
488                bTotal += 1
489            if bTotal > 1:
490                raise TypeError(
491                      "SimObjects do not support multiple inheritance")
492
493        base = bases[0]
494
495        # Set up general inheritance via multidicts.  A subclass will
496        # inherit all its settings from the base class.  The only time
497        # the following is not true is when we define the SimObject
498        # class itself (in which case the multidicts have no parent).
499        if isinstance(base, MetaSimObject):
500            cls._base = base
501            cls._params.parent = base._params
502            cls._ports.parent = base._ports
503            cls._values.parent = base._values
504            cls._hr_values.parent = base._hr_values
505            cls._children.parent = base._children
506            cls._port_refs.parent = base._port_refs
507            # mark base as having been subclassed
508            base._instantiated = True
509        else:
510            cls._base = None
511
512        # default keyword values
513        if 'type' in cls._value_dict:
514            if 'cxx_class' not in cls._value_dict:
515                cls._value_dict['cxx_class'] = cls._value_dict['type']
516
517            cls._value_dict['cxx_type'] = '%s *' % cls._value_dict['cxx_class']
518
519            if 'cxx_header' not in cls._value_dict:
520                global noCxxHeader
521                noCxxHeader = True
522                warn("No header file specified for SimObject: %s", name)
523
524        # Now process the _value_dict items.  They could be defining
525        # new (or overriding existing) parameters or ports, setting
526        # class keywords (e.g., 'abstract'), or setting parameter
527        # values or port bindings.  The first 3 can only be set when
528        # the class is defined, so we handle them here.  The others
529        # can be set later too, so just emulate that by calling
530        # setattr().
531        for key,val in cls._value_dict.items():
532            # param descriptions
533            if isinstance(val, ParamDesc):
534                cls._new_param(key, val)
535
536            # port objects
537            elif isinstance(val, Port):
538                cls._new_port(key, val)
539
540            # init-time-only keywords
541            elif key in cls.init_keywords:
542                cls._set_keyword(key, val, cls.init_keywords[key])
543
544            # default: use normal path (ends up in __setattr__)
545            else:
546                setattr(cls, key, val)
547
548    def _set_keyword(cls, keyword, val, kwtype):
549        if not isinstance(val, kwtype):
550            raise TypeError('keyword %s has bad type %s (expecting %s)' % \
551                  (keyword, type(val), kwtype))
552        if isinstance(val, FunctionType):
553            val = classmethod(val)
554        type.__setattr__(cls, keyword, val)
555
556    def _new_param(cls, name, pdesc):
557        # each param desc should be uniquely assigned to one variable
558        assert(not hasattr(pdesc, 'name'))
559        pdesc.name = name
560        cls._params[name] = pdesc
561        if hasattr(pdesc, 'default'):
562            cls._set_param(name, pdesc.default, pdesc)
563
564    def _set_param(cls, name, value, param):
565        assert(param.name == name)
566        try:
567            hr_value = value
568            value = param.convert(value)
569        except Exception as e:
570            msg = "%s\nError setting param %s.%s to %s\n" % \
571                  (e, cls.__name__, name, value)
572            e.args = (msg, )
573            raise
574        cls._values[name] = value
575        # if param value is a SimObject, make it a child too, so that
576        # it gets cloned properly when the class is instantiated
577        if isSimObjectOrVector(value) and not value.has_parent():
578            cls._add_cls_child(name, value)
579        # update human-readable values of the param if it has a literal
580        # value and is not an object or proxy.
581        if not (isSimObjectOrVector(value) or\
582                isinstance(value, m5.proxy.BaseProxy)):
583            cls._hr_values[name] = hr_value
584
585    def _add_cls_child(cls, name, child):
586        # It's a little funky to have a class as a parent, but these
587        # objects should never be instantiated (only cloned, which
588        # clears the parent pointer), and this makes it clear that the
589        # object is not an orphan and can provide better error
590        # messages.
591        child.set_parent(cls, name)
592        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
886        args, varargs, keywords, defaults = inspect.getargspec(func)
887        if varargs or keywords:
888            raise ValueError("Wrapped methods must not contain variable " \
889                             "arguments")
890
891        # Create tuples of (argument, default)
892        if defaults:
893            args = args[:-len(defaults)] + \
894                   list(zip(args[-len(defaults):], defaults))
895        # Don't include self in the argument list to PyBind
896        args = args[1:]
897
898
899        @wraps(func)
900        def cxx_call(self, *args, **kwargs):
901            ccobj = self.getCCObject()
902            return getattr(ccobj, name)(*args, **kwargs)
903
904        @wraps(func)
905        def py_call(self, *args, **kwargs):
906            return func(self, *args, **kwargs)
907
908        f = py_call if override else cxx_call
909        f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args)
910
911        return f
912
913    if len(args) == 0:
914        return decorate
915    elif len(args) == 1 and len(kwargs) == 0:
916        return decorate(*args)
917    else:
918        raise TypeError("One argument and no kwargs, or only kwargs expected")
919
920# This class holds information about each simobject parameter
921# that should be displayed on the command line for use in the
922# configuration system.
923class ParamInfo(object):
924  def __init__(self, type, desc, type_str, example, default_val, access_str):
925    self.type = type
926    self.desc = desc
927    self.type_str = type_str
928    self.example_str = example
929    self.default_val = default_val
930    # The string representation used to access this param through python.
931    # The method to access this parameter presented on the command line may
932    # be different, so this needs to be stored for later use.
933    self.access_str = access_str
934    self.created = True
935
936  # Make it so we can only set attributes at initialization time
937  # and effectively make this a const object.
938  def __setattr__(self, name, value):
939    if not "created" in self.__dict__:
940      self.__dict__[name] = value
941
942class SimObjectCliWrapperException(Exception):
943    def __init__(self, message):
944        super(Exception, self).__init__(message)
945
946class SimObjectCliWrapper(object):
947    """
948    Wrapper class to restrict operations that may be done
949    from the command line on SimObjects.
950
951    Only parameters may be set, and only children may be accessed.
952
953    Slicing allows for multiple simultaneous assignment of items in
954    one statement.
955    """
956
957    def __init__(self, sim_objects):
958        self.__dict__['_sim_objects'] = list(sim_objects)
959
960    def __getattr__(self, key):
961        return SimObjectCliWrapper(sim_object._children[key]
962                for sim_object in self._sim_objects)
963
964    def __setattr__(self, key, val):
965        for sim_object in self._sim_objects:
966            if key in sim_object._params:
967                if sim_object._params[key].isCmdLineSettable():
968                    setattr(sim_object, key, val)
969                else:
970                    raise SimObjectCliWrapperException(
971                            'tried to set or unsettable' \
972                            'object parameter: ' + key)
973            else:
974                raise SimObjectCliWrapperException(
975                            'tried to set or access non-existent' \
976                            'object parameter: ' + key)
977
978    def __getitem__(self, idx):
979        """
980        Extends the list() semantics to also allow tuples,
981        for example object[1, 3] selects items 1 and 3.
982        """
983        out = []
984        if isinstance(idx, tuple):
985            for t in idx:
986                out.extend(self[t]._sim_objects)
987        else:
988            if isinstance(idx, int):
989                _range = range(idx, idx + 1)
990            elif not isinstance(idx, slice):
991                raise SimObjectCliWrapperException( \
992                        'invalid index type: ' + repr(idx))
993            for sim_object in self._sim_objects:
994                if isinstance(idx, slice):
995                    _range = range(*idx.indices(len(sim_object)))
996                out.extend(sim_object[i] for i in _range)
997        return SimObjectCliWrapper(out)
998
999# The SimObject class is the root of the special hierarchy.  Most of
1000# the code in this class deals with the configuration hierarchy itself
1001# (parent/child node relationships).
1002class SimObject(object):
1003    # Specify metaclass.  Any class inheriting from SimObject will
1004    # get this metaclass.
1005    __metaclass__ = MetaSimObject
1006    type = 'SimObject'
1007    abstract = True
1008
1009    cxx_header = "sim/sim_object.hh"
1010    cxx_extra_bases = [ "Drainable", "Serializable" ]
1011    eventq_index = Param.UInt32(Parent.eventq_index, "Event Queue Index")
1012
1013    cxx_exports = [
1014        PyBindMethod("init"),
1015        PyBindMethod("initState"),
1016        PyBindMethod("memInvalidate"),
1017        PyBindMethod("memWriteback"),
1018        PyBindMethod("regStats"),
1019        PyBindMethod("resetStats"),
1020        PyBindMethod("regProbePoints"),
1021        PyBindMethod("regProbeListeners"),
1022        PyBindMethod("startup"),
1023    ]
1024
1025    cxx_param_exports = [
1026        PyBindProperty("name"),
1027    ]
1028
1029    @cxxMethod
1030    def loadState(self, cp):
1031        """Load SimObject state from a checkpoint"""
1032        pass
1033
1034    # Returns a dict of all the option strings that can be
1035    # generated as command line options for this simobject instance
1036    # by tracing all reachable params in the top level instance and
1037    # any children it contains.
1038    def enumerateParams(self, flags_dict = {},
1039                        cmd_line_str = "", access_str = ""):
1040        if hasattr(self, "_paramEnumed"):
1041            print("Cycle detected enumerating params")
1042        else:
1043            self._paramEnumed = True
1044            # Scan the children first to pick up all the objects in this SimObj
1045            for keys in self._children:
1046                child = self._children[keys]
1047                next_cmdline_str = cmd_line_str + keys
1048                next_access_str = access_str + keys
1049                if not isSimObjectVector(child):
1050                    next_cmdline_str = next_cmdline_str + "."
1051                    next_access_str = next_access_str + "."
1052                flags_dict = child.enumerateParams(flags_dict,
1053                                                   next_cmdline_str,
1054                                                   next_access_str)
1055
1056            # Go through the simple params in the simobject in this level
1057            # of the simobject hierarchy and save information about the
1058            # parameter to be used for generating and processing command line
1059            # options to the simulator to set these parameters.
1060            for keys,values in self._params.items():
1061                if values.isCmdLineSettable():
1062                    type_str = ''
1063                    ex_str = values.example_str()
1064                    ptype = None
1065                    if isinstance(values, VectorParamDesc):
1066                        type_str = 'Vector_%s' % values.ptype_str
1067                        ptype = values
1068                    else:
1069                        type_str = '%s' % values.ptype_str
1070                        ptype = values.ptype
1071
1072                    if keys in self._hr_values\
1073                       and keys in self._values\
1074                       and not isinstance(self._values[keys],
1075                                          m5.proxy.BaseProxy):
1076                        cmd_str = cmd_line_str + keys
1077                        acc_str = access_str + keys
1078                        flags_dict[cmd_str] = ParamInfo(ptype,
1079                                    self._params[keys].desc, type_str, ex_str,
1080                                    values.pretty_print(self._hr_values[keys]),
1081                                    acc_str)
1082                    elif not keys in self._hr_values\
1083                         and not keys in self._values:
1084                        # Empty param
1085                        cmd_str = cmd_line_str + keys
1086                        acc_str = access_str + keys
1087                        flags_dict[cmd_str] = ParamInfo(ptype,
1088                                    self._params[keys].desc,
1089                                    type_str, ex_str, '', acc_str)
1090
1091        return flags_dict
1092
1093    # Initialize new instance.  For objects with SimObject-valued
1094    # children, we need to recursively clone the classes represented
1095    # by those param values as well in a consistent "deep copy"-style
1096    # fashion.  That is, we want to make sure that each instance is
1097    # cloned only once, and that if there are multiple references to
1098    # the same original object, we end up with the corresponding
1099    # cloned references all pointing to the same cloned instance.
1100    def __init__(self, **kwargs):
1101        ancestor = kwargs.get('_ancestor')
1102        memo_dict = kwargs.get('_memo')
1103        if memo_dict is None:
1104            # prepare to memoize any recursively instantiated objects
1105            memo_dict = {}
1106        elif ancestor:
1107            # memoize me now to avoid problems with recursive calls
1108            memo_dict[ancestor] = self
1109
1110        if not ancestor:
1111            ancestor = self.__class__
1112        ancestor._instantiated = True
1113
1114        # initialize required attributes
1115        self._parent = None
1116        self._name = None
1117        self._ccObject = None  # pointer to C++ object
1118        self._ccParams = None
1119        self._instantiated = False # really "cloned"
1120
1121        # Clone children specified at class level.  No need for a
1122        # multidict here since we will be cloning everything.
1123        # Do children before parameter values so that children that
1124        # are also param values get cloned properly.
1125        self._children = {}
1126        for key,val in ancestor._children.items():
1127            self.add_child(key, val(_memo=memo_dict))
1128
1129        # Inherit parameter values from class using multidict so
1130        # individual value settings can be overridden but we still
1131        # inherit late changes to non-overridden class values.
1132        self._values = multidict(ancestor._values)
1133        self._hr_values = multidict(ancestor._hr_values)
1134        # clone SimObject-valued parameters
1135        for key,val in ancestor._values.items():
1136            val = tryAsSimObjectOrVector(val)
1137            if val is not None:
1138                self._values[key] = val(_memo=memo_dict)
1139
1140        # clone port references.  no need to use a multidict here
1141        # since we will be creating new references for all ports.
1142        self._port_refs = {}
1143        for key,val in ancestor._port_refs.items():
1144            self._port_refs[key] = val.clone(self, memo_dict)
1145        # apply attribute assignments from keyword args, if any
1146        for key,val in kwargs.items():
1147            setattr(self, key, val)
1148
1149    # "Clone" the current instance by creating another instance of
1150    # this instance's class, but that inherits its parameter values
1151    # and port mappings from the current instance.  If we're in a
1152    # "deep copy" recursive clone, check the _memo dict to see if
1153    # we've already cloned this instance.
1154    def __call__(self, **kwargs):
1155        memo_dict = kwargs.get('_memo')
1156        if memo_dict is None:
1157            # no memo_dict: must be top-level clone operation.
1158            # this is only allowed at the root of a hierarchy
1159            if self._parent:
1160                raise RuntimeError("attempt to clone object %s " \
1161                      "not at the root of a tree (parent = %s)" \
1162                      % (self, self._parent))
1163            # create a new dict and use that.
1164            memo_dict = {}
1165            kwargs['_memo'] = memo_dict
1166        elif self in memo_dict:
1167            # clone already done & memoized
1168            return memo_dict[self]
1169        return self.__class__(_ancestor = self, **kwargs)
1170
1171    def _get_port_ref(self, attr):
1172        # Return reference that can be assigned to another port
1173        # via __setattr__.  There is only ever one reference
1174        # object per port, but we create them lazily here.
1175        ref = self._port_refs.get(attr)
1176        if ref == None:
1177            ref = self._ports[attr].makeRef(self)
1178            self._port_refs[attr] = ref
1179        return ref
1180
1181    def __getattr__(self, attr):
1182        if attr in self._ports:
1183            return self._get_port_ref(attr)
1184
1185        if attr in self._values:
1186            return self._values[attr]
1187
1188        if attr in self._children:
1189            return self._children[attr]
1190
1191        # If the attribute exists on the C++ object, transparently
1192        # forward the reference there.  This is typically used for
1193        # methods exported to Python (e.g., init(), and startup())
1194        if self._ccObject and hasattr(self._ccObject, attr):
1195            return getattr(self._ccObject, attr)
1196
1197        err_string = "object '%s' has no attribute '%s'" \
1198              % (self.__class__.__name__, attr)
1199
1200        if not self._ccObject:
1201            err_string += "\n  (C++ object is not yet constructed," \
1202                          " so wrapped C++ methods are unavailable.)"
1203
1204        raise AttributeError(err_string)
1205
1206    # Set attribute (called on foo.attr = value when foo is an
1207    # instance of class cls).
1208    def __setattr__(self, attr, value):
1209        # normal processing for private attributes
1210        if attr.startswith('_'):
1211            object.__setattr__(self, attr, value)
1212            return
1213
1214        if attr in self._ports:
1215            # set up port connection
1216            self._get_port_ref(attr).connect(value)
1217            return
1218
1219        param = self._params.get(attr)
1220        if param:
1221            try:
1222                hr_value = value
1223                value = param.convert(value)
1224            except Exception as e:
1225                msg = "%s\nError setting param %s.%s to %s\n" % \
1226                      (e, self.__class__.__name__, attr, value)
1227                e.args = (msg, )
1228                raise
1229            self._values[attr] = value
1230            # implicitly parent unparented objects assigned as params
1231            if isSimObjectOrVector(value) and not value.has_parent():
1232                self.add_child(attr, value)
1233            # set the human-readable value dict if this is a param
1234            # with a literal value and is not being set as an object
1235            # or proxy.
1236            if not (isSimObjectOrVector(value) or\
1237                    isinstance(value, m5.proxy.BaseProxy)):
1238                self._hr_values[attr] = hr_value
1239
1240            return
1241
1242        # if RHS is a SimObject, it's an implicit child assignment
1243        if isSimObjectOrSequence(value):
1244            self.add_child(attr, value)
1245            return
1246
1247        # no valid assignment... raise exception
1248        raise AttributeError("Class %s has no parameter %s" \
1249              % (self.__class__.__name__, attr))
1250
1251
1252    # this hack allows tacking a '[0]' onto parameters that may or may
1253    # not be vectors, and always getting the first element (e.g. cpus)
1254    def __getitem__(self, key):
1255        if key == 0:
1256            return self
1257        raise IndexError("Non-zero index '%s' to SimObject" % key)
1258
1259    # this hack allows us to iterate over a SimObject that may
1260    # not be a vector, so we can call a loop over it and get just one
1261    # element.
1262    def __len__(self):
1263        return 1
1264
1265    # Also implemented by SimObjectVector
1266    def clear_parent(self, old_parent):
1267        assert self._parent is old_parent
1268        self._parent = None
1269
1270    # Also implemented by SimObjectVector
1271    def set_parent(self, parent, name):
1272        self._parent = parent
1273        self._name = name
1274
1275    # Return parent object of this SimObject, not implemented by
1276    # SimObjectVector because the elements in a SimObjectVector may not share
1277    # the same parent
1278    def get_parent(self):
1279        return self._parent
1280
1281    # Also implemented by SimObjectVector
1282    def get_name(self):
1283        return self._name
1284
1285    # Also implemented by SimObjectVector
1286    def has_parent(self):
1287        return self._parent is not None
1288
1289    # clear out child with given name. This code is not likely to be exercised.
1290    # See comment in add_child.
1291    def clear_child(self, name):
1292        child = self._children[name]
1293        child.clear_parent(self)
1294        del self._children[name]
1295
1296    # Add a new child to this object.
1297    def add_child(self, name, child):
1298        child = coerceSimObjectOrVector(child)
1299        if child.has_parent():
1300            warn("add_child('%s'): child '%s' already has parent", name,
1301                child.get_name())
1302        if name in self._children:
1303            # This code path had an undiscovered bug that would make it fail
1304            # at runtime. It had been here for a long time and was only
1305            # exposed by a buggy script. Changes here will probably not be
1306            # exercised without specialized testing.
1307            self.clear_child(name)
1308        child.set_parent(self, name)
1309        if not isNullPointer(child):
1310            self._children[name] = child
1311
1312    # Take SimObject-valued parameters that haven't been explicitly
1313    # assigned as children and make them children of the object that
1314    # they were assigned to as a parameter value.  This guarantees
1315    # that when we instantiate all the parameter objects we're still
1316    # inside the configuration hierarchy.
1317    def adoptOrphanParams(self):
1318        for key,val in self._values.items():
1319            if not isSimObjectVector(val) and isSimObjectSequence(val):
1320                # need to convert raw SimObject sequences to
1321                # SimObjectVector class so we can call has_parent()
1322                val = SimObjectVector(val)
1323                self._values[key] = val
1324            if isSimObjectOrVector(val) and not val.has_parent():
1325                warn("%s adopting orphan SimObject param '%s'", self, key)
1326                self.add_child(key, val)
1327
1328    def path(self):
1329        if not self._parent:
1330            return '<orphan %s>' % self.__class__
1331        elif isinstance(self._parent, MetaSimObject):
1332            return str(self.__class__)
1333
1334        ppath = self._parent.path()
1335        if ppath == 'root':
1336            return self._name
1337        return ppath + "." + self._name
1338
1339    def __str__(self):
1340        return self.path()
1341
1342    def config_value(self):
1343        return self.path()
1344
1345    def ini_str(self):
1346        return self.path()
1347
1348    def find_any(self, ptype):
1349        if isinstance(self, ptype):
1350            return self, True
1351
1352        found_obj = None
1353        for child in self._children.values():
1354            visited = False
1355            if hasattr(child, '_visited'):
1356              visited = getattr(child, '_visited')
1357
1358            if isinstance(child, ptype) and not visited:
1359                if found_obj != None and child != found_obj:
1360                    raise AttributeError(
1361                          'parent.any matched more than one: %s %s' % \
1362                          (found_obj.path, child.path))
1363                found_obj = child
1364        # search param space
1365        for pname,pdesc in self._params.items():
1366            if issubclass(pdesc.ptype, ptype):
1367                match_obj = self._values[pname]
1368                if found_obj != None and found_obj != match_obj:
1369                    raise AttributeError(
1370                          'parent.any matched more than one: %s and %s' % \
1371                          (found_obj.path, match_obj.path))
1372                found_obj = match_obj
1373        return found_obj, found_obj != None
1374
1375    def find_all(self, ptype):
1376        all = {}
1377        # search children
1378        for child in self._children.values():
1379            # a child could be a list, so ensure we visit each item
1380            if isinstance(child, list):
1381                children = child
1382            else:
1383                children = [child]
1384
1385            for child in children:
1386                if isinstance(child, ptype) and not isproxy(child) and \
1387                        not isNullPointer(child):
1388                    all[child] = True
1389                if isSimObject(child):
1390                    # also add results from the child itself
1391                    child_all, done = child.find_all(ptype)
1392                    all.update(dict(zip(child_all, [done] * len(child_all))))
1393        # search param space
1394        for pname,pdesc in self._params.items():
1395            if issubclass(pdesc.ptype, ptype):
1396                match_obj = self._values[pname]
1397                if not isproxy(match_obj) and not isNullPointer(match_obj):
1398                    all[match_obj] = True
1399        # Also make sure to sort the keys based on the objects' path to
1400        # ensure that the order is the same on all hosts
1401        return sorted(all.keys(), key = lambda o: o.path()), True
1402
1403    def unproxy(self, base):
1404        return self
1405
1406    def unproxyParams(self):
1407        for param in self._params.keys():
1408            value = self._values.get(param)
1409            if value != None and isproxy(value):
1410                try:
1411                    value = value.unproxy(self)
1412                except:
1413                    print("Error in unproxying param '%s' of %s" %
1414                          (param, self.path()))
1415                    raise
1416                setattr(self, param, value)
1417
1418        # Unproxy ports in sorted order so that 'append' operations on
1419        # vector ports are done in a deterministic fashion.
1420        port_names = list(self._ports.keys())
1421        port_names.sort()
1422        for port_name in port_names:
1423            port = self._port_refs.get(port_name)
1424            if port != None:
1425                port.unproxy(self)
1426
1427    def print_ini(self, ini_file):
1428        print('[' + self.path() + ']', file=ini_file)    # .ini section header
1429
1430        instanceDict[self.path()] = self
1431
1432        if hasattr(self, 'type'):
1433            print('type=%s' % self.type, file=ini_file)
1434
1435        if len(self._children.keys()):
1436            print('children=%s' %
1437                  ' '.join(self._children[n].get_name()
1438                           for n in sorted(self._children.keys())),
1439                  file=ini_file)
1440
1441        for param in sorted(self._params.keys()):
1442            value = self._values.get(param)
1443            if value != None:
1444                print('%s=%s' % (param, self._values[param].ini_str()),
1445                      file=ini_file)
1446
1447        for port_name in sorted(self._ports.keys()):
1448            port = self._port_refs.get(port_name, None)
1449            if port != None:
1450                print('%s=%s' % (port_name, port.ini_str()), file=ini_file)
1451
1452        print(file=ini_file)        # blank line between objects
1453
1454    # generate a tree of dictionaries expressing all the parameters in the
1455    # instantiated system for use by scripts that want to do power, thermal
1456    # visualization, and other similar tasks
1457    def get_config_as_dict(self):
1458        d = attrdict()
1459        if hasattr(self, 'type'):
1460            d.type = self.type
1461        if hasattr(self, 'cxx_class'):
1462            d.cxx_class = self.cxx_class
1463        # Add the name and path of this object to be able to link to
1464        # the stats
1465        d.name = self.get_name()
1466        d.path = self.path()
1467
1468        for param in sorted(self._params.keys()):
1469            value = self._values.get(param)
1470            if value != None:
1471                d[param] = value.config_value()
1472
1473        for n in sorted(self._children.keys()):
1474            child = self._children[n]
1475            # Use the name of the attribute (and not get_name()) as
1476            # the key in the JSON dictionary to capture the hierarchy
1477            # in the Python code that assembled this system
1478            d[n] = child.get_config_as_dict()
1479
1480        for port_name in sorted(self._ports.keys()):
1481            port = self._port_refs.get(port_name, None)
1482            if port != None:
1483                # Represent each port with a dictionary containing the
1484                # prominent attributes
1485                d[port_name] = port.get_config_as_dict()
1486
1487        return d
1488
1489    def getCCParams(self):
1490        if self._ccParams:
1491            return self._ccParams
1492
1493        cc_params_struct = getattr(m5.internal.params, '%sParams' % self.type)
1494        cc_params = cc_params_struct()
1495        cc_params.name = str(self)
1496
1497        param_names = list(self._params.keys())
1498        param_names.sort()
1499        for param in param_names:
1500            value = self._values.get(param)
1501            if value is None:
1502                fatal("%s.%s without default or user set value",
1503                      self.path(), param)
1504
1505            value = value.getValue()
1506            if isinstance(self._params[param], VectorParamDesc):
1507                assert isinstance(value, list)
1508                vec = getattr(cc_params, param)
1509                assert not len(vec)
1510                # Some types are exposed as opaque types. They support
1511                # the append operation unlike the automatically
1512                # wrapped types.
1513                if isinstance(vec, list):
1514                    setattr(cc_params, param, list(value))
1515                else:
1516                    for v in value:
1517                        getattr(cc_params, param).append(v)
1518            else:
1519                setattr(cc_params, param, value)
1520
1521        port_names = list(self._ports.keys())
1522        port_names.sort()
1523        for port_name in port_names:
1524            port = self._port_refs.get(port_name, None)
1525            if port != None:
1526                port_count = len(port)
1527            else:
1528                port_count = 0
1529            setattr(cc_params, 'port_' + port_name + '_connection_count',
1530                    port_count)
1531        self._ccParams = cc_params
1532        return self._ccParams
1533
1534    # Get C++ object corresponding to this object, calling C++ if
1535    # necessary to construct it.  Does *not* recursively create
1536    # children.
1537    def getCCObject(self):
1538        if not self._ccObject:
1539            # Make sure this object is in the configuration hierarchy
1540            if not self._parent and not isRoot(self):
1541                raise RuntimeError("Attempt to instantiate orphan node")
1542            # Cycles in the configuration hierarchy are not supported. This
1543            # will catch the resulting recursion and stop.
1544            self._ccObject = -1
1545            if not self.abstract:
1546                params = self.getCCParams()
1547                self._ccObject = params.create()
1548        elif self._ccObject == -1:
1549            raise RuntimeError("%s: Cycle found in configuration hierarchy." \
1550                  % self.path())
1551        return self._ccObject
1552
1553    def descendants(self):
1554        yield self
1555        # The order of the dict is implementation dependent, so sort
1556        # it based on the key (name) to ensure the order is the same
1557        # on all hosts
1558        for (name, child) in sorted(self._children.items()):
1559            for obj in child.descendants():
1560                yield obj
1561
1562    # Call C++ to create C++ object corresponding to this object
1563    def createCCObject(self):
1564        self.getCCParams()
1565        self.getCCObject() # force creation
1566
1567    def getValue(self):
1568        return self.getCCObject()
1569
1570    # Create C++ port connections corresponding to the connections in
1571    # _port_refs
1572    def connectPorts(self):
1573        # Sort the ports based on their attribute name to ensure the
1574        # order is the same on all hosts
1575        for (attr, portRef) in sorted(self._port_refs.items()):
1576            portRef.ccConnect()
1577
1578    # Default function for generating the device structure.
1579    # Can be overloaded by the inheriting class
1580    def generateDeviceTree(self, state):
1581        return # return without yielding anything
1582        yield  # make this function a (null) generator
1583
1584    def recurseDeviceTree(self, state):
1585        for child in self._children.values():
1586            for item in child: # For looping over SimObjectVectors
1587                for dt in item.generateDeviceTree(state):
1588                    yield dt
1589
1590    # On a separate method otherwise certain buggy Python versions
1591    # would fail with: SyntaxError: unqualified exec is not allowed
1592    # in function 'apply_config'
1593    def _apply_config_get_dict(self):
1594        return {
1595            child_name: SimObjectCliWrapper(
1596                iter(self._children[child_name]))
1597            for child_name in self._children
1598        }
1599
1600    def apply_config(self, params):
1601        """
1602        exec a list of Python code strings contained in params.
1603
1604        The only exposed globals to those strings are the child
1605        SimObjects of this node.
1606
1607        This function is intended to allow users to modify SimObject
1608        parameters from the command line with Python statements.
1609        """
1610        d = self._apply_config_get_dict()
1611        for param in params:
1612            exec(param, d)
1613
1614# Function to provide to C++ so it can look up instances based on paths
1615def resolveSimObject(name):
1616    obj = instanceDict[name]
1617    return obj.getCCObject()
1618
1619def isSimObject(value):
1620    return isinstance(value, SimObject)
1621
1622def isSimObjectClass(value):
1623    return issubclass(value, SimObject)
1624
1625def isSimObjectVector(value):
1626    return isinstance(value, SimObjectVector)
1627
1628def isSimObjectSequence(value):
1629    if not isinstance(value, (list, tuple)) or len(value) == 0:
1630        return False
1631
1632    for val in value:
1633        if not isNullPointer(val) and not isSimObject(val):
1634            return False
1635
1636    return True
1637
1638def isSimObjectOrSequence(value):
1639    return isSimObject(value) or isSimObjectSequence(value)
1640
1641def isRoot(obj):
1642    from m5.objects import Root
1643    return obj and obj is Root.getInstance()
1644
1645def isSimObjectOrVector(value):
1646    return isSimObject(value) or isSimObjectVector(value)
1647
1648def tryAsSimObjectOrVector(value):
1649    if isSimObjectOrVector(value):
1650        return value
1651    if isSimObjectSequence(value):
1652        return SimObjectVector(value)
1653    return None
1654
1655def coerceSimObjectOrVector(value):
1656    value = tryAsSimObjectOrVector(value)
1657    if value is None:
1658        raise TypeError("SimObject or SimObjectVector expected")
1659    return value
1660
1661baseClasses = allClasses.copy()
1662baseInstances = instanceDict.copy()
1663
1664def clear():
1665    global allClasses, instanceDict, noCxxHeader
1666
1667    allClasses = baseClasses.copy()
1668    instanceDict = baseInstances.copy()
1669    noCxxHeader = False
1670
1671# __all__ defines the list of symbols that get exported when
1672# 'from config import *' is invoked.  Try to keep this reasonably
1673# short to avoid polluting other namespaces.
1674__all__ = [
1675    'SimObject',
1676    'cxxMethod',
1677    'PyBindMethod',
1678    'PyBindProperty',
1679]
1680