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