1 # Copyright (c) 2017-2019 ARM Limited
4 # The license below extends only to copyright in the software and shall
5 # not be construed as granting a license to any other intellectual
6 # property including but not limited to intellectual property relating
7 # to a hardware implementation of the functionality of the software
8 # licensed hereunder. You may use the software subject to the license
9 # terms below provided that you ensure that this notice is replicated
10 # unmodified and in its entirety in all distributions of the software,
11 # modified or unmodified, in source code or in binary form.
13 # Copyright (c) 2004-2006 The Regents of The University of Michigan
14 # Copyright (c) 2010-20013 Advanced Micro Devices, Inc.
15 # Copyright (c) 2013 Mark D. Hill and David A. Wood
16 # All rights reserved.
18 # Redistribution and use in source and binary forms, with or without
19 # modification, are permitted provided that the following conditions are
20 # met: redistributions of source code must retain the above copyright
21 # notice, this list of conditions and the following disclaimer;
22 # redistributions in binary form must reproduce the above copyright
23 # notice, this list of conditions and the following disclaimer in the
24 # documentation and/or other materials provided with the distribution;
25 # neither the name of the copyright holders nor the names of its
26 # contributors may be used to endorse or promote products derived from
27 # this software without specific prior written permission.
29 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 # 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
== 'pybind_class':
659 return '_COLONS_'.join(cls
.cxx_class_path
)
661 if attr
in cls
._values
:
662 return cls
._values
[attr
]
664 if attr
in cls
._children
:
665 return cls
._children
[attr
]
668 return getattr(cls
.getCCClass(), attr
)
669 except AttributeError:
670 raise AttributeError(
671 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
))
677 return getattr(m5
.internal
.params
, cls
.pybind_class
)
679 # See ParamValue.cxx_predecls for description.
680 def cxx_predecls(cls
, code
):
681 code('#include "params/$cls.hh"')
683 def pybind_predecls(cls
, code
):
684 code('#include "${{cls.cxx_header}}"')
686 def pybind_decl(cls
, code
):
687 py_class_name
= cls
.pybind_class
689 # The 'local' attribute restricts us to the params declared in
690 # the object itself, not including inherited params (which
691 # will also be inherited from the base class's param struct
692 # here). Sort the params based on their key
693 params
= map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items()))
694 ports
= cls
._ports
.local
696 code('''#include "pybind11/pybind11.h"
697 #include "pybind11/stl.h"
699 #include "params/$cls.hh"
700 #include "python/pybind11/core.hh"
701 #include "sim/init.hh"
702 #include "sim/sim_object.hh"
704 #include "${{cls.cxx_header}}"
709 param
.pybind_predecls(code
)
711 code('''namespace py = pybind11;
714 module_init(py::module &m_internal)
716 py::module m = m_internal.def_submodule("param_${cls}");
720 code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
721 'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
722 'm, "${cls}Params")')
724 code('py::class_<${cls}Params, ' \
725 'std::unique_ptr<${cls}Params, py::nodelete>>(' \
726 'm, "${cls}Params")')
729 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
730 code('.def(py::init<>())')
731 code('.def("create", &${cls}Params::create)')
733 param_exports
= cls
.cxx_param_exports
+ [
735 for k
, v
in sorted(cls
._params
.local
.items())
737 PyBindProperty("port_%s_connection_count" % port
.name
)
738 for port
in ports
.values()
740 for exp
in param_exports
:
741 exp
.export(code
, "%sParams" % cls
)
748 if 'cxx_base' in cls
._value
_dict
:
749 # If the c++ base class implied by python inheritance was
750 # overridden, use that value.
752 bases
.append(cls
.cxx_base
)
754 # If not and if there was a SimObject base, use its c++ class
755 # as this class' base.
756 bases
.append(cls
._base
.cxx_class
)
757 # Add in any extra bases that were requested.
758 bases
.extend(cls
.cxx_extra_bases
)
761 base_str
= ", ".join(bases
)
762 code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
763 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
764 'm, "${py_class_name}")')
766 code('py::class_<${{cls.cxx_class}}, ' \
767 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
768 'm, "${py_class_name}")')
770 for exp
in cls
.cxx_exports
:
771 exp
.export(code
, cls
.cxx_class
)
778 code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
779 cls
, cls
._base
.type if cls
._base
else "")
781 _warned_about_nested_templates
= False
783 # Generate the C++ declaration (.hh file) for this SimObject's
784 # param struct. Called from src/SConscript.
785 def cxx_param_decl(cls
, code
):
786 # The 'local' attribute restricts us to the params declared in
787 # the object itself, not including inherited params (which
788 # will also be inherited from the base class's param struct
789 # here). Sort the params based on their key
790 params
= map(lambda k_v
: k_v
[1], sorted(cls
._params
.local
.items()))
791 ports
= cls
._ports
.local
793 ptypes
= [p
.ptype
for p
in params
]
795 print(cls
, p
, p
.ptype_str
)
799 class CxxClass(object):
800 def __init__(self
, sig
, template_params
=[]):
801 # Split the signature into its constituent parts. This could
802 # potentially be done with regular expressions, but
803 # it's simple enough to pick appart a class signature
805 parts
= sig
.split('<', 1)
809 # The signature had template arguments.
810 text
= parts
[1].rstrip(' \t\n>')
812 # Keep track of nesting to avoid splitting on ","s embedded
813 # in the arguments themselves.
818 if depth
> 0 and not \
819 self
._warned
_about
_nested
_templates
:
820 self
._warned
_about
_nested
_templates
= True
821 print('Nested template argument in cxx_class.'
822 ' This feature is largely untested and '
826 elif c
== ',' and depth
== 0:
827 t_args
.append(arg
.strip())
832 t_args
.append(arg
.strip())
833 # Split the non-template part on :: boundaries.
834 class_path
= base
.split('::')
836 # The namespaces are everything except the last part of the
838 self
.namespaces
= class_path
[:-1]
839 # And the class name is the last part.
840 self
.name
= class_path
[-1]
842 self
.template_params
= template_params
843 self
.template_arguments
= []
844 # Iterate through the template arguments and their values. This
845 # will likely break if parameter packs are used.
846 for arg
, param
in zip(t_args
, template_params
):
847 type_keys
= ('class', 'typename')
848 # If a parameter is a type, parse it recursively. Otherwise
849 # assume it's a constant, and store it verbatim.
850 if any(param
.strip().startswith(kw
) for kw
in type_keys
):
851 self
.template_arguments
.append(CxxClass(arg
))
853 self
.template_arguments
.append(arg
)
855 def declare(self
, code
):
856 # First declare any template argument types.
857 for arg
in self
.template_arguments
:
858 if isinstance(arg
, CxxClass
):
860 # Re-open the target namespace.
861 for ns
in self
.namespaces
:
862 code('namespace $ns {')
863 # If this is a class template...
864 if self
.template_params
:
865 code('template <${{", ".join(self.template_params)}}>')
866 # The actual class declaration.
867 code('class ${{self.name}};')
868 # Close the target namespaces.
869 for ns
in reversed(self
.namespaces
):
870 code('} // namespace $ns')
873 #ifndef __PARAMS__${cls}__
874 #define __PARAMS__${cls}__
879 # The base SimObject has a couple of params that get
880 # automatically set from Python without being declared through
881 # the normal Param mechanism; we slip them in here (needed
882 # predecls now, actual declarations below)
884 code('''#include <string>''')
886 cxx_class
= CxxClass(cls
._value
_dict
['cxx_class'],
887 cls
._value
_dict
['cxx_template_params'])
889 # A forward class declaration is sufficient since we are just
890 # declaring a pointer.
891 cxx_class
.declare(code
)
894 param
.cxx_predecls(code
)
895 for port
in ports
.values():
896 port
.cxx_predecls(code
)
900 code('#include "params/${{cls._base.type}}.hh"')
904 if issubclass(ptype
, Enum
):
905 code('#include "enums/${{ptype.__name__}}.hh"')
908 # now generate the actual param struct
909 code("struct ${cls}Params")
911 code(" : public ${{cls._base.type}}Params")
913 if not hasattr(cls
, 'abstract') or not cls
.abstract
:
914 if 'type' in cls
.__dict
__:
915 code(" ${{cls.cxx_type}} create();")
921 virtual ~SimObjectParams() {}
928 for port
in ports
.values():
935 code('#endif // __PARAMS__${cls}__')
938 # Generate the C++ declaration/definition files for this SimObject's
939 # param struct to allow C++ initialisation
940 def cxx_config_param_file(cls
, code
, is_header
):
941 createCxxConfigDirectoryEntryFile(code
, cls
.__name
__, cls
, is_header
)
944 # This *temporary* definition is required to support calls from the
945 # SimObject class definition to the MetaSimObject methods (in
946 # particular _set_param, which gets called for parameters with default
947 # values defined on the SimObject class itself). It will get
948 # overridden by the permanent definition (which requires that
949 # SimObject be defined) lower in this file.
950 def isSimObjectOrVector(value
):
953 def cxxMethod(*args
, **kwargs
):
954 """Decorator to export C++ functions to Python"""
958 override
= kwargs
.get("override", False)
959 cxx_name
= kwargs
.get("cxx_name", name
)
960 return_value_policy
= kwargs
.get("return_value_policy", None)
961 static
= kwargs
.get("static", False)
963 args
, varargs
, keywords
, defaults
= inspect
.getargspec(func
)
964 if varargs
or keywords
:
965 raise ValueError("Wrapped methods must not contain variable " \
968 # Create tuples of (argument, default)
970 args
= args
[:-len(defaults
)] + \
971 list(zip(args
[-len(defaults
):], defaults
))
972 # Don't include self in the argument list to PyBind
977 def cxx_call(self
, *args
, **kwargs
):
978 ccobj
= self
.getCCClass() if static
else self
.getCCObject()
979 return getattr(ccobj
, name
)(*args
, **kwargs
)
982 def py_call(self
, *args
, **kwargs
):
983 return func(self
, *args
, **kwargs
)
985 f
= py_call
if override
else cxx_call
986 f
.__pybind
= PyBindMethod(name
, cxx_name
=cxx_name
, args
=args
,
987 return_value_policy
=return_value_policy
,
994 elif len(args
) == 1 and len(kwargs
) == 0:
995 return decorate(*args
)
997 raise TypeError("One argument and no kwargs, or only kwargs expected")
999 # This class holds information about each simobject parameter
1000 # that should be displayed on the command line for use in the
1001 # configuration system.
1002 class ParamInfo(object):
1003 def __init__(self
, type, desc
, type_str
, example
, default_val
, access_str
):
1006 self
.type_str
= type_str
1007 self
.example_str
= example
1008 self
.default_val
= default_val
1009 # The string representation used to access this param through python.
1010 # The method to access this parameter presented on the command line may
1011 # be different, so this needs to be stored for later use.
1012 self
.access_str
= access_str
1015 # Make it so we can only set attributes at initialization time
1016 # and effectively make this a const object.
1017 def __setattr__(self
, name
, value
):
1018 if not "created" in self
.__dict
__:
1019 self
.__dict
__[name
] = value
1021 class SimObjectCliWrapperException(Exception):
1022 def __init__(self
, message
):
1023 super(Exception, self
).__init
__(message
)
1025 class SimObjectCliWrapper(object):
1027 Wrapper class to restrict operations that may be done
1028 from the command line on SimObjects.
1030 Only parameters may be set, and only children may be accessed.
1032 Slicing allows for multiple simultaneous assignment of items in
1036 def __init__(self
, sim_objects
):
1037 self
.__dict
__['_sim_objects'] = list(sim_objects
)
1039 def __getattr__(self
, key
):
1040 return SimObjectCliWrapper(sim_object
._children
[key
]
1041 for sim_object
in self
._sim
_objects
)
1043 def __setattr__(self
, key
, val
):
1044 for sim_object
in self
._sim
_objects
:
1045 if key
in sim_object
._params
:
1046 if sim_object
._params
[key
].isCmdLineSettable():
1047 setattr(sim_object
, key
, val
)
1049 raise SimObjectCliWrapperException(
1050 'tried to set or unsettable' \
1051 'object parameter: ' + key
)
1053 raise SimObjectCliWrapperException(
1054 'tried to set or access non-existent' \
1055 'object parameter: ' + key
)
1057 def __getitem__(self
, idx
):
1059 Extends the list() semantics to also allow tuples,
1060 for example object[1, 3] selects items 1 and 3.
1063 if isinstance(idx
, tuple):
1065 out
.extend(self
[t
]._sim
_objects
)
1067 if isinstance(idx
, int):
1068 _range
= range(idx
, idx
+ 1)
1069 elif not isinstance(idx
, slice):
1070 raise SimObjectCliWrapperException( \
1071 'invalid index type: ' + repr(idx
))
1072 for sim_object
in self
._sim
_objects
:
1073 if isinstance(idx
, slice):
1074 _range
= range(*idx
.indices(len(sim_object
)))
1075 out
.extend(sim_object
[i
] for i
in _range
)
1076 return SimObjectCliWrapper(out
)
1078 # The SimObject class is the root of the special hierarchy. Most of
1079 # the code in this class deals with the configuration hierarchy itself
1080 # (parent/child node relationships).
1081 class SimObject(object):
1082 # Specify metaclass. Any class inheriting from SimObject will
1083 # get this metaclass.
1084 __metaclass__
= MetaSimObject
1088 cxx_header
= "sim/sim_object.hh"
1089 cxx_extra_bases
= [ "Drainable", "Serializable" ]
1090 eventq_index
= Param
.UInt32(Parent
.eventq_index
, "Event Queue Index")
1093 PyBindMethod("init"),
1094 PyBindMethod("initState"),
1095 PyBindMethod("memInvalidate"),
1096 PyBindMethod("memWriteback"),
1097 PyBindMethod("regStats"),
1098 PyBindMethod("resetStats"),
1099 PyBindMethod("regProbePoints"),
1100 PyBindMethod("regProbeListeners"),
1101 PyBindMethod("startup"),
1104 cxx_param_exports
= [
1105 PyBindProperty("name"),
1109 def loadState(self
, cp
):
1110 """Load SimObject state from a checkpoint"""
1113 # Returns a dict of all the option strings that can be
1114 # generated as command line options for this simobject instance
1115 # by tracing all reachable params in the top level instance and
1116 # any children it contains.
1117 def enumerateParams(self
, flags_dict
= {},
1118 cmd_line_str
= "", access_str
= ""):
1119 if hasattr(self
, "_paramEnumed"):
1120 print("Cycle detected enumerating params")
1122 self
._paramEnumed
= True
1123 # Scan the children first to pick up all the objects in this SimObj
1124 for keys
in self
._children
:
1125 child
= self
._children
[keys
]
1126 next_cmdline_str
= cmd_line_str
+ keys
1127 next_access_str
= access_str
+ keys
1128 if not isSimObjectVector(child
):
1129 next_cmdline_str
= next_cmdline_str
+ "."
1130 next_access_str
= next_access_str
+ "."
1131 flags_dict
= child
.enumerateParams(flags_dict
,
1135 # Go through the simple params in the simobject in this level
1136 # of the simobject hierarchy and save information about the
1137 # parameter to be used for generating and processing command line
1138 # options to the simulator to set these parameters.
1139 for keys
,values
in self
._params
.items():
1140 if values
.isCmdLineSettable():
1142 ex_str
= values
.example_str()
1144 if isinstance(values
, VectorParamDesc
):
1145 type_str
= 'Vector_%s' % values
.ptype_str
1148 type_str
= '%s' % values
.ptype_str
1149 ptype
= values
.ptype
1151 if keys
in self
._hr
_values\
1152 and keys
in self
._values\
1153 and not isinstance(self
._values
[keys
],
1154 m5
.proxy
.BaseProxy
):
1155 cmd_str
= cmd_line_str
+ keys
1156 acc_str
= access_str
+ keys
1157 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1158 self
._params
[keys
].desc
, type_str
, ex_str
,
1159 values
.pretty_print(self
._hr
_values
[keys
]),
1161 elif not keys
in self
._hr
_values\
1162 and not keys
in self
._values
:
1164 cmd_str
= cmd_line_str
+ keys
1165 acc_str
= access_str
+ keys
1166 flags_dict
[cmd_str
] = ParamInfo(ptype
,
1167 self
._params
[keys
].desc
,
1168 type_str
, ex_str
, '', acc_str
)
1172 # Initialize new instance. For objects with SimObject-valued
1173 # children, we need to recursively clone the classes represented
1174 # by those param values as well in a consistent "deep copy"-style
1175 # fashion. That is, we want to make sure that each instance is
1176 # cloned only once, and that if there are multiple references to
1177 # the same original object, we end up with the corresponding
1178 # cloned references all pointing to the same cloned instance.
1179 def __init__(self
, **kwargs
):
1180 ancestor
= kwargs
.get('_ancestor')
1181 memo_dict
= kwargs
.get('_memo')
1182 if memo_dict
is None:
1183 # prepare to memoize any recursively instantiated objects
1186 # memoize me now to avoid problems with recursive calls
1187 memo_dict
[ancestor
] = self
1190 ancestor
= self
.__class
__
1191 ancestor
._instantiated
= True
1193 # initialize required attributes
1196 self
._ccObject
= None # pointer to C++ object
1197 self
._ccParams
= None
1198 self
._instantiated
= False # really "cloned"
1200 # Clone children specified at class level. No need for a
1201 # multidict here since we will be cloning everything.
1202 # Do children before parameter values so that children that
1203 # are also param values get cloned properly.
1205 for key
,val
in ancestor
._children
.items():
1206 self
.add_child(key
, val(_memo
=memo_dict
))
1208 # Inherit parameter values from class using multidict so
1209 # individual value settings can be overridden but we still
1210 # inherit late changes to non-overridden class values.
1211 self
._values
= multidict(ancestor
._values
)
1212 self
._hr
_values
= multidict(ancestor
._hr
_values
)
1213 # clone SimObject-valued parameters
1214 for key
,val
in ancestor
._values
.items():
1215 val
= tryAsSimObjectOrVector(val
)
1217 self
._values
[key
] = val(_memo
=memo_dict
)
1219 # clone port references. no need to use a multidict here
1220 # since we will be creating new references for all ports.
1221 self
._port
_refs
= {}
1222 for key
,val
in ancestor
._port
_refs
.items():
1223 self
._port
_refs
[key
] = val
.clone(self
, memo_dict
)
1224 # apply attribute assignments from keyword args, if any
1225 for key
,val
in kwargs
.items():
1226 setattr(self
, key
, val
)
1228 # "Clone" the current instance by creating another instance of
1229 # this instance's class, but that inherits its parameter values
1230 # and port mappings from the current instance. If we're in a
1231 # "deep copy" recursive clone, check the _memo dict to see if
1232 # we've already cloned this instance.
1233 def __call__(self
, **kwargs
):
1234 memo_dict
= kwargs
.get('_memo')
1235 if memo_dict
is None:
1236 # no memo_dict: must be top-level clone operation.
1237 # this is only allowed at the root of a hierarchy
1239 raise RuntimeError("attempt to clone object %s " \
1240 "not at the root of a tree (parent = %s)" \
1241 % (self
, self
._parent
))
1242 # create a new dict and use that.
1244 kwargs
['_memo'] = memo_dict
1245 elif self
in memo_dict
:
1246 # clone already done & memoized
1247 return memo_dict
[self
]
1248 return self
.__class
__(_ancestor
= self
, **kwargs
)
1250 def _get_port_ref(self
, attr
):
1251 # Return reference that can be assigned to another port
1252 # via __setattr__. There is only ever one reference
1253 # object per port, but we create them lazily here.
1254 ref
= self
._port
_refs
.get(attr
)
1256 ref
= self
._ports
[attr
].makeRef(self
)
1257 self
._port
_refs
[attr
] = ref
1260 def __getattr__(self
, attr
):
1261 if attr
in self
._ports
:
1262 return self
._get
_port
_ref
(attr
)
1264 if attr
in self
._values
:
1265 return self
._values
[attr
]
1267 if attr
in self
._children
:
1268 return self
._children
[attr
]
1270 # If the attribute exists on the C++ object, transparently
1271 # forward the reference there. This is typically used for
1272 # methods exported to Python (e.g., init(), and startup())
1273 if self
._ccObject
and hasattr(self
._ccObject
, attr
):
1274 return getattr(self
._ccObject
, attr
)
1276 err_string
= "object '%s' has no attribute '%s'" \
1277 % (self
.__class
__.__name
__, attr
)
1279 if not self
._ccObject
:
1280 err_string
+= "\n (C++ object is not yet constructed," \
1281 " so wrapped C++ methods are unavailable.)"
1283 raise AttributeError(err_string
)
1285 # Set attribute (called on foo.attr = value when foo is an
1286 # instance of class cls).
1287 def __setattr__(self
, attr
, value
):
1288 # normal processing for private attributes
1289 if attr
.startswith('_'):
1290 object.__setattr
__(self
, attr
, value
)
1293 if attr
in self
._ports
:
1294 # set up port connection
1295 self
._get
_port
_ref
(attr
).connect(value
)
1298 param
= self
._params
.get(attr
)
1302 value
= param
.convert(value
)
1303 except Exception as e
:
1304 msg
= "%s\nError setting param %s.%s to %s\n" % \
1305 (e
, self
.__class
__.__name
__, attr
, value
)
1308 self
._values
[attr
] = value
1309 # implicitly parent unparented objects assigned as params
1310 if isSimObjectOrVector(value
) and not value
.has_parent():
1311 self
.add_child(attr
, value
)
1312 # set the human-readable value dict if this is a param
1313 # with a literal value and is not being set as an object
1315 if not (isSimObjectOrVector(value
) or\
1316 isinstance(value
, m5
.proxy
.BaseProxy
)):
1317 self
._hr
_values
[attr
] = hr_value
1321 # if RHS is a SimObject, it's an implicit child assignment
1322 if isSimObjectOrSequence(value
):
1323 self
.add_child(attr
, value
)
1326 # no valid assignment... raise exception
1327 raise AttributeError("Class %s has no parameter %s" \
1328 % (self
.__class
__.__name
__, attr
))
1331 # this hack allows tacking a '[0]' onto parameters that may or may
1332 # not be vectors, and always getting the first element (e.g. cpus)
1333 def __getitem__(self
, key
):
1336 raise IndexError("Non-zero index '%s' to SimObject" % key
)
1338 # this hack allows us to iterate over a SimObject that may
1339 # not be a vector, so we can call a loop over it and get just one
1344 # Also implemented by SimObjectVector
1345 def clear_parent(self
, old_parent
):
1346 assert self
._parent
is old_parent
1349 # Also implemented by SimObjectVector
1350 def set_parent(self
, parent
, name
):
1351 self
._parent
= parent
1354 # Return parent object of this SimObject, not implemented by
1355 # SimObjectVector because the elements in a SimObjectVector may not share
1357 def get_parent(self
):
1360 # Also implemented by SimObjectVector
1364 # Also implemented by SimObjectVector
1365 def has_parent(self
):
1366 return self
._parent
is not None
1368 # clear out child with given name. This code is not likely to be exercised.
1369 # See comment in add_child.
1370 def clear_child(self
, name
):
1371 child
= self
._children
[name
]
1372 child
.clear_parent(self
)
1373 del self
._children
[name
]
1375 # Add a new child to this object.
1376 def add_child(self
, name
, child
):
1377 child
= coerceSimObjectOrVector(child
)
1378 if child
.has_parent():
1379 warn("add_child('%s'): child '%s' already has parent", name
,
1381 if name
in self
._children
:
1382 # This code path had an undiscovered bug that would make it fail
1383 # at runtime. It had been here for a long time and was only
1384 # exposed by a buggy script. Changes here will probably not be
1385 # exercised without specialized testing.
1386 self
.clear_child(name
)
1387 child
.set_parent(self
, name
)
1388 if not isNullPointer(child
):
1389 self
._children
[name
] = child
1391 # Take SimObject-valued parameters that haven't been explicitly
1392 # assigned as children and make them children of the object that
1393 # they were assigned to as a parameter value. This guarantees
1394 # that when we instantiate all the parameter objects we're still
1395 # inside the configuration hierarchy.
1396 def adoptOrphanParams(self
):
1397 for key
,val
in self
._values
.items():
1398 if not isSimObjectVector(val
) and isSimObjectSequence(val
):
1399 # need to convert raw SimObject sequences to
1400 # SimObjectVector class so we can call has_parent()
1401 val
= SimObjectVector(val
)
1402 self
._values
[key
] = val
1403 if isSimObjectOrVector(val
) and not val
.has_parent():
1404 warn("%s adopting orphan SimObject param '%s'", self
, key
)
1405 self
.add_child(key
, val
)
1408 if not self
._parent
:
1409 return '<orphan %s>' % self
.__class
__
1410 elif isinstance(self
._parent
, MetaSimObject
):
1411 return str(self
.__class
__)
1413 ppath
= self
._parent
.path()
1416 return ppath
+ "." + self
._name
1421 def config_value(self
):
1427 def find_any(self
, ptype
):
1428 if isinstance(self
, ptype
):
1432 for child
in self
._children
.values():
1434 if hasattr(child
, '_visited'):
1435 visited
= getattr(child
, '_visited')
1437 if isinstance(child
, ptype
) and not visited
:
1438 if found_obj
!= None and child
!= found_obj
:
1439 raise AttributeError(
1440 'parent.any matched more than one: %s %s' % \
1441 (found_obj
.path
, child
.path
))
1443 # search param space
1444 for pname
,pdesc
in self
._params
.items():
1445 if issubclass(pdesc
.ptype
, ptype
):
1446 match_obj
= self
._values
[pname
]
1447 if found_obj
!= None and found_obj
!= match_obj
:
1448 raise AttributeError(
1449 'parent.any matched more than one: %s and %s' % \
1450 (found_obj
.path
, match_obj
.path
))
1451 found_obj
= match_obj
1452 return found_obj
, found_obj
!= None
1454 def find_all(self
, ptype
):
1457 for child
in self
._children
.values():
1458 # a child could be a list, so ensure we visit each item
1459 if isinstance(child
, list):
1464 for child
in children
:
1465 if isinstance(child
, ptype
) and not isproxy(child
) and \
1466 not isNullPointer(child
):
1468 if isSimObject(child
):
1469 # also add results from the child itself
1470 child_all
, done
= child
.find_all(ptype
)
1471 all
.update(dict(zip(child_all
, [done
] * len(child_all
))))
1472 # search param space
1473 for pname
,pdesc
in self
._params
.items():
1474 if issubclass(pdesc
.ptype
, ptype
):
1475 match_obj
= self
._values
[pname
]
1476 if not isproxy(match_obj
) and not isNullPointer(match_obj
):
1477 all
[match_obj
] = True
1478 # Also make sure to sort the keys based on the objects' path to
1479 # ensure that the order is the same on all hosts
1480 return sorted(all
.keys(), key
= lambda o
: o
.path()), True
1482 def unproxy(self
, base
):
1485 def unproxyParams(self
):
1486 for param
in self
._params
.keys():
1487 value
= self
._values
.get(param
)
1488 if value
!= None and isproxy(value
):
1490 value
= value
.unproxy(self
)
1492 print("Error in unproxying param '%s' of %s" %
1493 (param
, self
.path()))
1495 setattr(self
, param
, value
)
1497 # Unproxy ports in sorted order so that 'append' operations on
1498 # vector ports are done in a deterministic fashion.
1499 port_names
= list(self
._ports
.keys())
1501 for port_name
in port_names
:
1502 port
= self
._port
_refs
.get(port_name
)
1506 def print_ini(self
, ini_file
):
1507 print('[' + self
.path() + ']', file=ini_file
) # .ini section header
1509 instanceDict
[self
.path()] = self
1511 if hasattr(self
, 'type'):
1512 print('type=%s' % self
.type, file=ini_file
)
1514 if len(self
._children
.keys()):
1515 print('children=%s' %
1516 ' '.join(self
._children
[n
].get_name()
1517 for n
in sorted(self
._children
.keys())),
1520 for param
in sorted(self
._params
.keys()):
1521 value
= self
._values
.get(param
)
1523 print('%s=%s' % (param
, self
._values
[param
].ini_str()),
1526 for port_name
in sorted(self
._ports
.keys()):
1527 port
= self
._port
_refs
.get(port_name
, None)
1529 print('%s=%s' % (port_name
, port
.ini_str()), file=ini_file
)
1531 print(file=ini_file
) # blank line between objects
1533 # generate a tree of dictionaries expressing all the parameters in the
1534 # instantiated system for use by scripts that want to do power, thermal
1535 # visualization, and other similar tasks
1536 def get_config_as_dict(self
):
1538 if hasattr(self
, 'type'):
1540 if hasattr(self
, 'cxx_class'):
1541 d
.cxx_class
= self
.cxx_class
1542 # Add the name and path of this object to be able to link to
1544 d
.name
= self
.get_name()
1545 d
.path
= self
.path()
1547 for param
in sorted(self
._params
.keys()):
1548 value
= self
._values
.get(param
)
1550 d
[param
] = value
.config_value()
1552 for n
in sorted(self
._children
.keys()):
1553 child
= self
._children
[n
]
1554 # Use the name of the attribute (and not get_name()) as
1555 # the key in the JSON dictionary to capture the hierarchy
1556 # in the Python code that assembled this system
1557 d
[n
] = child
.get_config_as_dict()
1559 for port_name
in sorted(self
._ports
.keys()):
1560 port
= self
._port
_refs
.get(port_name
, None)
1562 # Represent each port with a dictionary containing the
1563 # prominent attributes
1564 d
[port_name
] = port
.get_config_as_dict()
1568 def getCCParams(self
):
1570 return self
._ccParams
1572 cc_params_struct
= getattr(m5
.internal
.params
, '%sParams' % self
.type)
1573 cc_params
= cc_params_struct()
1574 cc_params
.name
= str(self
)
1576 param_names
= list(self
._params
.keys())
1578 for param
in param_names
:
1579 value
= self
._values
.get(param
)
1581 fatal("%s.%s without default or user set value",
1584 value
= value
.getValue()
1585 if isinstance(self
._params
[param
], VectorParamDesc
):
1586 assert isinstance(value
, list)
1587 vec
= getattr(cc_params
, param
)
1589 # Some types are exposed as opaque types. They support
1590 # the append operation unlike the automatically
1592 if isinstance(vec
, list):
1593 setattr(cc_params
, param
, list(value
))
1596 getattr(cc_params
, param
).append(v
)
1598 setattr(cc_params
, param
, value
)
1600 port_names
= list(self
._ports
.keys())
1602 for port_name
in port_names
:
1603 port
= self
._port
_refs
.get(port_name
, None)
1605 port_count
= len(port
)
1608 setattr(cc_params
, 'port_' + port_name
+ '_connection_count',
1610 self
._ccParams
= cc_params
1611 return self
._ccParams
1613 # Get C++ object corresponding to this object, calling C++ if
1614 # necessary to construct it. Does *not* recursively create
1616 def getCCObject(self
):
1617 if not self
._ccObject
:
1618 # Make sure this object is in the configuration hierarchy
1619 if not self
._parent
and not isRoot(self
):
1620 raise RuntimeError("Attempt to instantiate orphan node")
1621 # Cycles in the configuration hierarchy are not supported. This
1622 # will catch the resulting recursion and stop.
1624 if not self
.abstract
:
1625 params
= self
.getCCParams()
1626 self
._ccObject
= params
.create()
1627 elif self
._ccObject
== -1:
1628 raise RuntimeError("%s: Cycle found in configuration hierarchy." \
1630 return self
._ccObject
1632 def descendants(self
):
1634 # The order of the dict is implementation dependent, so sort
1635 # it based on the key (name) to ensure the order is the same
1637 for (name
, child
) in sorted(self
._children
.items()):
1638 for obj
in child
.descendants():
1641 # Call C++ to create C++ object corresponding to this object
1642 def createCCObject(self
):
1644 self
.getCCObject() # force creation
1647 return self
.getCCObject()
1649 @cxxMethod(return_value_policy
="reference")
1650 def getPort(self
, if_name
, idx
):
1653 # Create C++ port connections corresponding to the connections in
1655 def connectPorts(self
):
1656 # Sort the ports based on their attribute name to ensure the
1657 # order is the same on all hosts
1658 for (attr
, portRef
) in sorted(self
._port
_refs
.items()):
1661 # Default function for generating the device structure.
1662 # Can be overloaded by the inheriting class
1663 def generateDeviceTree(self
, state
):
1664 return # return without yielding anything
1665 yield # make this function a (null) generator
1667 def recurseDeviceTree(self
, state
):
1668 for child
in self
._children
.values():
1669 for item
in child
: # For looping over SimObjectVectors
1670 for dt
in item
.generateDeviceTree(state
):
1673 # On a separate method otherwise certain buggy Python versions
1674 # would fail with: SyntaxError: unqualified exec is not allowed
1675 # in function 'apply_config'
1676 def _apply_config_get_dict(self
):
1678 child_name
: SimObjectCliWrapper(
1679 iter(self
._children
[child_name
]))
1680 for child_name
in self
._children
1683 def apply_config(self
, params
):
1685 exec a list of Python code strings contained in params.
1687 The only exposed globals to those strings are the child
1688 SimObjects of this node.
1690 This function is intended to allow users to modify SimObject
1691 parameters from the command line with Python statements.
1693 d
= self
._apply
_config
_get
_dict
()
1694 for param
in params
:
1697 # Function to provide to C++ so it can look up instances based on paths
1698 def resolveSimObject(name
):
1699 obj
= instanceDict
[name
]
1700 return obj
.getCCObject()
1702 def isSimObject(value
):
1703 return isinstance(value
, SimObject
)
1705 def isSimObjectClass(value
):
1706 return issubclass(value
, SimObject
)
1708 def isSimObjectVector(value
):
1709 return isinstance(value
, SimObjectVector
)
1711 def isSimObjectSequence(value
):
1712 if not isinstance(value
, (list, tuple)) or len(value
) == 0:
1716 if not isNullPointer(val
) and not isSimObject(val
):
1721 def isSimObjectOrSequence(value
):
1722 return isSimObject(value
) or isSimObjectSequence(value
)
1725 from m5
.objects
import Root
1726 return obj
and obj
is Root
.getInstance()
1728 def isSimObjectOrVector(value
):
1729 return isSimObject(value
) or isSimObjectVector(value
)
1731 def tryAsSimObjectOrVector(value
):
1732 if isSimObjectOrVector(value
):
1734 if isSimObjectSequence(value
):
1735 return SimObjectVector(value
)
1738 def coerceSimObjectOrVector(value
):
1739 value
= tryAsSimObjectOrVector(value
)
1741 raise TypeError("SimObject or SimObjectVector expected")
1744 baseClasses
= allClasses
.copy()
1745 baseInstances
= instanceDict
.copy()
1748 global allClasses
, instanceDict
, noCxxHeader
1750 allClasses
= baseClasses
.copy()
1751 instanceDict
= baseInstances
.copy()
1754 # __all__ defines the list of symbols that get exported when
1755 # 'from config import *' is invoked. Try to keep this reasonably
1756 # short to avoid polluting other namespaces.