1 # Copyright (c) 2017-2019 ARM Limited
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.
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.
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.
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.
41 from __future__
import print_function
42 from __future__
import absolute_import
43 from six
import add_metaclass
49 from types
import FunctionType
, MethodType
, ModuleType
50 from functools
import wraps
55 from m5
.util
.pybind
import *
56 # Use the pyfdt and not the helper class, because the fdthelper
57 # relies on the SimObject definition
58 from m5
.ext
.pyfdt
import pyfdt
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)...
63 from 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
66 from m5
.params
import ParamDesc
, VectorParamDesc
, \
67 isNullPointer
, SimObjectVector
, Port
69 from m5
.proxy
import *
70 from m5
.proxy
import isproxy
72 #####################################################################
74 # M5 Python Configuration Utility
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.
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:
92 # cache = BaseCache(size='64KB')
93 # cache.hit_latency = 3
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.
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.
110 #####################################################################
112 # list of all SimObject classes
115 # dict to look up SimObjects based on path
118 # Did any of the SimObjects lack a header file?
121 def public_value(key
, value
):
122 return key
.startswith('_') or \
123 isinstance(value
, (FunctionType
, MethodType
, ModuleType
,
126 def createCxxConfigDirectoryEntryFile(code
, name
, simobj
, is_header
):
127 entry_class
= 'CxxConfigDirectoryEntry_%s' % name
128 param_class
= '%sCxxConfigParams' % name
130 code('#include "params/%s.hh"' % name
)
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
__)
138 param
.ptype
.cxx_ini_predecls(code
)
143 code('#include "sim/cxx_config.hh"')
145 code('class ${param_class} : public CxxConfigParams,'
146 ' public ${name}Params')
150 code('class DirectoryEntry : public CxxConfigDirectoryEntry')
154 code('DirectoryEntry();');
156 code('CxxConfigParams *makeParamsObject() const')
157 code('{ return new ${param_class}; }')
165 member_prefix
= '%s::' % param_class
167 code('#include "%s"' % simobj
._value
_dict
['cxx_header'])
168 code('#include "base/str.hh"')
169 code('#include "cxx_config/${name}.hh"')
172 code('${member_prefix}DirectoryEntry::DirectoryEntry()');
176 return 'true' if b
else 'false'
179 for param
in simobj
._params
.values():
180 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
181 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
183 code('parameters["%s"] = new ParamDesc("%s", %s, %s);' %
184 (param
.name
, param
.name
, cxx_bool(is_vector
),
185 cxx_bool(is_simobj
)));
187 for port
in simobj
._ports
.values():
188 is_vector
= isinstance(port
, m5
.params
.VectorPort
)
189 is_master
= port
.role
== 'MASTER'
191 code('ports["%s"] = new PortDesc("%s", %s, %s);' %
192 (port
.name
, port
.name
, cxx_bool(is_vector
),
193 cxx_bool(is_master
)))
199 code('bool ${member_prefix}setSimObject(const std::string &name,')
200 code(' SimObject *simObject)${end_of_decl}')
205 code('bool ret = true;')
208 for param
in simobj
._params
.values():
209 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
210 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
212 if is_simobj
and not is_vector
:
213 code('} else if (name == "${{param.name}}") {')
215 code('this->${{param.name}} = '
216 'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);')
217 code('if (simObject && !this->${{param.name}})')
218 code(' ret = false;')
221 code(' ret = false;')
229 code('bool ${member_prefix}setSimObjectVector('
230 'const std::string &name,')
231 code(' const std::vector<SimObject *> &simObjects)${end_of_decl}')
236 code('bool ret = true;')
239 for param
in simobj
._params
.values():
240 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
241 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
243 if is_simobj
and is_vector
:
244 code('} else if (name == "${{param.name}}") {')
246 code('this->${{param.name}}.clear();')
247 code('for (auto i = simObjects.begin(); '
248 'ret && i != simObjects.end(); i ++)')
251 code('${{param.ptype.cxx_type}} object = '
252 'dynamic_cast<${{param.ptype.cxx_type}}>(*i);')
253 code('if (*i && !object)')
254 code(' ret = false;')
256 code(' this->${{param.name}}.push_back(object);')
261 code(' ret = false;')
269 code('void ${member_prefix}setName(const std::string &name_)'
275 code('this->name = name_;')
280 code('const std::string &${member_prefix}getName()')
281 code('{ return this->name; }')
284 code('bool ${member_prefix}setParam(const std::string &name,')
285 code(' const std::string &value, const Flags flags)${end_of_decl}')
290 code('bool ret = true;')
293 for param
in simobj
._params
.values():
294 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
295 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
297 if not is_simobj
and not is_vector
:
298 code('} else if (name == "${{param.name}}") {')
300 param
.ptype
.cxx_ini_parse(code
,
301 'value', 'this->%s' % param
.name
, 'ret =')
304 code(' ret = false;')
312 code('bool ${member_prefix}setParamVector('
313 'const std::string &name,')
314 code(' const std::vector<std::string> &values,')
315 code(' const Flags flags)${end_of_decl}')
320 code('bool ret = true;')
323 for param
in simobj
._params
.values():
324 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
325 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
327 if not is_simobj
and is_vector
:
328 code('} else if (name == "${{param.name}}") {')
330 code('${{param.name}}.clear();')
331 code('for (auto i = values.begin(); '
332 'ret && i != values.end(); i ++)')
335 code('${{param.ptype.cxx_type}} elem;')
336 param
.ptype
.cxx_ini_parse(code
,
337 '*i', 'elem', 'ret =')
339 code(' this->${{param.name}}.push_back(elem);')
344 code(' ret = false;')
352 code('bool ${member_prefix}setPortConnectionCount('
353 'const std::string &name,')
354 code(' unsigned int count)${end_of_decl}')
359 code('bool ret = true;')
363 for port
in simobj
._ports
.values():
364 code('else if (name == "${{port.name}}")')
365 code(' this->port_${{port.name}}_connection_count = count;')
367 code(' ret = false;')
374 code('SimObject *${member_prefix}simObjectCreate()${end_of_decl}')
378 if hasattr(simobj
, 'abstract') and simobj
.abstract
:
379 code(' return NULL;')
381 code(' return this->create();')
386 code('static CxxConfigDirectoryEntry'
387 ' *${member_prefix}makeDirectoryEntry()')
388 code('{ return new DirectoryEntry; }')
394 # The metaclass for SimObject. This class controls how new classes
395 # that derive from SimObject are instantiated, and provides inherited
396 # class behavior (just like a class controls how instances of that
397 # class are instantiated, and provides inherited instance behavior).
398 class MetaSimObject(type):
399 # Attributes that can be set only at initialization time
406 'cxx_base' : (str, type(None)),
407 'cxx_extra_bases' : list,
408 'cxx_exports' : list,
409 'cxx_param_exports' : list,
410 'cxx_template_params' : list,
412 # Attributes that can be set any time
413 keywords
= { 'check' : FunctionType
}
415 # __new__ is called before __init__, and is where the statements
416 # in the body of the class definition get loaded into the class's
417 # __dict__. We intercept this to filter out parameter & port assignments
418 # and only allow "private" attributes to be passed to the base
419 # __new__ (starting with underscore).
420 def __new__(mcls
, name
, bases
, dict):
421 assert name
not in allClasses
, "SimObject %s already present" % name
423 # Copy "private" attributes, functions, and classes to the
424 # official dict. Everything else goes in _init_dict to be
425 # filtered in __init__.
429 for key
,val
in dict.items():
431 cxx_exports
.append(getattr(val
, "__pybind"))
432 except AttributeError:
435 if public_value(key
, val
):
438 # must be a param/port setting
439 value_dict
[key
] = val
440 if 'abstract' not in value_dict
:
441 value_dict
['abstract'] = False
442 if 'cxx_extra_bases' not in value_dict
:
443 value_dict
['cxx_extra_bases'] = []
444 if 'cxx_exports' not in value_dict
:
445 value_dict
['cxx_exports'] = cxx_exports
447 value_dict
['cxx_exports'] += cxx_exports
448 if 'cxx_param_exports' not in value_dict
:
449 value_dict
['cxx_param_exports'] = []
450 if 'cxx_template_params' not in value_dict
:
451 value_dict
['cxx_template_params'] = []
452 cls_dict
['_value_dict'] = value_dict
453 cls
= super(MetaSimObject
, mcls
).__new
__(mcls
, name
, bases
, cls_dict
)
454 if 'type' in value_dict
:
455 allClasses
[name
] = cls
458 # subclass initialization
459 def __init__(cls
, name
, bases
, dict):
460 # calls type.__init__()... I think that's a no-op, but leave
461 # it here just in case it's not.
462 super(MetaSimObject
, cls
).__init
__(name
, bases
, dict)
464 # initialize required attributes
466 # class-only attributes
467 cls
._params
= multidict() # param descriptions
468 cls
._ports
= multidict() # port descriptions
470 # class or instance attributes
471 cls
._values
= multidict() # param values
472 cls
._hr
_values
= multidict() # human readable param values
473 cls
._children
= multidict() # SimObject children
474 cls
._port
_refs
= multidict() # port ref objects
475 cls
._instantiated
= False # really instantiated, cloned, or subclassed
477 # We don't support multiple inheritance of sim objects. If you want
478 # to, you must fix multidict to deal with it properly. Non sim-objects
482 if isinstance(c
, MetaSimObject
):
486 "SimObjects do not support multiple inheritance")
490 # Set up general inheritance via multidicts. A subclass will
491 # inherit all its settings from the base class. The only time
492 # the following is not true is when we define the SimObject
493 # class itself (in which case the multidicts have no parent).
494 if isinstance(base
, MetaSimObject
):
496 cls
._params
.parent
= base
._params
497 cls
._ports
.parent
= base
._ports
498 cls
._values
.parent
= base
._values
499 cls
._hr
_values
.parent
= base
._hr
_values
500 cls
._children
.parent
= base
._children
501 cls
._port
_refs
.parent
= base
._port
_refs
502 # mark base as having been subclassed
503 base
._instantiated
= True
507 # default keyword values
508 if 'type' in cls
._value
_dict
:
509 if 'cxx_class' not in cls
._value
_dict
:
510 cls
._value
_dict
['cxx_class'] = cls
._value
_dict
['type']
512 cls
._value
_dict
['cxx_type'] = '%s *' % cls
._value
_dict
['cxx_class']
514 if 'cxx_header' not in cls
._value
_dict
:
517 warn("No header file specified for SimObject: %s", name
)
519 # Now process the _value_dict items. They could be defining
520 # new (or overriding existing) parameters or ports, setting
521 # class keywords (e.g., 'abstract'), or setting parameter
522 # values or port bindings. The first 3 can only be set when
523 # the class is defined, so we handle them here. The others
524 # can be set later too, so just emulate that by calling
526 for key
,val
in cls
._value
_dict
.items():
528 if isinstance(val
, ParamDesc
):
529 cls
._new
_param
(key
, val
)
532 elif isinstance(val
, Port
):
533 cls
._new
_port
(key
, val
)
535 # init-time-only keywords
536 elif key
in cls
.init_keywords
:
537 cls
._set
_keyword
(key
, val
, cls
.init_keywords
[key
])
539 # default: use normal path (ends up in __setattr__)
541 setattr(cls
, key
, val
)
543 def _set_keyword(cls
, keyword
, val
, kwtype
):
544 if not isinstance(val
, kwtype
):
545 raise TypeError('keyword %s has bad type %s (expecting %s)' % \
546 (keyword
, type(val
), kwtype
))
547 if isinstance(val
, FunctionType
):
548 val
= classmethod(val
)
549 type.__setattr
__(cls
, keyword
, val
)
551 def _new_param(cls
, name
, pdesc
):
552 # each param desc should be uniquely assigned to one variable
553 assert(not hasattr(pdesc
, 'name'))
555 cls
._params
[name
] = pdesc
556 if hasattr(pdesc
, 'default'):
557 cls
._set
_param
(name
, pdesc
.default
, pdesc
)
559 def _set_param(cls
, name
, value
, param
):
560 assert(param
.name
== name
)
563 value
= param
.convert(value
)
564 except Exception as e
:
565 msg
= "%s\nError setting param %s.%s to %s\n" % \
566 (e
, cls
.__name
__, name
, value
)
569 cls
._values
[name
] = value
570 # if param value is a SimObject, make it a child too, so that
571 # it gets cloned properly when the class is instantiated
572 if isSimObjectOrVector(value
) and not value
.has_parent():
573 cls
._add
_cls
_child
(name
, value
)
574 # update human-readable values of the param if it has a literal
575 # value and is not an object or proxy.
576 if not (isSimObjectOrVector(value
) or\
577 isinstance(value
, m5
.proxy
.BaseProxy
)):
578 cls
._hr
_values
[name
] = hr_value
580 def _add_cls_child(cls
, name
, child
):
581 # It's a little funky to have a class as a parent, but these
582 # objects should never be instantiated (only cloned, which
583 # clears the parent pointer), and this makes it clear that the
584 # object is not an orphan and can provide better error
586 child
.set_parent(cls
, name
)
587 if not isNullPointer(child
):
588 cls
._children
[name
] = child
590 def _new_port(cls
, name
, port
):
591 # each port should be uniquely assigned to one variable
592 assert(not hasattr(port
, 'name'))
594 cls
._ports
[name
] = port
596 # same as _get_port_ref, effectively, but for classes
597 def _cls_get_port_ref(cls
, attr
):
598 # Return reference that can be assigned to another port
599 # via __setattr__. There is only ever one reference
600 # object per port, but we create them lazily here.
601 ref
= cls
._port
_refs
.get(attr
)
603 ref
= cls
._ports
[attr
].makeRef(cls
)
604 cls
._port
_refs
[attr
] = ref
607 # Set attribute (called on foo.attr = value when foo is an
608 # instance of class cls).
609 def __setattr__(cls
, attr
, value
):
610 # normal processing for private attributes
611 if public_value(attr
, value
):
612 type.__setattr
__(cls
, attr
, value
)
615 if attr
in cls
.keywords
:
616 cls
._set
_keyword
(attr
, value
, cls
.keywords
[attr
])
619 if attr
in cls
._ports
:
620 cls
._cls
_get
_port
_ref
(attr
).connect(value
)
623 if isSimObjectOrSequence(value
) and cls
._instantiated
:
625 "cannot set SimObject parameter '%s' after\n" \
626 " class %s has been instantiated or subclassed" \
627 % (attr
, cls
.__name
__))
630 param
= cls
._params
.get(attr
)
632 cls
._set
_param
(attr
, value
, param
)
635 if isSimObjectOrSequence(value
):
636 # If RHS is a SimObject, it's an implicit child assignment.
637 cls
._add
_cls
_child
(attr
, coerceSimObjectOrVector(value
))
640 # no valid assignment... raise exception
641 raise AttributeError(
642 "Class %s has no parameter \'%s\'" % (cls
.__name
__, attr
))
644 def __getattr__(cls
, attr
):
645 if attr
== 'cxx_class_path':
646 return cls
.cxx_class
.split('::')
648 if attr
== 'cxx_class_name':
649 return cls
.cxx_class_path
[-1]
651 if attr
== 'cxx_namespaces':
652 return cls
.cxx_class_path
[:-1]
654 if attr
== 'pybind_class':
655 return '_COLONS_'.join(cls
.cxx_class_path
)
657 if attr
in cls
._values
:
658 return cls
._values
[attr
]
660 if attr
in cls
._children
:
661 return cls
._children
[attr
]
664 return getattr(cls
.getCCClass(), attr
)
665 except AttributeError:
666 raise AttributeError(
667 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
))
673 return getattr(m5
.internal
.params
, cls
.pybind_class
)
675 # See ParamValue.cxx_predecls for description.
676 def cxx_predecls(cls
, code
):
677 code('#include "params/$cls.hh"')
679 def pybind_predecls(cls
, code
):
680 code('#include "${{cls.cxx_header}}"')
682 def pybind_decl(cls
, code
):
683 py_class_name
= cls
.pybind_class
685 # The 'local' attribute restricts us to the params declared in
686 # the object itself, not including inherited params (which
687 # will also be inherited from the base class's param struct
688 # here). Sort the params based on their key
689 params
= map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items()))
690 ports
= cls
._ports
.local
692 code('''#include "pybind11/pybind11.h"
693 #include "pybind11/stl.h"
695 #include "params/$cls.hh"
696 #include "python/pybind11/core.hh"
697 #include "sim/init.hh"
698 #include "sim/sim_object.hh"
700 #include "${{cls.cxx_header}}"
705 param
.pybind_predecls(code
)
707 code('''namespace py = pybind11;
710 module_init(py::module &m_internal)
712 py::module m = m_internal.def_submodule("param_${cls}");
716 code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
717 'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
718 'm, "${cls}Params")')
720 code('py::class_<${cls}Params, ' \
721 'std::unique_ptr<${cls}Params, py::nodelete>>(' \
722 'm, "${cls}Params")')
725 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
726 code('.def(py::init<>())')
727 code('.def("create", &${cls}Params::create)')
729 param_exports
= cls
.cxx_param_exports
+ [
731 for k
, v
in sorted(cls
._params
.local
.items())
733 PyBindProperty("port_%s_connection_count" % port
.name
)
734 for port
in ports
.values()
736 for exp
in param_exports
:
737 exp
.export(code
, "%sParams" % cls
)
744 if 'cxx_base' in cls
._value
_dict
:
745 # If the c++ base class implied by python inheritance was
746 # overridden, use that value.
748 bases
.append(cls
.cxx_base
)
750 # If not and if there was a SimObject base, use its c++ class
751 # as this class' base.
752 bases
.append(cls
._base
.cxx_class
)
753 # Add in any extra bases that were requested.
754 bases
.extend(cls
.cxx_extra_bases
)
757 base_str
= ", ".join(bases
)
758 code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
759 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
760 'm, "${py_class_name}")')
762 code('py::class_<${{cls.cxx_class}}, ' \
763 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
764 'm, "${py_class_name}")')
766 for exp
in cls
.cxx_exports
:
767 exp
.export(code
, cls
.cxx_class
)
774 code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
775 cls
, cls
._base
.type if cls
._base
else "")
777 _warned_about_nested_templates
= False
779 # Generate the C++ declaration (.hh file) for this SimObject's
780 # param struct. Called from src/SConscript.
781 def cxx_param_decl(cls
, code
):
782 # The 'local' attribute restricts us to the params declared in
783 # the object itself, not including inherited params (which
784 # will also be inherited from the base class's param struct
785 # here). Sort the params based on their key
786 params
= map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items()))
787 ports
= cls
._ports
.local
789 ptypes
= [p
.ptype
for p
in params
]
791 print(cls
, p
, p
.ptype_str
)
795 class CxxClass(object):
796 def __init__(self
, sig
, template_params
=[]):
797 # Split the signature into its constituent parts. This could
798 # potentially be done with regular expressions, but
799 # it's simple enough to pick appart a class signature
801 parts
= sig
.split('<', 1)
805 # The signature had template arguments.
806 text
= parts
[1].rstrip(' \t\n>')
808 # Keep track of nesting to avoid splitting on ","s embedded
809 # in the arguments themselves.
814 if depth
> 0 and not \
815 self
._warned
_about
_nested
_templates
:
816 self
._warned
_about
_nested
_templates
= True
817 print('Nested template argument in cxx_class.'
818 ' This feature is largely untested and '
822 elif c
== ',' and depth
== 0:
823 t_args
.append(arg
.strip())
828 t_args
.append(arg
.strip())
829 # Split the non-template part on :: boundaries.
830 class_path
= base
.split('::')
832 # The namespaces are everything except the last part of the
834 self
.namespaces
= class_path
[:-1]
835 # And the class name is the last part.
836 self
.name
= class_path
[-1]
838 self
.template_params
= template_params
839 self
.template_arguments
= []
840 # Iterate through the template arguments and their values. This
841 # will likely break if parameter packs are used.
842 for arg
, param
in zip(t_args
, template_params
):
843 type_keys
= ('class', 'typename')
844 # If a parameter is a type, parse it recursively. Otherwise
845 # assume it's a constant, and store it verbatim.
846 if any(param
.strip().startswith(kw
) for kw
in type_keys
):
847 self
.template_arguments
.append(CxxClass(arg
))
849 self
.template_arguments
.append(arg
)
851 def declare(self
, code
):
852 # First declare any template argument types.
853 for arg
in self
.template_arguments
:
854 if isinstance(arg
, CxxClass
):
856 # Re-open the target namespace.
857 for ns
in self
.namespaces
:
858 code('namespace $ns {')
859 # If this is a class template...
860 if self
.template_params
:
861 code('template <${{", ".join(self.template_params)}}>')
862 # The actual class declaration.
863 code('class ${{self.name}};')
864 # Close the target namespaces.
865 for ns
in reversed(self
.namespaces
):
866 code('} // namespace $ns')
869 #ifndef __PARAMS__${cls}__
870 #define __PARAMS__${cls}__
875 # The base SimObject has a couple of params that get
876 # automatically set from Python without being declared through
877 # the normal Param mechanism; we slip them in here (needed
878 # predecls now, actual declarations below)
880 code('''#include <string>''')
882 cxx_class
= CxxClass(cls
._value
_dict
['cxx_class'],
883 cls
._value
_dict
['cxx_template_params'])
885 # A forward class declaration is sufficient since we are just
886 # declaring a pointer.
887 cxx_class
.declare(code
)
890 param
.cxx_predecls(code
)
891 for port
in ports
.values():
892 port
.cxx_predecls(code
)
896 code('#include "params/${{cls._base.type}}.hh"')
900 if issubclass(ptype
, Enum
):
901 code('#include "enums/${{ptype.__name__}}.hh"')
904 # now generate the actual param struct
905 code("struct ${cls}Params")
907 code(" : public ${{cls._base.type}}Params")
909 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
910 if 'type' in cls
.__dict
__:
911 code(" ${{cls.cxx_type}} create();")
917 virtual ~SimObjectParams() {}
924 for port
in ports
.values():
931 code('#endif // __PARAMS__${cls}__')
934 # Generate the C++ declaration/definition files for this SimObject's
935 # param struct to allow C++ initialisation
936 def cxx_config_param_file(cls
, code
, is_header
):
937 createCxxConfigDirectoryEntryFile(code
, cls
.__name
__, cls
, is_header
)
940 # This *temporary* definition is required to support calls from the
941 # SimObject class definition to the MetaSimObject methods (in
942 # particular _set_param, which gets called for parameters with default
943 # values defined on the SimObject class itself). It will get
944 # overridden by the permanent definition (which requires that
945 # SimObject be defined) lower in this file.
946 def isSimObjectOrVector(value
):
949 def cxxMethod(*args
, **kwargs
):
950 """Decorator to export C++ functions to Python"""
954 override
= kwargs
.get("override", False)
955 cxx_name
= kwargs
.get("cxx_name", name
)
956 return_value_policy
= kwargs
.get("return_value_policy", None)
957 static
= kwargs
.get("static", False)
959 args
, varargs
, keywords
, defaults
= inspect
.getargspec(func
)
960 if varargs
or keywords
:
961 raise ValueError("Wrapped methods must not contain variable " \
964 # Create tuples of (argument, default)
966 args
= args
[:-len(defaults
)] + \
967 list(zip(args
[-len(defaults
):], defaults
))
968 # Don't include self in the argument list to PyBind
973 def cxx_call(self
, *args
, **kwargs
):
974 ccobj
= self
.getCCClass() if static
else self
.getCCObject()
975 return getattr(ccobj
, name
)(*args
, **kwargs
)
978 def py_call(self
, *args
, **kwargs
):
979 return func(self
, *args
, **kwargs
)
981 f
= py_call
if override
else cxx_call
982 f
.__pybind
= PyBindMethod(name
, cxx_name
=cxx_name
, args
=args
,
983 return_value_policy
=return_value_policy
,
990 elif len(args
) == 1 and len(kwargs
) == 0:
991 return decorate(*args
)
993 raise TypeError("One argument and no kwargs, or only kwargs expected")
995 # This class holds information about each simobject parameter
996 # that should be displayed on the command line for use in the
997 # configuration system.
998 class ParamInfo(object):
999 def __init__(self
, type, desc
, type_str
, example
, default_val
, access_str
):
1002 self
.type_str
= type_str
1003 self
.example_str
= example
1004 self
.default_val
= default_val
1005 # The string representation used to access this param through python.
1006 # The method to access this parameter presented on the command line may
1007 # be different, so this needs to be stored for later use.
1008 self
.access_str
= access_str
1011 # Make it so we can only set attributes at initialization time
1012 # and effectively make this a const object.
1013 def __setattr__(self
, name
, value
):
1014 if not "created" in self
.__dict
__:
1015 self
.__dict
__[name
] = value
1017 class SimObjectCliWrapperException(Exception):
1018 def __init__(self
, message
):
1019 super(Exception, self
).__init
__(message
)
1021 class SimObjectCliWrapper(object):
1023 Wrapper class to restrict operations that may be done
1024 from the command line on SimObjects.
1026 Only parameters may be set, and only children may be accessed.
1028 Slicing allows for multiple simultaneous assignment of items in
1032 def __init__(self
, sim_objects
):
1033 self
.__dict
__['_sim_objects'] = list(sim_objects
)
1035 def __getattr__(self
, key
):
1036 return SimObjectCliWrapper(sim_object
._children
[key
]
1037 for sim_object
in self
._sim
_objects
)
1039 def __setattr__(self
, key
, val
):
1040 for sim_object
in self
._sim
_objects
:
1041 if key
in sim_object
._params
:
1042 if sim_object
._params
[key
].isCmdLineSettable():
1043 setattr(sim_object
, key
, val
)
1045 raise SimObjectCliWrapperException(
1046 'tried to set or unsettable' \
1047 'object parameter: ' + key
)
1049 raise SimObjectCliWrapperException(
1050 'tried to set or access non-existent' \
1051 'object parameter: ' + key
)
1053 def __getitem__(self
, idx
):
1055 Extends the list() semantics to also allow tuples,
1056 for example object[1, 3] selects items 1 and 3.
1059 if isinstance(idx
, tuple):
1061 out
.extend(self
[t
]._sim
_objects
)
1063 if isinstance(idx
, int):
1064 _range
= range(idx
, idx
+ 1)
1065 elif not isinstance(idx
, slice):
1066 raise SimObjectCliWrapperException( \
1067 'invalid index type: ' + repr(idx
))
1068 for sim_object
in self
._sim
_objects
:
1069 if isinstance(idx
, slice):
1070 _range
= range(*idx
.indices(len(sim_object
)))
1071 out
.extend(sim_object
[i
] for i
in _range
)
1072 return SimObjectCliWrapper(out
)
1074 # The SimObject class is the root of the special hierarchy. Most of
1075 # the code in this class deals with the configuration hierarchy itself
1076 # (parent/child node relationships).
1077 @add_metaclass(MetaSimObject
)
1078 class SimObject(object):
1079 # Specify metaclass. Any class inheriting from SimObject will
1080 # get this metaclass.
1084 cxx_header
= "sim/sim_object.hh"
1085 cxx_extra_bases
= [ "Drainable", "Serializable", "Stats::Group" ]
1086 eventq_index
= Param
.UInt32(Parent
.eventq_index
, "Event Queue Index")
1089 PyBindMethod("init"),
1090 PyBindMethod("initState"),
1091 PyBindMethod("memInvalidate"),
1092 PyBindMethod("memWriteback"),
1093 PyBindMethod("regProbePoints"),
1094 PyBindMethod("regProbeListeners"),
1095 PyBindMethod("startup"),
1098 cxx_param_exports
= [
1099 PyBindProperty("name"),
1103 def loadState(self
, cp
):
1104 """Load SimObject state from a checkpoint"""
1107 # Returns a dict of all the option strings that can be
1108 # generated as command line options for this simobject instance
1109 # by tracing all reachable params in the top level instance and
1110 # any children it contains.
1111 def enumerateParams(self
, flags_dict
= {},
1112 cmd_line_str
= "", access_str
= ""):
1113 if hasattr(self
, "_paramEnumed"):
1114 print("Cycle detected enumerating params")
1116 self
._paramEnumed
= True
1117 # Scan the children first to pick up all the objects in this SimObj
1118 for keys
in self
._children
:
1119 child
= self
._children
[keys
]
1120 next_cmdline_str
= cmd_line_str
+ keys
1121 next_access_str
= access_str
+ keys
1122 if not isSimObjectVector(child
):
1123 next_cmdline_str
= next_cmdline_str
+ "."
1124 next_access_str
= next_access_str
+ "."
1125 flags_dict
= child
.enumerateParams(flags_dict
,
1129 # Go through the simple params in the simobject in this level
1130 # of the simobject hierarchy and save information about the
1131 # parameter to be used for generating and processing command line
1132 # options to the simulator to set these parameters.
1133 for keys
,values
in self
._params
.items():
1134 if values
.isCmdLineSettable():
1136 ex_str
= values
.example_str()
1138 if isinstance(values
, VectorParamDesc
):
1139 type_str
= 'Vector_%s' % values
.ptype_str
1142 type_str
= '%s' % values
.ptype_str
1143 ptype
= values
.ptype
1145 if keys
in self
._hr
_values\
1146 and keys
in self
._values\
1147 and not isinstance(self
._values
[keys
],
1148 m5
.proxy
.BaseProxy
):
1149 cmd_str
= cmd_line_str
+ keys
1150 acc_str
= access_str
+ keys
1151 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1152 self
._params
[keys
].desc
, type_str
, ex_str
,
1153 values
.pretty_print(self
._hr
_values
[keys
]),
1155 elif not keys
in self
._hr
_values\
1156 and not keys
in self
._values
:
1158 cmd_str
= cmd_line_str
+ keys
1159 acc_str
= access_str
+ keys
1160 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1161 self
._params
[keys
].desc
,
1162 type_str
, ex_str
, '', acc_str
)
1166 # Initialize new instance. For objects with SimObject-valued
1167 # children, we need to recursively clone the classes represented
1168 # by those param values as well in a consistent "deep copy"-style
1169 # fashion. That is, we want to make sure that each instance is
1170 # cloned only once, and that if there are multiple references to
1171 # the same original object, we end up with the corresponding
1172 # cloned references all pointing to the same cloned instance.
1173 def __init__(self
, **kwargs
):
1174 ancestor
= kwargs
.get('_ancestor')
1175 memo_dict
= kwargs
.get('_memo')
1176 if memo_dict
is None:
1177 # prepare to memoize any recursively instantiated objects
1180 # memoize me now to avoid problems with recursive calls
1181 memo_dict
[ancestor
] = self
1184 ancestor
= self
.__class
__
1185 ancestor
._instantiated
= True
1187 # initialize required attributes
1190 self
._ccObject
= None # pointer to C++ object
1191 self
._ccParams
= None
1192 self
._instantiated
= False # really "cloned"
1194 # Clone children specified at class level. No need for a
1195 # multidict here since we will be cloning everything.
1196 # Do children before parameter values so that children that
1197 # are also param values get cloned properly.
1199 for key
,val
in ancestor
._children
.items():
1200 self
.add_child(key
, val(_memo
=memo_dict
))
1202 # Inherit parameter values from class using multidict so
1203 # individual value settings can be overridden but we still
1204 # inherit late changes to non-overridden class values.
1205 self
._values
= multidict(ancestor
._values
)
1206 self
._hr
_values
= multidict(ancestor
._hr
_values
)
1207 # clone SimObject-valued parameters
1208 for key
,val
in ancestor
._values
.items():
1209 val
= tryAsSimObjectOrVector(val
)
1211 self
._values
[key
] = val(_memo
=memo_dict
)
1213 # clone port references. no need to use a multidict here
1214 # since we will be creating new references for all ports.
1215 self
._port
_refs
= {}
1216 for key
,val
in ancestor
._port
_refs
.items():
1217 self
._port
_refs
[key
] = val
.clone(self
, memo_dict
)
1218 # apply attribute assignments from keyword args, if any
1219 for key
,val
in kwargs
.items():
1220 setattr(self
, key
, val
)
1222 # "Clone" the current instance by creating another instance of
1223 # this instance's class, but that inherits its parameter values
1224 # and port mappings from the current instance. If we're in a
1225 # "deep copy" recursive clone, check the _memo dict to see if
1226 # we've already cloned this instance.
1227 def __call__(self
, **kwargs
):
1228 memo_dict
= kwargs
.get('_memo')
1229 if memo_dict
is None:
1230 # no memo_dict: must be top-level clone operation.
1231 # this is only allowed at the root of a hierarchy
1233 raise RuntimeError("attempt to clone object %s " \
1234 "not at the root of a tree (parent = %s)" \
1235 % (self
, self
._parent
))
1236 # create a new dict and use that.
1238 kwargs
['_memo'] = memo_dict
1239 elif self
in memo_dict
:
1240 # clone already done & memoized
1241 return memo_dict
[self
]
1242 return self
.__class
__(_ancestor
= self
, **kwargs
)
1244 def _get_port_ref(self
, attr
):
1245 # Return reference that can be assigned to another port
1246 # via __setattr__. There is only ever one reference
1247 # object per port, but we create them lazily here.
1248 ref
= self
._port
_refs
.get(attr
)
1250 ref
= self
._ports
[attr
].makeRef(self
)
1251 self
._port
_refs
[attr
] = ref
1254 def __getattr__(self
, attr
):
1255 if attr
in self
._ports
:
1256 return self
._get
_port
_ref
(attr
)
1258 if attr
in self
._values
:
1259 return self
._values
[attr
]
1261 if attr
in self
._children
:
1262 return self
._children
[attr
]
1264 # If the attribute exists on the C++ object, transparently
1265 # forward the reference there. This is typically used for
1266 # methods exported to Python (e.g., init(), and startup())
1267 if self
._ccObject
and hasattr(self
._ccObject
, attr
):
1268 return getattr(self
._ccObject
, attr
)
1270 err_string
= "object '%s' has no attribute '%s'" \
1271 % (self
.__class
__.__name
__, attr
)
1273 if not self
._ccObject
:
1274 err_string
+= "\n (C++ object is not yet constructed," \
1275 " so wrapped C++ methods are unavailable.)"
1277 raise AttributeError(err_string
)
1279 # Set attribute (called on foo.attr = value when foo is an
1280 # instance of class cls).
1281 def __setattr__(self
, attr
, value
):
1282 # normal processing for private attributes
1283 if attr
.startswith('_'):
1284 object.__setattr
__(self
, attr
, value
)
1287 if attr
in self
._ports
:
1288 # set up port connection
1289 self
._get
_port
_ref
(attr
).connect(value
)
1292 param
= self
._params
.get(attr
)
1296 value
= param
.convert(value
)
1297 except Exception as e
:
1298 msg
= "%s\nError setting param %s.%s to %s\n" % \
1299 (e
, self
.__class
__.__name
__, attr
, value
)
1302 self
._values
[attr
] = value
1303 # implicitly parent unparented objects assigned as params
1304 if isSimObjectOrVector(value
) and not value
.has_parent():
1305 self
.add_child(attr
, value
)
1306 # set the human-readable value dict if this is a param
1307 # with a literal value and is not being set as an object
1309 if not (isSimObjectOrVector(value
) or\
1310 isinstance(value
, m5
.proxy
.BaseProxy
)):
1311 self
._hr
_values
[attr
] = hr_value
1315 # if RHS is a SimObject, it's an implicit child assignment
1316 if isSimObjectOrSequence(value
):
1317 self
.add_child(attr
, value
)
1320 # no valid assignment... raise exception
1321 raise AttributeError("Class %s has no parameter %s" \
1322 % (self
.__class
__.__name
__, attr
))
1325 # this hack allows tacking a '[0]' onto parameters that may or may
1326 # not be vectors, and always getting the first element (e.g. cpus)
1327 def __getitem__(self
, key
):
1330 raise IndexError("Non-zero index '%s' to SimObject" % key
)
1332 # this hack allows us to iterate over a SimObject that may
1333 # not be a vector, so we can call a loop over it and get just one
1338 # Also implemented by SimObjectVector
1339 def clear_parent(self
, old_parent
):
1340 assert self
._parent
is old_parent
1343 # Also implemented by SimObjectVector
1344 def set_parent(self
, parent
, name
):
1345 self
._parent
= parent
1348 # Return parent object of this SimObject, not implemented by
1349 # SimObjectVector because the elements in a SimObjectVector may not share
1351 def get_parent(self
):
1354 # Also implemented by SimObjectVector
1358 # Also implemented by SimObjectVector
1359 def has_parent(self
):
1360 return self
._parent
is not None
1362 # clear out child with given name. This code is not likely to be exercised.
1363 # See comment in add_child.
1364 def clear_child(self
, name
):
1365 child
= self
._children
[name
]
1366 child
.clear_parent(self
)
1367 del self
._children
[name
]
1369 # Add a new child to this object.
1370 def add_child(self
, name
, child
):
1371 child
= coerceSimObjectOrVector(child
)
1372 if child
.has_parent():
1373 warn("add_child('%s'): child '%s' already has parent", name
,
1375 if name
in self
._children
:
1376 # This code path had an undiscovered bug that would make it fail
1377 # at runtime. It had been here for a long time and was only
1378 # exposed by a buggy script. Changes here will probably not be
1379 # exercised without specialized testing.
1380 self
.clear_child(name
)
1381 child
.set_parent(self
, name
)
1382 if not isNullPointer(child
):
1383 self
._children
[name
] = child
1385 # Take SimObject-valued parameters that haven't been explicitly
1386 # assigned as children and make them children of the object that
1387 # they were assigned to as a parameter value. This guarantees
1388 # that when we instantiate all the parameter objects we're still
1389 # inside the configuration hierarchy.
1390 def adoptOrphanParams(self
):
1391 for key
,val
in self
._values
.items():
1392 if not isSimObjectVector(val
) and isSimObjectSequence(val
):
1393 # need to convert raw SimObject sequences to
1394 # SimObjectVector class so we can call has_parent()
1395 val
= SimObjectVector(val
)
1396 self
._values
[key
] = val
1397 if isSimObjectOrVector(val
) and not val
.has_parent():
1398 warn("%s adopting orphan SimObject param '%s'", self
, key
)
1399 self
.add_child(key
, val
)
1402 if not self
._parent
:
1403 return '<orphan %s>' % self
.__class
__
1404 elif isinstance(self
._parent
, MetaSimObject
):
1405 return str(self
.__class
__)
1407 ppath
= self
._parent
.path()
1410 return ppath
+ "." + self
._name
1412 def path_list(self
):
1414 return self
._parent
.path_list() + [ self
._name
, ]
1416 # Don't include the root node
1422 def config_value(self
):
1428 def find_any(self
, ptype
):
1429 if isinstance(self
, ptype
):
1433 for child
in self
._children
.values():
1435 if hasattr(child
, '_visited'):
1436 visited
= getattr(child
, '_visited')
1438 if isinstance(child
, ptype
) and not visited
:
1439 if found_obj
!= None and child
!= found_obj
:
1440 raise AttributeError(
1441 'parent.any matched more than one: %s %s' % \
1442 (found_obj
.path
, child
.path
))
1444 # search param space
1445 for pname
,pdesc
in self
._params
.items():
1446 if issubclass(pdesc
.ptype
, ptype
):
1447 match_obj
= self
._values
[pname
]
1448 if found_obj
!= None and found_obj
!= match_obj
:
1449 raise AttributeError(
1450 'parent.any matched more than one: %s and %s' % \
1451 (found_obj
.path
, match_obj
.path
))
1452 found_obj
= match_obj
1453 return found_obj
, found_obj
!= None
1455 def find_all(self
, ptype
):
1458 for child
in self
._children
.values():
1459 # a child could be a list, so ensure we visit each item
1460 if isinstance(child
, list):
1465 for child
in children
:
1466 if isinstance(child
, ptype
) and not isproxy(child
) and \
1467 not isNullPointer(child
):
1469 if isSimObject(child
):
1470 # also add results from the child itself
1471 child_all
, done
= child
.find_all(ptype
)
1472 all
.update(dict(zip(child_all
, [done
] * len(child_all
))))
1473 # search param space
1474 for pname
,pdesc
in self
._params
.items():
1475 if issubclass(pdesc
.ptype
, ptype
):
1476 match_obj
= self
._values
[pname
]
1477 if not isproxy(match_obj
) and not isNullPointer(match_obj
):
1478 all
[match_obj
] = True
1479 # Also make sure to sort the keys based on the objects' path to
1480 # ensure that the order is the same on all hosts
1481 return sorted(all
.keys(), key
= lambda o
: o
.path()), True
1483 def unproxy(self
, base
):
1486 def unproxyParams(self
):
1487 for param
in self
._params
.keys():
1488 value
= self
._values
.get(param
)
1489 if value
!= None and isproxy(value
):
1491 value
= value
.unproxy(self
)
1493 print("Error in unproxying param '%s' of %s" %
1494 (param
, self
.path()))
1496 setattr(self
, param
, value
)
1498 # Unproxy ports in sorted order so that 'append' operations on
1499 # vector ports are done in a deterministic fashion.
1500 port_names
= list(self
._ports
.keys())
1502 for port_name
in port_names
:
1503 port
= self
._port
_refs
.get(port_name
)
1507 def print_ini(self
, ini_file
):
1508 print('[' + self
.path() + ']', file=ini_file
) # .ini section header
1510 instanceDict
[self
.path()] = self
1512 if hasattr(self
, 'type'):
1513 print('type=%s' % self
.type, file=ini_file
)
1515 if len(self
._children
.keys()):
1516 print('children=%s' %
1517 ' '.join(self
._children
[n
].get_name()
1518 for n
in sorted(self
._children
.keys())),
1521 for param
in sorted(self
._params
.keys()):
1522 value
= self
._values
.get(param
)
1524 print('%s=%s' % (param
, self
._values
[param
].ini_str()),
1527 for port_name
in sorted(self
._ports
.keys()):
1528 port
= self
._port
_refs
.get(port_name
, None)
1530 print('%s=%s' % (port_name
, port
.ini_str()), file=ini_file
)
1532 print(file=ini_file
) # blank line between objects
1534 # generate a tree of dictionaries expressing all the parameters in the
1535 # instantiated system for use by scripts that want to do power, thermal
1536 # visualization, and other similar tasks
1537 def get_config_as_dict(self
):
1539 if hasattr(self
, 'type'):
1541 if hasattr(self
, 'cxx_class'):
1542 d
.cxx_class
= self
.cxx_class
1543 # Add the name and path of this object to be able to link to
1545 d
.name
= self
.get_name()
1546 d
.path
= self
.path()
1548 for param
in sorted(self
._params
.keys()):
1549 value
= self
._values
.get(param
)
1551 d
[param
] = value
.config_value()
1553 for n
in sorted(self
._children
.keys()):
1554 child
= self
._children
[n
]
1555 # Use the name of the attribute (and not get_name()) as
1556 # the key in the JSON dictionary to capture the hierarchy
1557 # in the Python code that assembled this system
1558 d
[n
] = child
.get_config_as_dict()
1560 for port_name
in sorted(self
._ports
.keys()):
1561 port
= self
._port
_refs
.get(port_name
, None)
1563 # Represent each port with a dictionary containing the
1564 # prominent attributes
1565 d
[port_name
] = port
.get_config_as_dict()
1569 def getCCParams(self
):
1571 return self
._ccParams
1573 cc_params_struct
= getattr(m5
.internal
.params
, '%sParams' % self
.type)
1574 cc_params
= cc_params_struct()
1575 cc_params
.name
= str(self
)
1577 param_names
= list(self
._params
.keys())
1579 for param
in param_names
:
1580 value
= self
._values
.get(param
)
1582 fatal("%s.%s without default or user set value",
1585 value
= value
.getValue()
1586 if isinstance(self
._params
[param
], VectorParamDesc
):
1587 assert isinstance(value
, list)
1588 vec
= getattr(cc_params
, param
)
1590 # Some types are exposed as opaque types. They support
1591 # the append operation unlike the automatically
1593 if isinstance(vec
, list):
1594 setattr(cc_params
, param
, list(value
))
1597 getattr(cc_params
, param
).append(v
)
1599 setattr(cc_params
, param
, value
)
1601 port_names
= list(self
._ports
.keys())
1603 for port_name
in port_names
:
1604 port
= self
._port
_refs
.get(port_name
, None)
1606 port_count
= len(port
)
1609 setattr(cc_params
, 'port_' + port_name
+ '_connection_count',
1611 self
._ccParams
= cc_params
1612 return self
._ccParams
1614 # Get C++ object corresponding to this object, calling C++ if
1615 # necessary to construct it. Does *not* recursively create
1617 def getCCObject(self
):
1618 if not self
._ccObject
:
1619 # Make sure this object is in the configuration hierarchy
1620 if not self
._parent
and not isRoot(self
):
1621 raise RuntimeError("Attempt to instantiate orphan node")
1622 # Cycles in the configuration hierarchy are not supported. This
1623 # will catch the resulting recursion and stop.
1625 if not self
.abstract
:
1626 params
= self
.getCCParams()
1627 self
._ccObject
= params
.create()
1628 elif self
._ccObject
== -1:
1629 raise RuntimeError("%s: Cycle found in configuration hierarchy." \
1631 return self
._ccObject
1633 def descendants(self
):
1635 # The order of the dict is implementation dependent, so sort
1636 # it based on the key (name) to ensure the order is the same
1638 for (name
, child
) in sorted(self
._children
.items()):
1639 for obj
in child
.descendants():
1642 # Call C++ to create C++ object corresponding to this object
1643 def createCCObject(self
):
1645 self
.getCCObject() # force creation
1648 return self
.getCCObject()
1650 @cxxMethod(return_value_policy
="reference")
1651 def getPort(self
, if_name
, idx
):
1654 # Create C++ port connections corresponding to the connections in
1656 def connectPorts(self
):
1657 # Sort the ports based on their attribute name to ensure the
1658 # order is the same on all hosts
1659 for (attr
, portRef
) in sorted(self
._port
_refs
.items()):
1662 # Default function for generating the device structure.
1663 # Can be overloaded by the inheriting class
1664 def generateDeviceTree(self
, state
):
1665 return # return without yielding anything
1666 yield # make this function a (null) generator
1668 def recurseDeviceTree(self
, state
):
1669 for child
in self
._children
.values():
1670 for item
in child
: # For looping over SimObjectVectors
1671 for dt
in item
.generateDeviceTree(state
):
1674 # On a separate method otherwise certain buggy Python versions
1675 # would fail with: SyntaxError: unqualified exec is not allowed
1676 # in function 'apply_config'
1677 def _apply_config_get_dict(self
):
1679 child_name
: SimObjectCliWrapper(
1680 iter(self
._children
[child_name
]))
1681 for child_name
in self
._children
1684 def apply_config(self
, params
):
1686 exec a list of Python code strings contained in params.
1688 The only exposed globals to those strings are the child
1689 SimObjects of this node.
1691 This function is intended to allow users to modify SimObject
1692 parameters from the command line with Python statements.
1694 d
= self
._apply
_config
_get
_dict
()
1695 for param
in params
:
1698 # Function to provide to C++ so it can look up instances based on paths
1699 def resolveSimObject(name
):
1700 obj
= instanceDict
[name
]
1701 return obj
.getCCObject()
1703 def isSimObject(value
):
1704 return isinstance(value
, SimObject
)
1706 def isSimObjectClass(value
):
1707 return issubclass(value
, SimObject
)
1709 def isSimObjectVector(value
):
1710 return isinstance(value
, SimObjectVector
)
1712 def isSimObjectSequence(value
):
1713 if not isinstance(value
, (list, tuple)) or len(value
) == 0:
1717 if not isNullPointer(val
) and not isSimObject(val
):
1722 def isSimObjectOrSequence(value
):
1723 return isSimObject(value
) or isSimObjectSequence(value
)
1726 from m5
.objects
import Root
1727 return obj
and obj
is Root
.getInstance()
1729 def isSimObjectOrVector(value
):
1730 return isSimObject(value
) or isSimObjectVector(value
)
1732 def tryAsSimObjectOrVector(value
):
1733 if isSimObjectOrVector(value
):
1735 if isSimObjectSequence(value
):
1736 return SimObjectVector(value
)
1739 def coerceSimObjectOrVector(value
):
1740 value
= tryAsSimObjectOrVector(value
)
1742 raise TypeError("SimObject or SimObjectVector expected")
1745 baseClasses
= allClasses
.copy()
1746 baseInstances
= instanceDict
.copy()
1749 global allClasses
, instanceDict
, noCxxHeader
1751 allClasses
= baseClasses
.copy()
1752 instanceDict
= baseInstances
.copy()
1755 # __all__ defines the list of symbols that get exported when
1756 # 'from config import *' is invoked. Try to keep this reasonably
1757 # short to avoid polluting other namespaces.