1 # Copyright (c) 2017-2018 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 # Authors: Steve Reinhardt
46 from __future__
import print_function
47 from __future__
import absolute_import
53 from types
import FunctionType
, MethodType
, ModuleType
54 from functools
import wraps
59 from m5
.util
.pybind
import *
60 # Use the pyfdt and not the helper class, because the fdthelper
61 # relies on the SimObject definition
62 from m5
.ext
.pyfdt
import pyfdt
64 # Have to import params up top since Param is referenced on initial
65 # load (when SimObject class references Param to create a class
66 # variable, the 'name' param)...
67 from m5
.params
import *
68 # There are a few things we need that aren't in params.__all__ since
69 # normal users don't need them
70 from m5
.params
import ParamDesc
, VectorParamDesc
, \
71 isNullPointer
, SimObjectVector
, Port
73 from m5
.proxy
import *
74 from m5
.proxy
import isproxy
76 #####################################################################
78 # M5 Python Configuration Utility
80 # The basic idea is to write simple Python programs that build Python
81 # objects corresponding to M5 SimObjects for the desired simulation
82 # configuration. For now, the Python emits a .ini file that can be
83 # parsed by M5. In the future, some tighter integration between M5
84 # and the Python interpreter may allow bypassing the .ini file.
86 # Each SimObject class in M5 is represented by a Python class with the
87 # same name. The Python inheritance tree mirrors the M5 C++ tree
88 # (e.g., SimpleCPU derives from BaseCPU in both cases, and all
89 # SimObjects inherit from a single SimObject base class). To specify
90 # an instance of an M5 SimObject in a configuration, the user simply
91 # instantiates the corresponding Python object. The parameters for
92 # that SimObject are given by assigning to attributes of the Python
93 # object, either using keyword assignment in the constructor or in
94 # separate assignment statements. For example:
96 # cache = BaseCache(size='64KB')
97 # cache.hit_latency = 3
100 # The magic lies in the mapping of the Python attributes for SimObject
101 # classes to the actual SimObject parameter specifications. This
102 # allows parameter validity checking in the Python code. Continuing
103 # the example above, the statements "cache.blurfl=3" or
104 # "cache.assoc='hello'" would both result in runtime errors in Python,
105 # since the BaseCache object has no 'blurfl' parameter and the 'assoc'
106 # parameter requires an integer, respectively. This magic is done
107 # primarily by overriding the special __setattr__ method that controls
108 # assignment to object attributes.
110 # Once a set of Python objects have been instantiated in a hierarchy,
111 # calling 'instantiate(obj)' (where obj is the root of the hierarchy)
112 # will generate a .ini file.
114 #####################################################################
116 # list of all SimObject classes
119 # dict to look up SimObjects based on path
122 # Did any of the SimObjects lack a header file?
125 def public_value(key
, value
):
126 return key
.startswith('_') or \
127 isinstance(value
, (FunctionType
, MethodType
, ModuleType
,
130 def createCxxConfigDirectoryEntryFile(code
, name
, simobj
, is_header
):
131 entry_class
= 'CxxConfigDirectoryEntry_%s' % name
132 param_class
= '%sCxxConfigParams' % name
134 code('#include "params/%s.hh"' % name
)
137 for param
in simobj
._params
.values():
138 if isSimObjectClass(param
.ptype
):
139 code('#include "%s"' % param
.ptype
._value
_dict
['cxx_header'])
140 code('#include "params/%s.hh"' % param
.ptype
.__name
__)
142 param
.ptype
.cxx_ini_predecls(code
)
147 code('#include "sim/cxx_config.hh"')
149 code('class ${param_class} : public CxxConfigParams,'
150 ' public ${name}Params')
154 code('class DirectoryEntry : public CxxConfigDirectoryEntry')
158 code('DirectoryEntry();');
160 code('CxxConfigParams *makeParamsObject() const')
161 code('{ return new ${param_class}; }')
169 member_prefix
= '%s::' % param_class
171 code('#include "%s"' % simobj
._value
_dict
['cxx_header'])
172 code('#include "base/str.hh"')
173 code('#include "cxx_config/${name}.hh"')
176 code('${member_prefix}DirectoryEntry::DirectoryEntry()');
180 return 'true' if b
else 'false'
183 for param
in simobj
._params
.values():
184 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
185 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
187 code('parameters["%s"] = new ParamDesc("%s", %s, %s);' %
188 (param
.name
, param
.name
, cxx_bool(is_vector
),
189 cxx_bool(is_simobj
)));
191 for port
in simobj
._ports
.values():
192 is_vector
= isinstance(port
, m5
.params
.VectorPort
)
193 is_master
= port
.role
== 'MASTER'
195 code('ports["%s"] = new PortDesc("%s", %s, %s);' %
196 (port
.name
, port
.name
, cxx_bool(is_vector
),
197 cxx_bool(is_master
)))
203 code('bool ${member_prefix}setSimObject(const std::string &name,')
204 code(' SimObject *simObject)${end_of_decl}')
209 code('bool ret = true;')
212 for param
in simobj
._params
.values():
213 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
214 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
216 if is_simobj
and not is_vector
:
217 code('} else if (name == "${{param.name}}") {')
219 code('this->${{param.name}} = '
220 'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);')
221 code('if (simObject && !this->${{param.name}})')
222 code(' ret = false;')
225 code(' ret = false;')
233 code('bool ${member_prefix}setSimObjectVector('
234 'const std::string &name,')
235 code(' const std::vector<SimObject *> &simObjects)${end_of_decl}')
240 code('bool ret = true;')
243 for param
in simobj
._params
.values():
244 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
245 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
247 if is_simobj
and is_vector
:
248 code('} else if (name == "${{param.name}}") {')
250 code('this->${{param.name}}.clear();')
251 code('for (auto i = simObjects.begin(); '
252 'ret && i != simObjects.end(); i ++)')
255 code('${{param.ptype.cxx_type}} object = '
256 'dynamic_cast<${{param.ptype.cxx_type}}>(*i);')
257 code('if (*i && !object)')
258 code(' ret = false;')
260 code(' this->${{param.name}}.push_back(object);')
265 code(' ret = false;')
273 code('void ${member_prefix}setName(const std::string &name_)'
279 code('this->name = name_;')
284 code('const std::string &${member_prefix}getName()')
285 code('{ return this->name; }')
288 code('bool ${member_prefix}setParam(const std::string &name,')
289 code(' const std::string &value, const Flags flags)${end_of_decl}')
294 code('bool ret = true;')
297 for param
in simobj
._params
.values():
298 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
299 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
301 if not is_simobj
and not is_vector
:
302 code('} else if (name == "${{param.name}}") {')
304 param
.ptype
.cxx_ini_parse(code
,
305 'value', 'this->%s' % param
.name
, 'ret =')
308 code(' ret = false;')
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}')
324 code('bool ret = true;')
327 for param
in simobj
._params
.values():
328 is_vector
= isinstance(param
, m5
.params
.VectorParamDesc
)
329 is_simobj
= issubclass(param
.ptype
, m5
.SimObject
.SimObject
)
331 if not is_simobj
and is_vector
:
332 code('} else if (name == "${{param.name}}") {')
334 code('${{param.name}}.clear();')
335 code('for (auto i = values.begin(); '
336 'ret && i != values.end(); i ++)')
339 code('${{param.ptype.cxx_type}} elem;')
340 param
.ptype
.cxx_ini_parse(code
,
341 '*i', 'elem', 'ret =')
343 code(' this->${{param.name}}.push_back(elem);')
348 code(' ret = false;')
356 code('bool ${member_prefix}setPortConnectionCount('
357 'const std::string &name,')
358 code(' unsigned int count)${end_of_decl}')
363 code('bool ret = true;')
367 for port
in simobj
._ports
.values():
368 code('else if (name == "${{port.name}}")')
369 code(' this->port_${{port.name}}_connection_count = count;')
371 code(' ret = false;')
378 code('SimObject *${member_prefix}simObjectCreate()${end_of_decl}')
382 if hasattr(simobj
, 'abstract') and simobj
.abstract
:
383 code(' return NULL;')
385 code(' return this->create();')
390 code('static CxxConfigDirectoryEntry'
391 ' *${member_prefix}makeDirectoryEntry()')
392 code('{ return new DirectoryEntry; }')
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).
402 class MetaSimObject(type):
403 # Attributes that can be set only at initialization time
410 'cxx_base' : (str, type(None)),
411 'cxx_extra_bases' : list,
412 'cxx_exports' : list,
413 'cxx_param_exports' : list,
414 'cxx_template_params' : list,
416 # Attributes that can be set any time
417 keywords
= { 'check' : FunctionType
}
419 # __new__ is called before __init__, and is where the statements
420 # in the body of the class definition get loaded into the class's
421 # __dict__. We intercept this to filter out parameter & port assignments
422 # and only allow "private" attributes to be passed to the base
423 # __new__ (starting with underscore).
424 def __new__(mcls
, name
, bases
, dict):
425 assert name
not in allClasses
, "SimObject %s already present" % name
427 # Copy "private" attributes, functions, and classes to the
428 # official dict. Everything else goes in _init_dict to be
429 # filtered in __init__.
433 for key
,val
in dict.items():
435 cxx_exports
.append(getattr(val
, "__pybind"))
436 except AttributeError:
439 if public_value(key
, val
):
442 # must be a param/port setting
443 value_dict
[key
] = val
444 if 'abstract' not in value_dict
:
445 value_dict
['abstract'] = False
446 if 'cxx_extra_bases' not in value_dict
:
447 value_dict
['cxx_extra_bases'] = []
448 if 'cxx_exports' not in value_dict
:
449 value_dict
['cxx_exports'] = cxx_exports
451 value_dict
['cxx_exports'] += cxx_exports
452 if 'cxx_param_exports' not in value_dict
:
453 value_dict
['cxx_param_exports'] = []
454 if 'cxx_template_params' not in value_dict
:
455 value_dict
['cxx_template_params'] = []
456 cls_dict
['_value_dict'] = value_dict
457 cls
= super(MetaSimObject
, mcls
).__new
__(mcls
, name
, bases
, cls_dict
)
458 if 'type' in value_dict
:
459 allClasses
[name
] = cls
462 # subclass initialization
463 def __init__(cls
, name
, bases
, dict):
464 # calls type.__init__()... I think that's a no-op, but leave
465 # it here just in case it's not.
466 super(MetaSimObject
, cls
).__init
__(name
, bases
, dict)
468 # initialize required attributes
470 # class-only attributes
471 cls
._params
= multidict() # param descriptions
472 cls
._ports
= multidict() # port descriptions
474 # class or instance attributes
475 cls
._values
= multidict() # param values
476 cls
._hr
_values
= multidict() # human readable param values
477 cls
._children
= multidict() # SimObject children
478 cls
._port
_refs
= multidict() # port ref objects
479 cls
._instantiated
= False # really instantiated, cloned, or subclassed
481 # We don't support multiple inheritance of sim objects. If you want
482 # to, you must fix multidict to deal with it properly. Non sim-objects
486 if isinstance(c
, MetaSimObject
):
490 "SimObjects do not support multiple inheritance")
494 # Set up general inheritance via multidicts. A subclass will
495 # inherit all its settings from the base class. The only time
496 # the following is not true is when we define the SimObject
497 # class itself (in which case the multidicts have no parent).
498 if isinstance(base
, MetaSimObject
):
500 cls
._params
.parent
= base
._params
501 cls
._ports
.parent
= base
._ports
502 cls
._values
.parent
= base
._values
503 cls
._hr
_values
.parent
= base
._hr
_values
504 cls
._children
.parent
= base
._children
505 cls
._port
_refs
.parent
= base
._port
_refs
506 # mark base as having been subclassed
507 base
._instantiated
= True
511 # default keyword values
512 if 'type' in cls
._value
_dict
:
513 if 'cxx_class' not in cls
._value
_dict
:
514 cls
._value
_dict
['cxx_class'] = cls
._value
_dict
['type']
516 cls
._value
_dict
['cxx_type'] = '%s *' % cls
._value
_dict
['cxx_class']
518 if 'cxx_header' not in cls
._value
_dict
:
521 warn("No header file specified for SimObject: %s", name
)
523 # Now process the _value_dict items. They could be defining
524 # new (or overriding existing) parameters or ports, setting
525 # class keywords (e.g., 'abstract'), or setting parameter
526 # values or port bindings. The first 3 can only be set when
527 # the class is defined, so we handle them here. The others
528 # can be set later too, so just emulate that by calling
530 for key
,val
in cls
._value
_dict
.items():
532 if isinstance(val
, ParamDesc
):
533 cls
._new
_param
(key
, val
)
536 elif isinstance(val
, Port
):
537 cls
._new
_port
(key
, val
)
539 # init-time-only keywords
540 elif key
in cls
.init_keywords
:
541 cls
._set
_keyword
(key
, val
, cls
.init_keywords
[key
])
543 # default: use normal path (ends up in __setattr__)
545 setattr(cls
, key
, val
)
547 def _set_keyword(cls
, keyword
, val
, kwtype
):
548 if not isinstance(val
, kwtype
):
549 raise TypeError('keyword %s has bad type %s (expecting %s)' % \
550 (keyword
, type(val
), kwtype
))
551 if isinstance(val
, FunctionType
):
552 val
= classmethod(val
)
553 type.__setattr
__(cls
, keyword
, val
)
555 def _new_param(cls
, name
, pdesc
):
556 # each param desc should be uniquely assigned to one variable
557 assert(not hasattr(pdesc
, 'name'))
559 cls
._params
[name
] = pdesc
560 if hasattr(pdesc
, 'default'):
561 cls
._set
_param
(name
, pdesc
.default
, pdesc
)
563 def _set_param(cls
, name
, value
, param
):
564 assert(param
.name
== name
)
567 value
= param
.convert(value
)
568 except Exception as e
:
569 msg
= "%s\nError setting param %s.%s to %s\n" % \
570 (e
, cls
.__name
__, name
, value
)
573 cls
._values
[name
] = value
574 # if param value is a SimObject, make it a child too, so that
575 # it gets cloned properly when the class is instantiated
576 if isSimObjectOrVector(value
) and not value
.has_parent():
577 cls
._add
_cls
_child
(name
, value
)
578 # update human-readable values of the param if it has a literal
579 # value and is not an object or proxy.
580 if not (isSimObjectOrVector(value
) or\
581 isinstance(value
, m5
.proxy
.BaseProxy
)):
582 cls
._hr
_values
[name
] = hr_value
584 def _add_cls_child(cls
, name
, child
):
585 # It's a little funky to have a class as a parent, but these
586 # objects should never be instantiated (only cloned, which
587 # clears the parent pointer), and this makes it clear that the
588 # object is not an orphan and can provide better error
590 child
.set_parent(cls
, name
)
591 if not isNullPointer(child
):
592 cls
._children
[name
] = child
594 def _new_port(cls
, name
, port
):
595 # each port should be uniquely assigned to one variable
596 assert(not hasattr(port
, 'name'))
598 cls
._ports
[name
] = port
600 # same as _get_port_ref, effectively, but for classes
601 def _cls_get_port_ref(cls
, attr
):
602 # Return reference that can be assigned to another port
603 # via __setattr__. There is only ever one reference
604 # object per port, but we create them lazily here.
605 ref
= cls
._port
_refs
.get(attr
)
607 ref
= cls
._ports
[attr
].makeRef(cls
)
608 cls
._port
_refs
[attr
] = ref
611 # Set attribute (called on foo.attr = value when foo is an
612 # instance of class cls).
613 def __setattr__(cls
, attr
, value
):
614 # normal processing for private attributes
615 if public_value(attr
, value
):
616 type.__setattr
__(cls
, attr
, value
)
619 if attr
in cls
.keywords
:
620 cls
._set
_keyword
(attr
, value
, cls
.keywords
[attr
])
623 if attr
in cls
._ports
:
624 cls
._cls
_get
_port
_ref
(attr
).connect(value
)
627 if isSimObjectOrSequence(value
) and cls
._instantiated
:
629 "cannot set SimObject parameter '%s' after\n" \
630 " class %s has been instantiated or subclassed" \
631 % (attr
, cls
.__name
__))
634 param
= cls
._params
.get(attr
)
636 cls
._set
_param
(attr
, value
, param
)
639 if isSimObjectOrSequence(value
):
640 # If RHS is a SimObject, it's an implicit child assignment.
641 cls
._add
_cls
_child
(attr
, coerceSimObjectOrVector(value
))
644 # no valid assignment... raise exception
645 raise AttributeError(
646 "Class %s has no parameter \'%s\'" % (cls
.__name
__, attr
))
648 def __getattr__(cls
, attr
):
649 if attr
== 'cxx_class_path':
650 return cls
.cxx_class
.split('::')
652 if attr
== 'cxx_class_name':
653 return cls
.cxx_class_path
[-1]
655 if attr
== 'cxx_namespaces':
656 return cls
.cxx_class_path
[:-1]
658 if attr
in cls
._values
:
659 return cls
._values
[attr
]
661 if attr
in cls
._children
:
662 return cls
._children
[attr
]
664 raise AttributeError(
665 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
))
670 # See ParamValue.cxx_predecls for description.
671 def cxx_predecls(cls
, code
):
672 code('#include "params/$cls.hh"')
674 def pybind_predecls(cls
, code
):
675 code('#include "${{cls.cxx_header}}"')
677 def pybind_decl(cls
, code
):
678 class_path
= cls
.cxx_class
.split('::')
679 namespaces
, classname
= class_path
[:-1], class_path
[-1]
680 py_class_name
= '_COLONS_'.join(class_path
) if namespaces
else \
683 # The 'local' attribute restricts us to the params declared in
684 # the object itself, not including inherited params (which
685 # will also be inherited from the base class's param struct
686 # here). Sort the params based on their key
687 params
= map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items()))
688 ports
= cls
._ports
.local
690 code('''#include "pybind11/pybind11.h"
691 #include "pybind11/stl.h"
693 #include "params/$cls.hh"
694 #include "python/pybind11/core.hh"
695 #include "sim/init.hh"
696 #include "sim/sim_object.hh"
698 #include "${{cls.cxx_header}}"
703 param
.pybind_predecls(code
)
705 code('''namespace py = pybind11;
708 module_init(py::module &m_internal)
710 py::module m = m_internal.def_submodule("param_${cls}");
714 code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
715 'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
716 'm, "${cls}Params")')
718 code('py::class_<${cls}Params, ' \
719 'std::unique_ptr<${cls}Params, py::nodelete>>(' \
720 'm, "${cls}Params")')
723 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
724 code('.def(py::init<>())')
725 code('.def("create", &${cls}Params::create)')
727 param_exports
= cls
.cxx_param_exports
+ [
729 for k
, v
in sorted(cls
._params
.local
.items())
731 PyBindProperty("port_%s_connection_count" % port
.name
)
732 for port
in ports
.values()
734 for exp
in param_exports
:
735 exp
.export(code
, "%sParams" % cls
)
742 if 'cxx_base' in cls
._value
_dict
:
743 # If the c++ base class implied by python inheritance was
744 # overridden, use that value.
746 bases
.append(cls
.cxx_base
)
748 # If not and if there was a SimObject base, use its c++ class
749 # as this class' base.
750 bases
.append(cls
._base
.cxx_class
)
751 # Add in any extra bases that were requested.
752 bases
.extend(cls
.cxx_extra_bases
)
755 base_str
= ", ".join(bases
)
756 code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
757 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
758 'm, "${py_class_name}")')
760 code('py::class_<${{cls.cxx_class}}, ' \
761 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
762 'm, "${py_class_name}")')
764 for exp
in cls
.cxx_exports
:
765 exp
.export(code
, cls
.cxx_class
)
772 code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
773 cls
, cls
._base
.type if cls
._base
else "")
775 _warned_about_nested_templates
= False
777 # Generate the C++ declaration (.hh file) for this SimObject's
778 # param struct. Called from src/SConscript.
779 def cxx_param_decl(cls
, code
):
780 # The 'local' attribute restricts us to the params declared in
781 # the object itself, not including inherited params (which
782 # will also be inherited from the base class's param struct
783 # here). Sort the params based on their key
784 params
= map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items()))
785 ports
= cls
._ports
.local
787 ptypes
= [p
.ptype
for p
in params
]
789 print(cls
, p
, p
.ptype_str
)
793 class CxxClass(object):
794 def __init__(self
, sig
, template_params
=[]):
795 # Split the signature into its constituent parts. This could
796 # potentially be done with regular expressions, but
797 # it's simple enough to pick appart a class signature
799 parts
= sig
.split('<', 1)
803 # The signature had template arguments.
804 text
= parts
[1].rstrip(' \t\n>')
806 # Keep track of nesting to avoid splitting on ","s embedded
807 # in the arguments themselves.
812 if depth
> 0 and not \
813 self
._warned
_about
_nested
_templates
:
814 self
._warned
_about
_nested
_templates
= True
815 print('Nested template argument in cxx_class.'
816 ' This feature is largely untested and '
820 elif c
== ',' and depth
== 0:
821 t_args
.append(arg
.strip())
826 t_args
.append(arg
.strip())
827 # Split the non-template part on :: boundaries.
828 class_path
= base
.split('::')
830 # The namespaces are everything except the last part of the
832 self
.namespaces
= class_path
[:-1]
833 # And the class name is the last part.
834 self
.name
= class_path
[-1]
836 self
.template_params
= template_params
837 self
.template_arguments
= []
838 # Iterate through the template arguments and their values. This
839 # will likely break if parameter packs are used.
840 for arg
, param
in zip(t_args
, template_params
):
841 type_keys
= ('class', 'typename')
842 # If a parameter is a type, parse it recursively. Otherwise
843 # assume it's a constant, and store it verbatim.
844 if any(param
.strip().startswith(kw
) for kw
in type_keys
):
845 self
.template_arguments
.append(CxxClass(arg
))
847 self
.template_arguments
.append(arg
)
849 def declare(self
, code
):
850 # First declare any template argument types.
851 for arg
in self
.template_arguments
:
852 if isinstance(arg
, CxxClass
):
854 # Re-open the target namespace.
855 for ns
in self
.namespaces
:
856 code('namespace $ns {')
857 # If this is a class template...
858 if self
.template_params
:
859 code('template <${{", ".join(self.template_params)}}>')
860 # The actual class declaration.
861 code('class ${{self.name}};')
862 # Close the target namespaces.
863 for ns
in reversed(self
.namespaces
):
864 code('} // namespace $ns')
867 #ifndef __PARAMS__${cls}__
868 #define __PARAMS__${cls}__
873 # The base SimObject has a couple of params that get
874 # automatically set from Python without being declared through
875 # the normal Param mechanism; we slip them in here (needed
876 # predecls now, actual declarations below)
878 code('''#include <string>''')
880 cxx_class
= CxxClass(cls
._value
_dict
['cxx_class'],
881 cls
._value
_dict
['cxx_template_params'])
883 # A forward class declaration is sufficient since we are just
884 # declaring a pointer.
885 cxx_class
.declare(code
)
888 param
.cxx_predecls(code
)
889 for port
in ports
.values():
890 port
.cxx_predecls(code
)
894 code('#include "params/${{cls._base.type}}.hh"')
898 if issubclass(ptype
, Enum
):
899 code('#include "enums/${{ptype.__name__}}.hh"')
902 # now generate the actual param struct
903 code("struct ${cls}Params")
905 code(" : public ${{cls._base.type}}Params")
907 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
908 if 'type' in cls
.__dict
__:
909 code(" ${{cls.cxx_type}} create();")
915 virtual ~SimObjectParams() {}
922 for port
in ports
.values():
929 code('#endif // __PARAMS__${cls}__')
932 # Generate the C++ declaration/definition files for this SimObject's
933 # param struct to allow C++ initialisation
934 def cxx_config_param_file(cls
, code
, is_header
):
935 createCxxConfigDirectoryEntryFile(code
, cls
.__name
__, cls
, is_header
)
938 # This *temporary* definition is required to support calls from the
939 # SimObject class definition to the MetaSimObject methods (in
940 # particular _set_param, which gets called for parameters with default
941 # values defined on the SimObject class itself). It will get
942 # overridden by the permanent definition (which requires that
943 # SimObject be defined) lower in this file.
944 def isSimObjectOrVector(value
):
947 def cxxMethod(*args
, **kwargs
):
948 """Decorator to export C++ functions to Python"""
952 override
= kwargs
.get("override", False)
953 cxx_name
= kwargs
.get("cxx_name", name
)
954 return_value_policy
= kwargs
.get("return_value_policy", None)
956 args
, varargs
, keywords
, defaults
= inspect
.getargspec(func
)
957 if varargs
or keywords
:
958 raise ValueError("Wrapped methods must not contain variable " \
961 # Create tuples of (argument, default)
963 args
= args
[:-len(defaults
)] + \
964 list(zip(args
[-len(defaults
):], defaults
))
965 # Don't include self in the argument list to PyBind
970 def cxx_call(self
, *args
, **kwargs
):
971 ccobj
= self
.getCCObject()
972 return getattr(ccobj
, name
)(*args
, **kwargs
)
975 def py_call(self
, *args
, **kwargs
):
976 return func(self
, *args
, **kwargs
)
978 f
= py_call
if override
else cxx_call
979 f
.__pybind
= PyBindMethod(name
, cxx_name
=cxx_name
, args
=args
,
980 return_value_policy
=return_value_policy
)
986 elif len(args
) == 1 and len(kwargs
) == 0:
987 return decorate(*args
)
989 raise TypeError("One argument and no kwargs, or only kwargs expected")
991 # This class holds information about each simobject parameter
992 # that should be displayed on the command line for use in the
993 # configuration system.
994 class ParamInfo(object):
995 def __init__(self
, type, desc
, type_str
, example
, default_val
, access_str
):
998 self
.type_str
= type_str
999 self
.example_str
= example
1000 self
.default_val
= default_val
1001 # The string representation used to access this param through python.
1002 # The method to access this parameter presented on the command line may
1003 # be different, so this needs to be stored for later use.
1004 self
.access_str
= access_str
1007 # Make it so we can only set attributes at initialization time
1008 # and effectively make this a const object.
1009 def __setattr__(self
, name
, value
):
1010 if not "created" in self
.__dict
__:
1011 self
.__dict
__[name
] = value
1013 class SimObjectCliWrapperException(Exception):
1014 def __init__(self
, message
):
1015 super(Exception, self
).__init
__(message
)
1017 class SimObjectCliWrapper(object):
1019 Wrapper class to restrict operations that may be done
1020 from the command line on SimObjects.
1022 Only parameters may be set, and only children may be accessed.
1024 Slicing allows for multiple simultaneous assignment of items in
1028 def __init__(self
, sim_objects
):
1029 self
.__dict
__['_sim_objects'] = list(sim_objects
)
1031 def __getattr__(self
, key
):
1032 return SimObjectCliWrapper(sim_object
._children
[key
]
1033 for sim_object
in self
._sim
_objects
)
1035 def __setattr__(self
, key
, val
):
1036 for sim_object
in self
._sim
_objects
:
1037 if key
in sim_object
._params
:
1038 if sim_object
._params
[key
].isCmdLineSettable():
1039 setattr(sim_object
, key
, val
)
1041 raise SimObjectCliWrapperException(
1042 'tried to set or unsettable' \
1043 'object parameter: ' + key
)
1045 raise SimObjectCliWrapperException(
1046 'tried to set or access non-existent' \
1047 'object parameter: ' + key
)
1049 def __getitem__(self
, idx
):
1051 Extends the list() semantics to also allow tuples,
1052 for example object[1, 3] selects items 1 and 3.
1055 if isinstance(idx
, tuple):
1057 out
.extend(self
[t
]._sim
_objects
)
1059 if isinstance(idx
, int):
1060 _range
= range(idx
, idx
+ 1)
1061 elif not isinstance(idx
, slice):
1062 raise SimObjectCliWrapperException( \
1063 'invalid index type: ' + repr(idx
))
1064 for sim_object
in self
._sim
_objects
:
1065 if isinstance(idx
, slice):
1066 _range
= range(*idx
.indices(len(sim_object
)))
1067 out
.extend(sim_object
[i
] for i
in _range
)
1068 return SimObjectCliWrapper(out
)
1070 # The SimObject class is the root of the special hierarchy. Most of
1071 # the code in this class deals with the configuration hierarchy itself
1072 # (parent/child node relationships).
1073 class SimObject(object):
1074 # Specify metaclass. Any class inheriting from SimObject will
1075 # get this metaclass.
1076 __metaclass__
= MetaSimObject
1080 cxx_header
= "sim/sim_object.hh"
1081 cxx_extra_bases
= [ "Drainable", "Serializable" ]
1082 eventq_index
= Param
.UInt32(Parent
.eventq_index
, "Event Queue Index")
1085 PyBindMethod("init"),
1086 PyBindMethod("initState"),
1087 PyBindMethod("memInvalidate"),
1088 PyBindMethod("memWriteback"),
1089 PyBindMethod("regStats"),
1090 PyBindMethod("resetStats"),
1091 PyBindMethod("regProbePoints"),
1092 PyBindMethod("regProbeListeners"),
1093 PyBindMethod("startup"),
1096 cxx_param_exports
= [
1097 PyBindProperty("name"),
1101 def loadState(self
, cp
):
1102 """Load SimObject state from a checkpoint"""
1105 # Returns a dict of all the option strings that can be
1106 # generated as command line options for this simobject instance
1107 # by tracing all reachable params in the top level instance and
1108 # any children it contains.
1109 def enumerateParams(self
, flags_dict
= {},
1110 cmd_line_str
= "", access_str
= ""):
1111 if hasattr(self
, "_paramEnumed"):
1112 print("Cycle detected enumerating params")
1114 self
._paramEnumed
= True
1115 # Scan the children first to pick up all the objects in this SimObj
1116 for keys
in self
._children
:
1117 child
= self
._children
[keys
]
1118 next_cmdline_str
= cmd_line_str
+ keys
1119 next_access_str
= access_str
+ keys
1120 if not isSimObjectVector(child
):
1121 next_cmdline_str
= next_cmdline_str
+ "."
1122 next_access_str
= next_access_str
+ "."
1123 flags_dict
= child
.enumerateParams(flags_dict
,
1127 # Go through the simple params in the simobject in this level
1128 # of the simobject hierarchy and save information about the
1129 # parameter to be used for generating and processing command line
1130 # options to the simulator to set these parameters.
1131 for keys
,values
in self
._params
.items():
1132 if values
.isCmdLineSettable():
1134 ex_str
= values
.example_str()
1136 if isinstance(values
, VectorParamDesc
):
1137 type_str
= 'Vector_%s' % values
.ptype_str
1140 type_str
= '%s' % values
.ptype_str
1141 ptype
= values
.ptype
1143 if keys
in self
._hr
_values\
1144 and keys
in self
._values\
1145 and not isinstance(self
._values
[keys
],
1146 m5
.proxy
.BaseProxy
):
1147 cmd_str
= cmd_line_str
+ keys
1148 acc_str
= access_str
+ keys
1149 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1150 self
._params
[keys
].desc
, type_str
, ex_str
,
1151 values
.pretty_print(self
._hr
_values
[keys
]),
1153 elif not keys
in self
._hr
_values\
1154 and not keys
in self
._values
:
1156 cmd_str
= cmd_line_str
+ keys
1157 acc_str
= access_str
+ keys
1158 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1159 self
._params
[keys
].desc
,
1160 type_str
, ex_str
, '', acc_str
)
1164 # Initialize new instance. For objects with SimObject-valued
1165 # children, we need to recursively clone the classes represented
1166 # by those param values as well in a consistent "deep copy"-style
1167 # fashion. That is, we want to make sure that each instance is
1168 # cloned only once, and that if there are multiple references to
1169 # the same original object, we end up with the corresponding
1170 # cloned references all pointing to the same cloned instance.
1171 def __init__(self
, **kwargs
):
1172 ancestor
= kwargs
.get('_ancestor')
1173 memo_dict
= kwargs
.get('_memo')
1174 if memo_dict
is None:
1175 # prepare to memoize any recursively instantiated objects
1178 # memoize me now to avoid problems with recursive calls
1179 memo_dict
[ancestor
] = self
1182 ancestor
= self
.__class
__
1183 ancestor
._instantiated
= True
1185 # initialize required attributes
1188 self
._ccObject
= None # pointer to C++ object
1189 self
._ccParams
= None
1190 self
._instantiated
= False # really "cloned"
1192 # Clone children specified at class level. No need for a
1193 # multidict here since we will be cloning everything.
1194 # Do children before parameter values so that children that
1195 # are also param values get cloned properly.
1197 for key
,val
in ancestor
._children
.items():
1198 self
.add_child(key
, val(_memo
=memo_dict
))
1200 # Inherit parameter values from class using multidict so
1201 # individual value settings can be overridden but we still
1202 # inherit late changes to non-overridden class values.
1203 self
._values
= multidict(ancestor
._values
)
1204 self
._hr
_values
= multidict(ancestor
._hr
_values
)
1205 # clone SimObject-valued parameters
1206 for key
,val
in ancestor
._values
.items():
1207 val
= tryAsSimObjectOrVector(val
)
1209 self
._values
[key
] = val(_memo
=memo_dict
)
1211 # clone port references. no need to use a multidict here
1212 # since we will be creating new references for all ports.
1213 self
._port
_refs
= {}
1214 for key
,val
in ancestor
._port
_refs
.items():
1215 self
._port
_refs
[key
] = val
.clone(self
, memo_dict
)
1216 # apply attribute assignments from keyword args, if any
1217 for key
,val
in kwargs
.items():
1218 setattr(self
, key
, val
)
1220 # "Clone" the current instance by creating another instance of
1221 # this instance's class, but that inherits its parameter values
1222 # and port mappings from the current instance. If we're in a
1223 # "deep copy" recursive clone, check the _memo dict to see if
1224 # we've already cloned this instance.
1225 def __call__(self
, **kwargs
):
1226 memo_dict
= kwargs
.get('_memo')
1227 if memo_dict
is None:
1228 # no memo_dict: must be top-level clone operation.
1229 # this is only allowed at the root of a hierarchy
1231 raise RuntimeError("attempt to clone object %s " \
1232 "not at the root of a tree (parent = %s)" \
1233 % (self
, self
._parent
))
1234 # create a new dict and use that.
1236 kwargs
['_memo'] = memo_dict
1237 elif self
in memo_dict
:
1238 # clone already done & memoized
1239 return memo_dict
[self
]
1240 return self
.__class
__(_ancestor
= self
, **kwargs
)
1242 def _get_port_ref(self
, attr
):
1243 # Return reference that can be assigned to another port
1244 # via __setattr__. There is only ever one reference
1245 # object per port, but we create them lazily here.
1246 ref
= self
._port
_refs
.get(attr
)
1248 ref
= self
._ports
[attr
].makeRef(self
)
1249 self
._port
_refs
[attr
] = ref
1252 def __getattr__(self
, attr
):
1253 if attr
in self
._ports
:
1254 return self
._get
_port
_ref
(attr
)
1256 if attr
in self
._values
:
1257 return self
._values
[attr
]
1259 if attr
in self
._children
:
1260 return self
._children
[attr
]
1262 # If the attribute exists on the C++ object, transparently
1263 # forward the reference there. This is typically used for
1264 # methods exported to Python (e.g., init(), and startup())
1265 if self
._ccObject
and hasattr(self
._ccObject
, attr
):
1266 return getattr(self
._ccObject
, attr
)
1268 err_string
= "object '%s' has no attribute '%s'" \
1269 % (self
.__class
__.__name
__, attr
)
1271 if not self
._ccObject
:
1272 err_string
+= "\n (C++ object is not yet constructed," \
1273 " so wrapped C++ methods are unavailable.)"
1275 raise AttributeError(err_string
)
1277 # Set attribute (called on foo.attr = value when foo is an
1278 # instance of class cls).
1279 def __setattr__(self
, attr
, value
):
1280 # normal processing for private attributes
1281 if attr
.startswith('_'):
1282 object.__setattr
__(self
, attr
, value
)
1285 if attr
in self
._ports
:
1286 # set up port connection
1287 self
._get
_port
_ref
(attr
).connect(value
)
1290 param
= self
._params
.get(attr
)
1294 value
= param
.convert(value
)
1295 except Exception as e
:
1296 msg
= "%s\nError setting param %s.%s to %s\n" % \
1297 (e
, self
.__class
__.__name
__, attr
, value
)
1300 self
._values
[attr
] = value
1301 # implicitly parent unparented objects assigned as params
1302 if isSimObjectOrVector(value
) and not value
.has_parent():
1303 self
.add_child(attr
, value
)
1304 # set the human-readable value dict if this is a param
1305 # with a literal value and is not being set as an object
1307 if not (isSimObjectOrVector(value
) or\
1308 isinstance(value
, m5
.proxy
.BaseProxy
)):
1309 self
._hr
_values
[attr
] = hr_value
1313 # if RHS is a SimObject, it's an implicit child assignment
1314 if isSimObjectOrSequence(value
):
1315 self
.add_child(attr
, value
)
1318 # no valid assignment... raise exception
1319 raise AttributeError("Class %s has no parameter %s" \
1320 % (self
.__class
__.__name
__, attr
))
1323 # this hack allows tacking a '[0]' onto parameters that may or may
1324 # not be vectors, and always getting the first element (e.g. cpus)
1325 def __getitem__(self
, key
):
1328 raise IndexError("Non-zero index '%s' to SimObject" % key
)
1330 # this hack allows us to iterate over a SimObject that may
1331 # not be a vector, so we can call a loop over it and get just one
1336 # Also implemented by SimObjectVector
1337 def clear_parent(self
, old_parent
):
1338 assert self
._parent
is old_parent
1341 # Also implemented by SimObjectVector
1342 def set_parent(self
, parent
, name
):
1343 self
._parent
= parent
1346 # Return parent object of this SimObject, not implemented by
1347 # SimObjectVector because the elements in a SimObjectVector may not share
1349 def get_parent(self
):
1352 # Also implemented by SimObjectVector
1356 # Also implemented by SimObjectVector
1357 def has_parent(self
):
1358 return self
._parent
is not None
1360 # clear out child with given name. This code is not likely to be exercised.
1361 # See comment in add_child.
1362 def clear_child(self
, name
):
1363 child
= self
._children
[name
]
1364 child
.clear_parent(self
)
1365 del self
._children
[name
]
1367 # Add a new child to this object.
1368 def add_child(self
, name
, child
):
1369 child
= coerceSimObjectOrVector(child
)
1370 if child
.has_parent():
1371 warn("add_child('%s'): child '%s' already has parent", name
,
1373 if name
in self
._children
:
1374 # This code path had an undiscovered bug that would make it fail
1375 # at runtime. It had been here for a long time and was only
1376 # exposed by a buggy script. Changes here will probably not be
1377 # exercised without specialized testing.
1378 self
.clear_child(name
)
1379 child
.set_parent(self
, name
)
1380 if not isNullPointer(child
):
1381 self
._children
[name
] = child
1383 # Take SimObject-valued parameters that haven't been explicitly
1384 # assigned as children and make them children of the object that
1385 # they were assigned to as a parameter value. This guarantees
1386 # that when we instantiate all the parameter objects we're still
1387 # inside the configuration hierarchy.
1388 def adoptOrphanParams(self
):
1389 for key
,val
in self
._values
.items():
1390 if not isSimObjectVector(val
) and isSimObjectSequence(val
):
1391 # need to convert raw SimObject sequences to
1392 # SimObjectVector class so we can call has_parent()
1393 val
= SimObjectVector(val
)
1394 self
._values
[key
] = val
1395 if isSimObjectOrVector(val
) and not val
.has_parent():
1396 warn("%s adopting orphan SimObject param '%s'", self
, key
)
1397 self
.add_child(key
, val
)
1400 if not self
._parent
:
1401 return '<orphan %s>' % self
.__class
__
1402 elif isinstance(self
._parent
, MetaSimObject
):
1403 return str(self
.__class
__)
1405 ppath
= self
._parent
.path()
1408 return ppath
+ "." + self
._name
1413 def config_value(self
):
1419 def find_any(self
, ptype
):
1420 if isinstance(self
, ptype
):
1424 for child
in self
._children
.values():
1426 if hasattr(child
, '_visited'):
1427 visited
= getattr(child
, '_visited')
1429 if isinstance(child
, ptype
) and not visited
:
1430 if found_obj
!= None and child
!= found_obj
:
1431 raise AttributeError(
1432 'parent.any matched more than one: %s %s' % \
1433 (found_obj
.path
, child
.path
))
1435 # search param space
1436 for pname
,pdesc
in self
._params
.items():
1437 if issubclass(pdesc
.ptype
, ptype
):
1438 match_obj
= self
._values
[pname
]
1439 if found_obj
!= None and found_obj
!= match_obj
:
1440 raise AttributeError(
1441 'parent.any matched more than one: %s and %s' % \
1442 (found_obj
.path
, match_obj
.path
))
1443 found_obj
= match_obj
1444 return found_obj
, found_obj
!= None
1446 def find_all(self
, ptype
):
1449 for child
in self
._children
.values():
1450 # a child could be a list, so ensure we visit each item
1451 if isinstance(child
, list):
1456 for child
in children
:
1457 if isinstance(child
, ptype
) and not isproxy(child
) and \
1458 not isNullPointer(child
):
1460 if isSimObject(child
):
1461 # also add results from the child itself
1462 child_all
, done
= child
.find_all(ptype
)
1463 all
.update(dict(zip(child_all
, [done
] * len(child_all
))))
1464 # search param space
1465 for pname
,pdesc
in self
._params
.items():
1466 if issubclass(pdesc
.ptype
, ptype
):
1467 match_obj
= self
._values
[pname
]
1468 if not isproxy(match_obj
) and not isNullPointer(match_obj
):
1469 all
[match_obj
] = True
1470 # Also make sure to sort the keys based on the objects' path to
1471 # ensure that the order is the same on all hosts
1472 return sorted(all
.keys(), key
= lambda o
: o
.path()), True
1474 def unproxy(self
, base
):
1477 def unproxyParams(self
):
1478 for param
in self
._params
.keys():
1479 value
= self
._values
.get(param
)
1480 if value
!= None and isproxy(value
):
1482 value
= value
.unproxy(self
)
1484 print("Error in unproxying param '%s' of %s" %
1485 (param
, self
.path()))
1487 setattr(self
, param
, value
)
1489 # Unproxy ports in sorted order so that 'append' operations on
1490 # vector ports are done in a deterministic fashion.
1491 port_names
= list(self
._ports
.keys())
1493 for port_name
in port_names
:
1494 port
= self
._port
_refs
.get(port_name
)
1498 def print_ini(self
, ini_file
):
1499 print('[' + self
.path() + ']', file=ini_file
) # .ini section header
1501 instanceDict
[self
.path()] = self
1503 if hasattr(self
, 'type'):
1504 print('type=%s' % self
.type, file=ini_file
)
1506 if len(self
._children
.keys()):
1507 print('children=%s' %
1508 ' '.join(self
._children
[n
].get_name()
1509 for n
in sorted(self
._children
.keys())),
1512 for param
in sorted(self
._params
.keys()):
1513 value
= self
._values
.get(param
)
1515 print('%s=%s' % (param
, self
._values
[param
].ini_str()),
1518 for port_name
in sorted(self
._ports
.keys()):
1519 port
= self
._port
_refs
.get(port_name
, None)
1521 print('%s=%s' % (port_name
, port
.ini_str()), file=ini_file
)
1523 print(file=ini_file
) # blank line between objects
1525 # generate a tree of dictionaries expressing all the parameters in the
1526 # instantiated system for use by scripts that want to do power, thermal
1527 # visualization, and other similar tasks
1528 def get_config_as_dict(self
):
1530 if hasattr(self
, 'type'):
1532 if hasattr(self
, 'cxx_class'):
1533 d
.cxx_class
= self
.cxx_class
1534 # Add the name and path of this object to be able to link to
1536 d
.name
= self
.get_name()
1537 d
.path
= self
.path()
1539 for param
in sorted(self
._params
.keys()):
1540 value
= self
._values
.get(param
)
1542 d
[param
] = value
.config_value()
1544 for n
in sorted(self
._children
.keys()):
1545 child
= self
._children
[n
]
1546 # Use the name of the attribute (and not get_name()) as
1547 # the key in the JSON dictionary to capture the hierarchy
1548 # in the Python code that assembled this system
1549 d
[n
] = child
.get_config_as_dict()
1551 for port_name
in sorted(self
._ports
.keys()):
1552 port
= self
._port
_refs
.get(port_name
, None)
1554 # Represent each port with a dictionary containing the
1555 # prominent attributes
1556 d
[port_name
] = port
.get_config_as_dict()
1560 def getCCParams(self
):
1562 return self
._ccParams
1564 cc_params_struct
= getattr(m5
.internal
.params
, '%sParams' % self
.type)
1565 cc_params
= cc_params_struct()
1566 cc_params
.name
= str(self
)
1568 param_names
= list(self
._params
.keys())
1570 for param
in param_names
:
1571 value
= self
._values
.get(param
)
1573 fatal("%s.%s without default or user set value",
1576 value
= value
.getValue()
1577 if isinstance(self
._params
[param
], VectorParamDesc
):
1578 assert isinstance(value
, list)
1579 vec
= getattr(cc_params
, param
)
1581 # Some types are exposed as opaque types. They support
1582 # the append operation unlike the automatically
1584 if isinstance(vec
, list):
1585 setattr(cc_params
, param
, list(value
))
1588 getattr(cc_params
, param
).append(v
)
1590 setattr(cc_params
, param
, value
)
1592 port_names
= list(self
._ports
.keys())
1594 for port_name
in port_names
:
1595 port
= self
._port
_refs
.get(port_name
, None)
1597 port_count
= len(port
)
1600 setattr(cc_params
, 'port_' + port_name
+ '_connection_count',
1602 self
._ccParams
= cc_params
1603 return self
._ccParams
1605 # Get C++ object corresponding to this object, calling C++ if
1606 # necessary to construct it. Does *not* recursively create
1608 def getCCObject(self
):
1609 if not self
._ccObject
:
1610 # Make sure this object is in the configuration hierarchy
1611 if not self
._parent
and not isRoot(self
):
1612 raise RuntimeError("Attempt to instantiate orphan node")
1613 # Cycles in the configuration hierarchy are not supported. This
1614 # will catch the resulting recursion and stop.
1616 if not self
.abstract
:
1617 params
= self
.getCCParams()
1618 self
._ccObject
= params
.create()
1619 elif self
._ccObject
== -1:
1620 raise RuntimeError("%s: Cycle found in configuration hierarchy." \
1622 return self
._ccObject
1624 def descendants(self
):
1626 # The order of the dict is implementation dependent, so sort
1627 # it based on the key (name) to ensure the order is the same
1629 for (name
, child
) in sorted(self
._children
.items()):
1630 for obj
in child
.descendants():
1633 # Call C++ to create C++ object corresponding to this object
1634 def createCCObject(self
):
1636 self
.getCCObject() # force creation
1639 return self
.getCCObject()
1641 @cxxMethod(return_value_policy
="reference")
1642 def getPort(self
, if_name
, idx
):
1645 # Create C++ port connections corresponding to the connections in
1647 def connectPorts(self
):
1648 # Sort the ports based on their attribute name to ensure the
1649 # order is the same on all hosts
1650 for (attr
, portRef
) in sorted(self
._port
_refs
.items()):
1653 # Default function for generating the device structure.
1654 # Can be overloaded by the inheriting class
1655 def generateDeviceTree(self
, state
):
1656 return # return without yielding anything
1657 yield # make this function a (null) generator
1659 def recurseDeviceTree(self
, state
):
1660 for child
in self
._children
.values():
1661 for item
in child
: # For looping over SimObjectVectors
1662 for dt
in item
.generateDeviceTree(state
):
1665 # On a separate method otherwise certain buggy Python versions
1666 # would fail with: SyntaxError: unqualified exec is not allowed
1667 # in function 'apply_config'
1668 def _apply_config_get_dict(self
):
1670 child_name
: SimObjectCliWrapper(
1671 iter(self
._children
[child_name
]))
1672 for child_name
in self
._children
1675 def apply_config(self
, params
):
1677 exec a list of Python code strings contained in params.
1679 The only exposed globals to those strings are the child
1680 SimObjects of this node.
1682 This function is intended to allow users to modify SimObject
1683 parameters from the command line with Python statements.
1685 d
= self
._apply
_config
_get
_dict
()
1686 for param
in params
:
1689 # Function to provide to C++ so it can look up instances based on paths
1690 def resolveSimObject(name
):
1691 obj
= instanceDict
[name
]
1692 return obj
.getCCObject()
1694 def isSimObject(value
):
1695 return isinstance(value
, SimObject
)
1697 def isSimObjectClass(value
):
1698 return issubclass(value
, SimObject
)
1700 def isSimObjectVector(value
):
1701 return isinstance(value
, SimObjectVector
)
1703 def isSimObjectSequence(value
):
1704 if not isinstance(value
, (list, tuple)) or len(value
) == 0:
1708 if not isNullPointer(val
) and not isSimObject(val
):
1713 def isSimObjectOrSequence(value
):
1714 return isSimObject(value
) or isSimObjectSequence(value
)
1717 from m5
.objects
import Root
1718 return obj
and obj
is Root
.getInstance()
1720 def isSimObjectOrVector(value
):
1721 return isSimObject(value
) or isSimObjectVector(value
)
1723 def tryAsSimObjectOrVector(value
):
1724 if isSimObjectOrVector(value
):
1726 if isSimObjectSequence(value
):
1727 return SimObjectVector(value
)
1730 def coerceSimObjectOrVector(value
):
1731 value
= tryAsSimObjectOrVector(value
)
1733 raise TypeError("SimObject or SimObjectVector expected")
1736 baseClasses
= allClasses
.copy()
1737 baseInstances
= instanceDict
.copy()
1740 global allClasses
, instanceDict
, noCxxHeader
1742 allClasses
= baseClasses
.copy()
1743 instanceDict
= baseInstances
.copy()
1746 # __all__ defines the list of symbols that get exported when
1747 # 'from config import *' is invoked. Try to keep this reasonably
1748 # short to avoid polluting other namespaces.