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