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