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