1 # Copyright (c) 2017-2020 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 # Parameter names that are deprecated. Dict[str, DeprecatedParam]
471 # The key is the "old_name" so that when the old_name is used in
472 # python config files, we will use the DeprecatedParam object to
473 # translate to the new type.
474 cls
._deprecated
_params
= multidict()
476 # class or instance attributes
477 cls
._values
= multidict() # param values
478 cls
._hr
_values
= multidict() # human readable param values
479 cls
._children
= multidict() # SimObject children
480 cls
._port
_refs
= multidict() # port ref objects
481 cls
._instantiated
= False # really instantiated, cloned, or subclassed
483 # We don't support multiple inheritance of sim objects. If you want
484 # to, you must fix multidict to deal with it properly. Non sim-objects
488 if isinstance(c
, MetaSimObject
):
492 "SimObjects do not support multiple inheritance")
496 # Set up general inheritance via multidicts. A subclass will
497 # inherit all its settings from the base class. The only time
498 # the following is not true is when we define the SimObject
499 # class itself (in which case the multidicts have no parent).
500 if isinstance(base
, MetaSimObject
):
502 cls
._params
.parent
= base
._params
503 cls
._ports
.parent
= base
._ports
504 cls
._deprecated
_params
.parent
= base
._deprecated
_params
505 cls
._values
.parent
= base
._values
506 cls
._hr
_values
.parent
= base
._hr
_values
507 cls
._children
.parent
= base
._children
508 cls
._port
_refs
.parent
= base
._port
_refs
509 # mark base as having been subclassed
510 base
._instantiated
= True
514 # default keyword values
515 if 'type' in cls
._value
_dict
:
516 if 'cxx_class' not in cls
._value
_dict
:
517 cls
._value
_dict
['cxx_class'] = cls
._value
_dict
['type']
519 cls
._value
_dict
['cxx_type'] = '%s *' % cls
._value
_dict
['cxx_class']
521 if 'cxx_header' not in cls
._value
_dict
:
524 warn("No header file specified for SimObject: %s", name
)
526 # Now process the _value_dict items. They could be defining
527 # new (or overriding existing) parameters or ports, setting
528 # class keywords (e.g., 'abstract'), or setting parameter
529 # values or port bindings. The first 3 can only be set when
530 # the class is defined, so we handle them here. The others
531 # can be set later too, so just emulate that by calling
533 for key
,val
in cls
._value
_dict
.items():
535 if isinstance(val
, ParamDesc
):
536 cls
._new
_param
(key
, val
)
539 elif isinstance(val
, Port
):
540 cls
._new
_port
(key
, val
)
542 # Deprecated variable names
543 elif isinstance(val
, DeprecatedParam
):
544 new_name
, new_val
= cls
._get
_param
_by
_value
(val
.newParam
)
545 # Note: We don't know the (string) name of this variable until
546 # here, so now we can finish setting up the dep_param.
548 val
.newName
= new_name
549 cls
._deprecated
_params
[key
] = val
551 # init-time-only keywords
552 elif key
in cls
.init_keywords
:
553 cls
._set
_keyword
(key
, val
, cls
.init_keywords
[key
])
555 # default: use normal path (ends up in __setattr__)
557 setattr(cls
, key
, val
)
559 def _set_keyword(cls
, keyword
, val
, kwtype
):
560 if not isinstance(val
, kwtype
):
561 raise TypeError('keyword %s has bad type %s (expecting %s)' % \
562 (keyword
, type(val
), kwtype
))
563 if isinstance(val
, FunctionType
):
564 val
= classmethod(val
)
565 type.__setattr
__(cls
, keyword
, val
)
567 def _new_param(cls
, name
, pdesc
):
568 # each param desc should be uniquely assigned to one variable
569 assert(not hasattr(pdesc
, 'name'))
571 cls
._params
[name
] = pdesc
572 if hasattr(pdesc
, 'default'):
573 cls
._set
_param
(name
, pdesc
.default
, pdesc
)
575 def _set_param(cls
, name
, value
, param
):
576 assert(param
.name
== name
)
579 value
= param
.convert(value
)
580 except Exception as e
:
581 msg
= "%s\nError setting param %s.%s to %s\n" % \
582 (e
, cls
.__name
__, name
, value
)
585 cls
._values
[name
] = value
586 # if param value is a SimObject, make it a child too, so that
587 # it gets cloned properly when the class is instantiated
588 if isSimObjectOrVector(value
) and not value
.has_parent():
589 cls
._add
_cls
_child
(name
, value
)
590 # update human-readable values of the param if it has a literal
591 # value and is not an object or proxy.
592 if not (isSimObjectOrVector(value
) or\
593 isinstance(value
, m5
.proxy
.BaseProxy
)):
594 cls
._hr
_values
[name
] = hr_value
596 def _add_cls_child(cls
, name
, child
):
597 # It's a little funky to have a class as a parent, but these
598 # objects should never be instantiated (only cloned, which
599 # clears the parent pointer), and this makes it clear that the
600 # object is not an orphan and can provide better error
602 child
.set_parent(cls
, name
)
603 if not isNullPointer(child
):
604 cls
._children
[name
] = child
606 def _new_port(cls
, name
, port
):
607 # each port should be uniquely assigned to one variable
608 assert(not hasattr(port
, 'name'))
610 cls
._ports
[name
] = port
612 # same as _get_port_ref, effectively, but for classes
613 def _cls_get_port_ref(cls
, attr
):
614 # Return reference that can be assigned to another port
615 # via __setattr__. There is only ever one reference
616 # object per port, but we create them lazily here.
617 ref
= cls
._port
_refs
.get(attr
)
619 ref
= cls
._ports
[attr
].makeRef(cls
)
620 cls
._port
_refs
[attr
] = ref
623 def _get_param_by_value(cls
, value
):
624 """Given an object, value, return the name and the value from the
625 internal list of parameter values. If this value can't be found, raise
626 a runtime error. This will search both the current object and its
629 for k
,v
in cls
._value
_dict
.items():
632 raise RuntimeError("Cannot find parameter {} in parameter list"
635 # Set attribute (called on foo.attr = value when foo is an
636 # instance of class cls).
637 def __setattr__(cls
, attr
, value
):
638 # normal processing for private attributes
639 if public_value(attr
, value
):
640 type.__setattr
__(cls
, attr
, value
)
643 if attr
in cls
.keywords
:
644 cls
._set
_keyword
(attr
, value
, cls
.keywords
[attr
])
647 if attr
in cls
._ports
:
648 cls
._cls
_get
_port
_ref
(attr
).connect(value
)
651 if isSimObjectOrSequence(value
) and cls
._instantiated
:
653 "cannot set SimObject parameter '%s' after\n" \
654 " class %s has been instantiated or subclassed" \
655 % (attr
, cls
.__name
__))
658 param
= cls
._params
.get(attr
)
660 cls
._set
_param
(attr
, value
, param
)
663 if isSimObjectOrSequence(value
):
664 # If RHS is a SimObject, it's an implicit child assignment.
665 cls
._add
_cls
_child
(attr
, coerceSimObjectOrVector(value
))
668 # no valid assignment... raise exception
669 raise AttributeError(
670 "Class %s has no parameter \'%s\'" % (cls
.__name
__, attr
))
672 def __getattr__(cls
, attr
):
673 if attr
== 'cxx_class_path':
674 return cls
.cxx_class
.split('::')
676 if attr
== 'cxx_class_name':
677 return cls
.cxx_class_path
[-1]
679 if attr
== 'cxx_namespaces':
680 return cls
.cxx_class_path
[:-1]
682 if attr
== 'pybind_class':
683 return '_COLONS_'.join(cls
.cxx_class_path
)
685 if attr
in cls
._values
:
686 return cls
._values
[attr
]
688 if attr
in cls
._children
:
689 return cls
._children
[attr
]
692 return getattr(cls
.getCCClass(), attr
)
693 except AttributeError:
694 raise AttributeError(
695 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
))
701 return getattr(m5
.internal
.params
, cls
.pybind_class
)
703 # See ParamValue.cxx_predecls for description.
704 def cxx_predecls(cls
, code
):
705 code('#include "params/$cls.hh"')
707 def pybind_predecls(cls
, code
):
708 code('#include "${{cls.cxx_header}}"')
710 def pybind_decl(cls
, code
):
711 py_class_name
= cls
.pybind_class
713 # The 'local' attribute restricts us to the params declared in
714 # the object itself, not including inherited params (which
715 # will also be inherited from the base class's param struct
716 # here). Sort the params based on their key
717 params
= list(map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items())))
718 ports
= cls
._ports
.local
720 code('''#include "pybind11/pybind11.h"
721 #include "pybind11/stl.h"
723 #include "params/$cls.hh"
724 #include "python/pybind11/core.hh"
725 #include "sim/init.hh"
726 #include "sim/sim_object.hh"
728 #include "${{cls.cxx_header}}"
733 param
.pybind_predecls(code
)
735 code('''namespace py = pybind11;
738 module_init(py::module &m_internal)
740 py::module m = m_internal.def_submodule("param_${cls}");
744 code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
745 'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
746 'm, "${cls}Params")')
748 code('py::class_<${cls}Params, ' \
749 'std::unique_ptr<${cls}Params, py::nodelete>>(' \
750 'm, "${cls}Params")')
753 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
754 code('.def(py::init<>())')
755 code('.def("create", &${cls}Params::create)')
757 param_exports
= cls
.cxx_param_exports
+ [
759 for k
, v
in sorted(cls
._params
.local
.items())
761 PyBindProperty("port_%s_connection_count" % port
.name
)
762 for port
in ports
.values()
764 for exp
in param_exports
:
765 exp
.export(code
, "%sParams" % cls
)
772 if 'cxx_base' in cls
._value
_dict
:
773 # If the c++ base class implied by python inheritance was
774 # overridden, use that value.
776 bases
.append(cls
.cxx_base
)
778 # If not and if there was a SimObject base, use its c++ class
779 # as this class' base.
780 bases
.append(cls
._base
.cxx_class
)
781 # Add in any extra bases that were requested.
782 bases
.extend(cls
.cxx_extra_bases
)
785 base_str
= ", ".join(bases
)
786 code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
787 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
788 'm, "${py_class_name}")')
790 code('py::class_<${{cls.cxx_class}}, ' \
791 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
792 'm, "${py_class_name}")')
794 for exp
in cls
.cxx_exports
:
795 exp
.export(code
, cls
.cxx_class
)
802 code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
803 cls
, cls
._base
.type if cls
._base
else "")
805 _warned_about_nested_templates
= False
807 # Generate the C++ declaration (.hh file) for this SimObject's
808 # param struct. Called from src/SConscript.
809 def cxx_param_decl(cls
, code
):
810 # The 'local' attribute restricts us to the params declared in
811 # the object itself, not including inherited params (which
812 # will also be inherited from the base class's param struct
813 # here). Sort the params based on their key
814 params
= list(map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items())))
815 ports
= cls
._ports
.local
817 ptypes
= [p
.ptype
for p
in params
]
819 print(cls
, p
, p
.ptype_str
)
823 class CxxClass(object):
824 def __init__(self
, sig
, template_params
=[]):
825 # Split the signature into its constituent parts. This could
826 # potentially be done with regular expressions, but
827 # it's simple enough to pick appart a class signature
829 parts
= sig
.split('<', 1)
833 # The signature had template arguments.
834 text
= parts
[1].rstrip(' \t\n>')
836 # Keep track of nesting to avoid splitting on ","s embedded
837 # in the arguments themselves.
842 if depth
> 0 and not \
843 self
._warned
_about
_nested
_templates
:
844 self
._warned
_about
_nested
_templates
= True
845 print('Nested template argument in cxx_class.'
846 ' This feature is largely untested and '
850 elif c
== ',' and depth
== 0:
851 t_args
.append(arg
.strip())
856 t_args
.append(arg
.strip())
857 # Split the non-template part on :: boundaries.
858 class_path
= base
.split('::')
860 # The namespaces are everything except the last part of the
862 self
.namespaces
= class_path
[:-1]
863 # And the class name is the last part.
864 self
.name
= class_path
[-1]
866 self
.template_params
= template_params
867 self
.template_arguments
= []
868 # Iterate through the template arguments and their values. This
869 # will likely break if parameter packs are used.
870 for arg
, param
in zip(t_args
, template_params
):
871 type_keys
= ('class', 'typename')
872 # If a parameter is a type, parse it recursively. Otherwise
873 # assume it's a constant, and store it verbatim.
874 if any(param
.strip().startswith(kw
) for kw
in type_keys
):
875 self
.template_arguments
.append(CxxClass(arg
))
877 self
.template_arguments
.append(arg
)
879 def declare(self
, code
):
880 # First declare any template argument types.
881 for arg
in self
.template_arguments
:
882 if isinstance(arg
, CxxClass
):
884 # Re-open the target namespace.
885 for ns
in self
.namespaces
:
886 code('namespace $ns {')
887 # If this is a class template...
888 if self
.template_params
:
889 code('template <${{", ".join(self.template_params)}}>')
890 # The actual class declaration.
891 code('class ${{self.name}};')
892 # Close the target namespaces.
893 for ns
in reversed(self
.namespaces
):
894 code('} // namespace $ns')
897 #ifndef __PARAMS__${cls}__
898 #define __PARAMS__${cls}__
903 # The base SimObject has a couple of params that get
904 # automatically set from Python without being declared through
905 # the normal Param mechanism; we slip them in here (needed
906 # predecls now, actual declarations below)
908 code('''#include <string>''')
910 cxx_class
= CxxClass(cls
._value
_dict
['cxx_class'],
911 cls
._value
_dict
['cxx_template_params'])
913 # A forward class declaration is sufficient since we are just
914 # declaring a pointer.
915 cxx_class
.declare(code
)
918 param
.cxx_predecls(code
)
919 for port
in ports
.values():
920 port
.cxx_predecls(code
)
924 code('#include "params/${{cls._base.type}}.hh"')
928 if issubclass(ptype
, Enum
):
929 code('#include "enums/${{ptype.__name__}}.hh"')
932 # now generate the actual param struct
933 code("struct ${cls}Params")
935 code(" : public ${{cls._base.type}}Params")
937 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
938 if 'type' in cls
.__dict
__:
939 code(" ${{cls.cxx_type}} create();")
945 virtual ~SimObjectParams() {}
952 for port
in ports
.values():
959 code('#endif // __PARAMS__${cls}__')
962 # Generate the C++ declaration/definition files for this SimObject's
963 # param struct to allow C++ initialisation
964 def cxx_config_param_file(cls
, code
, is_header
):
965 createCxxConfigDirectoryEntryFile(code
, cls
.__name
__, cls
, is_header
)
968 # This *temporary* definition is required to support calls from the
969 # SimObject class definition to the MetaSimObject methods (in
970 # particular _set_param, which gets called for parameters with default
971 # values defined on the SimObject class itself). It will get
972 # overridden by the permanent definition (which requires that
973 # SimObject be defined) lower in this file.
974 def isSimObjectOrVector(value
):
977 def cxxMethod(*args
, **kwargs
):
978 """Decorator to export C++ functions to Python"""
982 override
= kwargs
.get("override", False)
983 cxx_name
= kwargs
.get("cxx_name", name
)
984 return_value_policy
= kwargs
.get("return_value_policy", None)
985 static
= kwargs
.get("static", False)
987 args
, varargs
, keywords
, defaults
= inspect
.getargspec(func
)
988 if varargs
or keywords
:
989 raise ValueError("Wrapped methods must not contain variable " \
992 # Create tuples of (argument, default)
994 args
= args
[:-len(defaults
)] + \
995 list(zip(args
[-len(defaults
):], defaults
))
996 # Don't include self in the argument list to PyBind
1001 def cxx_call(self
, *args
, **kwargs
):
1002 ccobj
= self
.getCCClass() if static
else self
.getCCObject()
1003 return getattr(ccobj
, name
)(*args
, **kwargs
)
1006 def py_call(self
, *args
, **kwargs
):
1007 return func(self
, *args
, **kwargs
)
1009 f
= py_call
if override
else cxx_call
1010 f
.__pybind
= PyBindMethod(name
, cxx_name
=cxx_name
, args
=args
,
1011 return_value_policy
=return_value_policy
,
1018 elif len(args
) == 1 and len(kwargs
) == 0:
1019 return decorate(*args
)
1021 raise TypeError("One argument and no kwargs, or only kwargs expected")
1023 # This class holds information about each simobject parameter
1024 # that should be displayed on the command line for use in the
1025 # configuration system.
1026 class ParamInfo(object):
1027 def __init__(self
, type, desc
, type_str
, example
, default_val
, access_str
):
1030 self
.type_str
= type_str
1031 self
.example_str
= example
1032 self
.default_val
= default_val
1033 # The string representation used to access this param through python.
1034 # The method to access this parameter presented on the command line may
1035 # be different, so this needs to be stored for later use.
1036 self
.access_str
= access_str
1039 # Make it so we can only set attributes at initialization time
1040 # and effectively make this a const object.
1041 def __setattr__(self
, name
, value
):
1042 if not "created" in self
.__dict
__:
1043 self
.__dict
__[name
] = value
1045 class SimObjectCliWrapperException(Exception):
1046 def __init__(self
, message
):
1047 super(Exception, self
).__init
__(message
)
1049 class SimObjectCliWrapper(object):
1051 Wrapper class to restrict operations that may be done
1052 from the command line on SimObjects.
1054 Only parameters may be set, and only children may be accessed.
1056 Slicing allows for multiple simultaneous assignment of items in
1060 def __init__(self
, sim_objects
):
1061 self
.__dict
__['_sim_objects'] = list(sim_objects
)
1063 def __getattr__(self
, key
):
1064 return SimObjectCliWrapper(sim_object
._children
[key
]
1065 for sim_object
in self
._sim
_objects
)
1067 def __setattr__(self
, key
, val
):
1068 for sim_object
in self
._sim
_objects
:
1069 if key
in sim_object
._params
:
1070 if sim_object
._params
[key
].isCmdLineSettable():
1071 setattr(sim_object
, key
, val
)
1073 raise SimObjectCliWrapperException(
1074 'tried to set or unsettable' \
1075 'object parameter: ' + key
)
1077 raise SimObjectCliWrapperException(
1078 'tried to set or access non-existent' \
1079 'object parameter: ' + key
)
1081 def __getitem__(self
, idx
):
1083 Extends the list() semantics to also allow tuples,
1084 for example object[1, 3] selects items 1 and 3.
1087 if isinstance(idx
, tuple):
1089 out
.extend(self
[t
]._sim
_objects
)
1091 if isinstance(idx
, int):
1092 _range
= range(idx
, idx
+ 1)
1093 elif not isinstance(idx
, slice):
1094 raise SimObjectCliWrapperException( \
1095 'invalid index type: ' + repr(idx
))
1096 for sim_object
in self
._sim
_objects
:
1097 if isinstance(idx
, slice):
1098 _range
= range(*idx
.indices(len(sim_object
)))
1099 out
.extend(sim_object
[i
] for i
in _range
)
1100 return SimObjectCliWrapper(out
)
1103 return iter(self
._sim
_objects
)
1105 # The SimObject class is the root of the special hierarchy. Most of
1106 # the code in this class deals with the configuration hierarchy itself
1107 # (parent/child node relationships).
1108 @add_metaclass(MetaSimObject
)
1109 class SimObject(object):
1110 # Specify metaclass. Any class inheriting from SimObject will
1111 # get this metaclass.
1115 cxx_header
= "sim/sim_object.hh"
1116 cxx_extra_bases
= [ "Drainable", "Serializable", "Stats::Group" ]
1117 eventq_index
= Param
.UInt32(Parent
.eventq_index
, "Event Queue Index")
1120 PyBindMethod("init"),
1121 PyBindMethod("initState"),
1122 PyBindMethod("memInvalidate"),
1123 PyBindMethod("memWriteback"),
1124 PyBindMethod("regProbePoints"),
1125 PyBindMethod("regProbeListeners"),
1126 PyBindMethod("startup"),
1129 cxx_param_exports
= [
1130 PyBindProperty("name"),
1134 def loadState(self
, cp
):
1135 """Load SimObject state from a checkpoint"""
1138 # Returns a dict of all the option strings that can be
1139 # generated as command line options for this simobject instance
1140 # by tracing all reachable params in the top level instance and
1141 # any children it contains.
1142 def enumerateParams(self
, flags_dict
= {},
1143 cmd_line_str
= "", access_str
= ""):
1144 if hasattr(self
, "_paramEnumed"):
1145 print("Cycle detected enumerating params")
1147 self
._paramEnumed
= True
1148 # Scan the children first to pick up all the objects in this SimObj
1149 for keys
in self
._children
:
1150 child
= self
._children
[keys
]
1151 next_cmdline_str
= cmd_line_str
+ keys
1152 next_access_str
= access_str
+ keys
1153 if not isSimObjectVector(child
):
1154 next_cmdline_str
= next_cmdline_str
+ "."
1155 next_access_str
= next_access_str
+ "."
1156 flags_dict
= child
.enumerateParams(flags_dict
,
1160 # Go through the simple params in the simobject in this level
1161 # of the simobject hierarchy and save information about the
1162 # parameter to be used for generating and processing command line
1163 # options to the simulator to set these parameters.
1164 for keys
,values
in self
._params
.items():
1165 if values
.isCmdLineSettable():
1167 ex_str
= values
.example_str()
1169 if isinstance(values
, VectorParamDesc
):
1170 type_str
= 'Vector_%s' % values
.ptype_str
1173 type_str
= '%s' % values
.ptype_str
1174 ptype
= values
.ptype
1176 if keys
in self
._hr
_values\
1177 and keys
in self
._values\
1178 and not isinstance(self
._values
[keys
],
1179 m5
.proxy
.BaseProxy
):
1180 cmd_str
= cmd_line_str
+ keys
1181 acc_str
= access_str
+ keys
1182 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1183 self
._params
[keys
].desc
, type_str
, ex_str
,
1184 values
.pretty_print(self
._hr
_values
[keys
]),
1186 elif not keys
in self
._hr
_values\
1187 and not keys
in self
._values
:
1189 cmd_str
= cmd_line_str
+ keys
1190 acc_str
= access_str
+ keys
1191 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1192 self
._params
[keys
].desc
,
1193 type_str
, ex_str
, '', acc_str
)
1197 # Initialize new instance. For objects with SimObject-valued
1198 # children, we need to recursively clone the classes represented
1199 # by those param values as well in a consistent "deep copy"-style
1200 # fashion. That is, we want to make sure that each instance is
1201 # cloned only once, and that if there are multiple references to
1202 # the same original object, we end up with the corresponding
1203 # cloned references all pointing to the same cloned instance.
1204 def __init__(self
, **kwargs
):
1205 ancestor
= kwargs
.get('_ancestor')
1206 memo_dict
= kwargs
.get('_memo')
1207 if memo_dict
is None:
1208 # prepare to memoize any recursively instantiated objects
1211 # memoize me now to avoid problems with recursive calls
1212 memo_dict
[ancestor
] = self
1215 ancestor
= self
.__class
__
1216 ancestor
._instantiated
= True
1218 # initialize required attributes
1221 self
._ccObject
= None # pointer to C++ object
1222 self
._ccParams
= None
1223 self
._instantiated
= False # really "cloned"
1225 # Clone children specified at class level. No need for a
1226 # multidict here since we will be cloning everything.
1227 # Do children before parameter values so that children that
1228 # are also param values get cloned properly.
1230 for key
,val
in ancestor
._children
.items():
1231 self
.add_child(key
, val(_memo
=memo_dict
))
1233 # Inherit parameter values from class using multidict so
1234 # individual value settings can be overridden but we still
1235 # inherit late changes to non-overridden class values.
1236 self
._values
= multidict(ancestor
._values
)
1237 self
._hr
_values
= multidict(ancestor
._hr
_values
)
1238 # clone SimObject-valued parameters
1239 for key
,val
in ancestor
._values
.items():
1240 val
= tryAsSimObjectOrVector(val
)
1242 self
._values
[key
] = val(_memo
=memo_dict
)
1244 # clone port references. no need to use a multidict here
1245 # since we will be creating new references for all ports.
1246 self
._port
_refs
= {}
1247 for key
,val
in ancestor
._port
_refs
.items():
1248 self
._port
_refs
[key
] = val
.clone(self
, memo_dict
)
1249 # apply attribute assignments from keyword args, if any
1250 for key
,val
in kwargs
.items():
1251 setattr(self
, key
, val
)
1253 # "Clone" the current instance by creating another instance of
1254 # this instance's class, but that inherits its parameter values
1255 # and port mappings from the current instance. If we're in a
1256 # "deep copy" recursive clone, check the _memo dict to see if
1257 # we've already cloned this instance.
1258 def __call__(self
, **kwargs
):
1259 memo_dict
= kwargs
.get('_memo')
1260 if memo_dict
is None:
1261 # no memo_dict: must be top-level clone operation.
1262 # this is only allowed at the root of a hierarchy
1264 raise RuntimeError("attempt to clone object %s " \
1265 "not at the root of a tree (parent = %s)" \
1266 % (self
, self
._parent
))
1267 # create a new dict and use that.
1269 kwargs
['_memo'] = memo_dict
1270 elif self
in memo_dict
:
1271 # clone already done & memoized
1272 return memo_dict
[self
]
1273 return self
.__class
__(_ancestor
= self
, **kwargs
)
1275 def _get_port_ref(self
, attr
):
1276 # Return reference that can be assigned to another port
1277 # via __setattr__. There is only ever one reference
1278 # object per port, but we create them lazily here.
1279 ref
= self
._port
_refs
.get(attr
)
1281 ref
= self
._ports
[attr
].makeRef(self
)
1282 self
._port
_refs
[attr
] = ref
1285 def __getattr__(self
, attr
):
1286 if attr
in self
._deprecated
_params
:
1287 dep_param
= self
._deprecated
_params
[attr
]
1288 dep_param
.printWarning(self
._name
, self
.__class
__.__name
__)
1289 return getattr(self
, self
._deprecated
_params
[attr
].newName
)
1291 if attr
in self
._ports
:
1292 return self
._get
_port
_ref
(attr
)
1294 if attr
in self
._values
:
1295 return self
._values
[attr
]
1297 if attr
in self
._children
:
1298 return self
._children
[attr
]
1300 # If the attribute exists on the C++ object, transparently
1301 # forward the reference there. This is typically used for
1302 # methods exported to Python (e.g., init(), and startup())
1303 if self
._ccObject
and hasattr(self
._ccObject
, attr
):
1304 return getattr(self
._ccObject
, attr
)
1306 err_string
= "object '%s' has no attribute '%s'" \
1307 % (self
.__class
__.__name
__, attr
)
1309 if not self
._ccObject
:
1310 err_string
+= "\n (C++ object is not yet constructed," \
1311 " so wrapped C++ methods are unavailable.)"
1313 raise AttributeError(err_string
)
1315 # Set attribute (called on foo.attr = value when foo is an
1316 # instance of class cls).
1317 def __setattr__(self
, attr
, value
):
1318 # normal processing for private attributes
1319 if attr
.startswith('_'):
1320 object.__setattr
__(self
, attr
, value
)
1323 if attr
in self
._deprecated
_params
:
1324 dep_param
= self
._deprecated
_params
[attr
]
1325 dep_param
.printWarning(self
._name
, self
.__class
__.__name
__)
1326 return setattr(self
, self
._deprecated
_params
[attr
].newName
, value
)
1328 if attr
in self
._ports
:
1329 # set up port connection
1330 self
._get
_port
_ref
(attr
).connect(value
)
1333 param
= self
._params
.get(attr
)
1337 value
= param
.convert(value
)
1338 except Exception as e
:
1339 msg
= "%s\nError setting param %s.%s to %s\n" % \
1340 (e
, self
.__class
__.__name
__, attr
, value
)
1343 self
._values
[attr
] = value
1344 # implicitly parent unparented objects assigned as params
1345 if isSimObjectOrVector(value
) and not value
.has_parent():
1346 self
.add_child(attr
, value
)
1347 # set the human-readable value dict if this is a param
1348 # with a literal value and is not being set as an object
1350 if not (isSimObjectOrVector(value
) or\
1351 isinstance(value
, m5
.proxy
.BaseProxy
)):
1352 self
._hr
_values
[attr
] = hr_value
1356 # if RHS is a SimObject, it's an implicit child assignment
1357 if isSimObjectOrSequence(value
):
1358 self
.add_child(attr
, value
)
1361 # no valid assignment... raise exception
1362 raise AttributeError("Class %s has no parameter %s" \
1363 % (self
.__class
__.__name
__, attr
))
1366 # this hack allows tacking a '[0]' onto parameters that may or may
1367 # not be vectors, and always getting the first element (e.g. cpus)
1368 def __getitem__(self
, key
):
1371 raise IndexError("Non-zero index '%s' to SimObject" % key
)
1373 # this hack allows us to iterate over a SimObject that may
1374 # not be a vector, so we can call a loop over it and get just one
1379 # Also implemented by SimObjectVector
1380 def clear_parent(self
, old_parent
):
1381 assert self
._parent
is old_parent
1384 # Also implemented by SimObjectVector
1385 def set_parent(self
, parent
, name
):
1386 self
._parent
= parent
1389 # Return parent object of this SimObject, not implemented by
1390 # SimObjectVector because the elements in a SimObjectVector may not share
1392 def get_parent(self
):
1395 # Also implemented by SimObjectVector
1399 # Also implemented by SimObjectVector
1400 def has_parent(self
):
1401 return self
._parent
is not None
1403 # clear out child with given name. This code is not likely to be exercised.
1404 # See comment in add_child.
1405 def clear_child(self
, name
):
1406 child
= self
._children
[name
]
1407 child
.clear_parent(self
)
1408 del self
._children
[name
]
1410 # Add a new child to this object.
1411 def add_child(self
, name
, child
):
1412 child
= coerceSimObjectOrVector(child
)
1413 if child
.has_parent():
1414 warn("add_child('%s'): child '%s' already has parent", name
,
1416 if name
in self
._children
:
1417 # This code path had an undiscovered bug that would make it fail
1418 # at runtime. It had been here for a long time and was only
1419 # exposed by a buggy script. Changes here will probably not be
1420 # exercised without specialized testing.
1421 self
.clear_child(name
)
1422 child
.set_parent(self
, name
)
1423 if not isNullPointer(child
):
1424 self
._children
[name
] = child
1426 # Take SimObject-valued parameters that haven't been explicitly
1427 # assigned as children and make them children of the object that
1428 # they were assigned to as a parameter value. This guarantees
1429 # that when we instantiate all the parameter objects we're still
1430 # inside the configuration hierarchy.
1431 def adoptOrphanParams(self
):
1432 for key
,val
in self
._values
.items():
1433 if not isSimObjectVector(val
) and isSimObjectSequence(val
):
1434 # need to convert raw SimObject sequences to
1435 # SimObjectVector class so we can call has_parent()
1436 val
= SimObjectVector(val
)
1437 self
._values
[key
] = val
1438 if isSimObjectOrVector(val
) and not val
.has_parent():
1439 warn("%s adopting orphan SimObject param '%s'", self
, key
)
1440 self
.add_child(key
, val
)
1443 if not self
._parent
:
1444 return '<orphan %s>' % self
.__class
__
1445 elif isinstance(self
._parent
, MetaSimObject
):
1446 return str(self
.__class
__)
1448 ppath
= self
._parent
.path()
1451 return ppath
+ "." + self
._name
1453 def path_list(self
):
1455 return self
._parent
.path_list() + [ self
._name
, ]
1457 # Don't include the root node
1463 def config_value(self
):
1469 def find_any(self
, ptype
):
1470 if isinstance(self
, ptype
):
1474 for child
in self
._children
.values():
1476 if hasattr(child
, '_visited'):
1477 visited
= getattr(child
, '_visited')
1479 if isinstance(child
, ptype
) and not visited
:
1480 if found_obj
!= None and child
!= found_obj
:
1481 raise AttributeError(
1482 'parent.any matched more than one: %s %s' % \
1483 (found_obj
.path
, child
.path
))
1485 # search param space
1486 for pname
,pdesc
in self
._params
.items():
1487 if issubclass(pdesc
.ptype
, ptype
):
1488 match_obj
= self
._values
[pname
]
1489 if found_obj
!= None and found_obj
!= match_obj
:
1490 raise AttributeError(
1491 'parent.any matched more than one: %s and %s' % \
1492 (found_obj
.path
, match_obj
.path
))
1493 found_obj
= match_obj
1494 return found_obj
, found_obj
!= None
1496 def find_all(self
, ptype
):
1499 for child
in self
._children
.values():
1500 # a child could be a list, so ensure we visit each item
1501 if isinstance(child
, list):
1506 for child
in children
:
1507 if isinstance(child
, ptype
) and not isproxy(child
) and \
1508 not isNullPointer(child
):
1510 if isSimObject(child
):
1511 # also add results from the child itself
1512 child_all
, done
= child
.find_all(ptype
)
1513 all
.update(dict(zip(child_all
, [done
] * len(child_all
))))
1514 # search param space
1515 for pname
,pdesc
in self
._params
.items():
1516 if issubclass(pdesc
.ptype
, ptype
):
1517 match_obj
= self
._values
[pname
]
1518 if not isproxy(match_obj
) and not isNullPointer(match_obj
):
1519 all
[match_obj
] = True
1520 # Also make sure to sort the keys based on the objects' path to
1521 # ensure that the order is the same on all hosts
1522 return sorted(all
.keys(), key
= lambda o
: o
.path()), True
1524 def unproxy(self
, base
):
1527 def unproxyParams(self
):
1528 for param
in self
._params
.keys():
1529 value
= self
._values
.get(param
)
1530 if value
!= None and isproxy(value
):
1532 value
= value
.unproxy(self
)
1534 print("Error in unproxying param '%s' of %s" %
1535 (param
, self
.path()))
1537 setattr(self
, param
, value
)
1539 # Unproxy ports in sorted order so that 'append' operations on
1540 # vector ports are done in a deterministic fashion.
1541 port_names
= list(self
._ports
.keys())
1543 for port_name
in port_names
:
1544 port
= self
._port
_refs
.get(port_name
)
1548 def print_ini(self
, ini_file
):
1549 print('[' + self
.path() + ']', file=ini_file
) # .ini section header
1551 instanceDict
[self
.path()] = self
1553 if hasattr(self
, 'type'):
1554 print('type=%s' % self
.type, file=ini_file
)
1556 if len(self
._children
.keys()):
1557 print('children=%s' %
1558 ' '.join(self
._children
[n
].get_name()
1559 for n
in sorted(self
._children
.keys())),
1562 for param
in sorted(self
._params
.keys()):
1563 value
= self
._values
.get(param
)
1565 print('%s=%s' % (param
, self
._values
[param
].ini_str()),
1568 for port_name
in sorted(self
._ports
.keys()):
1569 port
= self
._port
_refs
.get(port_name
, None)
1571 print('%s=%s' % (port_name
, port
.ini_str()), file=ini_file
)
1573 print(file=ini_file
) # blank line between objects
1575 # generate a tree of dictionaries expressing all the parameters in the
1576 # instantiated system for use by scripts that want to do power, thermal
1577 # visualization, and other similar tasks
1578 def get_config_as_dict(self
):
1580 if hasattr(self
, 'type'):
1582 if hasattr(self
, 'cxx_class'):
1583 d
.cxx_class
= self
.cxx_class
1584 # Add the name and path of this object to be able to link to
1586 d
.name
= self
.get_name()
1587 d
.path
= self
.path()
1589 for param
in sorted(self
._params
.keys()):
1590 value
= self
._values
.get(param
)
1592 d
[param
] = value
.config_value()
1594 for n
in sorted(self
._children
.keys()):
1595 child
= self
._children
[n
]
1596 # Use the name of the attribute (and not get_name()) as
1597 # the key in the JSON dictionary to capture the hierarchy
1598 # in the Python code that assembled this system
1599 d
[n
] = child
.get_config_as_dict()
1601 for port_name
in sorted(self
._ports
.keys()):
1602 port
= self
._port
_refs
.get(port_name
, None)
1604 # Represent each port with a dictionary containing the
1605 # prominent attributes
1606 d
[port_name
] = port
.get_config_as_dict()
1610 def getCCParams(self
):
1612 return self
._ccParams
1614 cc_params_struct
= getattr(m5
.internal
.params
, '%sParams' % self
.type)
1615 cc_params
= cc_params_struct()
1616 cc_params
.name
= str(self
)
1618 param_names
= list(self
._params
.keys())
1620 for param
in param_names
:
1621 value
= self
._values
.get(param
)
1623 fatal("%s.%s without default or user set value",
1626 value
= value
.getValue()
1627 if isinstance(self
._params
[param
], VectorParamDesc
):
1628 assert isinstance(value
, list)
1629 vec
= getattr(cc_params
, param
)
1631 # Some types are exposed as opaque types. They support
1632 # the append operation unlike the automatically
1634 if isinstance(vec
, list):
1635 setattr(cc_params
, param
, list(value
))
1638 getattr(cc_params
, param
).append(v
)
1640 setattr(cc_params
, param
, value
)
1642 port_names
= list(self
._ports
.keys())
1644 for port_name
in port_names
:
1645 port
= self
._port
_refs
.get(port_name
, None)
1647 port_count
= len(port
)
1650 setattr(cc_params
, 'port_' + port_name
+ '_connection_count',
1652 self
._ccParams
= cc_params
1653 return self
._ccParams
1655 # Get C++ object corresponding to this object, calling C++ if
1656 # necessary to construct it. Does *not* recursively create
1658 def getCCObject(self
):
1659 if not self
._ccObject
:
1660 # Make sure this object is in the configuration hierarchy
1661 if not self
._parent
and not isRoot(self
):
1662 raise RuntimeError("Attempt to instantiate orphan node")
1663 # Cycles in the configuration hierarchy are not supported. This
1664 # will catch the resulting recursion and stop.
1666 if not self
.abstract
:
1667 params
= self
.getCCParams()
1668 self
._ccObject
= params
.create()
1669 elif self
._ccObject
== -1:
1670 raise RuntimeError("%s: Cycle found in configuration hierarchy." \
1672 return self
._ccObject
1674 def descendants(self
):
1676 # The order of the dict is implementation dependent, so sort
1677 # it based on the key (name) to ensure the order is the same
1679 for (name
, child
) in sorted(self
._children
.items()):
1680 for obj
in child
.descendants():
1683 # Call C++ to create C++ object corresponding to this object
1684 def createCCObject(self
):
1686 self
.getCCObject() # force creation
1689 return self
.getCCObject()
1691 @cxxMethod(return_value_policy
="reference")
1692 def getPort(self
, if_name
, idx
):
1695 # Create C++ port connections corresponding to the connections in
1697 def connectPorts(self
):
1698 # Sort the ports based on their attribute name to ensure the
1699 # order is the same on all hosts
1700 for (attr
, portRef
) in sorted(self
._port
_refs
.items()):
1703 # Default function for generating the device structure.
1704 # Can be overloaded by the inheriting class
1705 def generateDeviceTree(self
, state
):
1706 return # return without yielding anything
1707 yield # make this function a (null) generator
1709 def recurseDeviceTree(self
, state
):
1710 for child
in self
._children
.values():
1711 for item
in child
: # For looping over SimObjectVectors
1712 for dt
in item
.generateDeviceTree(state
):
1715 # On a separate method otherwise certain buggy Python versions
1716 # would fail with: SyntaxError: unqualified exec is not allowed
1717 # in function 'apply_config'
1718 def _apply_config_get_dict(self
):
1720 child_name
: SimObjectCliWrapper(
1721 iter(self
._children
[child_name
]))
1722 for child_name
in self
._children
1725 def apply_config(self
, params
):
1727 exec a list of Python code strings contained in params.
1729 The only exposed globals to those strings are the child
1730 SimObjects of this node.
1732 This function is intended to allow users to modify SimObject
1733 parameters from the command line with Python statements.
1735 d
= self
._apply
_config
_get
_dict
()
1736 for param
in params
:
1739 def get_simobj(self
, simobj_path
):
1741 Get all sim objects that match a given string.
1743 The format is the same as that supported by SimObjectCliWrapper.
1745 :param simobj_path: Current state to be in.
1746 :type simobj_path: str
1748 d
= self
._apply
_config
_get
_dict
()
1749 return eval(simobj_path
, d
)
1751 # Function to provide to C++ so it can look up instances based on paths
1752 def resolveSimObject(name
):
1753 obj
= instanceDict
[name
]
1754 return obj
.getCCObject()
1756 def isSimObject(value
):
1757 return isinstance(value
, SimObject
)
1759 def isSimObjectClass(value
):
1760 return issubclass(value
, SimObject
)
1762 def isSimObjectVector(value
):
1763 return isinstance(value
, SimObjectVector
)
1765 def isSimObjectSequence(value
):
1766 if not isinstance(value
, (list, tuple)) or len(value
) == 0:
1770 if not isNullPointer(val
) and not isSimObject(val
):
1775 def isSimObjectOrSequence(value
):
1776 return isSimObject(value
) or isSimObjectSequence(value
)
1779 from m5
.objects
import Root
1780 return obj
and obj
is Root
.getInstance()
1782 def isSimObjectOrVector(value
):
1783 return isSimObject(value
) or isSimObjectVector(value
)
1785 def tryAsSimObjectOrVector(value
):
1786 if isSimObjectOrVector(value
):
1788 if isSimObjectSequence(value
):
1789 return SimObjectVector(value
)
1792 def coerceSimObjectOrVector(value
):
1793 value
= tryAsSimObjectOrVector(value
)
1795 raise TypeError("SimObject or SimObjectVector expected")
1798 baseClasses
= allClasses
.copy()
1799 baseInstances
= instanceDict
.copy()
1802 global allClasses
, instanceDict
, noCxxHeader
1804 allClasses
= baseClasses
.copy()
1805 instanceDict
= baseInstances
.copy()
1808 # __all__ defines the list of symbols that get exported when
1809 # 'from config import *' is invoked. Try to keep this reasonably
1810 # short to avoid polluting other namespaces.