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