params.py revision 3624
1# Copyright (c) 2004-2006 The Regents of The University of Michigan 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: redistributions of source code must retain the above copyright 7# notice, this list of conditions and the following disclaimer; 8# redistributions in binary form must reproduce the above copyright 9# notice, this list of conditions and the following disclaimer in the 10# documentation and/or other materials provided with the distribution; 11# neither the name of the copyright holders nor the names of its 12# contributors may be used to endorse or promote products derived from 13# this software without specific prior written permission. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26# 27# Authors: Steve Reinhardt 28# Nathan Binkert 29 30##################################################################### 31# 32# Parameter description classes 33# 34# The _params dictionary in each class maps parameter names to either 35# a Param or a VectorParam object. These objects contain the 36# parameter description string, the parameter type, and the default 37# value (if any). The convert() method on these objects is used to 38# force whatever value is assigned to the parameter to the appropriate 39# type. 40# 41# Note that the default values are loaded into the class's attribute 42# space when the parameter dictionary is initialized (in 43# MetaSimObject._new_param()); after that point they aren't used. 44# 45##################################################################### 46 47import sys, inspect, copy 48import convert 49from util import * 50 51# Dummy base class to identify types that are legitimate for SimObject 52# parameters. 53class ParamValue(object): 54 55 cxx_predecls = [] 56 swig_predecls = [] 57 58 # default for printing to .ini file is regular string conversion. 59 # will be overridden in some cases 60 def ini_str(self): 61 return str(self) 62 63 # allows us to blithely call unproxy() on things without checking 64 # if they're really proxies or not 65 def unproxy(self, base): 66 return self 67 68# Regular parameter description. 69class ParamDesc(object): 70 def __init__(self, ptype_str, ptype, *args, **kwargs): 71 self.ptype_str = ptype_str 72 # remember ptype only if it is provided 73 if ptype != None: 74 self.ptype = ptype 75 76 if args: 77 if len(args) == 1: 78 self.desc = args[0] 79 elif len(args) == 2: 80 self.default = args[0] 81 self.desc = args[1] 82 else: 83 raise TypeError, 'too many arguments' 84 85 if kwargs.has_key('desc'): 86 assert(not hasattr(self, 'desc')) 87 self.desc = kwargs['desc'] 88 del kwargs['desc'] 89 90 if kwargs.has_key('default'): 91 assert(not hasattr(self, 'default')) 92 self.default = kwargs['default'] 93 del kwargs['default'] 94 95 if kwargs: 96 raise TypeError, 'extra unknown kwargs %s' % kwargs 97 98 if not hasattr(self, 'desc'): 99 raise TypeError, 'desc attribute missing' 100 101 def __getattr__(self, attr): 102 if attr == 'ptype': 103 try: 104 ptype = eval(self.ptype_str, objects.__dict__) 105 if not isinstance(ptype, type): 106 raise NameError 107 self.ptype = ptype 108 return ptype 109 except NameError: 110 raise TypeError, \ 111 "Param qualifier '%s' is not a type" % self.ptype_str 112 raise AttributeError, "'%s' object has no attribute '%s'" % \ 113 (type(self).__name__, attr) 114 115 def convert(self, value): 116 if isinstance(value, proxy.BaseProxy): 117 value.set_param_desc(self) 118 return value 119 if not hasattr(self, 'ptype') and isNullPointer(value): 120 # deferred evaluation of SimObject; continue to defer if 121 # we're just assigning a null pointer 122 return value 123 if isinstance(value, self.ptype): 124 return value 125 if isNullPointer(value) and isSimObjectClass(self.ptype): 126 return value 127 return self.ptype(value) 128 129 def cxx_predecls(self): 130 return self.ptype.cxx_predecls 131 132 def swig_predecls(self): 133 return self.ptype.swig_predecls 134 135 def cxx_decl(self): 136 return '%s %s;' % (self.ptype.cxx_type, self.name) 137 138# Vector-valued parameter description. Just like ParamDesc, except 139# that the value is a vector (list) of the specified type instead of a 140# single value. 141 142class VectorParamValue(list): 143 def ini_str(self): 144 return ' '.join([v.ini_str() for v in self]) 145 146 def unproxy(self, base): 147 return [v.unproxy(base) for v in self] 148 149class SimObjVector(VectorParamValue): 150 def print_ini(self): 151 for v in self: 152 v.print_ini() 153 154class VectorParamDesc(ParamDesc): 155 # Convert assigned value to appropriate type. If the RHS is not a 156 # list or tuple, it generates a single-element list. 157 def convert(self, value): 158 if isinstance(value, (list, tuple)): 159 # list: coerce each element into new list 160 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 161 if isSimObjectSequence(tmp_list): 162 return SimObjVector(tmp_list) 163 else: 164 return VectorParamValue(tmp_list) 165 else: 166 # singleton: leave it be (could coerce to a single-element 167 # list here, but for some historical reason we don't... 168 return ParamDesc.convert(self, value) 169 170 def cxx_predecls(self): 171 return ['#include <vector>'] + self.ptype.cxx_predecls 172 173 def swig_predecls(self): 174 return ['%include "std_vector.i"'] + self.ptype.swig_predecls 175 176 def cxx_decl(self): 177 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) 178 179class ParamFactory(object): 180 def __init__(self, param_desc_class, ptype_str = None): 181 self.param_desc_class = param_desc_class 182 self.ptype_str = ptype_str 183 184 def __getattr__(self, attr): 185 if self.ptype_str: 186 attr = self.ptype_str + '.' + attr 187 return ParamFactory(self.param_desc_class, attr) 188 189 # E.g., Param.Int(5, "number of widgets") 190 def __call__(self, *args, **kwargs): 191 caller_frame = inspect.currentframe().f_back 192 ptype = None 193 try: 194 ptype = eval(self.ptype_str, 195 caller_frame.f_globals, caller_frame.f_locals) 196 if not isinstance(ptype, type): 197 raise TypeError, \ 198 "Param qualifier is not a type: %s" % ptype 199 except NameError: 200 # if name isn't defined yet, assume it's a SimObject, and 201 # try to resolve it later 202 pass 203 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 204 205Param = ParamFactory(ParamDesc) 206VectorParam = ParamFactory(VectorParamDesc) 207 208##################################################################### 209# 210# Parameter Types 211# 212# Though native Python types could be used to specify parameter types 213# (the 'ptype' field of the Param and VectorParam classes), it's more 214# flexible to define our own set of types. This gives us more control 215# over how Python expressions are converted to values (via the 216# __init__() constructor) and how these values are printed out (via 217# the __str__() conversion method). 218# 219##################################################################### 220 221# String-valued parameter. Just mixin the ParamValue class with the 222# built-in str class. 223class String(ParamValue,str): 224 cxx_type = 'std::string' 225 cxx_predecls = ['#include <string>'] 226 swig_predecls = ['%include "std_string.i"\n' + 227 '%apply const std::string& {std::string *};'] 228 pass 229 230# superclass for "numeric" parameter values, to emulate math 231# operations in a type-safe way. e.g., a Latency times an int returns 232# a new Latency object. 233class NumericParamValue(ParamValue): 234 def __str__(self): 235 return str(self.value) 236 237 def __float__(self): 238 return float(self.value) 239 240 # hook for bounds checking 241 def _check(self): 242 return 243 244 def __mul__(self, other): 245 newobj = self.__class__(self) 246 newobj.value *= other 247 newobj._check() 248 return newobj 249 250 __rmul__ = __mul__ 251 252 def __div__(self, other): 253 newobj = self.__class__(self) 254 newobj.value /= other 255 newobj._check() 256 return newobj 257 258 def __sub__(self, other): 259 newobj = self.__class__(self) 260 newobj.value -= other 261 newobj._check() 262 return newobj 263 264# Metaclass for bounds-checked integer parameters. See CheckedInt. 265class CheckedIntType(type): 266 def __init__(cls, name, bases, dict): 267 super(CheckedIntType, cls).__init__(name, bases, dict) 268 269 # CheckedInt is an abstract base class, so we actually don't 270 # want to do any processing on it... the rest of this code is 271 # just for classes that derive from CheckedInt. 272 if name == 'CheckedInt': 273 return 274 275 if not cls.cxx_predecls: 276 # most derived types require this, so we just do it here once 277 cls.cxx_predecls = ['#include "sim/host.hh"'] 278 279 if not cls.swig_predecls: 280 # most derived types require this, so we just do it here once 281 cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 282 '%import "sim/host.hh"'] 283 284 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 285 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 286 panic("CheckedInt subclass %s must define either\n" \ 287 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 288 % name); 289 if cls.unsigned: 290 cls.min = 0 291 cls.max = 2 ** cls.size - 1 292 else: 293 cls.min = -(2 ** (cls.size - 1)) 294 cls.max = (2 ** (cls.size - 1)) - 1 295 296# Abstract superclass for bounds-checked integer parameters. This 297# class is subclassed to generate parameter classes with specific 298# bounds. Initialization of the min and max bounds is done in the 299# metaclass CheckedIntType.__init__. 300class CheckedInt(NumericParamValue): 301 __metaclass__ = CheckedIntType 302 303 def _check(self): 304 if not self.min <= self.value <= self.max: 305 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 306 (self.min, self.value, self.max) 307 308 def __init__(self, value): 309 if isinstance(value, str): 310 self.value = convert.toInteger(value) 311 elif isinstance(value, (int, long, float)): 312 self.value = long(value) 313 self._check() 314 315class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 316class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 317 318class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 319class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 320class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 321class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 322class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 323class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 324class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 325class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 326 327class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 328class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 329class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 330class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 331 332class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 333 334class Float(ParamValue, float): 335 pass 336 337class MemorySize(CheckedInt): 338 cxx_type = 'uint64_t' 339 size = 64 340 unsigned = True 341 def __init__(self, value): 342 if isinstance(value, MemorySize): 343 self.value = value.value 344 else: 345 self.value = convert.toMemorySize(value) 346 self._check() 347 348class MemorySize32(CheckedInt): 349 size = 32 350 unsigned = True 351 def __init__(self, value): 352 if isinstance(value, MemorySize): 353 self.value = value.value 354 else: 355 self.value = convert.toMemorySize(value) 356 self._check() 357 358class Addr(CheckedInt): 359 cxx_type = 'Addr' 360 cxx_predecls = ['#include "targetarch/isa_traits.hh"'] 361 size = 64 362 unsigned = True 363 def __init__(self, value): 364 if isinstance(value, Addr): 365 self.value = value.value 366 else: 367 try: 368 self.value = convert.toMemorySize(value) 369 except TypeError: 370 self.value = long(value) 371 self._check() 372 def __add__(self, other): 373 if isinstance(other, Addr): 374 return self.value + other.value 375 else: 376 return self.value + other 377 378 379class MetaRange(type): 380 def __init__(cls, name, bases, dict): 381 super(MetaRange, cls).__init__(name, bases, dict) 382 if name == 'Range': 383 return 384 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 385 cls.cxx_predecls = \ 386 ['#include "base/range.hh"'] + cls.type.cxx_predecls 387 388class Range(ParamValue): 389 __metaclass__ = MetaRange 390 type = Int # default; can be overridden in subclasses 391 def __init__(self, *args, **kwargs): 392 def handle_kwargs(self, kwargs): 393 if 'end' in kwargs: 394 self.second = self.type(kwargs.pop('end')) 395 elif 'size' in kwargs: 396 self.second = self.first + self.type(kwargs.pop('size')) - 1 397 else: 398 raise TypeError, "Either end or size must be specified" 399 400 if len(args) == 0: 401 self.first = self.type(kwargs.pop('start')) 402 handle_kwargs(self, kwargs) 403 404 elif len(args) == 1: 405 if kwargs: 406 self.first = self.type(args[0]) 407 handle_kwargs(self, kwargs) 408 elif isinstance(args[0], Range): 409 self.first = self.type(args[0].first) 410 self.second = self.type(args[0].second) 411 else: 412 self.first = self.type(0) 413 self.second = self.type(args[0]) - 1 414 415 elif len(args) == 2: 416 self.first = self.type(args[0]) 417 self.second = self.type(args[1]) 418 else: 419 raise TypeError, "Too many arguments specified" 420 421 if kwargs: 422 raise TypeError, "too many keywords: %s" % kwargs.keys() 423 424 def __str__(self): 425 return '%s:%s' % (self.first, self.second) 426 427class AddrRange(Range): 428 type = Addr 429 430class TickRange(Range): 431 type = Tick 432 433# Boolean parameter type. Python doesn't let you subclass bool, since 434# it doesn't want to let you create multiple instances of True and 435# False. Thus this is a little more complicated than String. 436class Bool(ParamValue): 437 cxx_type = 'bool' 438 def __init__(self, value): 439 try: 440 self.value = convert.toBool(value) 441 except TypeError: 442 self.value = bool(value) 443 444 def __str__(self): 445 return str(self.value) 446 447 def ini_str(self): 448 if self.value: 449 return 'true' 450 return 'false' 451 452def IncEthernetAddr(addr, val = 1): 453 bytes = map(lambda x: int(x, 16), addr.split(':')) 454 bytes[5] += val 455 for i in (5, 4, 3, 2, 1): 456 val,rem = divmod(bytes[i], 256) 457 bytes[i] = rem 458 if val == 0: 459 break 460 bytes[i - 1] += val 461 assert(bytes[0] <= 255) 462 return ':'.join(map(lambda x: '%02x' % x, bytes)) 463 464class NextEthernetAddr(object): 465 addr = "00:90:00:00:00:01" 466 467 def __init__(self, inc = 1): 468 self.value = NextEthernetAddr.addr 469 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) 470 471class EthernetAddr(ParamValue): 472 cxx_type = 'Net::EthAddr' 473 cxx_predecls = ['#include "base/inet.hh"'] 474 swig_predecls = ['class Net::EthAddr;'] 475 def __init__(self, value): 476 if value == NextEthernetAddr: 477 self.value = value 478 return 479 480 if not isinstance(value, str): 481 raise TypeError, "expected an ethernet address and didn't get one" 482 483 bytes = value.split(':') 484 if len(bytes) != 6: 485 raise TypeError, 'invalid ethernet address %s' % value 486 487 for byte in bytes: 488 if not 0 <= int(byte) <= 256: 489 raise TypeError, 'invalid ethernet address %s' % value 490 491 self.value = value 492 493 def unproxy(self, base): 494 if self.value == NextEthernetAddr: 495 self.addr = self.value().value 496 return self 497 498 def __str__(self): 499 if self.value == NextEthernetAddr: 500 if hasattr(self, 'addr'): 501 return self.addr 502 else: 503 return "NextEthernetAddr (unresolved)" 504 else: 505 return self.value 506 507# Enumerated types are a little more complex. The user specifies the 508# type as Enum(foo) where foo is either a list or dictionary of 509# alternatives (typically strings, but not necessarily so). (In the 510# long run, the integer value of the parameter will be the list index 511# or the corresponding dictionary value. For now, since we only check 512# that the alternative is valid and then spit it into a .ini file, 513# there's not much point in using the dictionary.) 514 515# What Enum() must do is generate a new type encapsulating the 516# provided list/dictionary so that specific values of the parameter 517# can be instances of that type. We define two hidden internal 518# classes (_ListEnum and _DictEnum) to serve as base classes, then 519# derive the new type from the appropriate base class on the fly. 520 521 522# Metaclass for Enum types 523class MetaEnum(type): 524 def __init__(cls, name, bases, init_dict): 525 if init_dict.has_key('map'): 526 if not isinstance(cls.map, dict): 527 raise TypeError, "Enum-derived class attribute 'map' " \ 528 "must be of type dict" 529 # build list of value strings from map 530 cls.vals = cls.map.keys() 531 cls.vals.sort() 532 elif init_dict.has_key('vals'): 533 if not isinstance(cls.vals, list): 534 raise TypeError, "Enum-derived class attribute 'vals' " \ 535 "must be of type list" 536 # build string->value map from vals sequence 537 cls.map = {} 538 for idx,val in enumerate(cls.vals): 539 cls.map[val] = idx 540 else: 541 raise TypeError, "Enum-derived class must define "\ 542 "attribute 'map' or 'vals'" 543 544 cls.cxx_type = name + '::Enum' 545 546 super(MetaEnum, cls).__init__(name, bases, init_dict) 547 548 # Generate C++ class declaration for this enum type. 549 # Note that we wrap the enum in a class/struct to act as a namespace, 550 # so that the enum strings can be brief w/o worrying about collisions. 551 def cxx_decl(cls): 552 s = 'struct %s {\n enum Enum {\n ' % cls.__name__ 553 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 554 s += '\n };\n};\n' 555 return s 556 557# Base class for enum types. 558class Enum(ParamValue): 559 __metaclass__ = MetaEnum 560 vals = [] 561 562 def __init__(self, value): 563 if value not in self.map: 564 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 565 % (value, self.vals) 566 self.value = value 567 568 def __str__(self): 569 return self.value 570 571ticks_per_sec = None 572 573# how big does a rounding error need to be before we warn about it? 574frequency_tolerance = 0.001 # 0.1% 575 576# convert a floting-point # of ticks to integer, and warn if rounding 577# discards too much precision 578def tick_check(float_ticks): 579 if float_ticks == 0: 580 return 0 581 int_ticks = int(round(float_ticks)) 582 err = (float_ticks - int_ticks) / float_ticks 583 if err > frequency_tolerance: 584 print >> sys.stderr, "Warning: rounding error > tolerance" 585 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) 586 #raise ValueError 587 return int_ticks 588 589def getLatency(value): 590 if isinstance(value, Latency) or isinstance(value, Clock): 591 return value.value 592 elif isinstance(value, Frequency) or isinstance(value, RootClock): 593 return 1 / value.value 594 elif isinstance(value, str): 595 try: 596 return convert.toLatency(value) 597 except ValueError: 598 try: 599 return 1 / convert.toFrequency(value) 600 except ValueError: 601 pass # fall through 602 raise ValueError, "Invalid Frequency/Latency value '%s'" % value 603 604 605class Latency(NumericParamValue): 606 cxx_type = 'Tick' 607 cxx_predecls = ['#include "sim/host.hh"'] 608 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 609 '%import "sim/host.hh"'] 610 def __init__(self, value): 611 self.value = getLatency(value) 612 613 def __getattr__(self, attr): 614 if attr in ('latency', 'period'): 615 return self 616 if attr == 'frequency': 617 return Frequency(self) 618 raise AttributeError, "Latency object has no attribute '%s'" % attr 619 620 # convert latency to ticks 621 def ini_str(self): 622 return str(tick_check(self.value * ticks_per_sec)) 623 624class Frequency(NumericParamValue): 625 cxx_type = 'Tick' 626 cxx_predecls = ['#include "sim/host.hh"'] 627 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 628 '%import "sim/host.hh"'] 629 def __init__(self, value): 630 self.value = 1 / getLatency(value) 631 632 def __getattr__(self, attr): 633 if attr == 'frequency': 634 return self 635 if attr in ('latency', 'period'): 636 return Latency(self) 637 raise AttributeError, "Frequency object has no attribute '%s'" % attr 638 639 # convert frequency to ticks per period 640 def ini_str(self): 641 return self.period.ini_str() 642 643# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). 644# We can't inherit from Frequency because we don't want it to be directly 645# assignable to a regular Frequency parameter. 646class RootClock(ParamValue): 647 cxx_type = 'Tick' 648 cxx_predecls = ['#include "sim/host.hh"'] 649 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 650 '%import "sim/host.hh"'] 651 def __init__(self, value): 652 self.value = 1 / getLatency(value) 653 654 def __getattr__(self, attr): 655 if attr == 'frequency': 656 return Frequency(self) 657 if attr in ('latency', 'period'): 658 return Latency(self) 659 raise AttributeError, "Frequency object has no attribute '%s'" % attr 660 661 def ini_str(self): 662 return str(tick_check(self.value)) 663 664# A generic frequency and/or Latency value. Value is stored as a latency, 665# but to avoid ambiguity this object does not support numeric ops (* or /). 666# An explicit conversion to a Latency or Frequency must be made first. 667class Clock(ParamValue): 668 cxx_type = 'Tick' 669 cxx_predecls = ['#include "sim/host.hh"'] 670 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 671 '%import "sim/host.hh"'] 672 def __init__(self, value): 673 self.value = getLatency(value) 674 675 def __getattr__(self, attr): 676 if attr == 'frequency': 677 return Frequency(self) 678 if attr in ('latency', 'period'): 679 return Latency(self) 680 raise AttributeError, "Frequency object has no attribute '%s'" % attr 681 682 def ini_str(self): 683 return self.period.ini_str() 684 685class NetworkBandwidth(float,ParamValue): 686 cxx_type = 'float' 687 def __new__(cls, value): 688 val = convert.toNetworkBandwidth(value) / 8.0 689 return super(cls, NetworkBandwidth).__new__(cls, val) 690 691 def __str__(self): 692 return str(self.val) 693 694 def ini_str(self): 695 return '%f' % (ticks_per_sec / float(self)) 696 697class MemoryBandwidth(float,ParamValue): 698 cxx_type = 'float' 699 def __new__(self, value): 700 val = convert.toMemoryBandwidth(value) 701 return super(cls, MemoryBandwidth).__new__(cls, val) 702 703 def __str__(self): 704 return str(self.val) 705 706 def ini_str(self): 707 return '%f' % (ticks_per_sec / float(self)) 708 709# 710# "Constants"... handy aliases for various values. 711# 712 713# Special class for NULL pointers. Note the special check in 714# make_param_value() above that lets these be assigned where a 715# SimObject is required. 716# only one copy of a particular node 717class NullSimObject(object): 718 __metaclass__ = Singleton 719 720 def __call__(cls): 721 return cls 722 723 def _instantiate(self, parent = None, path = ''): 724 pass 725 726 def ini_str(self): 727 return 'Null' 728 729 def unproxy(self, base): 730 return self 731 732 def set_path(self, parent, name): 733 pass 734 def __str__(self): 735 return 'Null' 736 737# The only instance you'll ever need... 738NULL = NullSimObject() 739 740def isNullPointer(value): 741 return isinstance(value, NullSimObject) 742 743# Some memory range specifications use this as a default upper bound. 744MaxAddr = Addr.max 745MaxTick = Tick.max 746AllMemory = AddrRange(0, MaxAddr) 747 748 749##################################################################### 750# 751# Port objects 752# 753# Ports are used to interconnect objects in the memory system. 754# 755##################################################################### 756 757# Port reference: encapsulates a reference to a particular port on a 758# particular SimObject. 759class PortRef(object): 760 def __init__(self, simobj, name): 761 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 762 self.simobj = simobj 763 self.name = name 764 self.peer = None # not associated with another port yet 765 self.ccConnected = False # C++ port connection done? 766 self.index = -1 # always -1 for non-vector ports 767 768 def __str__(self): 769 return '%s.%s' % (self.simobj, self.name) 770 771 # for config.ini, print peer's name (not ours) 772 def ini_str(self): 773 return str(self.peer) 774 775 def __getattr__(self, attr): 776 if attr == 'peerObj': 777 # shorthand for proxies 778 return self.peer.simobj 779 raise AttributeError, "'%s' object has no attribute '%s'" % \ 780 (self.__class__.__name__, attr) 781 782 # Full connection is symmetric (both ways). Called via 783 # SimObject.__setattr__ as a result of a port assignment, e.g., 784 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 785 # e.g., "obj1.portA[3] = obj2.portB". 786 def connect(self, other): 787 if isinstance(other, VectorPortRef): 788 # reference to plain VectorPort is implicit append 789 other = other._get_next() 790 if self.peer and not proxy.isproxy(self.peer): 791 print "warning: overwriting port", self, \ 792 "value", self.peer, "with", other 793 self.peer = other 794 if proxy.isproxy(other): 795 other.set_param_desc(PortParamDesc()) 796 elif isinstance(other, PortRef): 797 if other.peer is not self: 798 other.connect(self) 799 else: 800 raise TypeError, \ 801 "assigning non-port reference '%s' to port '%s'" \ 802 % (other, self) 803 804 def clone(self, simobj, memo): 805 if memo.has_key(self): 806 return memo[self] 807 newRef = copy.copy(self) 808 memo[self] = newRef 809 newRef.simobj = simobj 810 assert(isSimObject(newRef.simobj)) 811 if self.peer and not proxy.isproxy(self.peer): 812 peerObj = self.peer.simobj(_memo=memo) 813 newRef.peer = self.peer.clone(peerObj, memo) 814 assert(not isinstance(newRef.peer, VectorPortRef)) 815 return newRef 816 817 def unproxy(self, simobj): 818 assert(simobj is self.simobj) 819 if proxy.isproxy(self.peer): 820 try: 821 realPeer = self.peer.unproxy(self.simobj) 822 except: 823 print "Error in unproxying port '%s' of %s" % \ 824 (self.name, self.simobj.path()) 825 raise 826 self.connect(realPeer) 827 828 # Call C++ to create corresponding port connection between C++ objects 829 def ccConnect(self): 830 if self.ccConnected: # already done this 831 return 832 peer = self.peer 833 internal.main.connectPorts(self.simobj.getCCObject(), self.name, 834 self.index, peer.simobj.getCCObject(), 835 peer.name, peer.index) 836 self.ccConnected = True 837 peer.ccConnected = True 838 839# A reference to an individual element of a VectorPort... much like a 840# PortRef, but has an index. 841class VectorPortElementRef(PortRef): 842 def __init__(self, simobj, name, index): 843 PortRef.__init__(self, simobj, name) 844 self.index = index 845 846 def __str__(self): 847 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 848 849# A reference to a complete vector-valued port (not just a single element). 850# Can be indexed to retrieve individual VectorPortElementRef instances. 851class VectorPortRef(object): 852 def __init__(self, simobj, name): 853 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 854 self.simobj = simobj 855 self.name = name 856 self.elements = [] 857 858 def __str__(self): 859 return '%s.%s[:]' % (self.simobj, self.name) 860 861 # for config.ini, print peer's name (not ours) 862 def ini_str(self): 863 return ' '.join([el.ini_str() for el in self.elements]) 864 865 def __getitem__(self, key): 866 if not isinstance(key, int): 867 raise TypeError, "VectorPort index must be integer" 868 if key >= len(self.elements): 869 # need to extend list 870 ext = [VectorPortElementRef(self.simobj, self.name, i) 871 for i in range(len(self.elements), key+1)] 872 self.elements.extend(ext) 873 return self.elements[key] 874 875 def _get_next(self): 876 return self[len(self.elements)] 877 878 def __setitem__(self, key, value): 879 if not isinstance(key, int): 880 raise TypeError, "VectorPort index must be integer" 881 self[key].connect(value) 882 883 def connect(self, other): 884 if isinstance(other, (list, tuple)): 885 # Assign list of port refs to vector port. 886 # For now, append them... not sure if that's the right semantics 887 # or if it should replace the current vector. 888 for ref in other: 889 self._get_next().connect(ref) 890 else: 891 # scalar assignment to plain VectorPort is implicit append 892 self._get_next().connect(other) 893 894 def clone(self, simobj, memo): 895 if memo.has_key(self): 896 return memo[self] 897 newRef = copy.copy(self) 898 memo[self] = newRef 899 newRef.simobj = simobj 900 assert(isSimObject(newRef.simobj)) 901 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 902 return newRef 903 904 def unproxy(self, simobj): 905 [el.unproxy(simobj) for el in self.elements] 906 907 def ccConnect(self): 908 [el.ccConnect() for el in self.elements] 909 910# Port description object. Like a ParamDesc object, this represents a 911# logical port in the SimObject class, not a particular port on a 912# SimObject instance. The latter are represented by PortRef objects. 913class Port(object): 914 # Port("description") or Port(default, "description") 915 def __init__(self, *args): 916 if len(args) == 1: 917 self.desc = args[0] 918 elif len(args) == 2: 919 self.default = args[0] 920 self.desc = args[1] 921 else: 922 raise TypeError, 'wrong number of arguments' 923 # self.name is set by SimObject class on assignment 924 # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 925 926 # Generate a PortRef for this port on the given SimObject with the 927 # given name 928 def makeRef(self, simobj): 929 return PortRef(simobj, self.name) 930 931 # Connect an instance of this port (on the given SimObject with 932 # the given name) with the port described by the supplied PortRef 933 def connect(self, simobj, ref): 934 self.makeRef(simobj).connect(ref) 935 936# VectorPort description object. Like Port, but represents a vector 937# of connections (e.g., as on a Bus). 938class VectorPort(Port): 939 def __init__(self, *args): 940 Port.__init__(self, *args) 941 self.isVec = True 942 943 def makeRef(self, simobj): 944 return VectorPortRef(simobj, self.name) 945 946# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 947# proxy objects (via set_param_desc()) so that proxy error messages 948# make sense. 949class PortParamDesc(object): 950 __metaclass__ = Singleton 951 952 ptype_str = 'Port' 953 ptype = Port 954 955 956__all__ = ['Param', 'VectorParam', 957 'Enum', 'Bool', 'String', 'Float', 958 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 959 'Int32', 'UInt32', 'Int64', 'UInt64', 960 'Counter', 'Addr', 'Tick', 'Percent', 961 'TcpPort', 'UdpPort', 'EthernetAddr', 962 'MemorySize', 'MemorySize32', 963 'Latency', 'Frequency', 'RootClock', 'Clock', 964 'NetworkBandwidth', 'MemoryBandwidth', 965 'Range', 'AddrRange', 'TickRange', 966 'MaxAddr', 'MaxTick', 'AllMemory', 967 'NextEthernetAddr', 'NULL', 968 'Port', 'VectorPort'] 969 970# see comment on imports at end of __init__.py. 971from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass 972import proxy 973import objects 974import internal 975