SimObject.py revision 14061:bd3e8e7a983d
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" ] 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("regStats"), 1098 PyBindMethod("resetStats"), 1099 PyBindMethod("regProbePoints"), 1100 PyBindMethod("regProbeListeners"), 1101 PyBindMethod("startup"), 1102 ] 1103 1104 cxx_param_exports = [ 1105 PyBindProperty("name"), 1106 ] 1107 1108 @cxxMethod 1109 def loadState(self, cp): 1110 """Load SimObject state from a checkpoint""" 1111 pass 1112 1113 # Returns a dict of all the option strings that can be 1114 # generated as command line options for this simobject instance 1115 # by tracing all reachable params in the top level instance and 1116 # any children it contains. 1117 def enumerateParams(self, flags_dict = {}, 1118 cmd_line_str = "", access_str = ""): 1119 if hasattr(self, "_paramEnumed"): 1120 print("Cycle detected enumerating params") 1121 else: 1122 self._paramEnumed = True 1123 # Scan the children first to pick up all the objects in this SimObj 1124 for keys in self._children: 1125 child = self._children[keys] 1126 next_cmdline_str = cmd_line_str + keys 1127 next_access_str = access_str + keys 1128 if not isSimObjectVector(child): 1129 next_cmdline_str = next_cmdline_str + "." 1130 next_access_str = next_access_str + "." 1131 flags_dict = child.enumerateParams(flags_dict, 1132 next_cmdline_str, 1133 next_access_str) 1134 1135 # Go through the simple params in the simobject in this level 1136 # of the simobject hierarchy and save information about the 1137 # parameter to be used for generating and processing command line 1138 # options to the simulator to set these parameters. 1139 for keys,values in self._params.items(): 1140 if values.isCmdLineSettable(): 1141 type_str = '' 1142 ex_str = values.example_str() 1143 ptype = None 1144 if isinstance(values, VectorParamDesc): 1145 type_str = 'Vector_%s' % values.ptype_str 1146 ptype = values 1147 else: 1148 type_str = '%s' % values.ptype_str 1149 ptype = values.ptype 1150 1151 if keys in self._hr_values\ 1152 and keys in self._values\ 1153 and not isinstance(self._values[keys], 1154 m5.proxy.BaseProxy): 1155 cmd_str = cmd_line_str + keys 1156 acc_str = access_str + keys 1157 flags_dict[cmd_str] = ParamInfo(ptype, 1158 self._params[keys].desc, type_str, ex_str, 1159 values.pretty_print(self._hr_values[keys]), 1160 acc_str) 1161 elif not keys in self._hr_values\ 1162 and not keys in self._values: 1163 # Empty param 1164 cmd_str = cmd_line_str + keys 1165 acc_str = access_str + keys 1166 flags_dict[cmd_str] = ParamInfo(ptype, 1167 self._params[keys].desc, 1168 type_str, ex_str, '', acc_str) 1169 1170 return flags_dict 1171 1172 # Initialize new instance. For objects with SimObject-valued 1173 # children, we need to recursively clone the classes represented 1174 # by those param values as well in a consistent "deep copy"-style 1175 # fashion. That is, we want to make sure that each instance is 1176 # cloned only once, and that if there are multiple references to 1177 # the same original object, we end up with the corresponding 1178 # cloned references all pointing to the same cloned instance. 1179 def __init__(self, **kwargs): 1180 ancestor = kwargs.get('_ancestor') 1181 memo_dict = kwargs.get('_memo') 1182 if memo_dict is None: 1183 # prepare to memoize any recursively instantiated objects 1184 memo_dict = {} 1185 elif ancestor: 1186 # memoize me now to avoid problems with recursive calls 1187 memo_dict[ancestor] = self 1188 1189 if not ancestor: 1190 ancestor = self.__class__ 1191 ancestor._instantiated = True 1192 1193 # initialize required attributes 1194 self._parent = None 1195 self._name = None 1196 self._ccObject = None # pointer to C++ object 1197 self._ccParams = None 1198 self._instantiated = False # really "cloned" 1199 1200 # Clone children specified at class level. No need for a 1201 # multidict here since we will be cloning everything. 1202 # Do children before parameter values so that children that 1203 # are also param values get cloned properly. 1204 self._children = {} 1205 for key,val in ancestor._children.items(): 1206 self.add_child(key, val(_memo=memo_dict)) 1207 1208 # Inherit parameter values from class using multidict so 1209 # individual value settings can be overridden but we still 1210 # inherit late changes to non-overridden class values. 1211 self._values = multidict(ancestor._values) 1212 self._hr_values = multidict(ancestor._hr_values) 1213 # clone SimObject-valued parameters 1214 for key,val in ancestor._values.items(): 1215 val = tryAsSimObjectOrVector(val) 1216 if val is not None: 1217 self._values[key] = val(_memo=memo_dict) 1218 1219 # clone port references. no need to use a multidict here 1220 # since we will be creating new references for all ports. 1221 self._port_refs = {} 1222 for key,val in ancestor._port_refs.items(): 1223 self._port_refs[key] = val.clone(self, memo_dict) 1224 # apply attribute assignments from keyword args, if any 1225 for key,val in kwargs.items(): 1226 setattr(self, key, val) 1227 1228 # "Clone" the current instance by creating another instance of 1229 # this instance's class, but that inherits its parameter values 1230 # and port mappings from the current instance. If we're in a 1231 # "deep copy" recursive clone, check the _memo dict to see if 1232 # we've already cloned this instance. 1233 def __call__(self, **kwargs): 1234 memo_dict = kwargs.get('_memo') 1235 if memo_dict is None: 1236 # no memo_dict: must be top-level clone operation. 1237 # this is only allowed at the root of a hierarchy 1238 if self._parent: 1239 raise RuntimeError("attempt to clone object %s " \ 1240 "not at the root of a tree (parent = %s)" \ 1241 % (self, self._parent)) 1242 # create a new dict and use that. 1243 memo_dict = {} 1244 kwargs['_memo'] = memo_dict 1245 elif self in memo_dict: 1246 # clone already done & memoized 1247 return memo_dict[self] 1248 return self.__class__(_ancestor = self, **kwargs) 1249 1250 def _get_port_ref(self, attr): 1251 # Return reference that can be assigned to another port 1252 # via __setattr__. There is only ever one reference 1253 # object per port, but we create them lazily here. 1254 ref = self._port_refs.get(attr) 1255 if ref == None: 1256 ref = self._ports[attr].makeRef(self) 1257 self._port_refs[attr] = ref 1258 return ref 1259 1260 def __getattr__(self, attr): 1261 if attr in self._ports: 1262 return self._get_port_ref(attr) 1263 1264 if attr in self._values: 1265 return self._values[attr] 1266 1267 if attr in self._children: 1268 return self._children[attr] 1269 1270 # If the attribute exists on the C++ object, transparently 1271 # forward the reference there. This is typically used for 1272 # methods exported to Python (e.g., init(), and startup()) 1273 if self._ccObject and hasattr(self._ccObject, attr): 1274 return getattr(self._ccObject, attr) 1275 1276 err_string = "object '%s' has no attribute '%s'" \ 1277 % (self.__class__.__name__, attr) 1278 1279 if not self._ccObject: 1280 err_string += "\n (C++ object is not yet constructed," \ 1281 " so wrapped C++ methods are unavailable.)" 1282 1283 raise AttributeError(err_string) 1284 1285 # Set attribute (called on foo.attr = value when foo is an 1286 # instance of class cls). 1287 def __setattr__(self, attr, value): 1288 # normal processing for private attributes 1289 if attr.startswith('_'): 1290 object.__setattr__(self, attr, value) 1291 return 1292 1293 if attr in self._ports: 1294 # set up port connection 1295 self._get_port_ref(attr).connect(value) 1296 return 1297 1298 param = self._params.get(attr) 1299 if param: 1300 try: 1301 hr_value = value 1302 value = param.convert(value) 1303 except Exception as e: 1304 msg = "%s\nError setting param %s.%s to %s\n" % \ 1305 (e, self.__class__.__name__, attr, value) 1306 e.args = (msg, ) 1307 raise 1308 self._values[attr] = value 1309 # implicitly parent unparented objects assigned as params 1310 if isSimObjectOrVector(value) and not value.has_parent(): 1311 self.add_child(attr, value) 1312 # set the human-readable value dict if this is a param 1313 # with a literal value and is not being set as an object 1314 # or proxy. 1315 if not (isSimObjectOrVector(value) or\ 1316 isinstance(value, m5.proxy.BaseProxy)): 1317 self._hr_values[attr] = hr_value 1318 1319 return 1320 1321 # if RHS is a SimObject, it's an implicit child assignment 1322 if isSimObjectOrSequence(value): 1323 self.add_child(attr, value) 1324 return 1325 1326 # no valid assignment... raise exception 1327 raise AttributeError("Class %s has no parameter %s" \ 1328 % (self.__class__.__name__, attr)) 1329 1330 1331 # this hack allows tacking a '[0]' onto parameters that may or may 1332 # not be vectors, and always getting the first element (e.g. cpus) 1333 def __getitem__(self, key): 1334 if key == 0: 1335 return self 1336 raise IndexError("Non-zero index '%s' to SimObject" % key) 1337 1338 # this hack allows us to iterate over a SimObject that may 1339 # not be a vector, so we can call a loop over it and get just one 1340 # element. 1341 def __len__(self): 1342 return 1 1343 1344 # Also implemented by SimObjectVector 1345 def clear_parent(self, old_parent): 1346 assert self._parent is old_parent 1347 self._parent = None 1348 1349 # Also implemented by SimObjectVector 1350 def set_parent(self, parent, name): 1351 self._parent = parent 1352 self._name = name 1353 1354 # Return parent object of this SimObject, not implemented by 1355 # SimObjectVector because the elements in a SimObjectVector may not share 1356 # the same parent 1357 def get_parent(self): 1358 return self._parent 1359 1360 # Also implemented by SimObjectVector 1361 def get_name(self): 1362 return self._name 1363 1364 # Also implemented by SimObjectVector 1365 def has_parent(self): 1366 return self._parent is not None 1367 1368 # clear out child with given name. This code is not likely to be exercised. 1369 # See comment in add_child. 1370 def clear_child(self, name): 1371 child = self._children[name] 1372 child.clear_parent(self) 1373 del self._children[name] 1374 1375 # Add a new child to this object. 1376 def add_child(self, name, child): 1377 child = coerceSimObjectOrVector(child) 1378 if child.has_parent(): 1379 warn("add_child('%s'): child '%s' already has parent", name, 1380 child.get_name()) 1381 if name in self._children: 1382 # This code path had an undiscovered bug that would make it fail 1383 # at runtime. It had been here for a long time and was only 1384 # exposed by a buggy script. Changes here will probably not be 1385 # exercised without specialized testing. 1386 self.clear_child(name) 1387 child.set_parent(self, name) 1388 if not isNullPointer(child): 1389 self._children[name] = child 1390 1391 # Take SimObject-valued parameters that haven't been explicitly 1392 # assigned as children and make them children of the object that 1393 # they were assigned to as a parameter value. This guarantees 1394 # that when we instantiate all the parameter objects we're still 1395 # inside the configuration hierarchy. 1396 def adoptOrphanParams(self): 1397 for key,val in self._values.items(): 1398 if not isSimObjectVector(val) and isSimObjectSequence(val): 1399 # need to convert raw SimObject sequences to 1400 # SimObjectVector class so we can call has_parent() 1401 val = SimObjectVector(val) 1402 self._values[key] = val 1403 if isSimObjectOrVector(val) and not val.has_parent(): 1404 warn("%s adopting orphan SimObject param '%s'", self, key) 1405 self.add_child(key, val) 1406 1407 def path(self): 1408 if not self._parent: 1409 return '<orphan %s>' % self.__class__ 1410 elif isinstance(self._parent, MetaSimObject): 1411 return str(self.__class__) 1412 1413 ppath = self._parent.path() 1414 if ppath == 'root': 1415 return self._name 1416 return ppath + "." + self._name 1417 1418 def __str__(self): 1419 return self.path() 1420 1421 def config_value(self): 1422 return self.path() 1423 1424 def ini_str(self): 1425 return self.path() 1426 1427 def find_any(self, ptype): 1428 if isinstance(self, ptype): 1429 return self, True 1430 1431 found_obj = None 1432 for child in self._children.values(): 1433 visited = False 1434 if hasattr(child, '_visited'): 1435 visited = getattr(child, '_visited') 1436 1437 if isinstance(child, ptype) and not visited: 1438 if found_obj != None and child != found_obj: 1439 raise AttributeError( 1440 'parent.any matched more than one: %s %s' % \ 1441 (found_obj.path, child.path)) 1442 found_obj = child 1443 # search param space 1444 for pname,pdesc in self._params.items(): 1445 if issubclass(pdesc.ptype, ptype): 1446 match_obj = self._values[pname] 1447 if found_obj != None and found_obj != match_obj: 1448 raise AttributeError( 1449 'parent.any matched more than one: %s and %s' % \ 1450 (found_obj.path, match_obj.path)) 1451 found_obj = match_obj 1452 return found_obj, found_obj != None 1453 1454 def find_all(self, ptype): 1455 all = {} 1456 # search children 1457 for child in self._children.values(): 1458 # a child could be a list, so ensure we visit each item 1459 if isinstance(child, list): 1460 children = child 1461 else: 1462 children = [child] 1463 1464 for child in children: 1465 if isinstance(child, ptype) and not isproxy(child) and \ 1466 not isNullPointer(child): 1467 all[child] = True 1468 if isSimObject(child): 1469 # also add results from the child itself 1470 child_all, done = child.find_all(ptype) 1471 all.update(dict(zip(child_all, [done] * len(child_all)))) 1472 # search param space 1473 for pname,pdesc in self._params.items(): 1474 if issubclass(pdesc.ptype, ptype): 1475 match_obj = self._values[pname] 1476 if not isproxy(match_obj) and not isNullPointer(match_obj): 1477 all[match_obj] = True 1478 # Also make sure to sort the keys based on the objects' path to 1479 # ensure that the order is the same on all hosts 1480 return sorted(all.keys(), key = lambda o: o.path()), True 1481 1482 def unproxy(self, base): 1483 return self 1484 1485 def unproxyParams(self): 1486 for param in self._params.keys(): 1487 value = self._values.get(param) 1488 if value != None and isproxy(value): 1489 try: 1490 value = value.unproxy(self) 1491 except: 1492 print("Error in unproxying param '%s' of %s" % 1493 (param, self.path())) 1494 raise 1495 setattr(self, param, value) 1496 1497 # Unproxy ports in sorted order so that 'append' operations on 1498 # vector ports are done in a deterministic fashion. 1499 port_names = list(self._ports.keys()) 1500 port_names.sort() 1501 for port_name in port_names: 1502 port = self._port_refs.get(port_name) 1503 if port != None: 1504 port.unproxy(self) 1505 1506 def print_ini(self, ini_file): 1507 print('[' + self.path() + ']', file=ini_file) # .ini section header 1508 1509 instanceDict[self.path()] = self 1510 1511 if hasattr(self, 'type'): 1512 print('type=%s' % self.type, file=ini_file) 1513 1514 if len(self._children.keys()): 1515 print('children=%s' % 1516 ' '.join(self._children[n].get_name() 1517 for n in sorted(self._children.keys())), 1518 file=ini_file) 1519 1520 for param in sorted(self._params.keys()): 1521 value = self._values.get(param) 1522 if value != None: 1523 print('%s=%s' % (param, self._values[param].ini_str()), 1524 file=ini_file) 1525 1526 for port_name in sorted(self._ports.keys()): 1527 port = self._port_refs.get(port_name, None) 1528 if port != None: 1529 print('%s=%s' % (port_name, port.ini_str()), file=ini_file) 1530 1531 print(file=ini_file) # blank line between objects 1532 1533 # generate a tree of dictionaries expressing all the parameters in the 1534 # instantiated system for use by scripts that want to do power, thermal 1535 # visualization, and other similar tasks 1536 def get_config_as_dict(self): 1537 d = attrdict() 1538 if hasattr(self, 'type'): 1539 d.type = self.type 1540 if hasattr(self, 'cxx_class'): 1541 d.cxx_class = self.cxx_class 1542 # Add the name and path of this object to be able to link to 1543 # the stats 1544 d.name = self.get_name() 1545 d.path = self.path() 1546 1547 for param in sorted(self._params.keys()): 1548 value = self._values.get(param) 1549 if value != None: 1550 d[param] = value.config_value() 1551 1552 for n in sorted(self._children.keys()): 1553 child = self._children[n] 1554 # Use the name of the attribute (and not get_name()) as 1555 # the key in the JSON dictionary to capture the hierarchy 1556 # in the Python code that assembled this system 1557 d[n] = child.get_config_as_dict() 1558 1559 for port_name in sorted(self._ports.keys()): 1560 port = self._port_refs.get(port_name, None) 1561 if port != None: 1562 # Represent each port with a dictionary containing the 1563 # prominent attributes 1564 d[port_name] = port.get_config_as_dict() 1565 1566 return d 1567 1568 def getCCParams(self): 1569 if self._ccParams: 1570 return self._ccParams 1571 1572 cc_params_struct = getattr(m5.internal.params, '%sParams' % self.type) 1573 cc_params = cc_params_struct() 1574 cc_params.name = str(self) 1575 1576 param_names = list(self._params.keys()) 1577 param_names.sort() 1578 for param in param_names: 1579 value = self._values.get(param) 1580 if value is None: 1581 fatal("%s.%s without default or user set value", 1582 self.path(), param) 1583 1584 value = value.getValue() 1585 if isinstance(self._params[param], VectorParamDesc): 1586 assert isinstance(value, list) 1587 vec = getattr(cc_params, param) 1588 assert not len(vec) 1589 # Some types are exposed as opaque types. They support 1590 # the append operation unlike the automatically 1591 # wrapped types. 1592 if isinstance(vec, list): 1593 setattr(cc_params, param, list(value)) 1594 else: 1595 for v in value: 1596 getattr(cc_params, param).append(v) 1597 else: 1598 setattr(cc_params, param, value) 1599 1600 port_names = list(self._ports.keys()) 1601 port_names.sort() 1602 for port_name in port_names: 1603 port = self._port_refs.get(port_name, None) 1604 if port != None: 1605 port_count = len(port) 1606 else: 1607 port_count = 0 1608 setattr(cc_params, 'port_' + port_name + '_connection_count', 1609 port_count) 1610 self._ccParams = cc_params 1611 return self._ccParams 1612 1613 # Get C++ object corresponding to this object, calling C++ if 1614 # necessary to construct it. Does *not* recursively create 1615 # children. 1616 def getCCObject(self): 1617 if not self._ccObject: 1618 # Make sure this object is in the configuration hierarchy 1619 if not self._parent and not isRoot(self): 1620 raise RuntimeError("Attempt to instantiate orphan node") 1621 # Cycles in the configuration hierarchy are not supported. This 1622 # will catch the resulting recursion and stop. 1623 self._ccObject = -1 1624 if not self.abstract: 1625 params = self.getCCParams() 1626 self._ccObject = params.create() 1627 elif self._ccObject == -1: 1628 raise RuntimeError("%s: Cycle found in configuration hierarchy." \ 1629 % self.path()) 1630 return self._ccObject 1631 1632 def descendants(self): 1633 yield self 1634 # The order of the dict is implementation dependent, so sort 1635 # it based on the key (name) to ensure the order is the same 1636 # on all hosts 1637 for (name, child) in sorted(self._children.items()): 1638 for obj in child.descendants(): 1639 yield obj 1640 1641 # Call C++ to create C++ object corresponding to this object 1642 def createCCObject(self): 1643 self.getCCParams() 1644 self.getCCObject() # force creation 1645 1646 def getValue(self): 1647 return self.getCCObject() 1648 1649 @cxxMethod(return_value_policy="reference") 1650 def getPort(self, if_name, idx): 1651 pass 1652 1653 # Create C++ port connections corresponding to the connections in 1654 # _port_refs 1655 def connectPorts(self): 1656 # Sort the ports based on their attribute name to ensure the 1657 # order is the same on all hosts 1658 for (attr, portRef) in sorted(self._port_refs.items()): 1659 portRef.ccConnect() 1660 1661 # Default function for generating the device structure. 1662 # Can be overloaded by the inheriting class 1663 def generateDeviceTree(self, state): 1664 return # return without yielding anything 1665 yield # make this function a (null) generator 1666 1667 def recurseDeviceTree(self, state): 1668 for child in self._children.values(): 1669 for item in child: # For looping over SimObjectVectors 1670 for dt in item.generateDeviceTree(state): 1671 yield dt 1672 1673 # On a separate method otherwise certain buggy Python versions 1674 # would fail with: SyntaxError: unqualified exec is not allowed 1675 # in function 'apply_config' 1676 def _apply_config_get_dict(self): 1677 return { 1678 child_name: SimObjectCliWrapper( 1679 iter(self._children[child_name])) 1680 for child_name in self._children 1681 } 1682 1683 def apply_config(self, params): 1684 """ 1685 exec a list of Python code strings contained in params. 1686 1687 The only exposed globals to those strings are the child 1688 SimObjects of this node. 1689 1690 This function is intended to allow users to modify SimObject 1691 parameters from the command line with Python statements. 1692 """ 1693 d = self._apply_config_get_dict() 1694 for param in params: 1695 exec(param, d) 1696 1697# Function to provide to C++ so it can look up instances based on paths 1698def resolveSimObject(name): 1699 obj = instanceDict[name] 1700 return obj.getCCObject() 1701 1702def isSimObject(value): 1703 return isinstance(value, SimObject) 1704 1705def isSimObjectClass(value): 1706 return issubclass(value, SimObject) 1707 1708def isSimObjectVector(value): 1709 return isinstance(value, SimObjectVector) 1710 1711def isSimObjectSequence(value): 1712 if not isinstance(value, (list, tuple)) or len(value) == 0: 1713 return False 1714 1715 for val in value: 1716 if not isNullPointer(val) and not isSimObject(val): 1717 return False 1718 1719 return True 1720 1721def isSimObjectOrSequence(value): 1722 return isSimObject(value) or isSimObjectSequence(value) 1723 1724def isRoot(obj): 1725 from m5.objects import Root 1726 return obj and obj is Root.getInstance() 1727 1728def isSimObjectOrVector(value): 1729 return isSimObject(value) or isSimObjectVector(value) 1730 1731def tryAsSimObjectOrVector(value): 1732 if isSimObjectOrVector(value): 1733 return value 1734 if isSimObjectSequence(value): 1735 return SimObjectVector(value) 1736 return None 1737 1738def coerceSimObjectOrVector(value): 1739 value = tryAsSimObjectOrVector(value) 1740 if value is None: 1741 raise TypeError("SimObject or SimObjectVector expected") 1742 return value 1743 1744baseClasses = allClasses.copy() 1745baseInstances = instanceDict.copy() 1746 1747def clear(): 1748 global allClasses, instanceDict, noCxxHeader 1749 1750 allClasses = baseClasses.copy() 1751 instanceDict = baseInstances.copy() 1752 noCxxHeader = False 1753 1754# __all__ defines the list of symbols that get exported when 1755# 'from config import *' is invoked. Try to keep this reasonably 1756# short to avoid polluting other namespaces. 1757__all__ = [ 1758 'SimObject', 1759 'cxxMethod', 1760 'PyBindMethod', 1761 'PyBindProperty', 1762] 1763