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