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