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