misc: Replaced master/slave terminology
[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_requestor = port.role == 'GEM5 REQUESTOR'
190
191 code('ports["%s"] = new PortDesc("%s", %s, %s);' %
192 (port.name, port.name, cxx_bool(is_vector),
193 cxx_bool(is_requestor)))
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 # Parameter names that are deprecated. Dict[str, DeprecatedParam]
471 # The key is the "old_name" so that when the old_name is used in
472 # python config files, we will use the DeprecatedParam object to
473 # translate to the new type.
474 cls._deprecated_params = multidict()
475
476 # class or instance attributes
477 cls._values = multidict() # param values
478 cls._hr_values = multidict() # human readable param values
479 cls._children = multidict() # SimObject children
480 cls._port_refs = multidict() # port ref objects
481 cls._instantiated = False # really instantiated, cloned, or subclassed
482
483 # We don't support multiple inheritance of sim objects. If you want
484 # to, you must fix multidict to deal with it properly. Non sim-objects
485 # are ok, though
486 bTotal = 0
487 for c in bases:
488 if isinstance(c, MetaSimObject):
489 bTotal += 1
490 if bTotal > 1:
491 raise TypeError(
492 "SimObjects do not support multiple inheritance")
493
494 base = bases[0]
495
496 # Set up general inheritance via multidicts. A subclass will
497 # inherit all its settings from the base class. The only time
498 # the following is not true is when we define the SimObject
499 # class itself (in which case the multidicts have no parent).
500 if isinstance(base, MetaSimObject):
501 cls._base = base
502 cls._params.parent = base._params
503 cls._ports.parent = base._ports
504 cls._deprecated_params.parent = base._deprecated_params
505 cls._values.parent = base._values
506 cls._hr_values.parent = base._hr_values
507 cls._children.parent = base._children
508 cls._port_refs.parent = base._port_refs
509 # mark base as having been subclassed
510 base._instantiated = True
511 else:
512 cls._base = None
513
514 # default keyword values
515 if 'type' in cls._value_dict:
516 if 'cxx_class' not in cls._value_dict:
517 cls._value_dict['cxx_class'] = cls._value_dict['type']
518
519 cls._value_dict['cxx_type'] = '%s *' % cls._value_dict['cxx_class']
520
521 if 'cxx_header' not in cls._value_dict:
522 global noCxxHeader
523 noCxxHeader = True
524 warn("No header file specified for SimObject: %s", name)
525
526 # Now process the _value_dict items. They could be defining
527 # new (or overriding existing) parameters or ports, setting
528 # class keywords (e.g., 'abstract'), or setting parameter
529 # values or port bindings. The first 3 can only be set when
530 # the class is defined, so we handle them here. The others
531 # can be set later too, so just emulate that by calling
532 # setattr().
533 for key,val in cls._value_dict.items():
534 # param descriptions
535 if isinstance(val, ParamDesc):
536 cls._new_param(key, val)
537
538 # port objects
539 elif isinstance(val, Port):
540 cls._new_port(key, val)
541
542 # Deprecated variable names
543 elif isinstance(val, DeprecatedParam):
544 new_name, new_val = cls._get_param_by_value(val.newParam)
545 # Note: We don't know the (string) name of this variable until
546 # here, so now we can finish setting up the dep_param.
547 val.oldName = key
548 val.newName = new_name
549 cls._deprecated_params[key] = val
550
551 # init-time-only keywords
552 elif key in cls.init_keywords:
553 cls._set_keyword(key, val, cls.init_keywords[key])
554
555 # default: use normal path (ends up in __setattr__)
556 else:
557 setattr(cls, key, val)
558
559 def _set_keyword(cls, keyword, val, kwtype):
560 if not isinstance(val, kwtype):
561 raise TypeError('keyword %s has bad type %s (expecting %s)' % \
562 (keyword, type(val), kwtype))
563 if isinstance(val, FunctionType):
564 val = classmethod(val)
565 type.__setattr__(cls, keyword, val)
566
567 def _new_param(cls, name, pdesc):
568 # each param desc should be uniquely assigned to one variable
569 assert(not hasattr(pdesc, 'name'))
570 pdesc.name = name
571 cls._params[name] = pdesc
572 if hasattr(pdesc, 'default'):
573 cls._set_param(name, pdesc.default, pdesc)
574
575 def _set_param(cls, name, value, param):
576 assert(param.name == name)
577 try:
578 hr_value = value
579 value = param.convert(value)
580 except Exception as e:
581 msg = "%s\nError setting param %s.%s to %s\n" % \
582 (e, cls.__name__, name, value)
583 e.args = (msg, )
584 raise
585 cls._values[name] = value
586 # if param value is a SimObject, make it a child too, so that
587 # it gets cloned properly when the class is instantiated
588 if isSimObjectOrVector(value) and not value.has_parent():
589 cls._add_cls_child(name, value)
590 # update human-readable values of the param if it has a literal
591 # value and is not an object or proxy.
592 if not (isSimObjectOrVector(value) or\
593 isinstance(value, m5.proxy.BaseProxy)):
594 cls._hr_values[name] = hr_value
595
596 def _add_cls_child(cls, name, child):
597 # It's a little funky to have a class as a parent, but these
598 # objects should never be instantiated (only cloned, which
599 # clears the parent pointer), and this makes it clear that the
600 # object is not an orphan and can provide better error
601 # messages.
602 child.set_parent(cls, name)
603 if not isNullPointer(child):
604 cls._children[name] = child
605
606 def _new_port(cls, name, port):
607 # each port should be uniquely assigned to one variable
608 assert(not hasattr(port, 'name'))
609 port.name = name
610 cls._ports[name] = port
611
612 # same as _get_port_ref, effectively, but for classes
613 def _cls_get_port_ref(cls, attr):
614 # Return reference that can be assigned to another port
615 # via __setattr__. There is only ever one reference
616 # object per port, but we create them lazily here.
617 ref = cls._port_refs.get(attr)
618 if not ref:
619 ref = cls._ports[attr].makeRef(cls)
620 cls._port_refs[attr] = ref
621 return ref
622
623 def _get_param_by_value(cls, value):
624 """Given an object, value, return the name and the value from the
625 internal list of parameter values. If this value can't be found, raise
626 a runtime error. This will search both the current object and its
627 parents.
628 """
629 for k,v in cls._value_dict.items():
630 if v == value:
631 return k,v
632 raise RuntimeError("Cannot find parameter {} in parameter list"
633 .format(value))
634
635 # Set attribute (called on foo.attr = value when foo is an
636 # instance of class cls).
637 def __setattr__(cls, attr, value):
638 # normal processing for private attributes
639 if public_value(attr, value):
640 type.__setattr__(cls, attr, value)
641 return
642
643 if attr in cls.keywords:
644 cls._set_keyword(attr, value, cls.keywords[attr])
645 return
646
647 if attr in cls._ports:
648 cls._cls_get_port_ref(attr).connect(value)
649 return
650
651 if isSimObjectOrSequence(value) and cls._instantiated:
652 raise RuntimeError(
653 "cannot set SimObject parameter '%s' after\n" \
654 " class %s has been instantiated or subclassed" \
655 % (attr, cls.__name__))
656
657 # check for param
658 param = cls._params.get(attr)
659 if param:
660 cls._set_param(attr, value, param)
661 return
662
663 if isSimObjectOrSequence(value):
664 # If RHS is a SimObject, it's an implicit child assignment.
665 cls._add_cls_child(attr, coerceSimObjectOrVector(value))
666 return
667
668 # no valid assignment... raise exception
669 raise AttributeError(
670 "Class %s has no parameter \'%s\'" % (cls.__name__, attr))
671
672 def __getattr__(cls, attr):
673 if attr == 'cxx_class_path':
674 return cls.cxx_class.split('::')
675
676 if attr == 'cxx_class_name':
677 return cls.cxx_class_path[-1]
678
679 if attr == 'cxx_namespaces':
680 return cls.cxx_class_path[:-1]
681
682 if attr == 'pybind_class':
683 return '_COLONS_'.join(cls.cxx_class_path)
684
685 if attr in cls._values:
686 return cls._values[attr]
687
688 if attr in cls._children:
689 return cls._children[attr]
690
691 try:
692 return getattr(cls.getCCClass(), attr)
693 except AttributeError:
694 raise AttributeError(
695 "object '%s' has no attribute '%s'" % (cls.__name__, attr))
696
697 def __str__(cls):
698 return cls.__name__
699
700 def getCCClass(cls):
701 return getattr(m5.internal.params, cls.pybind_class)
702
703 # See ParamValue.cxx_predecls for description.
704 def cxx_predecls(cls, code):
705 code('#include "params/$cls.hh"')
706
707 def pybind_predecls(cls, code):
708 code('#include "${{cls.cxx_header}}"')
709
710 def pybind_decl(cls, code):
711 py_class_name = cls.pybind_class
712
713 # The 'local' attribute restricts us to the params declared in
714 # the object itself, not including inherited params (which
715 # will also be inherited from the base class's param struct
716 # here). Sort the params based on their key
717 params = list(map(lambda k_v: k_v[1], sorted(cls._params.local.items())))
718 ports = cls._ports.local
719
720 code('''#include "pybind11/pybind11.h"
721 #include "pybind11/stl.h"
722
723 #include "params/$cls.hh"
724 #include "python/pybind11/core.hh"
725 #include "sim/init.hh"
726 #include "sim/sim_object.hh"
727
728 #include "${{cls.cxx_header}}"
729
730 ''')
731
732 for param in params:
733 param.pybind_predecls(code)
734
735 code('''namespace py = pybind11;
736
737 static void
738 module_init(py::module &m_internal)
739 {
740 py::module m = m_internal.def_submodule("param_${cls}");
741 ''')
742 code.indent()
743 if cls._base:
744 code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
745 'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
746 'm, "${cls}Params")')
747 else:
748 code('py::class_<${cls}Params, ' \
749 'std::unique_ptr<${cls}Params, py::nodelete>>(' \
750 'm, "${cls}Params")')
751
752 code.indent()
753 if not hasattr(cls, 'abstract') or not cls.abstract:
754 code('.def(py::init<>())')
755 code('.def("create", &${cls}Params::create)')
756
757 param_exports = cls.cxx_param_exports + [
758 PyBindProperty(k)
759 for k, v in sorted(cls._params.local.items())
760 ] + [
761 PyBindProperty("port_%s_connection_count" % port.name)
762 for port in ports.values()
763 ]
764 for exp in param_exports:
765 exp.export(code, "%sParams" % cls)
766
767 code(';')
768 code()
769 code.dedent()
770
771 bases = []
772 if 'cxx_base' in cls._value_dict:
773 # If the c++ base class implied by python inheritance was
774 # overridden, use that value.
775 if cls.cxx_base:
776 bases.append(cls.cxx_base)
777 elif cls._base:
778 # If not and if there was a SimObject base, use its c++ class
779 # as this class' base.
780 bases.append(cls._base.cxx_class)
781 # Add in any extra bases that were requested.
782 bases.extend(cls.cxx_extra_bases)
783
784 if bases:
785 base_str = ", ".join(bases)
786 code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
787 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
788 'm, "${py_class_name}")')
789 else:
790 code('py::class_<${{cls.cxx_class}}, ' \
791 'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
792 'm, "${py_class_name}")')
793 code.indent()
794 for exp in cls.cxx_exports:
795 exp.export(code, cls.cxx_class)
796 code(';')
797 code.dedent()
798 code()
799 code.dedent()
800 code('}')
801 code()
802 code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");',
803 cls, cls._base.type if cls._base else "")
804
805 _warned_about_nested_templates = False
806
807 # Generate the C++ declaration (.hh file) for this SimObject's
808 # param struct. Called from src/SConscript.
809 def cxx_param_decl(cls, code):
810 # The 'local' attribute restricts us to the params declared in
811 # the object itself, not including inherited params (which
812 # will also be inherited from the base class's param struct
813 # here). Sort the params based on their key
814 params = list(map(lambda k_v: k_v[1], sorted(cls._params.local.items())))
815 ports = cls._ports.local
816 try:
817 ptypes = [p.ptype for p in params]
818 except:
819 print(cls, p, p.ptype_str)
820 print(params)
821 raise
822
823 class CxxClass(object):
824 def __init__(self, sig, template_params=[]):
825 # Split the signature into its constituent parts. This could
826 # potentially be done with regular expressions, but
827 # it's simple enough to pick appart a class signature
828 # manually.
829 parts = sig.split('<', 1)
830 base = parts[0]
831 t_args = []
832 if len(parts) > 1:
833 # The signature had template arguments.
834 text = parts[1].rstrip(' \t\n>')
835 arg = ''
836 # Keep track of nesting to avoid splitting on ","s embedded
837 # in the arguments themselves.
838 depth = 0
839 for c in text:
840 if c == '<':
841 depth = depth + 1
842 if depth > 0 and not \
843 self._warned_about_nested_templates:
844 self._warned_about_nested_templates = True
845 print('Nested template argument in cxx_class.'
846 ' This feature is largely untested and '
847 ' may not work.')
848 elif c == '>':
849 depth = depth - 1
850 elif c == ',' and depth == 0:
851 t_args.append(arg.strip())
852 arg = ''
853 else:
854 arg = arg + c
855 if arg:
856 t_args.append(arg.strip())
857 # Split the non-template part on :: boundaries.
858 class_path = base.split('::')
859
860 # The namespaces are everything except the last part of the
861 # class path.
862 self.namespaces = class_path[:-1]
863 # And the class name is the last part.
864 self.name = class_path[-1]
865
866 self.template_params = template_params
867 self.template_arguments = []
868 # Iterate through the template arguments and their values. This
869 # will likely break if parameter packs are used.
870 for arg, param in zip(t_args, template_params):
871 type_keys = ('class', 'typename')
872 # If a parameter is a type, parse it recursively. Otherwise
873 # assume it's a constant, and store it verbatim.
874 if any(param.strip().startswith(kw) for kw in type_keys):
875 self.template_arguments.append(CxxClass(arg))
876 else:
877 self.template_arguments.append(arg)
878
879 def declare(self, code):
880 # First declare any template argument types.
881 for arg in self.template_arguments:
882 if isinstance(arg, CxxClass):
883 arg.declare(code)
884 # Re-open the target namespace.
885 for ns in self.namespaces:
886 code('namespace $ns {')
887 # If this is a class template...
888 if self.template_params:
889 code('template <${{", ".join(self.template_params)}}>')
890 # The actual class declaration.
891 code('class ${{self.name}};')
892 # Close the target namespaces.
893 for ns in reversed(self.namespaces):
894 code('} // namespace $ns')
895
896 code('''\
897 #ifndef __PARAMS__${cls}__
898 #define __PARAMS__${cls}__
899
900 ''')
901
902
903 # The base SimObject has a couple of params that get
904 # automatically set from Python without being declared through
905 # the normal Param mechanism; we slip them in here (needed
906 # predecls now, actual declarations below)
907 if cls == SimObject:
908 code('''#include <string>''')
909
910 cxx_class = CxxClass(cls._value_dict['cxx_class'],
911 cls._value_dict['cxx_template_params'])
912
913 # A forward class declaration is sufficient since we are just
914 # declaring a pointer.
915 cxx_class.declare(code)
916
917 for param in params:
918 param.cxx_predecls(code)
919 for port in ports.values():
920 port.cxx_predecls(code)
921 code()
922
923 if cls._base:
924 code('#include "params/${{cls._base.type}}.hh"')
925 code()
926
927 for ptype in ptypes:
928 if issubclass(ptype, Enum):
929 code('#include "enums/${{ptype.__name__}}.hh"')
930 code()
931
932 # now generate the actual param struct
933 code("struct ${cls}Params")
934 if cls._base:
935 code(" : public ${{cls._base.type}}Params")
936 code("{")
937 if not hasattr(cls, 'abstract') or not cls.abstract:
938 if 'type' in cls.__dict__:
939 code(" ${{cls.cxx_type}} create();")
940
941 code.indent()
942 if cls == SimObject:
943 code('''
944 SimObjectParams() {}
945 virtual ~SimObjectParams() {}
946
947 std::string name;
948 ''')
949
950 for param in params:
951 param.cxx_decl(code)
952 for port in ports.values():
953 port.cxx_decl(code)
954
955 code.dedent()
956 code('};')
957
958 code()
959 code('#endif // __PARAMS__${cls}__')
960 return code
961
962 # Generate the C++ declaration/definition files for this SimObject's
963 # param struct to allow C++ initialisation
964 def cxx_config_param_file(cls, code, is_header):
965 createCxxConfigDirectoryEntryFile(code, cls.__name__, cls, is_header)
966 return code
967
968 # This *temporary* definition is required to support calls from the
969 # SimObject class definition to the MetaSimObject methods (in
970 # particular _set_param, which gets called for parameters with default
971 # values defined on the SimObject class itself). It will get
972 # overridden by the permanent definition (which requires that
973 # SimObject be defined) lower in this file.
974 def isSimObjectOrVector(value):
975 return False
976
977 def cxxMethod(*args, **kwargs):
978 """Decorator to export C++ functions to Python"""
979
980 def decorate(func):
981 name = func.__name__
982 override = kwargs.get("override", False)
983 cxx_name = kwargs.get("cxx_name", name)
984 return_value_policy = kwargs.get("return_value_policy", None)
985 static = kwargs.get("static", False)
986
987 args, varargs, keywords, defaults = inspect.getargspec(func)
988 if varargs or keywords:
989 raise ValueError("Wrapped methods must not contain variable " \
990 "arguments")
991
992 # Create tuples of (argument, default)
993 if defaults:
994 args = args[:-len(defaults)] + \
995 list(zip(args[-len(defaults):], defaults))
996 # Don't include self in the argument list to PyBind
997 args = args[1:]
998
999
1000 @wraps(func)
1001 def cxx_call(self, *args, **kwargs):
1002 ccobj = self.getCCClass() if static else self.getCCObject()
1003 return getattr(ccobj, name)(*args, **kwargs)
1004
1005 @wraps(func)
1006 def py_call(self, *args, **kwargs):
1007 return func(self, *args, **kwargs)
1008
1009 f = py_call if override else cxx_call
1010 f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args,
1011 return_value_policy=return_value_policy,
1012 static=static)
1013
1014 return f
1015
1016 if len(args) == 0:
1017 return decorate
1018 elif len(args) == 1 and len(kwargs) == 0:
1019 return decorate(*args)
1020 else:
1021 raise TypeError("One argument and no kwargs, or only kwargs expected")
1022
1023 # This class holds information about each simobject parameter
1024 # that should be displayed on the command line for use in the
1025 # configuration system.
1026 class ParamInfo(object):
1027 def __init__(self, type, desc, type_str, example, default_val, access_str):
1028 self.type = type
1029 self.desc = desc
1030 self.type_str = type_str
1031 self.example_str = example
1032 self.default_val = default_val
1033 # The string representation used to access this param through python.
1034 # The method to access this parameter presented on the command line may
1035 # be different, so this needs to be stored for later use.
1036 self.access_str = access_str
1037 self.created = True
1038
1039 # Make it so we can only set attributes at initialization time
1040 # and effectively make this a const object.
1041 def __setattr__(self, name, value):
1042 if not "created" in self.__dict__:
1043 self.__dict__[name] = value
1044
1045 class SimObjectCliWrapperException(Exception):
1046 def __init__(self, message):
1047 super(Exception, self).__init__(message)
1048
1049 class SimObjectCliWrapper(object):
1050 """
1051 Wrapper class to restrict operations that may be done
1052 from the command line on SimObjects.
1053
1054 Only parameters may be set, and only children may be accessed.
1055
1056 Slicing allows for multiple simultaneous assignment of items in
1057 one statement.
1058 """
1059
1060 def __init__(self, sim_objects):
1061 self.__dict__['_sim_objects'] = list(sim_objects)
1062
1063 def __getattr__(self, key):
1064 return SimObjectCliWrapper(sim_object._children[key]
1065 for sim_object in self._sim_objects)
1066
1067 def __setattr__(self, key, val):
1068 for sim_object in self._sim_objects:
1069 if key in sim_object._params:
1070 if sim_object._params[key].isCmdLineSettable():
1071 setattr(sim_object, key, val)
1072 else:
1073 raise SimObjectCliWrapperException(
1074 'tried to set or unsettable' \
1075 'object parameter: ' + key)
1076 else:
1077 raise SimObjectCliWrapperException(
1078 'tried to set or access non-existent' \
1079 'object parameter: ' + key)
1080
1081 def __getitem__(self, idx):
1082 """
1083 Extends the list() semantics to also allow tuples,
1084 for example object[1, 3] selects items 1 and 3.
1085 """
1086 out = []
1087 if isinstance(idx, tuple):
1088 for t in idx:
1089 out.extend(self[t]._sim_objects)
1090 else:
1091 if isinstance(idx, int):
1092 _range = range(idx, idx + 1)
1093 elif not isinstance(idx, slice):
1094 raise SimObjectCliWrapperException( \
1095 'invalid index type: ' + repr(idx))
1096 for sim_object in self._sim_objects:
1097 if isinstance(idx, slice):
1098 _range = range(*idx.indices(len(sim_object)))
1099 out.extend(sim_object[i] for i in _range)
1100 return SimObjectCliWrapper(out)
1101
1102 def __iter__(self):
1103 return iter(self._sim_objects)
1104
1105 # The SimObject class is the root of the special hierarchy. Most of
1106 # the code in this class deals with the configuration hierarchy itself
1107 # (parent/child node relationships).
1108 @add_metaclass(MetaSimObject)
1109 class SimObject(object):
1110 # Specify metaclass. Any class inheriting from SimObject will
1111 # get this metaclass.
1112 type = 'SimObject'
1113 abstract = True
1114
1115 cxx_header = "sim/sim_object.hh"
1116 cxx_extra_bases = [ "Drainable", "Serializable", "Stats::Group" ]
1117 eventq_index = Param.UInt32(Parent.eventq_index, "Event Queue Index")
1118
1119 cxx_exports = [
1120 PyBindMethod("init"),
1121 PyBindMethod("initState"),
1122 PyBindMethod("memInvalidate"),
1123 PyBindMethod("memWriteback"),
1124 PyBindMethod("regProbePoints"),
1125 PyBindMethod("regProbeListeners"),
1126 PyBindMethod("startup"),
1127 ]
1128
1129 cxx_param_exports = [
1130 PyBindProperty("name"),
1131 ]
1132
1133 @cxxMethod
1134 def loadState(self, cp):
1135 """Load SimObject state from a checkpoint"""
1136 pass
1137
1138 # Returns a dict of all the option strings that can be
1139 # generated as command line options for this simobject instance
1140 # by tracing all reachable params in the top level instance and
1141 # any children it contains.
1142 def enumerateParams(self, flags_dict = {},
1143 cmd_line_str = "", access_str = ""):
1144 if hasattr(self, "_paramEnumed"):
1145 print("Cycle detected enumerating params")
1146 else:
1147 self._paramEnumed = True
1148 # Scan the children first to pick up all the objects in this SimObj
1149 for keys in self._children:
1150 child = self._children[keys]
1151 next_cmdline_str = cmd_line_str + keys
1152 next_access_str = access_str + keys
1153 if not isSimObjectVector(child):
1154 next_cmdline_str = next_cmdline_str + "."
1155 next_access_str = next_access_str + "."
1156 flags_dict = child.enumerateParams(flags_dict,
1157 next_cmdline_str,
1158 next_access_str)
1159
1160 # Go through the simple params in the simobject in this level
1161 # of the simobject hierarchy and save information about the
1162 # parameter to be used for generating and processing command line
1163 # options to the simulator to set these parameters.
1164 for keys,values in self._params.items():
1165 if values.isCmdLineSettable():
1166 type_str = ''
1167 ex_str = values.example_str()
1168 ptype = None
1169 if isinstance(values, VectorParamDesc):
1170 type_str = 'Vector_%s' % values.ptype_str
1171 ptype = values
1172 else:
1173 type_str = '%s' % values.ptype_str
1174 ptype = values.ptype
1175
1176 if keys in self._hr_values\
1177 and keys in self._values\
1178 and not isinstance(self._values[keys],
1179 m5.proxy.BaseProxy):
1180 cmd_str = cmd_line_str + keys
1181 acc_str = access_str + keys
1182 flags_dict[cmd_str] = ParamInfo(ptype,
1183 self._params[keys].desc, type_str, ex_str,
1184 values.pretty_print(self._hr_values[keys]),
1185 acc_str)
1186 elif not keys in self._hr_values\
1187 and not keys in self._values:
1188 # Empty param
1189 cmd_str = cmd_line_str + keys
1190 acc_str = access_str + keys
1191 flags_dict[cmd_str] = ParamInfo(ptype,
1192 self._params[keys].desc,
1193 type_str, ex_str, '', acc_str)
1194
1195 return flags_dict
1196
1197 # Initialize new instance. For objects with SimObject-valued
1198 # children, we need to recursively clone the classes represented
1199 # by those param values as well in a consistent "deep copy"-style
1200 # fashion. That is, we want to make sure that each instance is
1201 # cloned only once, and that if there are multiple references to
1202 # the same original object, we end up with the corresponding
1203 # cloned references all pointing to the same cloned instance.
1204 def __init__(self, **kwargs):
1205 ancestor = kwargs.get('_ancestor')
1206 memo_dict = kwargs.get('_memo')
1207 if memo_dict is None:
1208 # prepare to memoize any recursively instantiated objects
1209 memo_dict = {}
1210 elif ancestor:
1211 # memoize me now to avoid problems with recursive calls
1212 memo_dict[ancestor] = self
1213
1214 if not ancestor:
1215 ancestor = self.__class__
1216 ancestor._instantiated = True
1217
1218 # initialize required attributes
1219 self._parent = None
1220 self._name = None
1221 self._ccObject = None # pointer to C++ object
1222 self._ccParams = None
1223 self._instantiated = False # really "cloned"
1224
1225 # Clone children specified at class level. No need for a
1226 # multidict here since we will be cloning everything.
1227 # Do children before parameter values so that children that
1228 # are also param values get cloned properly.
1229 self._children = {}
1230 for key,val in ancestor._children.items():
1231 self.add_child(key, val(_memo=memo_dict))
1232
1233 # Inherit parameter values from class using multidict so
1234 # individual value settings can be overridden but we still
1235 # inherit late changes to non-overridden class values.
1236 self._values = multidict(ancestor._values)
1237 self._hr_values = multidict(ancestor._hr_values)
1238 # clone SimObject-valued parameters
1239 for key,val in ancestor._values.items():
1240 val = tryAsSimObjectOrVector(val)
1241 if val is not None:
1242 self._values[key] = val(_memo=memo_dict)
1243
1244 # clone port references. no need to use a multidict here
1245 # since we will be creating new references for all ports.
1246 self._port_refs = {}
1247 for key,val in ancestor._port_refs.items():
1248 self._port_refs[key] = val.clone(self, memo_dict)
1249 # apply attribute assignments from keyword args, if any
1250 for key,val in kwargs.items():
1251 setattr(self, key, val)
1252
1253 # "Clone" the current instance by creating another instance of
1254 # this instance's class, but that inherits its parameter values
1255 # and port mappings from the current instance. If we're in a
1256 # "deep copy" recursive clone, check the _memo dict to see if
1257 # we've already cloned this instance.
1258 def __call__(self, **kwargs):
1259 memo_dict = kwargs.get('_memo')
1260 if memo_dict is None:
1261 # no memo_dict: must be top-level clone operation.
1262 # this is only allowed at the root of a hierarchy
1263 if self._parent:
1264 raise RuntimeError("attempt to clone object %s " \
1265 "not at the root of a tree (parent = %s)" \
1266 % (self, self._parent))
1267 # create a new dict and use that.
1268 memo_dict = {}
1269 kwargs['_memo'] = memo_dict
1270 elif self in memo_dict:
1271 # clone already done & memoized
1272 return memo_dict[self]
1273 return self.__class__(_ancestor = self, **kwargs)
1274
1275 def _get_port_ref(self, attr):
1276 # Return reference that can be assigned to another port
1277 # via __setattr__. There is only ever one reference
1278 # object per port, but we create them lazily here.
1279 ref = self._port_refs.get(attr)
1280 if ref == None:
1281 ref = self._ports[attr].makeRef(self)
1282 self._port_refs[attr] = ref
1283 return ref
1284
1285 def __getattr__(self, attr):
1286 if attr in self._deprecated_params:
1287 dep_param = self._deprecated_params[attr]
1288 dep_param.printWarning(self._name, self.__class__.__name__)
1289 return getattr(self, self._deprecated_params[attr].newName)
1290
1291 if attr in self._ports:
1292 return self._get_port_ref(attr)
1293
1294 if attr in self._values:
1295 return self._values[attr]
1296
1297 if attr in self._children:
1298 return self._children[attr]
1299
1300 # If the attribute exists on the C++ object, transparently
1301 # forward the reference there. This is typically used for
1302 # methods exported to Python (e.g., init(), and startup())
1303 if self._ccObject and hasattr(self._ccObject, attr):
1304 return getattr(self._ccObject, attr)
1305
1306 err_string = "object '%s' has no attribute '%s'" \
1307 % (self.__class__.__name__, attr)
1308
1309 if not self._ccObject:
1310 err_string += "\n (C++ object is not yet constructed," \
1311 " so wrapped C++ methods are unavailable.)"
1312
1313 raise AttributeError(err_string)
1314
1315 # Set attribute (called on foo.attr = value when foo is an
1316 # instance of class cls).
1317 def __setattr__(self, attr, value):
1318 # normal processing for private attributes
1319 if attr.startswith('_'):
1320 object.__setattr__(self, attr, value)
1321 return
1322
1323 if attr in self._deprecated_params:
1324 dep_param = self._deprecated_params[attr]
1325 dep_param.printWarning(self._name, self.__class__.__name__)
1326 return setattr(self, self._deprecated_params[attr].newName, value)
1327
1328 if attr in self._ports:
1329 # set up port connection
1330 self._get_port_ref(attr).connect(value)
1331 return
1332
1333 param = self._params.get(attr)
1334 if param:
1335 try:
1336 hr_value = value
1337 value = param.convert(value)
1338 except Exception as e:
1339 msg = "%s\nError setting param %s.%s to %s\n" % \
1340 (e, self.__class__.__name__, attr, value)
1341 e.args = (msg, )
1342 raise
1343 self._values[attr] = value
1344 # implicitly parent unparented objects assigned as params
1345 if isSimObjectOrVector(value) and not value.has_parent():
1346 self.add_child(attr, value)
1347 # set the human-readable value dict if this is a param
1348 # with a literal value and is not being set as an object
1349 # or proxy.
1350 if not (isSimObjectOrVector(value) or\
1351 isinstance(value, m5.proxy.BaseProxy)):
1352 self._hr_values[attr] = hr_value
1353
1354 return
1355
1356 # if RHS is a SimObject, it's an implicit child assignment
1357 if isSimObjectOrSequence(value):
1358 self.add_child(attr, value)
1359 return
1360
1361 # no valid assignment... raise exception
1362 raise AttributeError("Class %s has no parameter %s" \
1363 % (self.__class__.__name__, attr))
1364
1365
1366 # this hack allows tacking a '[0]' onto parameters that may or may
1367 # not be vectors, and always getting the first element (e.g. cpus)
1368 def __getitem__(self, key):
1369 if key == 0:
1370 return self
1371 raise IndexError("Non-zero index '%s' to SimObject" % key)
1372
1373 # this hack allows us to iterate over a SimObject that may
1374 # not be a vector, so we can call a loop over it and get just one
1375 # element.
1376 def __len__(self):
1377 return 1
1378
1379 # Also implemented by SimObjectVector
1380 def clear_parent(self, old_parent):
1381 assert self._parent is old_parent
1382 self._parent = None
1383
1384 # Also implemented by SimObjectVector
1385 def set_parent(self, parent, name):
1386 self._parent = parent
1387 self._name = name
1388
1389 # Return parent object of this SimObject, not implemented by
1390 # SimObjectVector because the elements in a SimObjectVector may not share
1391 # the same parent
1392 def get_parent(self):
1393 return self._parent
1394
1395 # Also implemented by SimObjectVector
1396 def get_name(self):
1397 return self._name
1398
1399 # Also implemented by SimObjectVector
1400 def has_parent(self):
1401 return self._parent is not None
1402
1403 # clear out child with given name. This code is not likely to be exercised.
1404 # See comment in add_child.
1405 def clear_child(self, name):
1406 child = self._children[name]
1407 child.clear_parent(self)
1408 del self._children[name]
1409
1410 # Add a new child to this object.
1411 def add_child(self, name, child):
1412 child = coerceSimObjectOrVector(child)
1413 if child.has_parent():
1414 warn("add_child('%s'): child '%s' already has parent", name,
1415 child.get_name())
1416 if name in self._children:
1417 # This code path had an undiscovered bug that would make it fail
1418 # at runtime. It had been here for a long time and was only
1419 # exposed by a buggy script. Changes here will probably not be
1420 # exercised without specialized testing.
1421 self.clear_child(name)
1422 child.set_parent(self, name)
1423 if not isNullPointer(child):
1424 self._children[name] = child
1425
1426 # Take SimObject-valued parameters that haven't been explicitly
1427 # assigned as children and make them children of the object that
1428 # they were assigned to as a parameter value. This guarantees
1429 # that when we instantiate all the parameter objects we're still
1430 # inside the configuration hierarchy.
1431 def adoptOrphanParams(self):
1432 for key,val in self._values.items():
1433 if not isSimObjectVector(val) and isSimObjectSequence(val):
1434 # need to convert raw SimObject sequences to
1435 # SimObjectVector class so we can call has_parent()
1436 val = SimObjectVector(val)
1437 self._values[key] = val
1438 if isSimObjectOrVector(val) and not val.has_parent():
1439 warn("%s adopting orphan SimObject param '%s'", self, key)
1440 self.add_child(key, val)
1441
1442 def path(self):
1443 if not self._parent:
1444 return '<orphan %s>' % self.__class__
1445 elif isinstance(self._parent, MetaSimObject):
1446 return str(self.__class__)
1447
1448 ppath = self._parent.path()
1449 if ppath == 'root':
1450 return self._name
1451 return ppath + "." + self._name
1452
1453 def path_list(self):
1454 if self._parent:
1455 return self._parent.path_list() + [ self._name, ]
1456 else:
1457 # Don't include the root node
1458 return []
1459
1460 def __str__(self):
1461 return self.path()
1462
1463 def config_value(self):
1464 return self.path()
1465
1466 def ini_str(self):
1467 return self.path()
1468
1469 def find_any(self, ptype):
1470 if isinstance(self, ptype):
1471 return self, True
1472
1473 found_obj = None
1474 for child in self._children.values():
1475 visited = False
1476 if hasattr(child, '_visited'):
1477 visited = getattr(child, '_visited')
1478
1479 if isinstance(child, ptype) and not visited:
1480 if found_obj != None and child != found_obj:
1481 raise AttributeError(
1482 'parent.any matched more than one: %s %s' % \
1483 (found_obj.path, child.path))
1484 found_obj = child
1485 # search param space
1486 for pname,pdesc in self._params.items():
1487 if issubclass(pdesc.ptype, ptype):
1488 match_obj = self._values[pname]
1489 if found_obj != None and found_obj != match_obj:
1490 raise AttributeError(
1491 'parent.any matched more than one: %s and %s' % \
1492 (found_obj.path, match_obj.path))
1493 found_obj = match_obj
1494 return found_obj, found_obj != None
1495
1496 def find_all(self, ptype):
1497 all = {}
1498 # search children
1499 for child in self._children.values():
1500 # a child could be a list, so ensure we visit each item
1501 if isinstance(child, list):
1502 children = child
1503 else:
1504 children = [child]
1505
1506 for child in children:
1507 if isinstance(child, ptype) and not isproxy(child) and \
1508 not isNullPointer(child):
1509 all[child] = True
1510 if isSimObject(child):
1511 # also add results from the child itself
1512 child_all, done = child.find_all(ptype)
1513 all.update(dict(zip(child_all, [done] * len(child_all))))
1514 # search param space
1515 for pname,pdesc in self._params.items():
1516 if issubclass(pdesc.ptype, ptype):
1517 match_obj = self._values[pname]
1518 if not isproxy(match_obj) and not isNullPointer(match_obj):
1519 all[match_obj] = True
1520 # Also make sure to sort the keys based on the objects' path to
1521 # ensure that the order is the same on all hosts
1522 return sorted(all.keys(), key = lambda o: o.path()), True
1523
1524 def unproxy(self, base):
1525 return self
1526
1527 def unproxyParams(self):
1528 for param in self._params.keys():
1529 value = self._values.get(param)
1530 if value != None and isproxy(value):
1531 try:
1532 value = value.unproxy(self)
1533 except:
1534 print("Error in unproxying param '%s' of %s" %
1535 (param, self.path()))
1536 raise
1537 setattr(self, param, value)
1538
1539 # Unproxy ports in sorted order so that 'append' operations on
1540 # vector ports are done in a deterministic fashion.
1541 port_names = list(self._ports.keys())
1542 port_names.sort()
1543 for port_name in port_names:
1544 port = self._port_refs.get(port_name)
1545 if port != None:
1546 port.unproxy(self)
1547
1548 def print_ini(self, ini_file):
1549 print('[' + self.path() + ']', file=ini_file) # .ini section header
1550
1551 instanceDict[self.path()] = self
1552
1553 if hasattr(self, 'type'):
1554 print('type=%s' % self.type, file=ini_file)
1555
1556 if len(self._children.keys()):
1557 print('children=%s' %
1558 ' '.join(self._children[n].get_name()
1559 for n in sorted(self._children.keys())),
1560 file=ini_file)
1561
1562 for param in sorted(self._params.keys()):
1563 value = self._values.get(param)
1564 if value != None:
1565 print('%s=%s' % (param, self._values[param].ini_str()),
1566 file=ini_file)
1567
1568 for port_name in sorted(self._ports.keys()):
1569 port = self._port_refs.get(port_name, None)
1570 if port != None:
1571 print('%s=%s' % (port_name, port.ini_str()), file=ini_file)
1572
1573 print(file=ini_file) # blank line between objects
1574
1575 # generate a tree of dictionaries expressing all the parameters in the
1576 # instantiated system for use by scripts that want to do power, thermal
1577 # visualization, and other similar tasks
1578 def get_config_as_dict(self):
1579 d = attrdict()
1580 if hasattr(self, 'type'):
1581 d.type = self.type
1582 if hasattr(self, 'cxx_class'):
1583 d.cxx_class = self.cxx_class
1584 # Add the name and path of this object to be able to link to
1585 # the stats
1586 d.name = self.get_name()
1587 d.path = self.path()
1588
1589 for param in sorted(self._params.keys()):
1590 value = self._values.get(param)
1591 if value != None:
1592 d[param] = value.config_value()
1593
1594 for n in sorted(self._children.keys()):
1595 child = self._children[n]
1596 # Use the name of the attribute (and not get_name()) as
1597 # the key in the JSON dictionary to capture the hierarchy
1598 # in the Python code that assembled this system
1599 d[n] = child.get_config_as_dict()
1600
1601 for port_name in sorted(self._ports.keys()):
1602 port = self._port_refs.get(port_name, None)
1603 if port != None:
1604 # Represent each port with a dictionary containing the
1605 # prominent attributes
1606 d[port_name] = port.get_config_as_dict()
1607
1608 return d
1609
1610 def getCCParams(self):
1611 if self._ccParams:
1612 return self._ccParams
1613
1614 cc_params_struct = getattr(m5.internal.params, '%sParams' % self.type)
1615 cc_params = cc_params_struct()
1616 cc_params.name = str(self)
1617
1618 param_names = list(self._params.keys())
1619 param_names.sort()
1620 for param in param_names:
1621 value = self._values.get(param)
1622 if value is None:
1623 fatal("%s.%s without default or user set value",
1624 self.path(), param)
1625
1626 value = value.getValue()
1627 if isinstance(self._params[param], VectorParamDesc):
1628 assert isinstance(value, list)
1629 vec = getattr(cc_params, param)
1630 assert not len(vec)
1631 # Some types are exposed as opaque types. They support
1632 # the append operation unlike the automatically
1633 # wrapped types.
1634 if isinstance(vec, list):
1635 setattr(cc_params, param, list(value))
1636 else:
1637 for v in value:
1638 getattr(cc_params, param).append(v)
1639 else:
1640 setattr(cc_params, param, value)
1641
1642 port_names = list(self._ports.keys())
1643 port_names.sort()
1644 for port_name in port_names:
1645 port = self._port_refs.get(port_name, None)
1646 if port != None:
1647 port_count = len(port)
1648 else:
1649 port_count = 0
1650 setattr(cc_params, 'port_' + port_name + '_connection_count',
1651 port_count)
1652 self._ccParams = cc_params
1653 return self._ccParams
1654
1655 # Get C++ object corresponding to this object, calling C++ if
1656 # necessary to construct it. Does *not* recursively create
1657 # children.
1658 def getCCObject(self):
1659 if not self._ccObject:
1660 # Make sure this object is in the configuration hierarchy
1661 if not self._parent and not isRoot(self):
1662 raise RuntimeError("Attempt to instantiate orphan node")
1663 # Cycles in the configuration hierarchy are not supported. This
1664 # will catch the resulting recursion and stop.
1665 self._ccObject = -1
1666 if not self.abstract:
1667 params = self.getCCParams()
1668 self._ccObject = params.create()
1669 elif self._ccObject == -1:
1670 raise RuntimeError("%s: Cycle found in configuration hierarchy." \
1671 % self.path())
1672 return self._ccObject
1673
1674 def descendants(self):
1675 yield self
1676 # The order of the dict is implementation dependent, so sort
1677 # it based on the key (name) to ensure the order is the same
1678 # on all hosts
1679 for (name, child) in sorted(self._children.items()):
1680 for obj in child.descendants():
1681 yield obj
1682
1683 # Call C++ to create C++ object corresponding to this object
1684 def createCCObject(self):
1685 self.getCCParams()
1686 self.getCCObject() # force creation
1687
1688 def getValue(self):
1689 return self.getCCObject()
1690
1691 @cxxMethod(return_value_policy="reference")
1692 def getPort(self, if_name, idx):
1693 pass
1694
1695 # Create C++ port connections corresponding to the connections in
1696 # _port_refs
1697 def connectPorts(self):
1698 # Sort the ports based on their attribute name to ensure the
1699 # order is the same on all hosts
1700 for (attr, portRef) in sorted(self._port_refs.items()):
1701 portRef.ccConnect()
1702
1703 # Default function for generating the device structure.
1704 # Can be overloaded by the inheriting class
1705 def generateDeviceTree(self, state):
1706 return # return without yielding anything
1707 yield # make this function a (null) generator
1708
1709 def recurseDeviceTree(self, state):
1710 for child in self._children.values():
1711 for item in child: # For looping over SimObjectVectors
1712 for dt in item.generateDeviceTree(state):
1713 yield dt
1714
1715 # On a separate method otherwise certain buggy Python versions
1716 # would fail with: SyntaxError: unqualified exec is not allowed
1717 # in function 'apply_config'
1718 def _apply_config_get_dict(self):
1719 return {
1720 child_name: SimObjectCliWrapper(
1721 iter(self._children[child_name]))
1722 for child_name in self._children
1723 }
1724
1725 def apply_config(self, params):
1726 """
1727 exec a list of Python code strings contained in params.
1728
1729 The only exposed globals to those strings are the child
1730 SimObjects of this node.
1731
1732 This function is intended to allow users to modify SimObject
1733 parameters from the command line with Python statements.
1734 """
1735 d = self._apply_config_get_dict()
1736 for param in params:
1737 exec(param, d)
1738
1739 def get_simobj(self, simobj_path):
1740 """
1741 Get all sim objects that match a given string.
1742
1743 The format is the same as that supported by SimObjectCliWrapper.
1744
1745 :param simobj_path: Current state to be in.
1746 :type simobj_path: str
1747 """
1748 d = self._apply_config_get_dict()
1749 return eval(simobj_path, d)
1750
1751 # Function to provide to C++ so it can look up instances based on paths
1752 def resolveSimObject(name):
1753 obj = instanceDict[name]
1754 return obj.getCCObject()
1755
1756 def isSimObject(value):
1757 return isinstance(value, SimObject)
1758
1759 def isSimObjectClass(value):
1760 return issubclass(value, SimObject)
1761
1762 def isSimObjectVector(value):
1763 return isinstance(value, SimObjectVector)
1764
1765 def isSimObjectSequence(value):
1766 if not isinstance(value, (list, tuple)) or len(value) == 0:
1767 return False
1768
1769 for val in value:
1770 if not isNullPointer(val) and not isSimObject(val):
1771 return False
1772
1773 return True
1774
1775 def isSimObjectOrSequence(value):
1776 return isSimObject(value) or isSimObjectSequence(value)
1777
1778 def isRoot(obj):
1779 from m5.objects import Root
1780 return obj and obj is Root.getInstance()
1781
1782 def isSimObjectOrVector(value):
1783 return isSimObject(value) or isSimObjectVector(value)
1784
1785 def tryAsSimObjectOrVector(value):
1786 if isSimObjectOrVector(value):
1787 return value
1788 if isSimObjectSequence(value):
1789 return SimObjectVector(value)
1790 return None
1791
1792 def coerceSimObjectOrVector(value):
1793 value = tryAsSimObjectOrVector(value)
1794 if value is None:
1795 raise TypeError("SimObject or SimObjectVector expected")
1796 return value
1797
1798 baseClasses = allClasses.copy()
1799 baseInstances = instanceDict.copy()
1800
1801 def clear():
1802 global allClasses, instanceDict, noCxxHeader
1803
1804 allClasses = baseClasses.copy()
1805 instanceDict = baseInstances.copy()
1806 noCxxHeader = False
1807
1808 # __all__ defines the list of symbols that get exported when
1809 # 'from config import *' is invoked. Try to keep this reasonably
1810 # short to avoid polluting other namespaces.
1811 __all__ = [
1812 'SimObject',
1813 'cxxMethod',
1814 'PyBindMethod',
1815 'PyBindProperty',
1816 ]