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