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