params.py revision 3101
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 49 50# Dummy base class to identify types that are legitimate for SimObject 51# parameters. 52class ParamValue(object): 53 54 cxx_predecls = [] 55 swig_predecls = [] 56 57 # default for printing to .ini file is regular string conversion. 58 # will be overridden in some cases 59 def ini_str(self): 60 return str(self) 61 62 # allows us to blithely call unproxy() on things without checking 63 # if they're really proxies or not 64 def unproxy(self, base): 65 return self 66 67# Regular parameter description. 68class ParamDesc(object): 69 def __init__(self, ptype_str, ptype, *args, **kwargs): 70 self.ptype_str = ptype_str 71 # remember ptype only if it is provided 72 if ptype != None: 73 self.ptype = ptype 74 75 if args: 76 if len(args) == 1: 77 self.desc = args[0] 78 elif len(args) == 2: 79 self.default = args[0] 80 self.desc = args[1] 81 else: 82 raise TypeError, 'too many arguments' 83 84 if kwargs.has_key('desc'): 85 assert(not hasattr(self, 'desc')) 86 self.desc = kwargs['desc'] 87 del kwargs['desc'] 88 89 if kwargs.has_key('default'): 90 assert(not hasattr(self, 'default')) 91 self.default = kwargs['default'] 92 del kwargs['default'] 93 94 if kwargs: 95 raise TypeError, 'extra unknown kwargs %s' % kwargs 96 97 if not hasattr(self, 'desc'): 98 raise TypeError, 'desc attribute missing' 99 100 def __getattr__(self, attr): 101 if attr == 'ptype': 102 try: 103 ptype = eval(self.ptype_str, m5.objects.__dict__) 104 if not isinstance(ptype, type): 105 panic("Param qualifier is not a type: %s" % self.ptype) 106 self.ptype = ptype 107 return ptype 108 except NameError: 109 pass 110 raise AttributeError, "'%s' object has no attribute '%s'" % \ 111 (type(self).__name__, attr) 112 113 def convert(self, value): 114 if isinstance(value, proxy.BaseProxy): 115 value.set_param_desc(self) 116 return value 117 if not hasattr(self, 'ptype') and isNullPointer(value): 118 # deferred evaluation of SimObject; continue to defer if 119 # we're just assigning a null pointer 120 return value 121 if isinstance(value, self.ptype): 122 return value 123 if isNullPointer(value) and issubclass(self.ptype, SimObject): 124 return value 125 return self.ptype(value) 126 127 def cxx_predecls(self): 128 return self.ptype.cxx_predecls 129 130 def swig_predecls(self): 131 return self.ptype.swig_predecls 132 133 def cxx_decl(self): 134 return '%s %s;' % (self.ptype.cxx_type, self.name) 135 136# Vector-valued parameter description. Just like ParamDesc, except 137# that the value is a vector (list) of the specified type instead of a 138# single value. 139 140class VectorParamValue(list): 141 def ini_str(self): 142 return ' '.join([v.ini_str() for v in self]) 143 144 def unproxy(self, base): 145 return [v.unproxy(base) for v in self] 146 147class SimObjVector(VectorParamValue): 148 def print_ini(self): 149 for v in self: 150 v.print_ini() 151 152class VectorParamDesc(ParamDesc): 153 # Convert assigned value to appropriate type. If the RHS is not a 154 # list or tuple, it generates a single-element list. 155 def convert(self, value): 156 if isinstance(value, (list, tuple)): 157 # list: coerce each element into new list 158 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 159 if isSimObjectSequence(tmp_list): 160 return SimObjVector(tmp_list) 161 else: 162 return VectorParamValue(tmp_list) 163 else: 164 # singleton: leave it be (could coerce to a single-element 165 # list here, but for some historical reason we don't... 166 return ParamDesc.convert(self, value) 167 168 def cxx_predecls(self): 169 return ['#include <vector>'] + self.ptype.cxx_predecls 170 171 def swig_predecls(self): 172 return ['%include "std_vector.i"'] + self.ptype.swig_predecls 173 174 def cxx_decl(self): 175 return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) 176 177class ParamFactory(object): 178 def __init__(self, param_desc_class, ptype_str = None): 179 self.param_desc_class = param_desc_class 180 self.ptype_str = ptype_str 181 182 def __getattr__(self, attr): 183 if self.ptype_str: 184 attr = self.ptype_str + '.' + attr 185 return ParamFactory(self.param_desc_class, attr) 186 187 # E.g., Param.Int(5, "number of widgets") 188 def __call__(self, *args, **kwargs): 189 caller_frame = inspect.currentframe().f_back 190 ptype = None 191 try: 192 ptype = eval(self.ptype_str, 193 caller_frame.f_globals, caller_frame.f_locals) 194 if not isinstance(ptype, type): 195 raise TypeError, \ 196 "Param qualifier is not a type: %s" % ptype 197 except NameError: 198 # if name isn't defined yet, assume it's a SimObject, and 199 # try to resolve it later 200 pass 201 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 202 203Param = ParamFactory(ParamDesc) 204VectorParam = ParamFactory(VectorParamDesc) 205 206##################################################################### 207# 208# Parameter Types 209# 210# Though native Python types could be used to specify parameter types 211# (the 'ptype' field of the Param and VectorParam classes), it's more 212# flexible to define our own set of types. This gives us more control 213# over how Python expressions are converted to values (via the 214# __init__() constructor) and how these values are printed out (via 215# the __str__() conversion method). 216# 217##################################################################### 218 219# String-valued parameter. Just mixin the ParamValue class with the 220# built-in str class. 221class String(ParamValue,str): 222 cxx_type = 'std::string' 223 cxx_predecls = ['#include <string>'] 224 swig_predecls = ['%include "std_string.i"\n' + 225 '%apply const std::string& {std::string *};'] 226 pass 227 228# superclass for "numeric" parameter values, to emulate math 229# operations in a type-safe way. e.g., a Latency times an int returns 230# a new Latency object. 231class NumericParamValue(ParamValue): 232 def __str__(self): 233 return str(self.value) 234 235 def __float__(self): 236 return float(self.value) 237 238 # hook for bounds checking 239 def _check(self): 240 return 241 242 def __mul__(self, other): 243 newobj = self.__class__(self) 244 newobj.value *= other 245 newobj._check() 246 return newobj 247 248 __rmul__ = __mul__ 249 250 def __div__(self, other): 251 newobj = self.__class__(self) 252 newobj.value /= other 253 newobj._check() 254 return newobj 255 256 def __sub__(self, other): 257 newobj = self.__class__(self) 258 newobj.value -= other 259 newobj._check() 260 return newobj 261 262# Metaclass for bounds-checked integer parameters. See CheckedInt. 263class CheckedIntType(type): 264 def __init__(cls, name, bases, dict): 265 super(CheckedIntType, cls).__init__(name, bases, dict) 266 267 # CheckedInt is an abstract base class, so we actually don't 268 # want to do any processing on it... the rest of this code is 269 # just for classes that derive from CheckedInt. 270 if name == 'CheckedInt': 271 return 272 273 if not cls.cxx_predecls: 274 # most derived types require this, so we just do it here once 275 cls.cxx_predecls = ['#include "sim/host.hh"'] 276 277 if not cls.swig_predecls: 278 # most derived types require this, so we just do it here once 279 cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 280 '%import "sim/host.hh"'] 281 282 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 283 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 284 panic("CheckedInt subclass %s must define either\n" \ 285 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 286 % name); 287 if cls.unsigned: 288 cls.min = 0 289 cls.max = 2 ** cls.size - 1 290 else: 291 cls.min = -(2 ** (cls.size - 1)) 292 cls.max = (2 ** (cls.size - 1)) - 1 293 294# Abstract superclass for bounds-checked integer parameters. This 295# class is subclassed to generate parameter classes with specific 296# bounds. Initialization of the min and max bounds is done in the 297# metaclass CheckedIntType.__init__. 298class CheckedInt(NumericParamValue): 299 __metaclass__ = CheckedIntType 300 301 def _check(self): 302 if not self.min <= self.value <= self.max: 303 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 304 (self.min, self.value, self.max) 305 306 def __init__(self, value): 307 if isinstance(value, str): 308 self.value = toInteger(value) 309 elif isinstance(value, (int, long, float)): 310 self.value = long(value) 311 self._check() 312 313class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 314class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 315 316class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 317class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 318class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 319class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 320class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 321class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 322class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 323class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 324 325class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 326class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 327class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 328class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 329 330class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 331 332class Float(ParamValue, float): 333 pass 334 335class MemorySize(CheckedInt): 336 cxx_type = 'uint64_t' 337 size = 64 338 unsigned = True 339 def __init__(self, value): 340 if isinstance(value, MemorySize): 341 self.value = value.value 342 else: 343 self.value = toMemorySize(value) 344 self._check() 345 346class MemorySize32(CheckedInt): 347 size = 32 348 unsigned = True 349 def __init__(self, value): 350 if isinstance(value, MemorySize): 351 self.value = value.value 352 else: 353 self.value = toMemorySize(value) 354 self._check() 355 356class Addr(CheckedInt): 357 cxx_type = 'Addr' 358 cxx_predecls = ['#include "targetarch/isa_traits.hh"'] 359 size = 64 360 unsigned = True 361 def __init__(self, value): 362 if isinstance(value, Addr): 363 self.value = value.value 364 else: 365 try: 366 self.value = toMemorySize(value) 367 except TypeError: 368 self.value = long(value) 369 self._check() 370 371 372class MetaRange(type): 373 def __init__(cls, name, bases, dict): 374 super(MetaRange, cls).__init__(name, bases, dict) 375 if name == 'Range': 376 return 377 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 378 cls.cxx_predecls = \ 379 ['#include "base/range.hh"'] + cls.type.cxx_predecls 380 381class Range(ParamValue): 382 __metaclass__ = MetaRange 383 type = Int # default; can be overridden in subclasses 384 def __init__(self, *args, **kwargs): 385 def handle_kwargs(self, kwargs): 386 if 'end' in kwargs: 387 self.second = self.type(kwargs.pop('end')) 388 elif 'size' in kwargs: 389 self.second = self.first + self.type(kwargs.pop('size')) - 1 390 else: 391 raise TypeError, "Either end or size must be specified" 392 393 if len(args) == 0: 394 self.first = self.type(kwargs.pop('start')) 395 handle_kwargs(self, kwargs) 396 397 elif len(args) == 1: 398 if kwargs: 399 self.first = self.type(args[0]) 400 handle_kwargs(self, kwargs) 401 elif isinstance(args[0], Range): 402 self.first = self.type(args[0].first) 403 self.second = self.type(args[0].second) 404 else: 405 self.first = self.type(0) 406 self.second = self.type(args[0]) - 1 407 408 elif len(args) == 2: 409 self.first = self.type(args[0]) 410 self.second = self.type(args[1]) 411 else: 412 raise TypeError, "Too many arguments specified" 413 414 if kwargs: 415 raise TypeError, "too many keywords: %s" % kwargs.keys() 416 417 def __str__(self): 418 return '%s:%s' % (self.first, self.second) 419 420class AddrRange(Range): 421 type = Addr 422 423class TickRange(Range): 424 type = Tick 425 426# Boolean parameter type. Python doesn't let you subclass bool, since 427# it doesn't want to let you create multiple instances of True and 428# False. Thus this is a little more complicated than String. 429class Bool(ParamValue): 430 cxx_type = 'bool' 431 def __init__(self, value): 432 try: 433 self.value = toBool(value) 434 except TypeError: 435 self.value = bool(value) 436 437 def __str__(self): 438 return str(self.value) 439 440 def ini_str(self): 441 if self.value: 442 return 'true' 443 return 'false' 444 445def IncEthernetAddr(addr, val = 1): 446 bytes = map(lambda x: int(x, 16), addr.split(':')) 447 bytes[5] += val 448 for i in (5, 4, 3, 2, 1): 449 val,rem = divmod(bytes[i], 256) 450 bytes[i] = rem 451 if val == 0: 452 break 453 bytes[i - 1] += val 454 assert(bytes[0] <= 255) 455 return ':'.join(map(lambda x: '%02x' % x, bytes)) 456 457class NextEthernetAddr(object): 458 addr = "00:90:00:00:00:01" 459 460 def __init__(self, inc = 1): 461 self.value = NextEthernetAddr.addr 462 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) 463 464class EthernetAddr(ParamValue): 465 cxx_type = 'Net::EthAddr' 466 cxx_predecls = ['#include "base/inet.hh"'] 467 swig_predecls = ['class Net::EthAddr;'] 468 def __init__(self, value): 469 if value == NextEthernetAddr: 470 self.value = value 471 return 472 473 if not isinstance(value, str): 474 raise TypeError, "expected an ethernet address and didn't get one" 475 476 bytes = value.split(':') 477 if len(bytes) != 6: 478 raise TypeError, 'invalid ethernet address %s' % value 479 480 for byte in bytes: 481 if not 0 <= int(byte) <= 256: 482 raise TypeError, 'invalid ethernet address %s' % value 483 484 self.value = value 485 486 def unproxy(self, base): 487 if self.value == NextEthernetAddr: 488 self.addr = self.value().value 489 return self 490 491 def __str__(self): 492 if self.value == NextEthernetAddr: 493 if hasattr(self, 'addr'): 494 return self.addr 495 else: 496 return "NextEthernetAddr (unresolved)" 497 else: 498 return self.value 499 500# Enumerated types are a little more complex. The user specifies the 501# type as Enum(foo) where foo is either a list or dictionary of 502# alternatives (typically strings, but not necessarily so). (In the 503# long run, the integer value of the parameter will be the list index 504# or the corresponding dictionary value. For now, since we only check 505# that the alternative is valid and then spit it into a .ini file, 506# there's not much point in using the dictionary.) 507 508# What Enum() must do is generate a new type encapsulating the 509# provided list/dictionary so that specific values of the parameter 510# can be instances of that type. We define two hidden internal 511# classes (_ListEnum and _DictEnum) to serve as base classes, then 512# derive the new type from the appropriate base class on the fly. 513 514 515# Metaclass for Enum types 516class MetaEnum(type): 517 def __init__(cls, name, bases, init_dict): 518 if init_dict.has_key('map'): 519 if not isinstance(cls.map, dict): 520 raise TypeError, "Enum-derived class attribute 'map' " \ 521 "must be of type dict" 522 # build list of value strings from map 523 cls.vals = cls.map.keys() 524 cls.vals.sort() 525 elif init_dict.has_key('vals'): 526 if not isinstance(cls.vals, list): 527 raise TypeError, "Enum-derived class attribute 'vals' " \ 528 "must be of type list" 529 # build string->value map from vals sequence 530 cls.map = {} 531 for idx,val in enumerate(cls.vals): 532 cls.map[val] = idx 533 else: 534 raise TypeError, "Enum-derived class must define "\ 535 "attribute 'map' or 'vals'" 536 537 cls.cxx_type = name + '::Enum' 538 539 super(MetaEnum, cls).__init__(name, bases, init_dict) 540 541 # Generate C++ class declaration for this enum type. 542 # Note that we wrap the enum in a class/struct to act as a namespace, 543 # so that the enum strings can be brief w/o worrying about collisions. 544 def cxx_decl(cls): 545 s = 'struct %s {\n enum Enum {\n ' % cls.__name__ 546 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 547 s += '\n };\n};\n' 548 return s 549 550# Base class for enum types. 551class Enum(ParamValue): 552 __metaclass__ = MetaEnum 553 vals = [] 554 555 def __init__(self, value): 556 if value not in self.map: 557 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 558 % (value, self.vals) 559 self.value = value 560 561 def __str__(self): 562 return self.value 563 564ticks_per_sec = None 565 566# how big does a rounding error need to be before we warn about it? 567frequency_tolerance = 0.001 # 0.1% 568 569# convert a floting-point # of ticks to integer, and warn if rounding 570# discards too much precision 571def tick_check(float_ticks): 572 if float_ticks == 0: 573 return 0 574 int_ticks = int(round(float_ticks)) 575 err = (float_ticks - int_ticks) / float_ticks 576 if err > frequency_tolerance: 577 print >> sys.stderr, "Warning: rounding error > tolerance" 578 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) 579 #raise ValueError 580 return int_ticks 581 582def getLatency(value): 583 if isinstance(value, Latency) or isinstance(value, Clock): 584 return value.value 585 elif isinstance(value, Frequency) or isinstance(value, RootClock): 586 return 1 / value.value 587 elif isinstance(value, str): 588 try: 589 return toLatency(value) 590 except ValueError: 591 try: 592 return 1 / toFrequency(value) 593 except ValueError: 594 pass # fall through 595 raise ValueError, "Invalid Frequency/Latency value '%s'" % value 596 597 598class Latency(NumericParamValue): 599 cxx_type = 'Tick' 600 cxx_predecls = ['#include "sim/host.hh"'] 601 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 602 '%import "sim/host.hh"'] 603 def __init__(self, value): 604 self.value = getLatency(value) 605 606 def __getattr__(self, attr): 607 if attr in ('latency', 'period'): 608 return self 609 if attr == 'frequency': 610 return Frequency(self) 611 raise AttributeError, "Latency object has no attribute '%s'" % attr 612 613 # convert latency to ticks 614 def ini_str(self): 615 return str(tick_check(self.value * ticks_per_sec)) 616 617class Frequency(NumericParamValue): 618 cxx_type = 'Tick' 619 cxx_predecls = ['#include "sim/host.hh"'] 620 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 621 '%import "sim/host.hh"'] 622 def __init__(self, value): 623 self.value = 1 / getLatency(value) 624 625 def __getattr__(self, attr): 626 if attr == 'frequency': 627 return self 628 if attr in ('latency', 'period'): 629 return Latency(self) 630 raise AttributeError, "Frequency object has no attribute '%s'" % attr 631 632 # convert frequency to ticks per period 633 def ini_str(self): 634 return self.period.ini_str() 635 636# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). 637# We can't inherit from Frequency because we don't want it to be directly 638# assignable to a regular Frequency parameter. 639class RootClock(ParamValue): 640 cxx_type = 'Tick' 641 cxx_predecls = ['#include "sim/host.hh"'] 642 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 643 '%import "sim/host.hh"'] 644 def __init__(self, value): 645 self.value = 1 / getLatency(value) 646 647 def __getattr__(self, attr): 648 if attr == 'frequency': 649 return Frequency(self) 650 if attr in ('latency', 'period'): 651 return Latency(self) 652 raise AttributeError, "Frequency object has no attribute '%s'" % attr 653 654 def ini_str(self): 655 return str(tick_check(self.value)) 656 657# A generic frequency and/or Latency value. Value is stored as a latency, 658# but to avoid ambiguity this object does not support numeric ops (* or /). 659# An explicit conversion to a Latency or Frequency must be made first. 660class Clock(ParamValue): 661 cxx_type = 'Tick' 662 cxx_predecls = ['#include "sim/host.hh"'] 663 swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + 664 '%import "sim/host.hh"'] 665 def __init__(self, value): 666 self.value = getLatency(value) 667 668 def __getattr__(self, attr): 669 if attr == 'frequency': 670 return Frequency(self) 671 if attr in ('latency', 'period'): 672 return Latency(self) 673 raise AttributeError, "Frequency object has no attribute '%s'" % attr 674 675 def ini_str(self): 676 return self.period.ini_str() 677 678class NetworkBandwidth(float,ParamValue): 679 cxx_type = 'float' 680 def __new__(cls, value): 681 val = toNetworkBandwidth(value) / 8.0 682 return super(cls, NetworkBandwidth).__new__(cls, val) 683 684 def __str__(self): 685 return str(self.val) 686 687 def ini_str(self): 688 return '%f' % (ticks_per_sec / float(self)) 689 690class MemoryBandwidth(float,ParamValue): 691 cxx_type = 'float' 692 def __new__(self, value): 693 val = toMemoryBandwidth(value) 694 return super(cls, MemoryBandwidth).__new__(cls, val) 695 696 def __str__(self): 697 return str(self.val) 698 699 def ini_str(self): 700 return '%f' % (ticks_per_sec / float(self)) 701 702# 703# "Constants"... handy aliases for various values. 704# 705 706# Some memory range specifications use this as a default upper bound. 707MaxAddr = Addr.max 708MaxTick = Tick.max 709AllMemory = AddrRange(0, MaxAddr) 710 711 712##################################################################### 713# 714# Port objects 715# 716# Ports are used to interconnect objects in the memory system. 717# 718##################################################################### 719 720# Port reference: encapsulates a reference to a particular port on a 721# particular SimObject. 722class PortRef(object): 723 def __init__(self, simobj, name, isVec): 724 assert(isSimObject(simobj)) 725 self.simobj = simobj 726 self.name = name 727 self.index = -1 728 self.isVec = isVec # is this a vector port? 729 self.peer = None # not associated with another port yet 730 self.ccConnected = False # C++ port connection done? 731 732 # Set peer port reference. Called via __setattr__ as a result of 733 # a port assignment, e.g., "obj1.port1 = obj2.port2". 734 def setPeer(self, other): 735 if self.isVec: 736 curMap = self.simobj._port_map.get(self.name, []) 737 self.index = len(curMap) 738 curMap.append(other) 739 else: 740 curMap = self.simobj._port_map.get(self.name) 741 if curMap and not self.isVec: 742 print "warning: overwriting port", self.simobj, self.name 743 curMap = other 744 self.simobj._port_map[self.name] = curMap 745 self.peer = other 746 747 def clone(self, memo): 748 newRef = copy.copy(self) 749 assert(isSimObject(newRef.simobj)) 750 newRef.simobj = newRef.simobj(_memo=memo) 751 # Tricky: if I'm the *second* PortRef in the pair to be 752 # cloned, then my peer is still in the middle of its clone 753 # method, and thus hasn't returned to its owner's 754 # SimObject.__init__ to get installed in _port_map. As a 755 # result I have no way of finding the *new* peer object. So I 756 # mark myself as "waiting" for my peer, and I let the *first* 757 # PortRef clone call set up both peer pointers after I return. 758 newPeer = newRef.simobj._port_map.get(self.name) 759 if newPeer: 760 if self.isVec: 761 assert(self.index != -1) 762 newPeer = newPeer[self.index] 763 # other guy is all set up except for his peer pointer 764 assert(newPeer.peer == -1) # peer must be waiting for handshake 765 newPeer.peer = newRef 766 newRef.peer = newPeer 767 else: 768 # other guy is in clone; just wait for him to do the work 769 newRef.peer = -1 # mark as waiting for handshake 770 return newRef 771 772 # Call C++ to create corresponding port connection between C++ objects 773 def ccConnect(self): 774 if self.ccConnected: # already done this 775 return 776 peer = self.peer 777 cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, 778 peer.simobj.getCCObject(), peer.name, peer.index) 779 self.ccConnected = True 780 peer.ccConnected = True 781 782# Port description object. Like a ParamDesc object, this represents a 783# logical port in the SimObject class, not a particular port on a 784# SimObject instance. The latter are represented by PortRef objects. 785class Port(object): 786 def __init__(self, desc): 787 self.desc = desc 788 self.isVec = False 789 790 # Generate a PortRef for this port on the given SimObject with the 791 # given name 792 def makeRef(self, simobj, name): 793 return PortRef(simobj, name, self.isVec) 794 795 # Connect an instance of this port (on the given SimObject with 796 # the given name) with the port described by the supplied PortRef 797 def connect(self, simobj, name, ref): 798 if not isinstance(ref, PortRef): 799 raise TypeError, \ 800 "assigning non-port reference port '%s'" % name 801 myRef = self.makeRef(simobj, name) 802 myRef.setPeer(ref) 803 ref.setPeer(myRef) 804 805# VectorPort description object. Like Port, but represents a vector 806# of connections (e.g., as on a Bus). 807class VectorPort(Port): 808 def __init__(self, desc): 809 Port.__init__(self, desc) 810 self.isVec = True 811 812 813__all__ = ['Param', 'VectorParam', 814 'Enum', 'Bool', 'String', 'Float', 815 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 816 'Int32', 'UInt32', 'Int64', 'UInt64', 817 'Counter', 'Addr', 'Tick', 'Percent', 818 'TcpPort', 'UdpPort', 'EthernetAddr', 819 'MemorySize', 'MemorySize32', 820 'Latency', 'Frequency', 'RootClock', 'Clock', 821 'NetworkBandwidth', 'MemoryBandwidth', 822 'Range', 'AddrRange', 'TickRange', 823 'MaxAddr', 'MaxTick', 'AllMemory', 824 'Null', 'NULL', 825 'NextEthernetAddr', 826 'Port', 'VectorPort'] 827 828# see comment on imports at end of __init__.py. 829from SimObject import SimObject, isSimObject, isSimObjectSequence, \ 830 isNullPointer 831import proxy 832