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