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