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