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