params.py revision 3714
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 def __long__(self): 241 return long(self.value) 242 243 def __int__(self): 244 return int(self.value) 245 246 # hook for bounds checking 247 def _check(self): 248 return 249 250 def __mul__(self, other): 251 newobj = self.__class__(self) 252 newobj.value *= other 253 newobj._check() 254 return newobj 255 256 __rmul__ = __mul__ 257 258 def __div__(self, other): 259 newobj = self.__class__(self) 260 newobj.value /= other 261 newobj._check() 262 return newobj 263 264 def __sub__(self, other): 265 newobj = self.__class__(self) 266 newobj.value -= other 267 newobj._check() 268 return newobj 269 270# Metaclass for bounds-checked integer parameters. See CheckedInt. 271class CheckedIntType(type): 272 def __init__(cls, name, bases, dict): 273 super(CheckedIntType, cls).__init__(name, bases, dict) 274 275 # CheckedInt is an abstract base class, so we actually don't 276 # want to do any processing on it... the rest of this code is 277 # just for classes that derive from CheckedInt. 278 if name == 'CheckedInt': 279 return 280 281 if not cls.cxx_predecls: 282 # most derived types require this, so we just do it here once 283 cls.cxx_predecls = ['#include "sim/host.hh"'] 284 285 if not cls.swig_predecls: 286 # most derived types require this, so we just do it here once 287 cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 288 '%import "sim/host.hh"'] 289 290 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 291 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 292 panic("CheckedInt subclass %s must define either\n" \ 293 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 294 % name); 295 if cls.unsigned: 296 cls.min = 0 297 cls.max = 2 ** cls.size - 1 298 else: 299 cls.min = -(2 ** (cls.size - 1)) 300 cls.max = (2 ** (cls.size - 1)) - 1 301 302# Abstract superclass for bounds-checked integer parameters. This 303# class is subclassed to generate parameter classes with specific 304# bounds. Initialization of the min and max bounds is done in the 305# metaclass CheckedIntType.__init__. 306class CheckedInt(NumericParamValue): 307 __metaclass__ = CheckedIntType 308 309 def _check(self): 310 if not self.min <= self.value <= self.max: 311 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 312 (self.min, self.value, self.max) 313 314 def __init__(self, value): 315 if isinstance(value, str): 316 self.value = convert.toInteger(value) 317 elif isinstance(value, (int, long, float, NumericParamValue)): 318 self.value = long(value) 319 else: 320 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 321 % type(value).__name__ 322 self._check() 323 324class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 325class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 326 327class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 328class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 329class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 330class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 331class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 332class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 333class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 334class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 335 336class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 337class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 338class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 339class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 340 341class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 342 343class Float(ParamValue, float): 344 pass 345 346class MemorySize(CheckedInt): 347 cxx_type = 'uint64_t' 348 size = 64 349 unsigned = True 350 def __init__(self, value): 351 if isinstance(value, MemorySize): 352 self.value = value.value 353 else: 354 self.value = convert.toMemorySize(value) 355 self._check() 356 357class MemorySize32(CheckedInt): 358 size = 32 359 unsigned = True 360 def __init__(self, value): 361 if isinstance(value, MemorySize): 362 self.value = value.value 363 else: 364 self.value = convert.toMemorySize(value) 365 self._check() 366 367class Addr(CheckedInt): 368 cxx_type = 'Addr' 369 cxx_predecls = ['#include "targetarch/isa_traits.hh"'] 370 size = 64 371 unsigned = True 372 def __init__(self, value): 373 if isinstance(value, Addr): 374 self.value = value.value 375 else: 376 try: 377 self.value = convert.toMemorySize(value) 378 except TypeError: 379 self.value = long(value) 380 self._check() 381 def __add__(self, other): 382 if isinstance(other, Addr): 383 return self.value + other.value 384 else: 385 return self.value + other 386 387 388class MetaRange(type): 389 def __init__(cls, name, bases, dict): 390 super(MetaRange, cls).__init__(name, bases, dict) 391 if name == 'Range': 392 return 393 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 394 cls.cxx_predecls = \ 395 ['#include "base/range.hh"'] + cls.type.cxx_predecls 396 397class Range(ParamValue): 398 __metaclass__ = MetaRange 399 type = Int # default; can be overridden in subclasses 400 def __init__(self, *args, **kwargs): 401 def handle_kwargs(self, kwargs): 402 if 'end' in kwargs: 403 self.second = self.type(kwargs.pop('end')) 404 elif 'size' in kwargs: 405 self.second = self.first + self.type(kwargs.pop('size')) - 1 406 else: 407 raise TypeError, "Either end or size must be specified" 408 409 if len(args) == 0: 410 self.first = self.type(kwargs.pop('start')) 411 handle_kwargs(self, kwargs) 412 413 elif len(args) == 1: 414 if kwargs: 415 self.first = self.type(args[0]) 416 handle_kwargs(self, kwargs) 417 elif isinstance(args[0], Range): 418 self.first = self.type(args[0].first) 419 self.second = self.type(args[0].second) 420 else: 421 self.first = self.type(0) 422 self.second = self.type(args[0]) - 1 423 424 elif len(args) == 2: 425 self.first = self.type(args[0]) 426 self.second = self.type(args[1]) 427 else: 428 raise TypeError, "Too many arguments specified" 429 430 if kwargs: 431 raise TypeError, "too many keywords: %s" % kwargs.keys() 432 433 def __str__(self): 434 return '%s:%s' % (self.first, self.second) 435 436class AddrRange(Range): 437 type = Addr 438 439class TickRange(Range): 440 type = Tick 441 442# Boolean parameter type. Python doesn't let you subclass bool, since 443# it doesn't want to let you create multiple instances of True and 444# False. Thus this is a little more complicated than String. 445class Bool(ParamValue): 446 cxx_type = 'bool' 447 def __init__(self, value): 448 try: 449 self.value = convert.toBool(value) 450 except TypeError: 451 self.value = bool(value) 452 453 def __str__(self): 454 return str(self.value) 455 456 def ini_str(self): 457 if self.value: 458 return 'true' 459 return 'false' 460 461def IncEthernetAddr(addr, val = 1): 462 bytes = map(lambda x: int(x, 16), addr.split(':')) 463 bytes[5] += val 464 for i in (5, 4, 3, 2, 1): 465 val,rem = divmod(bytes[i], 256) 466 bytes[i] = rem 467 if val == 0: 468 break 469 bytes[i - 1] += val 470 assert(bytes[0] <= 255) 471 return ':'.join(map(lambda x: '%02x' % x, bytes)) 472 473class NextEthernetAddr(object): 474 addr = "00:90:00:00:00:01" 475 476 def __init__(self, inc = 1): 477 self.value = NextEthernetAddr.addr 478 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) 479 480class EthernetAddr(ParamValue): 481 cxx_type = 'Net::EthAddr' 482 cxx_predecls = ['#include "base/inet.hh"'] 483 swig_predecls = ['class Net::EthAddr;'] 484 def __init__(self, value): 485 if value == NextEthernetAddr: 486 self.value = value 487 return 488 489 if not isinstance(value, str): 490 raise TypeError, "expected an ethernet address and didn't get one" 491 492 bytes = value.split(':') 493 if len(bytes) != 6: 494 raise TypeError, 'invalid ethernet address %s' % value 495 496 for byte in bytes: 497 if not 0 <= int(byte) <= 256: 498 raise TypeError, 'invalid ethernet address %s' % value 499 500 self.value = value 501 502 def unproxy(self, base): 503 if self.value == NextEthernetAddr: 504 self.addr = self.value().value 505 return self 506 507 def __str__(self): 508 if self.value == NextEthernetAddr: 509 if hasattr(self, 'addr'): 510 return self.addr 511 else: 512 return "NextEthernetAddr (unresolved)" 513 else: 514 return self.value 515 516# Enumerated types are a little more complex. The user specifies the 517# type as Enum(foo) where foo is either a list or dictionary of 518# alternatives (typically strings, but not necessarily so). (In the 519# long run, the integer value of the parameter will be the list index 520# or the corresponding dictionary value. For now, since we only check 521# that the alternative is valid and then spit it into a .ini file, 522# there's not much point in using the dictionary.) 523 524# What Enum() must do is generate a new type encapsulating the 525# provided list/dictionary so that specific values of the parameter 526# can be instances of that type. We define two hidden internal 527# classes (_ListEnum and _DictEnum) to serve as base classes, then 528# derive the new type from the appropriate base class on the fly. 529 530 531# Metaclass for Enum types 532class MetaEnum(type): 533 def __init__(cls, name, bases, init_dict): 534 if init_dict.has_key('map'): 535 if not isinstance(cls.map, dict): 536 raise TypeError, "Enum-derived class attribute 'map' " \ 537 "must be of type dict" 538 # build list of value strings from map 539 cls.vals = cls.map.keys() 540 cls.vals.sort() 541 elif init_dict.has_key('vals'): 542 if not isinstance(cls.vals, list): 543 raise TypeError, "Enum-derived class attribute 'vals' " \ 544 "must be of type list" 545 # build string->value map from vals sequence 546 cls.map = {} 547 for idx,val in enumerate(cls.vals): 548 cls.map[val] = idx 549 else: 550 raise TypeError, "Enum-derived class must define "\ 551 "attribute 'map' or 'vals'" 552 553 cls.cxx_type = name + '::Enum' 554 555 super(MetaEnum, cls).__init__(name, bases, init_dict) 556 557 # Generate C++ class declaration for this enum type. 558 # Note that we wrap the enum in a class/struct to act as a namespace, 559 # so that the enum strings can be brief w/o worrying about collisions. 560 def cxx_decl(cls): 561 s = 'struct %s {\n enum Enum {\n ' % cls.__name__ 562 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 563 s += '\n };\n};\n' 564 return s 565 566# Base class for enum types. 567class Enum(ParamValue): 568 __metaclass__ = MetaEnum 569 vals = [] 570 571 def __init__(self, value): 572 if value not in self.map: 573 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 574 % (value, self.vals) 575 self.value = value 576 577 def __str__(self): 578 return self.value 579 580ticks_per_sec = None 581 582# how big does a rounding error need to be before we warn about it? 583frequency_tolerance = 0.001 # 0.1% 584 585# convert a floting-point # of ticks to integer, and warn if rounding 586# discards too much precision 587def tick_check(float_ticks): 588 if float_ticks == 0: 589 return 0 590 int_ticks = int(round(float_ticks)) 591 err = (float_ticks - int_ticks) / float_ticks 592 if err > frequency_tolerance: 593 print >> sys.stderr, "Warning: rounding error > tolerance" 594 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) 595 #raise ValueError 596 return int_ticks 597 598def getLatency(value): 599 if isinstance(value, Latency) or isinstance(value, Clock): 600 return value.value 601 elif isinstance(value, Frequency) or isinstance(value, RootClock): 602 return 1 / value.value 603 elif isinstance(value, str): 604 try: 605 return convert.toLatency(value) 606 except ValueError: 607 try: 608 return 1 / convert.toFrequency(value) 609 except ValueError: 610 pass # fall through 611 raise ValueError, "Invalid Frequency/Latency value '%s'" % value 612 613 614class Latency(NumericParamValue): 615 cxx_type = 'Tick' 616 cxx_predecls = ['#include "sim/host.hh"'] 617 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 618 '%import "sim/host.hh"'] 619 def __init__(self, value): 620 self.value = getLatency(value) 621 622 def __getattr__(self, attr): 623 if attr in ('latency', 'period'): 624 return self 625 if attr == 'frequency': 626 return Frequency(self) 627 raise AttributeError, "Latency object has no attribute '%s'" % attr 628 629 # convert latency to ticks 630 def ini_str(self): 631 return str(tick_check(self.value * ticks_per_sec)) 632 633class Frequency(NumericParamValue): 634 cxx_type = 'Tick' 635 cxx_predecls = ['#include "sim/host.hh"'] 636 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 637 '%import "sim/host.hh"'] 638 def __init__(self, value): 639 self.value = 1 / getLatency(value) 640 641 def __getattr__(self, attr): 642 if attr == 'frequency': 643 return self 644 if attr in ('latency', 'period'): 645 return Latency(self) 646 raise AttributeError, "Frequency object has no attribute '%s'" % attr 647 648 # convert frequency to ticks per period 649 def ini_str(self): 650 return self.period.ini_str() 651 652# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). 653# We can't inherit from Frequency because we don't want it to be directly 654# assignable to a regular Frequency parameter. 655class RootClock(ParamValue): 656 cxx_type = 'Tick' 657 cxx_predecls = ['#include "sim/host.hh"'] 658 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 659 '%import "sim/host.hh"'] 660 def __init__(self, value): 661 self.value = 1 / getLatency(value) 662 663 def __getattr__(self, attr): 664 if attr == 'frequency': 665 return Frequency(self) 666 if attr in ('latency', 'period'): 667 return Latency(self) 668 raise AttributeError, "Frequency object has no attribute '%s'" % attr 669 670 def ini_str(self): 671 return str(tick_check(self.value)) 672 673# A generic frequency and/or Latency value. Value is stored as a latency, 674# but to avoid ambiguity this object does not support numeric ops (* or /). 675# An explicit conversion to a Latency or Frequency must be made first. 676class Clock(ParamValue): 677 cxx_type = 'Tick' 678 cxx_predecls = ['#include "sim/host.hh"'] 679 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 680 '%import "sim/host.hh"'] 681 def __init__(self, value): 682 self.value = getLatency(value) 683 684 def __getattr__(self, attr): 685 if attr == 'frequency': 686 return Frequency(self) 687 if attr in ('latency', 'period'): 688 return Latency(self) 689 raise AttributeError, "Frequency object has no attribute '%s'" % attr 690 691 def ini_str(self): 692 return self.period.ini_str() 693 694class NetworkBandwidth(float,ParamValue): 695 cxx_type = 'float' 696 def __new__(cls, value): 697 val = convert.toNetworkBandwidth(value) / 8.0 698 return super(cls, NetworkBandwidth).__new__(cls, val) 699 700 def __str__(self): 701 return str(self.val) 702 703 def ini_str(self): 704 return '%f' % (ticks_per_sec / float(self)) 705 706class MemoryBandwidth(float,ParamValue): 707 cxx_type = 'float' 708 def __new__(self, value): 709 val = convert.toMemoryBandwidth(value) 710 return super(cls, MemoryBandwidth).__new__(cls, val) 711 712 def __str__(self): 713 return str(self.val) 714 715 def ini_str(self): 716 return '%f' % (ticks_per_sec / float(self)) 717 718# 719# "Constants"... handy aliases for various values. 720# 721 722# Special class for NULL pointers. Note the special check in 723# make_param_value() above that lets these be assigned where a 724# SimObject is required. 725# only one copy of a particular node 726class NullSimObject(object): 727 __metaclass__ = Singleton 728 729 def __call__(cls): 730 return cls 731 732 def _instantiate(self, parent = None, path = ''): 733 pass 734 735 def ini_str(self): 736 return 'Null' 737 738 def unproxy(self, base): 739 return self 740 741 def set_path(self, parent, name): 742 pass 743 def __str__(self): 744 return 'Null' 745 746# The only instance you'll ever need... 747NULL = NullSimObject() 748 749def isNullPointer(value): 750 return isinstance(value, NullSimObject) 751 752# Some memory range specifications use this as a default upper bound. 753MaxAddr = Addr.max 754MaxTick = Tick.max 755AllMemory = AddrRange(0, MaxAddr) 756 757 758##################################################################### 759# 760# Port objects 761# 762# Ports are used to interconnect objects in the memory system. 763# 764##################################################################### 765 766# Port reference: encapsulates a reference to a particular port on a 767# particular SimObject. 768class PortRef(object): 769 def __init__(self, simobj, name): 770 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 771 self.simobj = simobj 772 self.name = name 773 self.peer = None # not associated with another port yet 774 self.ccConnected = False # C++ port connection done? 775 self.index = -1 # always -1 for non-vector ports 776 777 def __str__(self): 778 return '%s.%s' % (self.simobj, self.name) 779 780 # for config.ini, print peer's name (not ours) 781 def ini_str(self): 782 return str(self.peer) 783 784 def __getattr__(self, attr): 785 if attr == 'peerObj': 786 # shorthand for proxies 787 return self.peer.simobj 788 raise AttributeError, "'%s' object has no attribute '%s'" % \ 789 (self.__class__.__name__, attr) 790 791 # Full connection is symmetric (both ways). Called via 792 # SimObject.__setattr__ as a result of a port assignment, e.g., 793 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 794 # e.g., "obj1.portA[3] = obj2.portB". 795 def connect(self, other): 796 if isinstance(other, VectorPortRef): 797 # reference to plain VectorPort is implicit append 798 other = other._get_next() 799 if self.peer and not proxy.isproxy(self.peer): 800 print "warning: overwriting port", self, \ 801 "value", self.peer, "with", other 802 self.peer = other 803 if proxy.isproxy(other): 804 other.set_param_desc(PortParamDesc()) 805 elif isinstance(other, PortRef): 806 if other.peer is not self: 807 other.connect(self) 808 else: 809 raise TypeError, \ 810 "assigning non-port reference '%s' to port '%s'" \ 811 % (other, self) 812 813 def clone(self, simobj, memo): 814 if memo.has_key(self): 815 return memo[self] 816 newRef = copy.copy(self) 817 memo[self] = newRef 818 newRef.simobj = simobj 819 assert(isSimObject(newRef.simobj)) 820 if self.peer and not proxy.isproxy(self.peer): 821 peerObj = self.peer.simobj(_memo=memo) 822 newRef.peer = self.peer.clone(peerObj, memo) 823 assert(not isinstance(newRef.peer, VectorPortRef)) 824 return newRef 825 826 def unproxy(self, simobj): 827 assert(simobj is self.simobj) 828 if proxy.isproxy(self.peer): 829 try: 830 realPeer = self.peer.unproxy(self.simobj) 831 except: 832 print "Error in unproxying port '%s' of %s" % \ 833 (self.name, self.simobj.path()) 834 raise 835 self.connect(realPeer) 836 837 # Call C++ to create corresponding port connection between C++ objects 838 def ccConnect(self): 839 if self.ccConnected: # already done this 840 return 841 peer = self.peer 842 internal.main.connectPorts(self.simobj.getCCObject(), self.name, 843 self.index, peer.simobj.getCCObject(), 844 peer.name, peer.index) 845 self.ccConnected = True 846 peer.ccConnected = True 847 848# A reference to an individual element of a VectorPort... much like a 849# PortRef, but has an index. 850class VectorPortElementRef(PortRef): 851 def __init__(self, simobj, name, index): 852 PortRef.__init__(self, simobj, name) 853 self.index = index 854 855 def __str__(self): 856 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 857 858# A reference to a complete vector-valued port (not just a single element). 859# Can be indexed to retrieve individual VectorPortElementRef instances. 860class VectorPortRef(object): 861 def __init__(self, simobj, name): 862 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 863 self.simobj = simobj 864 self.name = name 865 self.elements = [] 866 867 def __str__(self): 868 return '%s.%s[:]' % (self.simobj, self.name) 869 870 # for config.ini, print peer's name (not ours) 871 def ini_str(self): 872 return ' '.join([el.ini_str() for el in self.elements]) 873 874 def __getitem__(self, key): 875 if not isinstance(key, int): 876 raise TypeError, "VectorPort index must be integer" 877 if key >= len(self.elements): 878 # need to extend list 879 ext = [VectorPortElementRef(self.simobj, self.name, i) 880 for i in range(len(self.elements), key+1)] 881 self.elements.extend(ext) 882 return self.elements[key] 883 884 def _get_next(self): 885 return self[len(self.elements)] 886 887 def __setitem__(self, key, value): 888 if not isinstance(key, int): 889 raise TypeError, "VectorPort index must be integer" 890 self[key].connect(value) 891 892 def connect(self, other): 893 if isinstance(other, (list, tuple)): 894 # Assign list of port refs to vector port. 895 # For now, append them... not sure if that's the right semantics 896 # or if it should replace the current vector. 897 for ref in other: 898 self._get_next().connect(ref) 899 else: 900 # scalar assignment to plain VectorPort is implicit append 901 self._get_next().connect(other) 902 903 def clone(self, simobj, memo): 904 if memo.has_key(self): 905 return memo[self] 906 newRef = copy.copy(self) 907 memo[self] = newRef 908 newRef.simobj = simobj 909 assert(isSimObject(newRef.simobj)) 910 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 911 return newRef 912 913 def unproxy(self, simobj): 914 [el.unproxy(simobj) for el in self.elements] 915 916 def ccConnect(self): 917 [el.ccConnect() for el in self.elements] 918 919# Port description object. Like a ParamDesc object, this represents a 920# logical port in the SimObject class, not a particular port on a 921# SimObject instance. The latter are represented by PortRef objects. 922class Port(object): 923 # Port("description") or Port(default, "description") 924 def __init__(self, *args): 925 if len(args) == 1: 926 self.desc = args[0] 927 elif len(args) == 2: 928 self.default = args[0] 929 self.desc = args[1] 930 else: 931 raise TypeError, 'wrong number of arguments' 932 # self.name is set by SimObject class on assignment 933 # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 934 935 # Generate a PortRef for this port on the given SimObject with the 936 # given name 937 def makeRef(self, simobj): 938 return PortRef(simobj, self.name) 939 940 # Connect an instance of this port (on the given SimObject with 941 # the given name) with the port described by the supplied PortRef 942 def connect(self, simobj, ref): 943 self.makeRef(simobj).connect(ref) 944 945# VectorPort description object. Like Port, but represents a vector 946# of connections (e.g., as on a Bus). 947class VectorPort(Port): 948 def __init__(self, *args): 949 Port.__init__(self, *args) 950 self.isVec = True 951 952 def makeRef(self, simobj): 953 return VectorPortRef(simobj, self.name) 954 955# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 956# proxy objects (via set_param_desc()) so that proxy error messages 957# make sense. 958class PortParamDesc(object): 959 __metaclass__ = Singleton 960 961 ptype_str = 'Port' 962 ptype = Port 963 964 965__all__ = ['Param', 'VectorParam', 966 'Enum', 'Bool', 'String', 'Float', 967 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 968 'Int32', 'UInt32', 'Int64', 'UInt64', 969 'Counter', 'Addr', 'Tick', 'Percent', 970 'TcpPort', 'UdpPort', 'EthernetAddr', 971 'MemorySize', 'MemorySize32', 972 'Latency', 'Frequency', 'RootClock', 'Clock', 973 'NetworkBandwidth', 'MemoryBandwidth', 974 'Range', 'AddrRange', 'TickRange', 975 'MaxAddr', 'MaxTick', 'AllMemory', 976 'NextEthernetAddr', 'NULL', 977 'Port', 'VectorPort'] 978 979# see comment on imports at end of __init__.py. 980from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass 981import proxy 982import objects 983import internal 984