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