params.py revision 13558:2a8d8f64d900
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 kwargs.has_key('desc'): 161 assert(not hasattr(self, 'desc')) 162 self.desc = kwargs['desc'] 163 del kwargs['desc'] 164 165 if kwargs.has_key('default'): 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 not hasattr(self, 'ptype') 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, \ 238 "Not allowed to set %s on '%s'" % (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 def __str__(self): 458 return str(self.value) 459 460 def __float__(self): 461 return float(self.value) 462 463 def __long__(self): 464 return long(self.value) 465 466 def __int__(self): 467 return int(self.value) 468 469 # hook for bounds checking 470 def _check(self): 471 return 472 473 def __mul__(self, other): 474 newobj = self.__class__(self) 475 newobj.value *= other 476 newobj._check() 477 return newobj 478 479 __rmul__ = __mul__ 480 481 def __div__(self, other): 482 newobj = self.__class__(self) 483 newobj.value /= other 484 newobj._check() 485 return newobj 486 487 def __sub__(self, other): 488 newobj = self.__class__(self) 489 newobj.value -= other 490 newobj._check() 491 return newobj 492 493 def config_value(self): 494 return self.value 495 496 @classmethod 497 def cxx_ini_predecls(cls, code): 498 # Assume that base/str.hh will be included anyway 499 # code('#include "base/str.hh"') 500 pass 501 502 # The default for parsing PODs from an .ini entry is to extract from an 503 # istringstream and let overloading choose the right type according to 504 # the dest type. 505 @classmethod 506 def cxx_ini_parse(self, code, src, dest, ret): 507 code('%s to_number(%s, %s);' % (ret, src, dest)) 508 509# Metaclass for bounds-checked integer parameters. See CheckedInt. 510class CheckedIntType(MetaParamValue): 511 def __init__(cls, name, bases, dict): 512 super(CheckedIntType, cls).__init__(name, bases, dict) 513 514 # CheckedInt is an abstract base class, so we actually don't 515 # want to do any processing on it... the rest of this code is 516 # just for classes that derive from CheckedInt. 517 if name == 'CheckedInt': 518 return 519 520 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 521 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 522 panic("CheckedInt subclass %s must define either\n" \ 523 " 'min' and 'max' or 'size' and 'unsigned'\n", 524 name); 525 if cls.unsigned: 526 cls.min = 0 527 cls.max = 2 ** cls.size - 1 528 else: 529 cls.min = -(2 ** (cls.size - 1)) 530 cls.max = (2 ** (cls.size - 1)) - 1 531 532# Abstract superclass for bounds-checked integer parameters. This 533# class is subclassed to generate parameter classes with specific 534# bounds. Initialization of the min and max bounds is done in the 535# metaclass CheckedIntType.__init__. 536class CheckedInt(NumericParamValue): 537 __metaclass__ = CheckedIntType 538 cmd_line_settable = True 539 540 def _check(self): 541 if not self.min <= self.value <= self.max: 542 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 543 (self.min, self.value, self.max) 544 545 def __init__(self, value): 546 if isinstance(value, str): 547 self.value = convert.toInteger(value) 548 elif isinstance(value, (int, long, float, NumericParamValue)): 549 self.value = long(value) 550 else: 551 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 552 % type(value).__name__ 553 self._check() 554 555 def __call__(self, value): 556 self.__init__(value) 557 return value 558 559 @classmethod 560 def cxx_predecls(cls, code): 561 # most derived types require this, so we just do it here once 562 code('#include "base/types.hh"') 563 564 def getValue(self): 565 return long(self.value) 566 567class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 568class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 569 570class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 571class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 572class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 573class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 574class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 575class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 576class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 577class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 578 579class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 580class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 581class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 582class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 583 584class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 585 586class Cycles(CheckedInt): 587 cxx_type = 'Cycles' 588 size = 64 589 unsigned = True 590 591 def getValue(self): 592 from _m5.core import Cycles 593 return Cycles(self.value) 594 595 @classmethod 596 def cxx_ini_predecls(cls, code): 597 # Assume that base/str.hh will be included anyway 598 # code('#include "base/str.hh"') 599 pass 600 601 @classmethod 602 def cxx_ini_parse(cls, code, src, dest, ret): 603 code('uint64_t _temp;') 604 code('bool _ret = to_number(%s, _temp);' % src) 605 code('if (_ret)') 606 code(' %s = Cycles(_temp);' % dest) 607 code('%s _ret;' % ret) 608 609class Float(ParamValue, float): 610 cxx_type = 'double' 611 cmd_line_settable = True 612 613 def __init__(self, value): 614 if isinstance(value, (int, long, float, NumericParamValue, Float, str)): 615 self.value = float(value) 616 else: 617 raise TypeError, "Can't convert object of type %s to Float" \ 618 % type(value).__name__ 619 620 def __call__(self, value): 621 self.__init__(value) 622 return value 623 624 def getValue(self): 625 return float(self.value) 626 627 def config_value(self): 628 return self 629 630 @classmethod 631 def cxx_ini_predecls(cls, code): 632 code('#include <sstream>') 633 634 @classmethod 635 def cxx_ini_parse(self, code, src, dest, ret): 636 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 637 638class MemorySize(CheckedInt): 639 cxx_type = 'uint64_t' 640 ex_str = '512MB' 641 size = 64 642 unsigned = True 643 def __init__(self, value): 644 if isinstance(value, MemorySize): 645 self.value = value.value 646 else: 647 self.value = convert.toMemorySize(value) 648 self._check() 649 650class MemorySize32(CheckedInt): 651 cxx_type = 'uint32_t' 652 ex_str = '512MB' 653 size = 32 654 unsigned = True 655 def __init__(self, value): 656 if isinstance(value, MemorySize): 657 self.value = value.value 658 else: 659 self.value = convert.toMemorySize(value) 660 self._check() 661 662class Addr(CheckedInt): 663 cxx_type = 'Addr' 664 size = 64 665 unsigned = True 666 def __init__(self, value): 667 if isinstance(value, Addr): 668 self.value = value.value 669 else: 670 try: 671 # Often addresses are referred to with sizes. Ex: A device 672 # base address is at "512MB". Use toMemorySize() to convert 673 # these into addresses. If the address is not specified with a 674 # "size", an exception will occur and numeric translation will 675 # proceed below. 676 self.value = convert.toMemorySize(value) 677 except (TypeError, ValueError): 678 # Convert number to string and use long() to do automatic 679 # base conversion (requires base=0 for auto-conversion) 680 self.value = long(str(value), base=0) 681 682 self._check() 683 def __add__(self, other): 684 if isinstance(other, Addr): 685 return self.value + other.value 686 else: 687 return self.value + other 688 def pretty_print(self, value): 689 try: 690 val = convert.toMemorySize(value) 691 except TypeError: 692 val = long(value) 693 return "0x%x" % long(val) 694 695class AddrRange(ParamValue): 696 cxx_type = 'AddrRange' 697 698 def __init__(self, *args, **kwargs): 699 # Disable interleaving and hashing by default 700 self.intlvHighBit = 0 701 self.xorHighBit = 0 702 self.intlvBits = 0 703 self.intlvMatch = 0 704 705 def handle_kwargs(self, kwargs): 706 # An address range needs to have an upper limit, specified 707 # either explicitly with an end, or as an offset using the 708 # size keyword. 709 if 'end' in kwargs: 710 self.end = Addr(kwargs.pop('end')) 711 elif 'size' in kwargs: 712 self.end = self.start + Addr(kwargs.pop('size')) - 1 713 else: 714 raise TypeError, "Either end or size must be specified" 715 716 # Now on to the optional bit 717 if 'intlvHighBit' in kwargs: 718 self.intlvHighBit = int(kwargs.pop('intlvHighBit')) 719 if 'xorHighBit' in kwargs: 720 self.xorHighBit = int(kwargs.pop('xorHighBit')) 721 if 'intlvBits' in kwargs: 722 self.intlvBits = int(kwargs.pop('intlvBits')) 723 if 'intlvMatch' in kwargs: 724 self.intlvMatch = int(kwargs.pop('intlvMatch')) 725 726 if len(args) == 0: 727 self.start = Addr(kwargs.pop('start')) 728 handle_kwargs(self, kwargs) 729 730 elif len(args) == 1: 731 if kwargs: 732 self.start = Addr(args[0]) 733 handle_kwargs(self, kwargs) 734 elif isinstance(args[0], (list, tuple)): 735 self.start = Addr(args[0][0]) 736 self.end = Addr(args[0][1]) 737 else: 738 self.start = Addr(0) 739 self.end = Addr(args[0]) - 1 740 741 elif len(args) == 2: 742 self.start = Addr(args[0]) 743 self.end = Addr(args[1]) 744 else: 745 raise TypeError, "Too many arguments specified" 746 747 if kwargs: 748 raise TypeError, "Too many keywords: %s" % kwargs.keys() 749 750 def __str__(self): 751 return '%s:%s:%s:%s:%s:%s' \ 752 % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\ 753 self.intlvBits, self.intlvMatch) 754 755 def size(self): 756 # Divide the size by the size of the interleaving slice 757 return (long(self.end) - long(self.start) + 1) >> self.intlvBits 758 759 @classmethod 760 def cxx_predecls(cls, code): 761 Addr.cxx_predecls(code) 762 code('#include "base/addr_range.hh"') 763 764 @classmethod 765 def pybind_predecls(cls, code): 766 Addr.pybind_predecls(code) 767 code('#include "base/addr_range.hh"') 768 769 @classmethod 770 def cxx_ini_predecls(cls, code): 771 code('#include <sstream>') 772 773 @classmethod 774 def cxx_ini_parse(cls, code, src, dest, ret): 775 code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;') 776 code('uint64_t _intlvBits = 0, _intlvMatch = 0;') 777 code('char _sep;') 778 code('std::istringstream _stream(${src});') 779 code('_stream >> _start;') 780 code('_stream.get(_sep);') 781 code('_stream >> _end;') 782 code('if (!_stream.fail() && !_stream.eof()) {') 783 code(' _stream.get(_sep);') 784 code(' _stream >> _intlvHighBit;') 785 code(' _stream.get(_sep);') 786 code(' _stream >> _xorHighBit;') 787 code(' _stream.get(_sep);') 788 code(' _stream >> _intlvBits;') 789 code(' _stream.get(_sep);') 790 code(' _stream >> _intlvMatch;') 791 code('}') 792 code('bool _ret = !_stream.fail() &&' 793 '_stream.eof() && _sep == \':\';') 794 code('if (_ret)') 795 code(' ${dest} = AddrRange(_start, _end, _intlvHighBit, \ 796 _xorHighBit, _intlvBits, _intlvMatch);') 797 code('${ret} _ret;') 798 799 def getValue(self): 800 # Go from the Python class to the wrapped C++ class 801 from _m5.range import AddrRange 802 803 return AddrRange(long(self.start), long(self.end), 804 int(self.intlvHighBit), int(self.xorHighBit), 805 int(self.intlvBits), int(self.intlvMatch)) 806 807# Boolean parameter type. Python doesn't let you subclass bool, since 808# it doesn't want to let you create multiple instances of True and 809# False. Thus this is a little more complicated than String. 810class Bool(ParamValue): 811 cxx_type = 'bool' 812 cmd_line_settable = True 813 814 def __init__(self, value): 815 try: 816 self.value = convert.toBool(value) 817 except TypeError: 818 self.value = bool(value) 819 820 def __call__(self, value): 821 self.__init__(value) 822 return value 823 824 def getValue(self): 825 return bool(self.value) 826 827 def __str__(self): 828 return str(self.value) 829 830 # implement truth value testing for Bool parameters so that these params 831 # evaluate correctly during the python configuration phase 832 def __nonzero__(self): 833 return bool(self.value) 834 835 def ini_str(self): 836 if self.value: 837 return 'true' 838 return 'false' 839 840 def config_value(self): 841 return self.value 842 843 @classmethod 844 def cxx_ini_predecls(cls, code): 845 # Assume that base/str.hh will be included anyway 846 # code('#include "base/str.hh"') 847 pass 848 849 @classmethod 850 def cxx_ini_parse(cls, code, src, dest, ret): 851 code('%s to_bool(%s, %s);' % (ret, src, dest)) 852 853def IncEthernetAddr(addr, val = 1): 854 bytes = map(lambda x: int(x, 16), addr.split(':')) 855 bytes[5] += val 856 for i in (5, 4, 3, 2, 1): 857 val,rem = divmod(bytes[i], 256) 858 bytes[i] = rem 859 if val == 0: 860 break 861 bytes[i - 1] += val 862 assert(bytes[0] <= 255) 863 return ':'.join(map(lambda x: '%02x' % x, bytes)) 864 865_NextEthernetAddr = "00:90:00:00:00:01" 866def NextEthernetAddr(): 867 global _NextEthernetAddr 868 869 value = _NextEthernetAddr 870 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 871 return value 872 873class EthernetAddr(ParamValue): 874 cxx_type = 'Net::EthAddr' 875 ex_str = "00:90:00:00:00:01" 876 cmd_line_settable = True 877 878 @classmethod 879 def cxx_predecls(cls, code): 880 code('#include "base/inet.hh"') 881 882 def __init__(self, value): 883 if value == NextEthernetAddr: 884 self.value = value 885 return 886 887 if not isinstance(value, str): 888 raise TypeError, "expected an ethernet address and didn't get one" 889 890 bytes = value.split(':') 891 if len(bytes) != 6: 892 raise TypeError, 'invalid ethernet address %s' % value 893 894 for byte in bytes: 895 if not 0 <= int(byte, base=16) <= 0xff: 896 raise TypeError, 'invalid ethernet address %s' % value 897 898 self.value = value 899 900 def __call__(self, value): 901 self.__init__(value) 902 return value 903 904 def unproxy(self, base): 905 if self.value == NextEthernetAddr: 906 return EthernetAddr(self.value()) 907 return self 908 909 def getValue(self): 910 from _m5.net import EthAddr 911 return EthAddr(self.value) 912 913 def __str__(self): 914 return self.value 915 916 def ini_str(self): 917 return self.value 918 919 @classmethod 920 def cxx_ini_parse(self, code, src, dest, ret): 921 code('%s = Net::EthAddr(%s);' % (dest, src)) 922 code('%s true;' % ret) 923 924# When initializing an IpAddress, pass in an existing IpAddress, a string of 925# the form "a.b.c.d", or an integer representing an IP. 926class IpAddress(ParamValue): 927 cxx_type = 'Net::IpAddress' 928 ex_str = "127.0.0.1" 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 isinstance(value, IpAddress): 937 self.ip = value.ip 938 else: 939 try: 940 self.ip = convert.toIpAddress(value) 941 except TypeError: 942 self.ip = long(value) 943 self.verifyIp() 944 945 def __call__(self, value): 946 self.__init__(value) 947 return value 948 949 def __str__(self): 950 tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)] 951 return '%d.%d.%d.%d' % tuple(tup) 952 953 def __eq__(self, other): 954 if isinstance(other, IpAddress): 955 return self.ip == other.ip 956 elif isinstance(other, str): 957 try: 958 return self.ip == convert.toIpAddress(other) 959 except: 960 return False 961 else: 962 return self.ip == other 963 964 def __ne__(self, other): 965 return not (self == other) 966 967 def verifyIp(self): 968 if self.ip < 0 or self.ip >= (1 << 32): 969 raise TypeError, "invalid ip address %#08x" % self.ip 970 971 def getValue(self): 972 from _m5.net import IpAddress 973 return IpAddress(self.ip) 974 975# When initializing an IpNetmask, pass in an existing IpNetmask, a string of 976# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as 977# positional or keyword arguments. 978class IpNetmask(IpAddress): 979 cxx_type = 'Net::IpNetmask' 980 ex_str = "127.0.0.0/24" 981 cmd_line_settable = True 982 983 @classmethod 984 def cxx_predecls(cls, code): 985 code('#include "base/inet.hh"') 986 987 def __init__(self, *args, **kwargs): 988 def handle_kwarg(self, kwargs, key, elseVal = None): 989 if key in kwargs: 990 setattr(self, key, kwargs.pop(key)) 991 elif elseVal: 992 setattr(self, key, elseVal) 993 else: 994 raise TypeError, "No value set for %s" % key 995 996 if len(args) == 0: 997 handle_kwarg(self, kwargs, 'ip') 998 handle_kwarg(self, kwargs, 'netmask') 999 1000 elif len(args) == 1: 1001 if kwargs: 1002 if not 'ip' in kwargs and not 'netmask' in kwargs: 1003 raise TypeError, "Invalid arguments" 1004 handle_kwarg(self, kwargs, 'ip', args[0]) 1005 handle_kwarg(self, kwargs, 'netmask', args[0]) 1006 elif isinstance(args[0], IpNetmask): 1007 self.ip = args[0].ip 1008 self.netmask = args[0].netmask 1009 else: 1010 (self.ip, self.netmask) = convert.toIpNetmask(args[0]) 1011 1012 elif len(args) == 2: 1013 self.ip = args[0] 1014 self.netmask = args[1] 1015 else: 1016 raise TypeError, "Too many arguments specified" 1017 1018 if kwargs: 1019 raise TypeError, "Too many keywords: %s" % kwargs.keys() 1020 1021 self.verify() 1022 1023 def __call__(self, value): 1024 self.__init__(value) 1025 return value 1026 1027 def __str__(self): 1028 return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask) 1029 1030 def __eq__(self, other): 1031 if isinstance(other, IpNetmask): 1032 return self.ip == other.ip and self.netmask == other.netmask 1033 elif isinstance(other, str): 1034 try: 1035 return (self.ip, self.netmask) == convert.toIpNetmask(other) 1036 except: 1037 return False 1038 else: 1039 return False 1040 1041 def verify(self): 1042 self.verifyIp() 1043 if self.netmask < 0 or self.netmask > 32: 1044 raise TypeError, "invalid netmask %d" % netmask 1045 1046 def getValue(self): 1047 from _m5.net import IpNetmask 1048 return IpNetmask(self.ip, self.netmask) 1049 1050# When initializing an IpWithPort, pass in an existing IpWithPort, a string of 1051# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments. 1052class IpWithPort(IpAddress): 1053 cxx_type = 'Net::IpWithPort' 1054 ex_str = "127.0.0.1:80" 1055 cmd_line_settable = True 1056 1057 @classmethod 1058 def cxx_predecls(cls, code): 1059 code('#include "base/inet.hh"') 1060 1061 def __init__(self, *args, **kwargs): 1062 def handle_kwarg(self, kwargs, key, elseVal = None): 1063 if key in kwargs: 1064 setattr(self, key, kwargs.pop(key)) 1065 elif elseVal: 1066 setattr(self, key, elseVal) 1067 else: 1068 raise TypeError, "No value set for %s" % key 1069 1070 if len(args) == 0: 1071 handle_kwarg(self, kwargs, 'ip') 1072 handle_kwarg(self, kwargs, 'port') 1073 1074 elif len(args) == 1: 1075 if kwargs: 1076 if not 'ip' in kwargs and not 'port' in kwargs: 1077 raise TypeError, "Invalid arguments" 1078 handle_kwarg(self, kwargs, 'ip', args[0]) 1079 handle_kwarg(self, kwargs, 'port', args[0]) 1080 elif isinstance(args[0], IpWithPort): 1081 self.ip = args[0].ip 1082 self.port = args[0].port 1083 else: 1084 (self.ip, self.port) = convert.toIpWithPort(args[0]) 1085 1086 elif len(args) == 2: 1087 self.ip = args[0] 1088 self.port = args[1] 1089 else: 1090 raise TypeError, "Too many arguments specified" 1091 1092 if kwargs: 1093 raise TypeError, "Too many keywords: %s" % kwargs.keys() 1094 1095 self.verify() 1096 1097 def __call__(self, value): 1098 self.__init__(value) 1099 return value 1100 1101 def __str__(self): 1102 return "%s:%d" % (super(IpWithPort, self).__str__(), self.port) 1103 1104 def __eq__(self, other): 1105 if isinstance(other, IpWithPort): 1106 return self.ip == other.ip and self.port == other.port 1107 elif isinstance(other, str): 1108 try: 1109 return (self.ip, self.port) == convert.toIpWithPort(other) 1110 except: 1111 return False 1112 else: 1113 return False 1114 1115 def verify(self): 1116 self.verifyIp() 1117 if self.port < 0 or self.port > 0xffff: 1118 raise TypeError, "invalid port %d" % self.port 1119 1120 def getValue(self): 1121 from _m5.net import IpWithPort 1122 return IpWithPort(self.ip, self.port) 1123 1124time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 1125 "%a %b %d %H:%M:%S %Y", 1126 "%Y/%m/%d %H:%M:%S", 1127 "%Y/%m/%d %H:%M", 1128 "%Y/%m/%d", 1129 "%m/%d/%Y %H:%M:%S", 1130 "%m/%d/%Y %H:%M", 1131 "%m/%d/%Y", 1132 "%m/%d/%y %H:%M:%S", 1133 "%m/%d/%y %H:%M", 1134 "%m/%d/%y"] 1135 1136 1137def parse_time(value): 1138 from time import gmtime, strptime, struct_time, time 1139 from datetime import datetime, date 1140 1141 if isinstance(value, struct_time): 1142 return value 1143 1144 if isinstance(value, (int, long)): 1145 return gmtime(value) 1146 1147 if isinstance(value, (datetime, date)): 1148 return value.timetuple() 1149 1150 if isinstance(value, str): 1151 if value in ('Now', 'Today'): 1152 return time.gmtime(time.time()) 1153 1154 for format in time_formats: 1155 try: 1156 return strptime(value, format) 1157 except ValueError: 1158 pass 1159 1160 raise ValueError, "Could not parse '%s' as a time" % value 1161 1162class Time(ParamValue): 1163 cxx_type = 'tm' 1164 1165 @classmethod 1166 def cxx_predecls(cls, code): 1167 code('#include <time.h>') 1168 1169 def __init__(self, value): 1170 self.value = parse_time(value) 1171 1172 def __call__(self, value): 1173 self.__init__(value) 1174 return value 1175 1176 def getValue(self): 1177 from _m5.core import tm 1178 import calendar 1179 1180 return tm.gmtime(calendar.timegm(self.value)) 1181 1182 def __str__(self): 1183 return time.asctime(self.value) 1184 1185 def ini_str(self): 1186 return str(self) 1187 1188 def get_config_as_dict(self): 1189 assert false 1190 return str(self) 1191 1192 @classmethod 1193 def cxx_ini_predecls(cls, code): 1194 code('#include <time.h>') 1195 1196 @classmethod 1197 def cxx_ini_parse(cls, code, src, dest, ret): 1198 code('char *_parse_ret = strptime((${src}).c_str(),') 1199 code(' "%a %b %d %H:%M:%S %Y", &(${dest}));') 1200 code('${ret} _parse_ret && *_parse_ret == \'\\0\';'); 1201 1202# Enumerated types are a little more complex. The user specifies the 1203# type as Enum(foo) where foo is either a list or dictionary of 1204# alternatives (typically strings, but not necessarily so). (In the 1205# long run, the integer value of the parameter will be the list index 1206# or the corresponding dictionary value. For now, since we only check 1207# that the alternative is valid and then spit it into a .ini file, 1208# there's not much point in using the dictionary.) 1209 1210# What Enum() must do is generate a new type encapsulating the 1211# provided list/dictionary so that specific values of the parameter 1212# can be instances of that type. We define two hidden internal 1213# classes (_ListEnum and _DictEnum) to serve as base classes, then 1214# derive the new type from the appropriate base class on the fly. 1215 1216allEnums = {} 1217# Metaclass for Enum types 1218class MetaEnum(MetaParamValue): 1219 def __new__(mcls, name, bases, dict): 1220 assert name not in allEnums 1221 1222 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 1223 allEnums[name] = cls 1224 return cls 1225 1226 def __init__(cls, name, bases, init_dict): 1227 if init_dict.has_key('map'): 1228 if not isinstance(cls.map, dict): 1229 raise TypeError, "Enum-derived class attribute 'map' " \ 1230 "must be of type dict" 1231 # build list of value strings from map 1232 cls.vals = cls.map.keys() 1233 cls.vals.sort() 1234 elif init_dict.has_key('vals'): 1235 if not isinstance(cls.vals, list): 1236 raise TypeError, "Enum-derived class attribute 'vals' " \ 1237 "must be of type list" 1238 # build string->value map from vals sequence 1239 cls.map = {} 1240 for idx,val in enumerate(cls.vals): 1241 cls.map[val] = idx 1242 else: 1243 raise TypeError, "Enum-derived class must define "\ 1244 "attribute 'map' or 'vals'" 1245 1246 if cls.is_class: 1247 cls.cxx_type = '%s' % name 1248 else: 1249 cls.cxx_type = 'Enums::%s' % name 1250 1251 super(MetaEnum, cls).__init__(name, bases, init_dict) 1252 1253 # Generate C++ class declaration for this enum type. 1254 # Note that we wrap the enum in a class/struct to act as a namespace, 1255 # so that the enum strings can be brief w/o worrying about collisions. 1256 def cxx_decl(cls, code): 1257 wrapper_name = cls.wrapper_name 1258 wrapper = 'struct' if cls.wrapper_is_struct else 'namespace' 1259 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1260 idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name) 1261 1262 code('''\ 1263#ifndef $idem_macro 1264#define $idem_macro 1265 1266''') 1267 if cls.is_class: 1268 code('''\ 1269enum class $name { 1270''') 1271 else: 1272 code('''\ 1273$wrapper $wrapper_name { 1274 enum $name { 1275''') 1276 code.indent(1) 1277 code.indent(1) 1278 for val in cls.vals: 1279 code('$val = ${{cls.map[val]}},') 1280 code('Num_$name = ${{len(cls.vals)}}') 1281 code.dedent(1) 1282 code('};') 1283 1284 if cls.is_class: 1285 code('''\ 1286extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})]; 1287''') 1288 elif cls.wrapper_is_struct: 1289 code('static const char *${name}Strings[Num_${name}];') 1290 else: 1291 code('extern const char *${name}Strings[Num_${name}];') 1292 1293 if not cls.is_class: 1294 code.dedent(1) 1295 code('};') 1296 1297 code() 1298 code('#endif // $idem_macro') 1299 1300 def cxx_def(cls, code): 1301 wrapper_name = cls.wrapper_name 1302 file_name = cls.__name__ 1303 name = cls.__name__ if cls.enum_name is None else cls.enum_name 1304 1305 code('#include "enums/$file_name.hh"') 1306 if cls.wrapper_is_struct: 1307 code('const char *${wrapper_name}::${name}Strings' 1308 '[Num_${name}] =') 1309 else: 1310 if cls.is_class: 1311 code('''\ 1312const char *${name}Strings[static_cast<int>(${name}::Num_${name})] = 1313''') 1314 else: 1315 code('namespace Enums {') 1316 code.indent(1) 1317 code('const char *${name}Strings[Num_${name}] =') 1318 1319 code('{') 1320 code.indent(1) 1321 for val in cls.vals: 1322 code('"$val",') 1323 code.dedent(1) 1324 code('};') 1325 1326 if not cls.wrapper_is_struct and not cls.is_class: 1327 code.dedent(1) 1328 code('} // namespace $wrapper_name') 1329 1330 1331 def pybind_def(cls, code): 1332 name = cls.__name__ 1333 enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name 1334 wrapper_name = enum_name if cls.is_class else cls.wrapper_name 1335 1336 code('''#include "pybind11/pybind11.h" 1337#include "pybind11/stl.h" 1338 1339#include <sim/init.hh> 1340 1341namespace py = pybind11; 1342 1343static void 1344module_init(py::module &m_internal) 1345{ 1346 py::module m = m_internal.def_submodule("enum_${name}"); 1347 1348''') 1349 if cls.is_class: 1350 code('py::enum_<${enum_name}>(m, "enum_${name}")') 1351 else: 1352 code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")') 1353 1354 code.indent() 1355 code.indent() 1356 for val in cls.vals: 1357 code('.value("${val}", ${wrapper_name}::${val})') 1358 code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})') 1359 code('.export_values()') 1360 code(';') 1361 code.dedent() 1362 1363 code('}') 1364 code.dedent() 1365 code() 1366 code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);') 1367 1368 1369# Base class for enum types. 1370class Enum(ParamValue): 1371 __metaclass__ = MetaEnum 1372 vals = [] 1373 cmd_line_settable = True 1374 1375 # The name of the wrapping namespace or struct 1376 wrapper_name = 'Enums' 1377 1378 # If true, the enum is wrapped in a struct rather than a namespace 1379 wrapper_is_struct = False 1380 1381 is_class = False 1382 1383 # If not None, use this as the enum name rather than this class name 1384 enum_name = None 1385 1386 def __init__(self, value): 1387 if value not in self.map: 1388 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 1389 % (value, self.vals) 1390 self.value = value 1391 1392 def __call__(self, value): 1393 self.__init__(value) 1394 return value 1395 1396 @classmethod 1397 def cxx_predecls(cls, code): 1398 code('#include "enums/$0.hh"', cls.__name__) 1399 1400 @classmethod 1401 def cxx_ini_parse(cls, code, src, dest, ret): 1402 code('if (false) {') 1403 for elem_name in cls.map.iterkeys(): 1404 code('} else if (%s == "%s") {' % (src, elem_name)) 1405 code.indent() 1406 code('%s = Enums::%s;' % (dest, elem_name)) 1407 code('%s true;' % ret) 1408 code.dedent() 1409 code('} else {') 1410 code(' %s false;' % ret) 1411 code('}') 1412 1413 def getValue(self): 1414 import m5.internal.params 1415 e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__) 1416 return e(self.map[self.value]) 1417 1418 def __str__(self): 1419 return self.value 1420 1421# This param will generate a scoped c++ enum and its python bindings. 1422class ScopedEnum(Enum): 1423 __metaclass__ = MetaEnum 1424 vals = [] 1425 cmd_line_settable = True 1426 1427 # The name of the wrapping namespace or struct 1428 wrapper_name = None 1429 1430 # If true, the enum is wrapped in a struct rather than a namespace 1431 wrapper_is_struct = False 1432 1433 # If true, the generated enum is a scoped enum 1434 is_class = True 1435 1436 # If not None, use this as the enum name rather than this class name 1437 enum_name = None 1438 1439# how big does a rounding error need to be before we warn about it? 1440frequency_tolerance = 0.001 # 0.1% 1441 1442class TickParamValue(NumericParamValue): 1443 cxx_type = 'Tick' 1444 ex_str = "1MHz" 1445 cmd_line_settable = True 1446 1447 @classmethod 1448 def cxx_predecls(cls, code): 1449 code('#include "base/types.hh"') 1450 1451 def __call__(self, value): 1452 self.__init__(value) 1453 return value 1454 1455 def getValue(self): 1456 return long(self.value) 1457 1458 @classmethod 1459 def cxx_ini_predecls(cls, code): 1460 code('#include <sstream>') 1461 1462 # Ticks are expressed in seconds in JSON files and in plain 1463 # Ticks in .ini files. Switch based on a config flag 1464 @classmethod 1465 def cxx_ini_parse(self, code, src, dest, ret): 1466 code('${ret} to_number(${src}, ${dest});') 1467 1468class Latency(TickParamValue): 1469 ex_str = "100ns" 1470 1471 def __init__(self, value): 1472 if isinstance(value, (Latency, Clock)): 1473 self.ticks = value.ticks 1474 self.value = value.value 1475 elif isinstance(value, Frequency): 1476 self.ticks = value.ticks 1477 self.value = 1.0 / value.value 1478 elif value.endswith('t'): 1479 self.ticks = True 1480 self.value = int(value[:-1]) 1481 else: 1482 self.ticks = False 1483 self.value = convert.toLatency(value) 1484 1485 def __call__(self, value): 1486 self.__init__(value) 1487 return value 1488 1489 def __getattr__(self, attr): 1490 if attr in ('latency', 'period'): 1491 return self 1492 if attr == 'frequency': 1493 return Frequency(self) 1494 raise AttributeError, "Latency object has no attribute '%s'" % attr 1495 1496 def getValue(self): 1497 if self.ticks or self.value == 0: 1498 value = self.value 1499 else: 1500 value = ticks.fromSeconds(self.value) 1501 return long(value) 1502 1503 def config_value(self): 1504 return self.getValue() 1505 1506 # convert latency to ticks 1507 def ini_str(self): 1508 return '%d' % self.getValue() 1509 1510class Frequency(TickParamValue): 1511 ex_str = "1GHz" 1512 1513 def __init__(self, value): 1514 if isinstance(value, (Latency, Clock)): 1515 if value.value == 0: 1516 self.value = 0 1517 else: 1518 self.value = 1.0 / value.value 1519 self.ticks = value.ticks 1520 elif isinstance(value, Frequency): 1521 self.value = value.value 1522 self.ticks = value.ticks 1523 else: 1524 self.ticks = False 1525 self.value = convert.toFrequency(value) 1526 1527 def __call__(self, value): 1528 self.__init__(value) 1529 return value 1530 1531 def __getattr__(self, attr): 1532 if attr == 'frequency': 1533 return self 1534 if attr in ('latency', 'period'): 1535 return Latency(self) 1536 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1537 1538 # convert latency to ticks 1539 def getValue(self): 1540 if self.ticks or self.value == 0: 1541 value = self.value 1542 else: 1543 value = ticks.fromSeconds(1.0 / self.value) 1544 return long(value) 1545 1546 def config_value(self): 1547 return self.getValue() 1548 1549 def ini_str(self): 1550 return '%d' % self.getValue() 1551 1552# A generic Frequency and/or Latency value. Value is stored as a 1553# latency, just like Latency and Frequency. 1554class Clock(TickParamValue): 1555 def __init__(self, value): 1556 if isinstance(value, (Latency, Clock)): 1557 self.ticks = value.ticks 1558 self.value = value.value 1559 elif isinstance(value, Frequency): 1560 self.ticks = value.ticks 1561 self.value = 1.0 / value.value 1562 elif value.endswith('t'): 1563 self.ticks = True 1564 self.value = int(value[:-1]) 1565 else: 1566 self.ticks = False 1567 self.value = convert.anyToLatency(value) 1568 1569 def __call__(self, value): 1570 self.__init__(value) 1571 return value 1572 1573 def __str__(self): 1574 return "%s" % Latency(self) 1575 1576 def __getattr__(self, attr): 1577 if attr == 'frequency': 1578 return Frequency(self) 1579 if attr in ('latency', 'period'): 1580 return Latency(self) 1581 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1582 1583 def getValue(self): 1584 return self.period.getValue() 1585 1586 def config_value(self): 1587 return self.period.config_value() 1588 1589 def ini_str(self): 1590 return self.period.ini_str() 1591 1592class Voltage(Float): 1593 ex_str = "1V" 1594 1595 def __new__(cls, value): 1596 value = convert.toVoltage(value) 1597 return super(cls, Voltage).__new__(cls, value) 1598 1599 def __init__(self, value): 1600 value = convert.toVoltage(value) 1601 super(Voltage, self).__init__(value) 1602 1603class Current(Float): 1604 ex_str = "1mA" 1605 1606 def __new__(cls, value): 1607 value = convert.toCurrent(value) 1608 return super(cls, Current).__new__(cls, value) 1609 1610 def __init__(self, value): 1611 value = convert.toCurrent(value) 1612 super(Current, self).__init__(value) 1613 1614class Energy(Float): 1615 ex_str = "1pJ" 1616 1617 def __new__(cls, value): 1618 value = convert.toEnergy(value) 1619 return super(cls, Energy).__new__(cls, value) 1620 1621 def __init__(self, value): 1622 value = convert.toEnergy(value) 1623 super(Energy, self).__init__(value) 1624 1625class NetworkBandwidth(float,ParamValue): 1626 cxx_type = 'float' 1627 ex_str = "1Gbps" 1628 cmd_line_settable = True 1629 1630 def __new__(cls, value): 1631 # convert to bits per second 1632 val = convert.toNetworkBandwidth(value) 1633 return super(cls, NetworkBandwidth).__new__(cls, val) 1634 1635 def __str__(self): 1636 return str(self.val) 1637 1638 def __call__(self, value): 1639 val = convert.toNetworkBandwidth(value) 1640 self.__init__(val) 1641 return value 1642 1643 def getValue(self): 1644 # convert to seconds per byte 1645 value = 8.0 / float(self) 1646 # convert to ticks per byte 1647 value = ticks.fromSeconds(value) 1648 return float(value) 1649 1650 def ini_str(self): 1651 return '%f' % self.getValue() 1652 1653 def config_value(self): 1654 return '%f' % self.getValue() 1655 1656 @classmethod 1657 def cxx_ini_predecls(cls, code): 1658 code('#include <sstream>') 1659 1660 @classmethod 1661 def cxx_ini_parse(self, code, src, dest, ret): 1662 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1663 1664class MemoryBandwidth(float,ParamValue): 1665 cxx_type = 'float' 1666 ex_str = "1GB/s" 1667 cmd_line_settable = True 1668 1669 def __new__(cls, value): 1670 # convert to bytes per second 1671 val = convert.toMemoryBandwidth(value) 1672 return super(cls, MemoryBandwidth).__new__(cls, val) 1673 1674 def __call__(self, value): 1675 val = convert.toMemoryBandwidth(value) 1676 self.__init__(val) 1677 return value 1678 1679 def getValue(self): 1680 # convert to seconds per byte 1681 value = float(self) 1682 if value: 1683 value = 1.0 / float(self) 1684 # convert to ticks per byte 1685 value = ticks.fromSeconds(value) 1686 return float(value) 1687 1688 def ini_str(self): 1689 return '%f' % self.getValue() 1690 1691 def config_value(self): 1692 return '%f' % self.getValue() 1693 1694 @classmethod 1695 def cxx_ini_predecls(cls, code): 1696 code('#include <sstream>') 1697 1698 @classmethod 1699 def cxx_ini_parse(self, code, src, dest, ret): 1700 code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) 1701 1702# 1703# "Constants"... handy aliases for various values. 1704# 1705 1706# Special class for NULL pointers. Note the special check in 1707# make_param_value() above that lets these be assigned where a 1708# SimObject is required. 1709# only one copy of a particular node 1710class NullSimObject(object): 1711 __metaclass__ = Singleton 1712 _name = 'Null' 1713 1714 def __call__(cls): 1715 return cls 1716 1717 def _instantiate(self, parent = None, path = ''): 1718 pass 1719 1720 def ini_str(self): 1721 return 'Null' 1722 1723 def unproxy(self, base): 1724 return self 1725 1726 def set_path(self, parent, name): 1727 pass 1728 1729 def set_parent(self, parent, name): 1730 pass 1731 1732 def clear_parent(self, old_parent): 1733 pass 1734 1735 def descendants(self): 1736 return 1737 yield None 1738 1739 def get_config_as_dict(self): 1740 return {} 1741 1742 def __str__(self): 1743 return self._name 1744 1745 def config_value(self): 1746 return None 1747 1748 def getValue(self): 1749 return None 1750 1751# The only instance you'll ever need... 1752NULL = NullSimObject() 1753 1754def isNullPointer(value): 1755 return isinstance(value, NullSimObject) 1756 1757# Some memory range specifications use this as a default upper bound. 1758MaxAddr = Addr.max 1759MaxTick = Tick.max 1760AllMemory = AddrRange(0, MaxAddr) 1761 1762 1763##################################################################### 1764# 1765# Port objects 1766# 1767# Ports are used to interconnect objects in the memory system. 1768# 1769##################################################################### 1770 1771# Port reference: encapsulates a reference to a particular port on a 1772# particular SimObject. 1773class PortRef(object): 1774 def __init__(self, simobj, name, role): 1775 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1776 self.simobj = simobj 1777 self.name = name 1778 self.role = role 1779 self.peer = None # not associated with another port yet 1780 self.ccConnected = False # C++ port connection done? 1781 self.index = -1 # always -1 for non-vector ports 1782 1783 def __str__(self): 1784 return '%s.%s' % (self.simobj, self.name) 1785 1786 def __len__(self): 1787 # Return the number of connected ports, i.e. 0 is we have no 1788 # peer and 1 if we do. 1789 return int(self.peer != None) 1790 1791 # for config.ini, print peer's name (not ours) 1792 def ini_str(self): 1793 return str(self.peer) 1794 1795 # for config.json 1796 def get_config_as_dict(self): 1797 return {'role' : self.role, 'peer' : str(self.peer)} 1798 1799 def __getattr__(self, attr): 1800 if attr == 'peerObj': 1801 # shorthand for proxies 1802 return self.peer.simobj 1803 raise AttributeError, "'%s' object has no attribute '%s'" % \ 1804 (self.__class__.__name__, attr) 1805 1806 # Full connection is symmetric (both ways). Called via 1807 # SimObject.__setattr__ as a result of a port assignment, e.g., 1808 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 1809 # e.g., "obj1.portA[3] = obj2.portB". 1810 def connect(self, other): 1811 if isinstance(other, VectorPortRef): 1812 # reference to plain VectorPort is implicit append 1813 other = other._get_next() 1814 if self.peer and not proxy.isproxy(self.peer): 1815 fatal("Port %s is already connected to %s, cannot connect %s\n", 1816 self, self.peer, other); 1817 self.peer = other 1818 if proxy.isproxy(other): 1819 other.set_param_desc(PortParamDesc()) 1820 elif isinstance(other, PortRef): 1821 if other.peer is not self: 1822 other.connect(self) 1823 else: 1824 raise TypeError, \ 1825 "assigning non-port reference '%s' to port '%s'" \ 1826 % (other, self) 1827 1828 # Allow a master/slave port pair to be spliced between 1829 # a port and its connected peer. Useful operation for connecting 1830 # instrumentation structures into a system when it is necessary 1831 # to connect the instrumentation after the full system has been 1832 # constructed. 1833 def splice(self, new_master_peer, new_slave_peer): 1834 if not self.peer or proxy.isproxy(self.peer): 1835 fatal("Port %s not connected, cannot splice in new peers\n", self) 1836 1837 if not isinstance(new_master_peer, PortRef) or \ 1838 not isinstance(new_slave_peer, PortRef): 1839 raise TypeError, \ 1840 "Splicing non-port references '%s','%s' to port '%s'" % \ 1841 (new_master_peer, new_slave_peer, self) 1842 1843 old_peer = self.peer 1844 if self.role == 'SLAVE': 1845 self.peer = new_master_peer 1846 old_peer.peer = new_slave_peer 1847 new_master_peer.connect(self) 1848 new_slave_peer.connect(old_peer) 1849 elif self.role == 'MASTER': 1850 self.peer = new_slave_peer 1851 old_peer.peer = new_master_peer 1852 new_slave_peer.connect(self) 1853 new_master_peer.connect(old_peer) 1854 else: 1855 panic("Port %s has unknown role, "+\ 1856 "cannot splice in new peers\n", self) 1857 1858 def clone(self, simobj, memo): 1859 if memo.has_key(self): 1860 return memo[self] 1861 newRef = copy.copy(self) 1862 memo[self] = newRef 1863 newRef.simobj = simobj 1864 assert(isSimObject(newRef.simobj)) 1865 if self.peer and not proxy.isproxy(self.peer): 1866 peerObj = self.peer.simobj(_memo=memo) 1867 newRef.peer = self.peer.clone(peerObj, memo) 1868 assert(not isinstance(newRef.peer, VectorPortRef)) 1869 return newRef 1870 1871 def unproxy(self, simobj): 1872 assert(simobj is self.simobj) 1873 if proxy.isproxy(self.peer): 1874 try: 1875 realPeer = self.peer.unproxy(self.simobj) 1876 except: 1877 print("Error in unproxying port '%s' of %s" % 1878 (self.name, self.simobj.path())) 1879 raise 1880 self.connect(realPeer) 1881 1882 # Call C++ to create corresponding port connection between C++ objects 1883 def ccConnect(self): 1884 from _m5.pyobject import connectPorts 1885 1886 if self.role == 'SLAVE': 1887 # do nothing and let the master take care of it 1888 return 1889 1890 if self.ccConnected: # already done this 1891 return 1892 peer = self.peer 1893 if not self.peer: # nothing to connect to 1894 return 1895 1896 # check that we connect a master to a slave 1897 if self.role == peer.role: 1898 raise TypeError, \ 1899 "cannot connect '%s' and '%s' due to identical role '%s'" \ 1900 % (peer, self, self.role) 1901 1902 try: 1903 # self is always the master and peer the slave 1904 connectPorts(self.simobj.getCCObject(), self.name, self.index, 1905 peer.simobj.getCCObject(), peer.name, peer.index) 1906 except: 1907 print("Error connecting port %s.%s to %s.%s" % 1908 (self.simobj.path(), self.name, 1909 peer.simobj.path(), peer.name)) 1910 raise 1911 self.ccConnected = True 1912 peer.ccConnected = True 1913 1914# A reference to an individual element of a VectorPort... much like a 1915# PortRef, but has an index. 1916class VectorPortElementRef(PortRef): 1917 def __init__(self, simobj, name, role, index): 1918 PortRef.__init__(self, simobj, name, role) 1919 self.index = index 1920 1921 def __str__(self): 1922 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1923 1924# A reference to a complete vector-valued port (not just a single element). 1925# Can be indexed to retrieve individual VectorPortElementRef instances. 1926class VectorPortRef(object): 1927 def __init__(self, simobj, name, role): 1928 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1929 self.simobj = simobj 1930 self.name = name 1931 self.role = role 1932 self.elements = [] 1933 1934 def __str__(self): 1935 return '%s.%s[:]' % (self.simobj, self.name) 1936 1937 def __len__(self): 1938 # Return the number of connected peers, corresponding the the 1939 # length of the elements. 1940 return len(self.elements) 1941 1942 # for config.ini, print peer's name (not ours) 1943 def ini_str(self): 1944 return ' '.join([el.ini_str() for el in self.elements]) 1945 1946 # for config.json 1947 def get_config_as_dict(self): 1948 return {'role' : self.role, 1949 'peer' : [el.ini_str() for el in self.elements]} 1950 1951 def __getitem__(self, key): 1952 if not isinstance(key, int): 1953 raise TypeError, "VectorPort index must be integer" 1954 if key >= len(self.elements): 1955 # need to extend list 1956 ext = [VectorPortElementRef(self.simobj, self.name, self.role, i) 1957 for i in range(len(self.elements), key+1)] 1958 self.elements.extend(ext) 1959 return self.elements[key] 1960 1961 def _get_next(self): 1962 return self[len(self.elements)] 1963 1964 def __setitem__(self, key, value): 1965 if not isinstance(key, int): 1966 raise TypeError, "VectorPort index must be integer" 1967 self[key].connect(value) 1968 1969 def connect(self, other): 1970 if isinstance(other, (list, tuple)): 1971 # Assign list of port refs to vector port. 1972 # For now, append them... not sure if that's the right semantics 1973 # or if it should replace the current vector. 1974 for ref in other: 1975 self._get_next().connect(ref) 1976 else: 1977 # scalar assignment to plain VectorPort is implicit append 1978 self._get_next().connect(other) 1979 1980 def clone(self, simobj, memo): 1981 if memo.has_key(self): 1982 return memo[self] 1983 newRef = copy.copy(self) 1984 memo[self] = newRef 1985 newRef.simobj = simobj 1986 assert(isSimObject(newRef.simobj)) 1987 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 1988 return newRef 1989 1990 def unproxy(self, simobj): 1991 [el.unproxy(simobj) for el in self.elements] 1992 1993 def ccConnect(self): 1994 [el.ccConnect() for el in self.elements] 1995 1996# Port description object. Like a ParamDesc object, this represents a 1997# logical port in the SimObject class, not a particular port on a 1998# SimObject instance. The latter are represented by PortRef objects. 1999class Port(object): 2000 # Generate a PortRef for this port on the given SimObject with the 2001 # given name 2002 def makeRef(self, simobj): 2003 return PortRef(simobj, self.name, self.role) 2004 2005 # Connect an instance of this port (on the given SimObject with 2006 # the given name) with the port described by the supplied PortRef 2007 def connect(self, simobj, ref): 2008 self.makeRef(simobj).connect(ref) 2009 2010 # No need for any pre-declarations at the moment as we merely rely 2011 # on an unsigned int. 2012 def cxx_predecls(self, code): 2013 pass 2014 2015 def pybind_predecls(self, code): 2016 cls.cxx_predecls(self, code) 2017 2018 # Declare an unsigned int with the same name as the port, that 2019 # will eventually hold the number of connected ports (and thus the 2020 # number of elements for a VectorPort). 2021 def cxx_decl(self, code): 2022 code('unsigned int port_${{self.name}}_connection_count;') 2023 2024class MasterPort(Port): 2025 # MasterPort("description") 2026 def __init__(self, *args): 2027 if len(args) == 1: 2028 self.desc = args[0] 2029 self.role = 'MASTER' 2030 else: 2031 raise TypeError, 'wrong number of arguments' 2032 2033class SlavePort(Port): 2034 # SlavePort("description") 2035 def __init__(self, *args): 2036 if len(args) == 1: 2037 self.desc = args[0] 2038 self.role = 'SLAVE' 2039 else: 2040 raise TypeError, 'wrong number of arguments' 2041 2042# VectorPort description object. Like Port, but represents a vector 2043# of connections (e.g., as on a XBar). 2044class VectorPort(Port): 2045 def __init__(self, *args): 2046 self.isVec = True 2047 2048 def makeRef(self, simobj): 2049 return VectorPortRef(simobj, self.name, self.role) 2050 2051class VectorMasterPort(VectorPort): 2052 # VectorMasterPort("description") 2053 def __init__(self, *args): 2054 if len(args) == 1: 2055 self.desc = args[0] 2056 self.role = 'MASTER' 2057 VectorPort.__init__(self, *args) 2058 else: 2059 raise TypeError, 'wrong number of arguments' 2060 2061class VectorSlavePort(VectorPort): 2062 # VectorSlavePort("description") 2063 def __init__(self, *args): 2064 if len(args) == 1: 2065 self.desc = args[0] 2066 self.role = 'SLAVE' 2067 VectorPort.__init__(self, *args) 2068 else: 2069 raise TypeError, 'wrong number of arguments' 2070 2071# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 2072# proxy objects (via set_param_desc()) so that proxy error messages 2073# make sense. 2074class PortParamDesc(object): 2075 __metaclass__ = Singleton 2076 2077 ptype_str = 'Port' 2078 ptype = Port 2079 2080baseEnums = allEnums.copy() 2081baseParams = allParams.copy() 2082 2083def clear(): 2084 global allEnums, allParams 2085 2086 allEnums = baseEnums.copy() 2087 allParams = baseParams.copy() 2088 2089__all__ = ['Param', 'VectorParam', 2090 'Enum', 'ScopedEnum', 'Bool', 'String', 'Float', 2091 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 2092 'Int32', 'UInt32', 'Int64', 'UInt64', 2093 'Counter', 'Addr', 'Tick', 'Percent', 2094 'TcpPort', 'UdpPort', 'EthernetAddr', 2095 'IpAddress', 'IpNetmask', 'IpWithPort', 2096 'MemorySize', 'MemorySize32', 2097 'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy', 2098 'NetworkBandwidth', 'MemoryBandwidth', 2099 'AddrRange', 2100 'MaxAddr', 'MaxTick', 'AllMemory', 2101 'Time', 2102 'NextEthernetAddr', 'NULL', 2103 'MasterPort', 'SlavePort', 2104 'VectorMasterPort', 'VectorSlavePort'] 2105 2106import SimObject 2107