params.py revision 13709
1# Copyright (c) 2012-2014, 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-2011 Advanced Micro Devices, Inc. 15# All rights reserved. 16# 17# Redistribution and use in source and binary forms, with or without 18# modification, are permitted provided that the following conditions are 19# met: redistributions of source code must retain the above copyright 20# notice, this list of conditions and the following disclaimer; 21# redistributions in binary form must reproduce the above copyright 22# notice, this list of conditions and the following disclaimer in the 23# documentation and/or other materials provided with the distribution; 24# neither the name of the copyright holders nor the names of its 25# contributors may be used to endorse or promote products derived from 26# this software without specific prior written permission. 27# 28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39# 40# Authors: Steve Reinhardt 41# Nathan Binkert 42# Gabe Black 43# Andreas Hansson 44 45##################################################################### 46# 47# Parameter description classes 48# 49# The _params dictionary in each class maps parameter names to either 50# a Param or a VectorParam object. These objects contain the 51# parameter description string, the parameter type, and the default 52# value (if any). The convert() method on these objects is used to 53# force whatever value is assigned to the parameter to the appropriate 54# type. 55# 56# Note that the default values are loaded into the class's attribute 57# space when the parameter dictionary is initialized (in 58# MetaSimObject._new_param()); after that point they aren't used. 59# 60##################################################################### 61 62from __future__ import print_function 63 64import copy 65import datetime 66import re 67import sys 68import time 69import math 70 71import proxy 72import ticks 73from util import * 74 75def isSimObject(*args, **kwargs): 76 return SimObject.isSimObject(*args, **kwargs) 77 78def isSimObjectSequence(*args, **kwargs): 79 return SimObject.isSimObjectSequence(*args, **kwargs) 80 81def isSimObjectClass(*args, **kwargs): 82 return SimObject.isSimObjectClass(*args, **kwargs) 83 84allParams = {} 85 86class MetaParamValue(type): 87 def __new__(mcls, name, bases, dct): 88 cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct) 89 assert name not in allParams 90 allParams[name] = cls 91 return cls 92 93 94# Dummy base class to identify types that are legitimate for SimObject 95# parameters. 96class ParamValue(object): 97 __metaclass__ = MetaParamValue 98 cmd_line_settable = False 99 100 # Generate the code needed as a prerequisite for declaring a C++ 101 # object of this type. Typically generates one or more #include 102 # statements. Used when declaring parameters of this type. 103 @classmethod 104 def cxx_predecls(cls, code): 105 pass 106 107 @classmethod 108 def pybind_predecls(cls, code): 109 cls.cxx_predecls(code) 110 111 # default for printing to .ini file is regular string conversion. 112 # will be overridden in some cases 113 def ini_str(self): 114 return str(self) 115 116 # default for printing to .json file is regular string conversion. 117 # will be overridden in some cases, mostly to use native Python 118 # types where there are similar JSON types 119 def config_value(self): 120 return str(self) 121 122 # Prerequisites for .ini parsing with cxx_ini_parse 123 @classmethod 124 def cxx_ini_predecls(cls, code): 125 pass 126 127 # parse a .ini file entry for this param from string expression 128 # src into lvalue dest (of the param's C++ type) 129 @classmethod 130 def cxx_ini_parse(cls, code, src, dest, ret): 131 code('// Unhandled param type: %s' % cls.__name__) 132 code('%s false;' % ret) 133 134 # allows us to blithely call unproxy() on things without checking 135 # if they're really proxies or not 136 def unproxy(self, base): 137 return self 138 139 # Produce a human readable version of the stored value 140 def pretty_print(self, value): 141 return str(value) 142 143# Regular parameter description. 144class ParamDesc(object): 145 def __init__(self, ptype_str, ptype, *args, **kwargs): 146 self.ptype_str = ptype_str 147 # remember ptype only if it is provided 148 if ptype != None: 149 self.ptype = ptype 150 151 if args: 152 if len(args) == 1: 153 self.desc = args[0] 154 elif len(args) == 2: 155 self.default = args[0] 156 self.desc = args[1] 157 else: 158 raise TypeError('too many arguments') 159 160 if 'desc' in kwargs: 161 assert(not hasattr(self, 'desc')) 162 self.desc = kwargs['desc'] 163 del kwargs['desc'] 164 165 if 'default' in kwargs: 166 assert(not hasattr(self, 'default')) 167 self.default = kwargs['default'] 168 del kwargs['default'] 169 170 if kwargs: 171 raise TypeError('extra unknown kwargs %s' % kwargs) 172 173 if not hasattr(self, 'desc'): 174 raise TypeError('desc attribute missing') 175 176 def __getattr__(self, attr): 177 if attr == 'ptype': 178 ptype = SimObject.allClasses[self.ptype_str] 179 assert isSimObjectClass(ptype) 180 self.ptype = ptype 181 return ptype 182 183 raise AttributeError("'%s' object has no attribute '%s'" % \ 184 (type(self).__name__, attr)) 185 186 def example_str(self): 187 if hasattr(self.ptype, "ex_str"): 188 return self.ptype.ex_str 189 else: 190 return self.ptype_str 191 192 # Is the param available to be exposed on the command line 193 def isCmdLineSettable(self): 194 if hasattr(self.ptype, "cmd_line_settable"): 195 return self.ptype.cmd_line_settable 196 else: 197 return False 198 199 def convert(self, value): 200 if isinstance(value, proxy.BaseProxy): 201 value.set_param_desc(self) 202 return value 203 if 'ptype' not in self.__dict__ and isNullPointer(value): 204 # deferred evaluation of SimObject; continue to defer if 205 # we're just assigning a null pointer 206 return value 207 if isinstance(value, self.ptype): 208 return value 209 if isNullPointer(value) and isSimObjectClass(self.ptype): 210 return value 211 return self.ptype(value) 212 213 def pretty_print(self, value): 214 if isinstance(value, proxy.BaseProxy): 215 return str(value) 216 if isNullPointer(value): 217 return NULL 218 return self.ptype(value).pretty_print(value) 219 220 def cxx_predecls(self, code): 221 code('#include <cstddef>') 222 self.ptype.cxx_predecls(code) 223 224 def pybind_predecls(self, code): 225 self.ptype.pybind_predecls(code) 226 227 def cxx_decl(self, code): 228 code('${{self.ptype.cxx_type}} ${{self.name}};') 229 230# Vector-valued parameter description. Just like ParamDesc, except 231# that the value is a vector (list) of the specified type instead of a 232# single value. 233 234class VectorParamValue(list): 235 __metaclass__ = MetaParamValue 236 def __setattr__(self, attr, value): 237 raise AttributeError("Not allowed to set %s on '%s'" % \ 238 (attr, type(self).__name__)) 239 240 def config_value(self): 241 return [v.config_value() for v in self] 242 243 def ini_str(self): 244 return ' '.join([v.ini_str() for v in self]) 245 246 def getValue(self): 247 return [ v.getValue() for v in self ] 248 249 def unproxy(self, base): 250 if len(self) == 1 and isinstance(self[0], proxy.BaseProxy): 251 # The value is a proxy (e.g. Parent.any, Parent.all or 252 # Parent.x) therefore try resolve it 253 return self[0].unproxy(base) 254 else: 255 return [v.unproxy(base) for v in self] 256 257class SimObjectVector(VectorParamValue): 258 # support clone operation 259 def __call__(self, **kwargs): 260 return SimObjectVector([v(**kwargs) for v in self]) 261 262 def clear_parent(self, old_parent): 263 for v in self: 264 v.clear_parent(old_parent) 265 266 def set_parent(self, parent, name): 267 if len(self) == 1: 268 self[0].set_parent(parent, name) 269 else: 270 width = int(math.ceil(math.log(len(self))/math.log(10))) 271 for i,v in enumerate(self): 272 v.set_parent(parent, "%s%0*d" % (name, width, i)) 273 274 def has_parent(self): 275 return any([e.has_parent() for e in self if not isNullPointer(e)]) 276 277 # return 'cpu0 cpu1' etc. for print_ini() 278 def get_name(self): 279 return ' '.join([v._name for v in self]) 280 281 # By iterating through the constituent members of the vector here 282 # we can nicely handle iterating over all a SimObject's children 283 # without having to provide lots of special functions on 284 # SimObjectVector directly. 285 def descendants(self): 286 for v in self: 287 for obj in v.descendants(): 288 yield obj 289 290 def get_config_as_dict(self): 291 a = [] 292 for v in self: 293 a.append(v.get_config_as_dict()) 294 return a 295 296 # If we are replacing an item in the vector, make sure to set the 297 # parent reference of the new SimObject to be the same as the parent 298 # of the SimObject being replaced. Useful to have if we created 299 # a SimObjectVector of temporary objects that will be modified later in 300 # configuration scripts. 301 def __setitem__(self, key, value): 302 val = self[key] 303 if value.has_parent(): 304 warn("SimObject %s already has a parent" % value.get_name() +\ 305 " that is being overwritten by a SimObjectVector") 306 value.set_parent(val.get_parent(), val._name) 307 super(SimObjectVector, self).__setitem__(key, value) 308 309 # Enumerate the params of each member of the SimObject vector. Creates 310 # strings that will allow indexing into the vector by the python code and 311 # allow it to be specified on the command line. 312 def enumerateParams(self, flags_dict = {}, 313 cmd_line_str = "", 314 access_str = ""): 315 if hasattr(self, "_paramEnumed"): 316 print("Cycle detected enumerating params at %s?!" % (cmd_line_str)) 317 else: 318 x = 0 319 for vals in self: 320 # Each entry in the SimObjectVector should be an 321 # instance of a SimObject 322 flags_dict = vals.enumerateParams(flags_dict, 323 cmd_line_str + "%d." % x, 324 access_str + "[%d]." % x) 325 x = x + 1 326 327 return flags_dict 328 329class VectorParamDesc(ParamDesc): 330 # Convert assigned value to appropriate type. If the RHS is not a 331 # list or tuple, it generates a single-element list. 332 def convert(self, value): 333 if isinstance(value, (list, tuple)): 334 # list: coerce each element into new list 335 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 336 elif isinstance(value, str): 337 # If input is a csv string 338 tmp_list = [ ParamDesc.convert(self, v) \ 339 for v in value.strip('[').strip(']').split(',') ] 340 else: 341 # singleton: coerce to a single-element list 342 tmp_list = [ ParamDesc.convert(self, value) ] 343 344 if isSimObjectSequence(tmp_list): 345 return SimObjectVector(tmp_list) 346 else: 347 return VectorParamValue(tmp_list) 348 349 # Produce a human readable example string that describes 350 # how to set this vector parameter in the absence of a default 351 # value. 352 def example_str(self): 353 s = super(VectorParamDesc, self).example_str() 354 help_str = "[" + s + "," + s + ", ...]" 355 return help_str 356 357 # Produce a human readable representation of the value of this vector param. 358 def pretty_print(self, value): 359 if isinstance(value, (list, tuple)): 360 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value ] 361 elif isinstance(value, str): 362 tmp_list = [ ParamDesc.pretty_print(self, v) for v in value.split(',') ] 363 else: 364 tmp_list = [ ParamDesc.pretty_print(self, value) ] 365 366 return tmp_list 367 368 # This is a helper function for the new config system 369 def __call__(self, value): 370 if isinstance(value, (list, tuple)): 371 # list: coerce each element into new list 372 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 373 elif isinstance(value, str): 374 # If input is a csv string 375 tmp_list = [ ParamDesc.convert(self, v) \ 376 for v in value.strip('[').strip(']').split(',') ] 377 else: 378 # singleton: coerce to a single-element list 379 tmp_list = [ ParamDesc.convert(self, value) ] 380 381 return VectorParamValue(tmp_list) 382 383 def cxx_predecls(self, code): 384 code('#include <vector>') 385 self.ptype.cxx_predecls(code) 386 387 def pybind_predecls(self, code): 388 code('#include <vector>') 389 self.ptype.pybind_predecls(code) 390 391 def cxx_decl(self, code): 392 code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};') 393 394class ParamFactory(object): 395 def __init__(self, param_desc_class, ptype_str = None): 396 self.param_desc_class = param_desc_class 397 self.ptype_str = ptype_str 398 399 def __getattr__(self, attr): 400 if self.ptype_str: 401 attr = self.ptype_str + '.' + attr 402 return ParamFactory(self.param_desc_class, attr) 403 404 # E.g., Param.Int(5, "number of widgets") 405 def __call__(self, *args, **kwargs): 406 ptype = None 407 try: 408 ptype = allParams[self.ptype_str] 409 except KeyError: 410 # if name isn't defined yet, assume it's a SimObject, and 411 # try to resolve it later 412 pass 413 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 414 415Param = ParamFactory(ParamDesc) 416VectorParam = ParamFactory(VectorParamDesc) 417 418##################################################################### 419# 420# Parameter Types 421# 422# Though native Python types could be used to specify parameter types 423# (the 'ptype' field of the Param and VectorParam classes), it's more 424# flexible to define our own set of types. This gives us more control 425# over how Python expressions are converted to values (via the 426# __init__() constructor) and how these values are printed out (via 427# the __str__() conversion method). 428# 429##################################################################### 430 431# String-valued parameter. Just mixin the ParamValue class with the 432# built-in str class. 433class String(ParamValue,str): 434 cxx_type = 'std::string' 435 cmd_line_settable = True 436 437 @classmethod 438 def cxx_predecls(self, code): 439 code('#include <string>') 440 441 def __call__(self, value): 442 self = value 443 return value 444 445 @classmethod 446 def cxx_ini_parse(self, code, src, dest, ret): 447 code('%s = %s;' % (dest, src)) 448 code('%s true;' % ret) 449 450 def getValue(self): 451 return self 452 453# superclass for "numeric" parameter values, to emulate math 454# operations in a type-safe way. e.g., a Latency times an int returns 455# a new Latency object. 456class NumericParamValue(ParamValue): 457 @staticmethod 458 def unwrap(v): 459 return v.value if isinstance(v, NumericParamValue) else v 460 461 def __str__(self): 462 return str(self.value) 463 464 def __float__(self): 465 return float(self.value) 466 467 def __long__(self): 468 return long(self.value) 469 470 def __int__(self): 471 return int(self.value) 472 473 # hook for bounds checking 474 def _check(self): 475 return 476 477 def __mul__(self, other): 478 newobj = self.__class__(self) 479 newobj.value *= NumericParamValue.unwrap(other) 480 newobj._check() 481 return newobj 482 483 __rmul__ = __mul__ 484 485 def __truediv__(self, other): 486 newobj = self.__class__(self) 487 newobj.value /= NumericParamValue.unwrap(other) 488 newobj._check() 489 return newobj 490 491 def __floordiv__(self, other): 492 newobj = self.__class__(self) 493 newobj.value //= NumericParamValue.unwrap(other) 494 newobj._check() 495 return newobj 496 497 498 def __add__(self, other): 499 newobj = self.__class__(self) 500 newobj.value += NumericParamValue.unwrap(other) 501 newobj._check() 502 return newobj 503 504 def __sub__(self, other): 505 newobj = self.__class__(self) 506 newobj.value -= NumericParamValue.unwrap(other) 507 newobj._check() 508 return newobj 509 510 def __iadd__(self, other): 511 self.value += NumericParamValue.unwrap(other) 512 self._check() 513 return self 514 515 def __isub__(self, other): 516 self.value -= NumericParamValue.unwrap(other) 517 self._check() 518 return self 519 520 def __imul__(self, other): 521 self.value *= NumericParamValue.unwrap(other) 522 self._check() 523 return self 524 525 def __itruediv__(self, other): 526 self.value /= NumericParamValue.unwrap(other) 527 self._check() 528 return self 529 530 def __ifloordiv__(self, other): 531 self.value //= NumericParamValue.unwrap(other) 532 self._check() 533 return self 534 535 def __lt__(self, other): 536 return self.value < NumericParamValue.unwrap(other) 537 538 # Python 2.7 pre __future__.division operators 539 # TODO: Remove these when after "import division from __future__" 540 __div__ = __truediv__ 541 __idiv__ = __itruediv__ 542 543 def config_value(self): 544 return self.value 545 546 @classmethod 547 def cxx_ini_predecls(cls, code): 548 # Assume that base/str.hh will be included anyway 549 # code('#include "base/str.hh"') 550 pass 551 552 # The default for parsing PODs from an .ini entry is to extract from an 553 # istringstream and let overloading choose the right type according to 554 # the dest type. 555 @classmethod 556 def cxx_ini_parse(self, code, src, dest, ret): 557 code('%s to_number(%s, %s);' % (ret, src, dest)) 558 559# Metaclass for bounds-checked integer parameters. See CheckedInt. 560class CheckedIntType(MetaParamValue): 561 def __init__(cls, name, bases, dict): 562 super(CheckedIntType, cls).__init__(name, bases, dict) 563 564 # CheckedInt is an abstract base class, so we actually don't 565 # want to do any processing on it... the rest of this code is 566 # just for classes that derive from CheckedInt. 567 if name == 'CheckedInt': 568 return 569 570 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 571 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 572 panic("CheckedInt subclass %s must define either\n" \ 573 " 'min' and 'max' or 'size' and 'unsigned'\n", 574 name); 575 if cls.unsigned: 576 cls.min = 0 577 cls.max = 2 ** cls.size - 1 578 else: 579 cls.min = -(2 ** (cls.size - 1)) 580 cls.max = (2 ** (cls.size - 1)) - 1 581 582# Abstract superclass for bounds-checked integer parameters. This 583# class is subclassed to generate parameter classes with specific 584# bounds. Initialization of the min and max bounds is done in the 585# metaclass CheckedIntType.__init__. 586class CheckedInt(NumericParamValue): 587 __metaclass__ = CheckedIntType 588 cmd_line_settable = True 589 590 def _check(self): 591 if not self.min <= self.value <= self.max: 592 raise TypeError('Integer param out of bounds %d < %d < %d' % \ 593 (self.min, self.value, self.max)) 594 595 def __init__(self, value): 596 if isinstance(value, str): 597 self.value = convert.toInteger(value) 598 elif isinstance(value, (int, long, float, NumericParamValue)): 599 self.value = long(value) 600 else: 601 raise TypeError("Can't convert object of type %s to CheckedInt" \ 602 % type(value).__name__) 603 self._check() 604 605 def __call__(self, value): 606 self.__init__(value) 607 return value 608 609 @classmethod 610 def cxx_predecls(cls, code): 611 # most derived types require this, so we just do it here once 612 code('#include "base/types.hh"') 613 614 def getValue(self): 615 return long(self.value) 616 617class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 618class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 619 620class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 621class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 622class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 623class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 624class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 625class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 626class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 627class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 628 629class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 630class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 631class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 632class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 633 634class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 635 636class Cycles(CheckedInt): 637 cxx_type = 'Cycles' 638 size = 64 639 unsigned = True 640 641 def getValue(self): 642 from _m5.core import Cycles 643 return Cycles(self.value) 644 645 @classmethod 646 def cxx_ini_predecls(cls, code): 647 # Assume that base/str.hh will be included anyway 648 # code('#include "base/str.hh"') 649 pass 650 651 @classmethod 652 def cxx_ini_parse(cls, code, src, dest, ret): 653 code('uint64_t _temp;') 654 code('bool _ret = to_number(%s, _temp);' % src) 655 code('if (_ret)') 656 code(' %s = Cycles(_temp);' % dest) 657 code('%s _ret;' % ret) 658 659class Float(ParamValue, float): 660 cxx_type = 'double' 661 cmd_line_settable = True 662 663 def __init__(self, value): 664 if isinstance(value, (int, long, float, NumericParamValue, Float, str)): 665 self.value = float(value) 666 else: 667 raise TypeError("Can't convert object of type %s to Float" \ 668 % type(value).__name__) 669 670 def __call__(self, value): 671 self.__init__(value) 672 return value 673 674 def getValue(self): 675 return float(self.value) 676 677 def config_value(self): 678 return self 679 680 @classmethod 681 def cxx_ini_predecls(cls, code): 682 code('#include <sstream>') 683 684 @classmethod 685 def cxx_ini_parse(self, code, src, dest, ret): 686 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 687 688class MemorySize(CheckedInt): 689 cxx_type = 'uint64_t' 690 ex_str = '512MB' 691 size = 64 692 unsigned = True 693 def __init__(self, value): 694 if isinstance(value, MemorySize): 695 self.value = value.value 696 else: 697 self.value = convert.toMemorySize(value) 698 self._check() 699 700class MemorySize32(CheckedInt): 701 cxx_type = 'uint32_t' 702 ex_str = '512MB' 703 size = 32 704 unsigned = True 705 def __init__(self, value): 706 if isinstance(value, MemorySize): 707 self.value = value.value 708 else: 709 self.value = convert.toMemorySize(value) 710 self._check() 711 712class Addr(CheckedInt): 713 cxx_type = 'Addr' 714 size = 64 715 unsigned = True 716 def __init__(self, value): 717 if isinstance(value, Addr): 718 self.value = value.value 719 else: 720 try: 721 # Often addresses are referred to with sizes. Ex: A device 722 # base address is at "512MB". Use toMemorySize() to convert 723 # these into addresses. If the address is not specified with a 724 # "size", an exception will occur and numeric translation will 725 # proceed below. 726 self.value = convert.toMemorySize(value) 727 except (TypeError, ValueError): 728 # Convert number to string and use long() to do automatic 729 # base conversion (requires base=0 for auto-conversion) 730 self.value = long(str(value), base=0) 731 732 self._check() 733 def __add__(self, other): 734 if isinstance(other, Addr): 735 return self.value + other.value 736 else: 737 return self.value + other 738 def pretty_print(self, value): 739 try: 740 val = convert.toMemorySize(value) 741 except TypeError: 742 val = long(value) 743 return "0x%x" % long(val) 744 745class AddrRange(ParamValue): 746 cxx_type = 'AddrRange' 747 748 def __init__(self, *args, **kwargs): 749 # Disable interleaving and hashing by default 750 self.intlvHighBit = 0 751 self.xorHighBit = 0 752 self.intlvBits = 0 753 self.intlvMatch = 0 754 755 def handle_kwargs(self, kwargs): 756 # An address range needs to have an upper limit, specified 757 # either explicitly with an end, or as an offset using the 758 # size keyword. 759 if 'end' in kwargs: 760 self.end = Addr(kwargs.pop('end')) 761 elif 'size' in kwargs: 762 self.end = self.start + Addr(kwargs.pop('size')) - 1 763 else: 764 raise TypeError("Either end or size must be specified") 765 766 # Now on to the optional bit 767 if 'intlvHighBit' in kwargs: 768 self.intlvHighBit = int(kwargs.pop('intlvHighBit')) 769 if 'xorHighBit' in kwargs: 770 self.xorHighBit = int(kwargs.pop('xorHighBit')) 771 if 'intlvBits' in kwargs: 772 self.intlvBits = int(kwargs.pop('intlvBits')) 773 if 'intlvMatch' in kwargs: 774 self.intlvMatch = int(kwargs.pop('intlvMatch')) 775 776 if len(args) == 0: 777 self.start = Addr(kwargs.pop('start')) 778 handle_kwargs(self, kwargs) 779 780 elif len(args) == 1: 781 if kwargs: 782 self.start = Addr(args[0]) 783 handle_kwargs(self, kwargs) 784 elif isinstance(args[0], (list, tuple)): 785 self.start = Addr(args[0][0]) 786 self.end = Addr(args[0][1]) 787 else: 788 self.start = Addr(0) 789 self.end = Addr(args[0]) - 1 790 791 elif len(args) == 2: 792 self.start = Addr(args[0]) 793 self.end = Addr(args[1]) 794 else: 795 raise TypeError("Too many arguments specified") 796 797 if kwargs: 798 raise TypeError("Too many keywords: %s" % list(kwargs.keys())) 799 800 def __str__(self): 801 return '%s:%s:%s:%s:%s:%s' \ 802 % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\ 803 self.intlvBits, self.intlvMatch) 804 805 def size(self): 806 # Divide the size by the size of the interleaving slice 807 return (long(self.end) - long(self.start) + 1) >> self.intlvBits 808 809 @classmethod 810 def cxx_predecls(cls, code): 811 Addr.cxx_predecls(code) 812 code('#include "base/addr_range.hh"') 813 814 @classmethod 815 def pybind_predecls(cls, code): 816 Addr.pybind_predecls(code) 817 code('#include "base/addr_range.hh"') 818 819 @classmethod 820 def cxx_ini_predecls(cls, code): 821 code('#include <sstream>') 822 823 @classmethod 824 def cxx_ini_parse(cls, code, src, dest, ret): 825 code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;') 826 code('uint64_t _intlvBits = 0, _intlvMatch = 0;') 827 code('char _sep;') 828 code('std::istringstream _stream(${src});') 829 code('_stream >> _start;') 830 code('_stream.get(_sep);') 831 code('_stream >> _end;') 832 code('if (!_stream.fail() && !_stream.eof()) {') 833 code(' _stream.get(_sep);') 834 code(' _stream >> _intlvHighBit;') 835 code(' _stream.get(_sep);') 836 code(' _stream >> _xorHighBit;') 837 code(' _stream.get(_sep);') 838 code(' _stream >> _intlvBits;') 839 code(' _stream.get(_sep);') 840 code(' _stream >> _intlvMatch;') 841 code('}') 842 code('bool _ret = !_stream.fail() &&' 843 '_stream.eof() && _sep == \':\';') 844 code('if (_ret)') 845 code(' ${dest} = AddrRange(_start, _end, _intlvHighBit, \ 846 _xorHighBit, _intlvBits, _intlvMatch);') 847 code('${ret} _ret;') 848 849 def getValue(self): 850 # Go from the Python class to the wrapped C++ class 851 from _m5.range import AddrRange 852 853 return AddrRange(long(self.start), long(self.end), 854 int(self.intlvHighBit), int(self.xorHighBit), 855 int(self.intlvBits), int(self.intlvMatch)) 856 857# Boolean parameter type. Python doesn't let you subclass bool, since 858# it doesn't want to let you create multiple instances of True and 859# False. Thus this is a little more complicated than String. 860class Bool(ParamValue): 861 cxx_type = 'bool' 862 cmd_line_settable = True 863 864 def __init__(self, value): 865 try: 866 self.value = convert.toBool(value) 867 except TypeError: 868 self.value = bool(value) 869 870 def __call__(self, value): 871 self.__init__(value) 872 return value 873 874 def getValue(self): 875 return bool(self.value) 876 877 def __str__(self): 878 return str(self.value) 879 880 # implement truth value testing for Bool parameters so that these params 881 # evaluate correctly during the python configuration phase 882 def __bool__(self): 883 return bool(self.value) 884 885 # Python 2.7 uses __nonzero__ instead of __bool__ 886 __nonzero__ = __bool__ 887 888 def ini_str(self): 889 if self.value: 890 return 'true' 891 return 'false' 892 893 def config_value(self): 894 return self.value 895 896 @classmethod 897 def cxx_ini_predecls(cls, code): 898 # Assume that base/str.hh will be included anyway 899 # code('#include "base/str.hh"') 900 pass 901 902 @classmethod 903 def cxx_ini_parse(cls, code, src, dest, ret): 904 code('%s to_bool(%s, %s);' % (ret, src, dest)) 905 906def IncEthernetAddr(addr, val = 1): 907 bytes = [ int(x, 16) for x in addr.split(':') ] 908 bytes[5] += val 909 for i in (5, 4, 3, 2, 1): 910 val,rem = divmod(bytes[i], 256) 911 bytes[i] = rem 912 if val == 0: 913 break 914 bytes[i - 1] += val 915 assert(bytes[0] <= 255) 916 return ':'.join(map(lambda x: '%02x' % x, bytes)) 917 918_NextEthernetAddr = "00:90:00:00:00:01" 919def NextEthernetAddr(): 920 global _NextEthernetAddr 921 922 value = _NextEthernetAddr 923 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 924 return value 925 926class EthernetAddr(ParamValue): 927 cxx_type = 'Net::EthAddr' 928 ex_str = "00:90:00:00:00:01" 929 cmd_line_settable = True 930 931 @classmethod 932 def cxx_predecls(cls, code): 933 code('#include "base/inet.hh"') 934 935 def __init__(self, value): 936 if value == NextEthernetAddr: 937 self.value = value 938 return 939 940 if not isinstance(value, str): 941 raise TypeError("expected an ethernet address and didn't get one") 942 943 bytes = value.split(':') 944 if len(bytes) != 6: 945 raise TypeError('invalid ethernet address %s' % value) 946 947 for byte in bytes: 948 if not 0 <= int(byte, base=16) <= 0xff: 949 raise TypeError('invalid ethernet address %s' % value) 950 951 self.value = value 952 953 def __call__(self, value): 954 self.__init__(value) 955 return value 956 957 def unproxy(self, base): 958 if self.value == NextEthernetAddr: 959 return EthernetAddr(self.value()) 960 return self 961 962 def getValue(self): 963 from _m5.net import EthAddr 964 return EthAddr(self.value) 965 966 def __str__(self): 967 return self.value 968 969 def ini_str(self): 970 return self.value 971 972 @classmethod 973 def cxx_ini_parse(self, code, src, dest, ret): 974 code('%s = Net::EthAddr(%s);' % (dest, src)) 975 code('%s true;' % ret) 976 977# When initializing an IpAddress, pass in an existing IpAddress, a string of 978# the form "a.b.c.d", or an integer representing an IP. 979class IpAddress(ParamValue): 980 cxx_type = 'Net::IpAddress' 981 ex_str = "127.0.0.1" 982 cmd_line_settable = True 983 984 @classmethod 985 def cxx_predecls(cls, code): 986 code('#include "base/inet.hh"') 987 988 def __init__(self, value): 989 if isinstance(value, IpAddress): 990 self.ip = value.ip 991 else: 992 try: 993 self.ip = convert.toIpAddress(value) 994 except TypeError: 995 self.ip = long(value) 996 self.verifyIp() 997 998 def __call__(self, value): 999 self.__init__(value) 1000 return value 1001 1002 def __str__(self): 1003 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)] 1004 return '%d.%d.%d.%d' % tuple(tup) 1005 1006 def __eq__(self, other): 1007 if isinstance(other, IpAddress): 1008 return self.ip == other.ip 1009 elif isinstance(other, str): 1010 try: 1011 return self.ip == convert.toIpAddress(other) 1012 except: 1013 return False 1014 else: 1015 return self.ip == other 1016 1017 def __ne__(self, other): 1018 return not (self == other) 1019 1020 def verifyIp(self): 1021 if self.ip < 0 or self.ip >= (1 << 32): 1022 raise TypeError("invalid ip address %#08x" % self.ip) 1023 1024 def getValue(self): 1025 from _m5.net import IpAddress 1026 return IpAddress(self.ip) 1027 1028# When initializing an IpNetmask, pass in an existing IpNetmask, a string of 1029# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as 1030# positional or keyword arguments. 1031class IpNetmask(IpAddress): 1032 cxx_type = 'Net::IpNetmask' 1033 ex_str = "127.0.0.0/24" 1034 cmd_line_settable = True 1035 1036 @classmethod 1037 def cxx_predecls(cls, code): 1038 code('#include "base/inet.hh"') 1039 1040 def __init__(self, *args, **kwargs): 1041 def handle_kwarg(self, kwargs, key, elseVal = None): 1042 if key in kwargs: 1043 setattr(self, key, kwargs.pop(key)) 1044 elif elseVal: 1045 setattr(self, key, elseVal) 1046 else: 1047 raise TypeError("No value set for %s" % key) 1048 1049 if len(args) == 0: 1050 handle_kwarg(self, kwargs, 'ip') 1051 handle_kwarg(self, kwargs, 'netmask') 1052 1053 elif len(args) == 1: 1054 if kwargs: 1055 if not 'ip' in kwargs and not 'netmask' in kwargs: 1056 raise TypeError("Invalid arguments") 1057 handle_kwarg(self, kwargs, 'ip', args[0]) 1058 handle_kwarg(self, kwargs, 'netmask', args[0]) 1059 elif isinstance(args[0], IpNetmask): 1060 self.ip = args[0].ip 1061 self.netmask = args[0].netmask 1062 else: 1063 (self.ip, self.netmask) = convert.toIpNetmask(args[0]) 1064 1065 elif len(args) == 2: 1066 self.ip = args[0] 1067 self.netmask = args[1] 1068 else: 1069 raise TypeError("Too many arguments specified") 1070 1071 if kwargs: 1072 raise TypeError("Too many keywords: %s" % list(kwargs.keys())) 1073 1074 self.verify() 1075 1076 def __call__(self, value): 1077 self.__init__(value) 1078 return value 1079 1080 def __str__(self): 1081 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask) 1082 1083 def __eq__(self, other): 1084 if isinstance(other, IpNetmask): 1085 return self.ip == other.ip and self.netmask == other.netmask 1086 elif isinstance(other, str): 1087 try: 1088 return (self.ip, self.netmask) == convert.toIpNetmask(other) 1089 except: 1090 return False 1091 else: 1092 return False 1093 1094 def verify(self): 1095 self.verifyIp() 1096 if self.netmask < 0 or self.netmask > 32: 1097 raise TypeError("invalid netmask %d" % netmask) 1098 1099 def getValue(self): 1100 from _m5.net import IpNetmask 1101 return IpNetmask(self.ip, self.netmask) 1102 1103# When initializing an IpWithPort, pass in an existing IpWithPort, a string of 1104# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments. 1105class IpWithPort(IpAddress): 1106 cxx_type = 'Net::IpWithPort' 1107 ex_str = "127.0.0.1:80" 1108 cmd_line_settable = True 1109 1110 @classmethod 1111 def cxx_predecls(cls, code): 1112 code('#include "base/inet.hh"') 1113 1114 def __init__(self, *args, **kwargs): 1115 def handle_kwarg(self, kwargs, key, elseVal = None): 1116 if key in kwargs: 1117 setattr(self, key, kwargs.pop(key)) 1118 elif elseVal: 1119 setattr(self, key, elseVal) 1120 else: 1121 raise TypeError("No value set for %s" % key) 1122 1123 if len(args) == 0: 1124 handle_kwarg(self, kwargs, 'ip') 1125 handle_kwarg(self, kwargs, 'port') 1126 1127 elif len(args) == 1: 1128 if kwargs: 1129 if not 'ip' in kwargs and not 'port' in kwargs: 1130 raise TypeError("Invalid arguments") 1131 handle_kwarg(self, kwargs, 'ip', args[0]) 1132 handle_kwarg(self, kwargs, 'port', args[0]) 1133 elif isinstance(args[0], IpWithPort): 1134 self.ip = args[0].ip 1135 self.port = args[0].port 1136 else: 1137 (self.ip, self.port) = convert.toIpWithPort(args[0]) 1138 1139 elif len(args) == 2: 1140 self.ip = args[0] 1141 self.port = args[1] 1142 else: 1143 raise TypeError("Too many arguments specified") 1144 1145 if kwargs: 1146 raise TypeError("Too many keywords: %s" % list(kwargs.keys())) 1147 1148 self.verify() 1149 1150 def __call__(self, value): 1151 self.__init__(value) 1152 return value 1153 1154 def __str__(self): 1155 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port) 1156 1157 def __eq__(self, other): 1158 if isinstance(other, IpWithPort): 1159 return self.ip == other.ip and self.port == other.port 1160 elif isinstance(other, str): 1161 try: 1162 return (self.ip, self.port) == convert.toIpWithPort(other) 1163 except: 1164 return False 1165 else: 1166 return False 1167 1168 def verify(self): 1169 self.verifyIp() 1170 if self.port < 0 or self.port > 0xffff: 1171 raise TypeError("invalid port %d" % self.port) 1172 1173 def getValue(self): 1174 from _m5.net import IpWithPort 1175 return IpWithPort(self.ip, self.port) 1176 1177time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 1178 "%a %b %d %H:%M:%S %Y", 1179 "%Y/%m/%d %H:%M:%S", 1180 "%Y/%m/%d %H:%M", 1181 "%Y/%m/%d", 1182 "%m/%d/%Y %H:%M:%S", 1183 "%m/%d/%Y %H:%M", 1184 "%m/%d/%Y", 1185 "%m/%d/%y %H:%M:%S", 1186 "%m/%d/%y %H:%M", 1187 "%m/%d/%y"] 1188 1189 1190def parse_time(value): 1191 from time import gmtime, strptime, struct_time, time 1192 from datetime import datetime, date 1193 1194 if isinstance(value, struct_time): 1195 return value 1196 1197 if isinstance(value, (int, long)): 1198 return gmtime(value) 1199 1200 if isinstance(value, (datetime, date)): 1201 return value.timetuple() 1202 1203 if isinstance(value, str): 1204 if value in ('Now', 'Today'): 1205 return time.gmtime(time.time()) 1206 1207 for format in time_formats: 1208 try: 1209 return strptime(value, format) 1210 except ValueError: 1211 pass 1212 1213 raise ValueError("Could not parse '%s' as a time" % value) 1214 1215class Time(ParamValue): 1216 cxx_type = 'tm' 1217 1218 @classmethod 1219 def cxx_predecls(cls, code): 1220 code('#include <time.h>') 1221 1222 def __init__(self, value): 1223 self.value = parse_time(value) 1224 1225 def __call__(self, value): 1226 self.__init__(value) 1227 return value 1228 1229 def getValue(self): 1230 from _m5.core import tm 1231 import calendar 1232 1233 return tm.gmtime(calendar.timegm(self.value)) 1234 1235 def __str__(self): 1236 return time.asctime(self.value) 1237 1238 def ini_str(self): 1239 return str(self) 1240 1241 def get_config_as_dict(self): 1242 assert false 1243 return str(self) 1244 1245 @classmethod 1246 def cxx_ini_predecls(cls, code): 1247 code('#include <time.h>') 1248 1249 @classmethod 1250 def cxx_ini_parse(cls, code, src, dest, ret): 1251 code('char *_parse_ret = strptime((${src}).c_str(),') 1252 code(' "%a %b %d %H:%M:%S %Y", &(${dest}));') 1253 code('${ret} _parse_ret && *_parse_ret == \'\\0\';'); 1254 1255# Enumerated types are a little more complex. The user specifies the 1256# type as Enum(foo) where foo is either a list or dictionary of 1257# alternatives (typically strings, but not necessarily so). (In the 1258# long run, the integer value of the parameter will be the list index 1259# or the corresponding dictionary value. For now, since we only check 1260# that the alternative is valid and then spit it into a .ini file, 1261# there's not much point in using the dictionary.) 1262 1263# What Enum() must do is generate a new type encapsulating the 1264# provided list/dictionary so that specific values of the parameter 1265# can be instances of that type. We define two hidden internal 1266# classes (_ListEnum and _DictEnum) to serve as base classes, then 1267# derive the new type from the appropriate base class on the fly. 1268 1269allEnums = {} 1270# Metaclass for Enum types 1271class MetaEnum(MetaParamValue): 1272 def __new__(mcls, name, bases, dict): 1273 assert name not in allEnums 1274 1275 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 1276 allEnums[name] = cls 1277 return cls 1278 1279 def __init__(cls, name, bases, init_dict): 1280 if 'map' in init_dict: 1281 if not isinstance(cls.map, dict): 1282 raise TypeError("Enum-derived class attribute 'map' " \ 1283 "must be of type dict") 1284 # build list of value strings from map 1285 cls.vals = list(cls.map.keys()) 1286 cls.vals.sort() 1287 elif 'vals' in init_dict: 1288 if not isinstance(cls.vals, list): 1289 raise TypeError("Enum-derived class attribute 'vals' " \ 1290 "must be of type list") 1291 # build string->value map from vals sequence 1292 cls.map = {} 1293 for idx,val in enumerate(cls.vals): 1294 cls.map[val] = idx 1295 else: 1296 raise TypeError("Enum-derived class must define "\ 1297 "attribute 'map' or 'vals'") 1298 1299 if cls.is_class: 1300 cls.cxx_type = '%s' % name 1301 else: 1302 cls.cxx_type = 'Enums::%s' % name 1303 1304 super(MetaEnum, cls).__init__(name, bases, init_dict) 1305 1306 # Generate C++ class declaration for this enum type. 1307 # Note that we wrap the enum in a class/struct to act as a namespace, 1308 # so that the enum strings can be brief w/o worrying about collisions. 1309 def cxx_decl(cls, code): 1310 wrapper_name = cls.wrapper_name 1311 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace' 1312 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1313 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name) 1314 1315 code('''\ 1316#ifndef $idem_macro 1317#define $idem_macro 1318 1319''') 1320 if cls.is_class: 1321 code('''\ 1322enum class $name { 1323''') 1324 else: 1325 code('''\ 1326$wrapper $wrapper_name { 1327 enum $name { 1328''') 1329 code.indent(1) 1330 code.indent(1) 1331 for val in cls.vals: 1332 code('$val = ${{cls.map[val]}},') 1333 code('Num_$name = ${{len(cls.vals)}}') 1334 code.dedent(1) 1335 code('};') 1336 1337 if cls.is_class: 1338 code('''\ 1339extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})]; 1340''') 1341 elif cls.wrapper_is_struct: 1342 code('static const char *${name}Strings[Num_${name}];') 1343 else: 1344 code('extern const char *${name}Strings[Num_${name}];') 1345 1346 if not cls.is_class: 1347 code.dedent(1) 1348 code('};') 1349 1350 code() 1351 code('#endif // $idem_macro') 1352 1353 def cxx_def(cls, code): 1354 wrapper_name = cls.wrapper_name 1355 file_name = cls.__name__ 1356 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1357 1358 code('#include "enums/$file_name.hh"') 1359 if cls.wrapper_is_struct: 1360 code('const char *${wrapper_name}::${name}Strings' 1361 '[Num_${name}] =') 1362 else: 1363 if cls.is_class: 1364 code('''\ 1365const char *${name}Strings[static_cast<int>(${name}::Num_${name})] = 1366''') 1367 else: 1368 code('namespace Enums {') 1369 code.indent(1) 1370 code('const char *${name}Strings[Num_${name}] =') 1371 1372 code('{') 1373 code.indent(1) 1374 for val in cls.vals: 1375 code('"$val",') 1376 code.dedent(1) 1377 code('};') 1378 1379 if not cls.wrapper_is_struct and not cls.is_class: 1380 code.dedent(1) 1381 code('} // namespace $wrapper_name') 1382 1383 1384 def pybind_def(cls, code): 1385 name = cls.__name__ 1386 enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name 1387 wrapper_name = enum_name if cls.is_class else cls.wrapper_name 1388 1389 code('''#include "pybind11/pybind11.h" 1390#include "pybind11/stl.h" 1391 1392#include <sim/init.hh> 1393 1394namespace py = pybind11; 1395 1396static void 1397module_init(py::module &m_internal) 1398{ 1399 py::module m = m_internal.def_submodule("enum_${name}"); 1400 1401''') 1402 if cls.is_class: 1403 code('py::enum_<${enum_name}>(m, "enum_${name}")') 1404 else: 1405 code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")') 1406 1407 code.indent() 1408 code.indent() 1409 for val in cls.vals: 1410 code('.value("${val}", ${wrapper_name}::${val})') 1411 code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})') 1412 code('.export_values()') 1413 code(';') 1414 code.dedent() 1415 1416 code('}') 1417 code.dedent() 1418 code() 1419 code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);') 1420 1421 1422# Base class for enum types. 1423class Enum(ParamValue): 1424 __metaclass__ = MetaEnum 1425 vals = [] 1426 cmd_line_settable = True 1427 1428 # The name of the wrapping namespace or struct 1429 wrapper_name = 'Enums' 1430 1431 # If true, the enum is wrapped in a struct rather than a namespace 1432 wrapper_is_struct = False 1433 1434 is_class = False 1435 1436 # If not None, use this as the enum name rather than this class name 1437 enum_name = None 1438 1439 def __init__(self, value): 1440 if value not in self.map: 1441 raise TypeError("Enum param got bad value '%s' (not in %s)" \ 1442 % (value, self.vals)) 1443 self.value = value 1444 1445 def __call__(self, value): 1446 self.__init__(value) 1447 return value 1448 1449 @classmethod 1450 def cxx_predecls(cls, code): 1451 code('#include "enums/$0.hh"', cls.__name__) 1452 1453 @classmethod 1454 def cxx_ini_parse(cls, code, src, dest, ret): 1455 code('if (false) {') 1456 for elem_name in cls.map.keys(): 1457 code('} else if (%s == "%s") {' % (src, elem_name)) 1458 code.indent() 1459 code('%s = Enums::%s;' % (dest, elem_name)) 1460 code('%s true;' % ret) 1461 code.dedent() 1462 code('} else {') 1463 code(' %s false;' % ret) 1464 code('}') 1465 1466 def getValue(self): 1467 import m5.internal.params 1468 e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__) 1469 return e(self.map[self.value]) 1470 1471 def __str__(self): 1472 return self.value 1473 1474# This param will generate a scoped c++ enum and its python bindings. 1475class ScopedEnum(Enum): 1476 __metaclass__ = MetaEnum 1477 vals = [] 1478 cmd_line_settable = True 1479 1480 # The name of the wrapping namespace or struct 1481 wrapper_name = None 1482 1483 # If true, the enum is wrapped in a struct rather than a namespace 1484 wrapper_is_struct = False 1485 1486 # If true, the generated enum is a scoped enum 1487 is_class = True 1488 1489 # If not None, use this as the enum name rather than this class name 1490 enum_name = None 1491 1492# how big does a rounding error need to be before we warn about it? 1493frequency_tolerance = 0.001 # 0.1% 1494 1495class TickParamValue(NumericParamValue): 1496 cxx_type = 'Tick' 1497 ex_str = "1MHz" 1498 cmd_line_settable = True 1499 1500 @classmethod 1501 def cxx_predecls(cls, code): 1502 code('#include "base/types.hh"') 1503 1504 def __call__(self, value): 1505 self.__init__(value) 1506 return value 1507 1508 def getValue(self): 1509 return long(self.value) 1510 1511 @classmethod 1512 def cxx_ini_predecls(cls, code): 1513 code('#include <sstream>') 1514 1515 # Ticks are expressed in seconds in JSON files and in plain 1516 # Ticks in .ini files. Switch based on a config flag 1517 @classmethod 1518 def cxx_ini_parse(self, code, src, dest, ret): 1519 code('${ret} to_number(${src}, ${dest});') 1520 1521class Latency(TickParamValue): 1522 ex_str = "100ns" 1523 1524 def __init__(self, value): 1525 if isinstance(value, (Latency, Clock)): 1526 self.ticks = value.ticks 1527 self.value = value.value 1528 elif isinstance(value, Frequency): 1529 self.ticks = value.ticks 1530 self.value = 1.0 / value.value 1531 elif value.endswith('t'): 1532 self.ticks = True 1533 self.value = int(value[:-1]) 1534 else: 1535 self.ticks = False 1536 self.value = convert.toLatency(value) 1537 1538 def __call__(self, value): 1539 self.__init__(value) 1540 return value 1541 1542 def __getattr__(self, attr): 1543 if attr in ('latency', 'period'): 1544 return self 1545 if attr == 'frequency': 1546 return Frequency(self) 1547 raise AttributeError("Latency object has no attribute '%s'" % attr) 1548 1549 def getValue(self): 1550 if self.ticks or self.value == 0: 1551 value = self.value 1552 else: 1553 value = ticks.fromSeconds(self.value) 1554 return long(value) 1555 1556 def config_value(self): 1557 return self.getValue() 1558 1559 # convert latency to ticks 1560 def ini_str(self): 1561 return '%d' % self.getValue() 1562 1563class Frequency(TickParamValue): 1564 ex_str = "1GHz" 1565 1566 def __init__(self, value): 1567 if isinstance(value, (Latency, Clock)): 1568 if value.value == 0: 1569 self.value = 0 1570 else: 1571 self.value = 1.0 / value.value 1572 self.ticks = value.ticks 1573 elif isinstance(value, Frequency): 1574 self.value = value.value 1575 self.ticks = value.ticks 1576 else: 1577 self.ticks = False 1578 self.value = convert.toFrequency(value) 1579 1580 def __call__(self, value): 1581 self.__init__(value) 1582 return value 1583 1584 def __getattr__(self, attr): 1585 if attr == 'frequency': 1586 return self 1587 if attr in ('latency', 'period'): 1588 return Latency(self) 1589 raise AttributeError("Frequency object has no attribute '%s'" % attr) 1590 1591 # convert latency to ticks 1592 def getValue(self): 1593 if self.ticks or self.value == 0: 1594 value = self.value 1595 else: 1596 value = ticks.fromSeconds(1.0 / self.value) 1597 return long(value) 1598 1599 def config_value(self): 1600 return self.getValue() 1601 1602 def ini_str(self): 1603 return '%d' % self.getValue() 1604 1605# A generic Frequency and/or Latency value. Value is stored as a 1606# latency, just like Latency and Frequency. 1607class Clock(TickParamValue): 1608 def __init__(self, value): 1609 if isinstance(value, (Latency, Clock)): 1610 self.ticks = value.ticks 1611 self.value = value.value 1612 elif isinstance(value, Frequency): 1613 self.ticks = value.ticks 1614 self.value = 1.0 / value.value 1615 elif value.endswith('t'): 1616 self.ticks = True 1617 self.value = int(value[:-1]) 1618 else: 1619 self.ticks = False 1620 self.value = convert.anyToLatency(value) 1621 1622 def __call__(self, value): 1623 self.__init__(value) 1624 return value 1625 1626 def __str__(self): 1627 return "%s" % Latency(self) 1628 1629 def __getattr__(self, attr): 1630 if attr == 'frequency': 1631 return Frequency(self) 1632 if attr in ('latency', 'period'): 1633 return Latency(self) 1634 raise AttributeError("Frequency object has no attribute '%s'" % attr) 1635 1636 def getValue(self): 1637 return self.period.getValue() 1638 1639 def config_value(self): 1640 return self.period.config_value() 1641 1642 def ini_str(self): 1643 return self.period.ini_str() 1644 1645class Voltage(Float): 1646 ex_str = "1V" 1647 1648 def __new__(cls, value): 1649 value = convert.toVoltage(value) 1650 return super(cls, Voltage).__new__(cls, value) 1651 1652 def __init__(self, value): 1653 value = convert.toVoltage(value) 1654 super(Voltage, self).__init__(value) 1655 1656class Current(Float): 1657 ex_str = "1mA" 1658 1659 def __new__(cls, value): 1660 value = convert.toCurrent(value) 1661 return super(cls, Current).__new__(cls, value) 1662 1663 def __init__(self, value): 1664 value = convert.toCurrent(value) 1665 super(Current, self).__init__(value) 1666 1667class Energy(Float): 1668 ex_str = "1pJ" 1669 1670 def __new__(cls, value): 1671 value = convert.toEnergy(value) 1672 return super(cls, Energy).__new__(cls, value) 1673 1674 def __init__(self, value): 1675 value = convert.toEnergy(value) 1676 super(Energy, self).__init__(value) 1677 1678class NetworkBandwidth(float,ParamValue): 1679 cxx_type = 'float' 1680 ex_str = "1Gbps" 1681 cmd_line_settable = True 1682 1683 def __new__(cls, value): 1684 # convert to bits per second 1685 val = convert.toNetworkBandwidth(value) 1686 return super(cls, NetworkBandwidth).__new__(cls, val) 1687 1688 def __str__(self): 1689 return str(self.val) 1690 1691 def __call__(self, value): 1692 val = convert.toNetworkBandwidth(value) 1693 self.__init__(val) 1694 return value 1695 1696 def getValue(self): 1697 # convert to seconds per byte 1698 value = 8.0 / float(self) 1699 # convert to ticks per byte 1700 value = ticks.fromSeconds(value) 1701 return float(value) 1702 1703 def ini_str(self): 1704 return '%f' % self.getValue() 1705 1706 def config_value(self): 1707 return '%f' % self.getValue() 1708 1709 @classmethod 1710 def cxx_ini_predecls(cls, code): 1711 code('#include <sstream>') 1712 1713 @classmethod 1714 def cxx_ini_parse(self, code, src, dest, ret): 1715 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1716 1717class MemoryBandwidth(float,ParamValue): 1718 cxx_type = 'float' 1719 ex_str = "1GB/s" 1720 cmd_line_settable = True 1721 1722 def __new__(cls, value): 1723 # convert to bytes per second 1724 val = convert.toMemoryBandwidth(value) 1725 return super(cls, MemoryBandwidth).__new__(cls, val) 1726 1727 def __call__(self, value): 1728 val = convert.toMemoryBandwidth(value) 1729 self.__init__(val) 1730 return value 1731 1732 def getValue(self): 1733 # convert to seconds per byte 1734 value = float(self) 1735 if value: 1736 value = 1.0 / float(self) 1737 # convert to ticks per byte 1738 value = ticks.fromSeconds(value) 1739 return float(value) 1740 1741 def ini_str(self): 1742 return '%f' % self.getValue() 1743 1744 def config_value(self): 1745 return '%f' % self.getValue() 1746 1747 @classmethod 1748 def cxx_ini_predecls(cls, code): 1749 code('#include <sstream>') 1750 1751 @classmethod 1752 def cxx_ini_parse(self, code, src, dest, ret): 1753 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1754 1755# 1756# "Constants"... handy aliases for various values. 1757# 1758 1759# Special class for NULL pointers. Note the special check in 1760# make_param_value() above that lets these be assigned where a 1761# SimObject is required. 1762# only one copy of a particular node 1763class NullSimObject(object): 1764 __metaclass__ = Singleton 1765 _name = 'Null' 1766 1767 def __call__(cls): 1768 return cls 1769 1770 def _instantiate(self, parent = None, path = ''): 1771 pass 1772 1773 def ini_str(self): 1774 return 'Null' 1775 1776 def unproxy(self, base): 1777 return self 1778 1779 def set_path(self, parent, name): 1780 pass 1781 1782 def set_parent(self, parent, name): 1783 pass 1784 1785 def clear_parent(self, old_parent): 1786 pass 1787 1788 def descendants(self): 1789 return 1790 yield None 1791 1792 def get_config_as_dict(self): 1793 return {} 1794 1795 def __str__(self): 1796 return self._name 1797 1798 def config_value(self): 1799 return None 1800 1801 def getValue(self): 1802 return None 1803 1804# The only instance you'll ever need... 1805NULL = NullSimObject() 1806 1807def isNullPointer(value): 1808 return isinstance(value, NullSimObject) 1809 1810# Some memory range specifications use this as a default upper bound. 1811MaxAddr = Addr.max 1812MaxTick = Tick.max 1813AllMemory = AddrRange(0, MaxAddr) 1814 1815 1816##################################################################### 1817# 1818# Port objects 1819# 1820# Ports are used to interconnect objects in the memory system. 1821# 1822##################################################################### 1823 1824# Port reference: encapsulates a reference to a particular port on a 1825# particular SimObject. 1826class PortRef(object): 1827 def __init__(self, simobj, name, role): 1828 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1829 self.simobj = simobj 1830 self.name = name 1831 self.role = role 1832 self.peer = None # not associated with another port yet 1833 self.ccConnected = False # C++ port connection done? 1834 self.index = -1 # always -1 for non-vector ports 1835 1836 def __str__(self): 1837 return '%s.%s' % (self.simobj, self.name) 1838 1839 def __len__(self): 1840 # Return the number of connected ports, i.e. 0 is we have no 1841 # peer and 1 if we do. 1842 return int(self.peer != None) 1843 1844 # for config.ini, print peer's name (not ours) 1845 def ini_str(self): 1846 return str(self.peer) 1847 1848 # for config.json 1849 def get_config_as_dict(self): 1850 return {'role' : self.role, 'peer' : str(self.peer)} 1851 1852 def __getattr__(self, attr): 1853 if attr == 'peerObj': 1854 # shorthand for proxies 1855 return self.peer.simobj 1856 raise AttributeError("'%s' object has no attribute '%s'" % \ 1857 (self.__class__.__name__, attr)) 1858 1859 # Full connection is symmetric (both ways). Called via 1860 # SimObject.__setattr__ as a result of a port assignment, e.g., 1861 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 1862 # e.g., "obj1.portA[3] = obj2.portB". 1863 def connect(self, other): 1864 if isinstance(other, VectorPortRef): 1865 # reference to plain VectorPort is implicit append 1866 other = other._get_next() 1867 if self.peer and not proxy.isproxy(self.peer): 1868 fatal("Port %s is already connected to %s, cannot connect %s\n", 1869 self, self.peer, other); 1870 self.peer = other 1871 if proxy.isproxy(other): 1872 other.set_param_desc(PortParamDesc()) 1873 elif isinstance(other, PortRef): 1874 if other.peer is not self: 1875 other.connect(self) 1876 else: 1877 raise TypeError("assigning non-port reference '%s' to port '%s'" \ 1878 % (other, self)) 1879 1880 # Allow a master/slave port pair to be spliced between 1881 # a port and its connected peer. Useful operation for connecting 1882 # instrumentation structures into a system when it is necessary 1883 # to connect the instrumentation after the full system has been 1884 # constructed. 1885 def splice(self, new_master_peer, new_slave_peer): 1886 if not self.peer or proxy.isproxy(self.peer): 1887 fatal("Port %s not connected, cannot splice in new peers\n", self) 1888 1889 if not isinstance(new_master_peer, PortRef) or \ 1890 not isinstance(new_slave_peer, PortRef): 1891 raise TypeError( 1892 "Splicing non-port references '%s','%s' to port '%s'" % \ 1893 (new_master_peer, new_slave_peer, self)) 1894 1895 old_peer = self.peer 1896 if self.role == 'SLAVE': 1897 self.peer = new_master_peer 1898 old_peer.peer = new_slave_peer 1899 new_master_peer.connect(self) 1900 new_slave_peer.connect(old_peer) 1901 elif self.role == 'MASTER': 1902 self.peer = new_slave_peer 1903 old_peer.peer = new_master_peer 1904 new_slave_peer.connect(self) 1905 new_master_peer.connect(old_peer) 1906 else: 1907 panic("Port %s has unknown role, "+\ 1908 "cannot splice in new peers\n", self) 1909 1910 def clone(self, simobj, memo): 1911 if self in memo: 1912 return memo[self] 1913 newRef = copy.copy(self) 1914 memo[self] = newRef 1915 newRef.simobj = simobj 1916 assert(isSimObject(newRef.simobj)) 1917 if self.peer and not proxy.isproxy(self.peer): 1918 peerObj = self.peer.simobj(_memo=memo) 1919 newRef.peer = self.peer.clone(peerObj, memo) 1920 assert(not isinstance(newRef.peer, VectorPortRef)) 1921 return newRef 1922 1923 def unproxy(self, simobj): 1924 assert(simobj is self.simobj) 1925 if proxy.isproxy(self.peer): 1926 try: 1927 realPeer = self.peer.unproxy(self.simobj) 1928 except: 1929 print("Error in unproxying port '%s' of %s" % 1930 (self.name, self.simobj.path())) 1931 raise 1932 self.connect(realPeer) 1933 1934 # Call C++ to create corresponding port connection between C++ objects 1935 def ccConnect(self): 1936 from _m5.pyobject import connectPorts 1937 1938 if self.ccConnected: # already done this 1939 return 1940 1941 peer = self.peer 1942 if not self.peer: # nothing to connect to 1943 return 1944 1945 # check that we connect a master to a slave 1946 if self.role == peer.role: 1947 raise TypeError( 1948 "cannot connect '%s' and '%s' due to identical role '%s'" % \ 1949 (peer, self, self.role)) 1950 1951 if self.role == 'SLAVE': 1952 # do nothing and let the master take care of it 1953 return 1954 1955 try: 1956 # self is always the master and peer the slave 1957 connectPorts(self.simobj.getCCObject(), self.name, self.index, 1958 peer.simobj.getCCObject(), peer.name, peer.index) 1959 except: 1960 print("Error connecting port %s.%s to %s.%s" % 1961 (self.simobj.path(), self.name, 1962 peer.simobj.path(), peer.name)) 1963 raise 1964 self.ccConnected = True 1965 peer.ccConnected = True 1966 1967# A reference to an individual element of a VectorPort... much like a 1968# PortRef, but has an index. 1969class VectorPortElementRef(PortRef): 1970 def __init__(self, simobj, name, role, index): 1971 PortRef.__init__(self, simobj, name, role) 1972 self.index = index 1973 1974 def __str__(self): 1975 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1976 1977# A reference to a complete vector-valued port (not just a single element). 1978# Can be indexed to retrieve individual VectorPortElementRef instances. 1979class VectorPortRef(object): 1980 def __init__(self, simobj, name, role): 1981 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1982 self.simobj = simobj 1983 self.name = name 1984 self.role = role 1985 self.elements = [] 1986 1987 def __str__(self): 1988 return '%s.%s[:]' % (self.simobj, self.name) 1989 1990 def __len__(self): 1991 # Return the number of connected peers, corresponding the the 1992 # length of the elements. 1993 return len(self.elements) 1994 1995 # for config.ini, print peer's name (not ours) 1996 def ini_str(self): 1997 return ' '.join([el.ini_str() for el in self.elements]) 1998 1999 # for config.json 2000 def get_config_as_dict(self): 2001 return {'role' : self.role, 2002 'peer' : [el.ini_str() for el in self.elements]} 2003 2004 def __getitem__(self, key): 2005 if not isinstance(key, int): 2006 raise TypeError("VectorPort index must be integer") 2007 if key >= len(self.elements): 2008 # need to extend list 2009 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i) 2010 for i in range(len(self.elements), key+1)] 2011 self.elements.extend(ext) 2012 return self.elements[key] 2013 2014 def _get_next(self): 2015 return self[len(self.elements)] 2016 2017 def __setitem__(self, key, value): 2018 if not isinstance(key, int): 2019 raise TypeError("VectorPort index must be integer") 2020 self[key].connect(value) 2021 2022 def connect(self, other): 2023 if isinstance(other, (list, tuple)): 2024 # Assign list of port refs to vector port. 2025 # For now, append them... not sure if that's the right semantics 2026 # or if it should replace the current vector. 2027 for ref in other: 2028 self._get_next().connect(ref) 2029 else: 2030 # scalar assignment to plain VectorPort is implicit append 2031 self._get_next().connect(other) 2032 2033 def clone(self, simobj, memo): 2034 if self in memo: 2035 return memo[self] 2036 newRef = copy.copy(self) 2037 memo[self] = newRef 2038 newRef.simobj = simobj 2039 assert(isSimObject(newRef.simobj)) 2040 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 2041 return newRef 2042 2043 def unproxy(self, simobj): 2044 [el.unproxy(simobj) for el in self.elements] 2045 2046 def ccConnect(self): 2047 [el.ccConnect() for el in self.elements] 2048 2049# Port description object. Like a ParamDesc object, this represents a 2050# logical port in the SimObject class, not a particular port on a 2051# SimObject instance. The latter are represented by PortRef objects. 2052class Port(object): 2053 # Generate a PortRef for this port on the given SimObject with the 2054 # given name 2055 def makeRef(self, simobj): 2056 return PortRef(simobj, self.name, self.role) 2057 2058 # Connect an instance of this port (on the given SimObject with 2059 # the given name) with the port described by the supplied PortRef 2060 def connect(self, simobj, ref): 2061 self.makeRef(simobj).connect(ref) 2062 2063 # No need for any pre-declarations at the moment as we merely rely 2064 # on an unsigned int. 2065 def cxx_predecls(self, code): 2066 pass 2067 2068 def pybind_predecls(self, code): 2069 cls.cxx_predecls(self, code) 2070 2071 # Declare an unsigned int with the same name as the port, that 2072 # will eventually hold the number of connected ports (and thus the 2073 # number of elements for a VectorPort). 2074 def cxx_decl(self, code): 2075 code('unsigned int port_${{self.name}}_connection_count;') 2076 2077class MasterPort(Port): 2078 # MasterPort("description") 2079 def __init__(self, *args): 2080 if len(args) == 1: 2081 self.desc = args[0] 2082 self.role = 'MASTER' 2083 else: 2084 raise TypeError('wrong number of arguments') 2085 2086class SlavePort(Port): 2087 # SlavePort("description") 2088 def __init__(self, *args): 2089 if len(args) == 1: 2090 self.desc = args[0] 2091 self.role = 'SLAVE' 2092 else: 2093 raise TypeError('wrong number of arguments') 2094 2095# VectorPort description object. Like Port, but represents a vector 2096# of connections (e.g., as on a XBar). 2097class VectorPort(Port): 2098 def __init__(self, *args): 2099 self.isVec = True 2100 2101 def makeRef(self, simobj): 2102 return VectorPortRef(simobj, self.name, self.role) 2103 2104class VectorMasterPort(VectorPort): 2105 # VectorMasterPort("description") 2106 def __init__(self, *args): 2107 if len(args) == 1: 2108 self.desc = args[0] 2109 self.role = 'MASTER' 2110 VectorPort.__init__(self, *args) 2111 else: 2112 raise TypeError('wrong number of arguments') 2113 2114class VectorSlavePort(VectorPort): 2115 # VectorSlavePort("description") 2116 def __init__(self, *args): 2117 if len(args) == 1: 2118 self.desc = args[0] 2119 self.role = 'SLAVE' 2120 VectorPort.__init__(self, *args) 2121 else: 2122 raise TypeError('wrong number of arguments') 2123 2124# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 2125# proxy objects (via set_param_desc()) so that proxy error messages 2126# make sense. 2127class PortParamDesc(object): 2128 __metaclass__ = Singleton 2129 2130 ptype_str = 'Port' 2131 ptype = Port 2132 2133baseEnums = allEnums.copy() 2134baseParams = allParams.copy() 2135 2136def clear(): 2137 global allEnums, allParams 2138 2139 allEnums = baseEnums.copy() 2140 allParams = baseParams.copy() 2141 2142__all__ = ['Param', 'VectorParam', 2143 'Enum', 'ScopedEnum', 'Bool', 'String', 'Float', 2144 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 2145 'Int32', 'UInt32', 'Int64', 'UInt64', 2146 'Counter', 'Addr', 'Tick', 'Percent', 2147 'TcpPort', 'UdpPort', 'EthernetAddr', 2148 'IpAddress', 'IpNetmask', 'IpWithPort', 2149 'MemorySize', 'MemorySize32', 2150 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy', 2151 'NetworkBandwidth', 'MemoryBandwidth', 2152 'AddrRange', 2153 'MaxAddr', 'MaxTick', 'AllMemory', 2154 'Time', 2155 'NextEthernetAddr', 'NULL', 2156 'MasterPort', 'SlavePort', 2157 'VectorMasterPort', 'VectorSlavePort'] 2158 2159import SimObject 2160