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