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