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