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