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