params.py revision 7677:c6e283904437
1# Copyright (c) 2004-2006 The Regents of The University of Michigan 2# Copyright (c) 2010 Advanced Micro Devices, Inc. 3# All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are 7# met: redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer; 9# redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in the 11# documentation and/or other materials provided with the distribution; 12# neither the name of the copyright holders nor the names of its 13# contributors may be used to endorse or promote products derived from 14# this software without specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27# 28# Authors: Steve Reinhardt 29# Nathan Binkert 30 31##################################################################### 32# 33# Parameter description classes 34# 35# The _params dictionary in each class maps parameter names to either 36# a Param or a VectorParam object. These objects contain the 37# parameter description string, the parameter type, and the default 38# value (if any). The convert() method on these objects is used to 39# force whatever value is assigned to the parameter to the appropriate 40# type. 41# 42# Note that the default values are loaded into the class's attribute 43# space when the parameter dictionary is initialized (in 44# MetaSimObject._new_param()); after that point they aren't used. 45# 46##################################################################### 47 48import copy 49import datetime 50import re 51import sys 52import time 53import math 54 55import proxy 56import ticks 57from util import * 58 59def isSimObject(*args, **kwargs): 60 return SimObject.isSimObject(*args, **kwargs) 61 62def isSimObjectSequence(*args, **kwargs): 63 return SimObject.isSimObjectSequence(*args, **kwargs) 64 65def isSimObjectClass(*args, **kwargs): 66 return SimObject.isSimObjectClass(*args, **kwargs) 67 68allParams = {} 69 70class MetaParamValue(type): 71 def __new__(mcls, name, bases, dct): 72 cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct) 73 assert name not in allParams 74 allParams[name] = cls 75 return cls 76 77 78# Dummy base class to identify types that are legitimate for SimObject 79# parameters. 80class ParamValue(object): 81 __metaclass__ = MetaParamValue 82 83 @classmethod 84 def cxx_predecls(cls, code): 85 pass 86 87 @classmethod 88 def swig_predecls(cls, code): 89 pass 90 91 # default for printing to .ini file is regular string conversion. 92 # will be overridden in some cases 93 def ini_str(self): 94 return str(self) 95 96 # allows us to blithely call unproxy() on things without checking 97 # if they're really proxies or not 98 def unproxy(self, base): 99 return self 100 101# Regular parameter description. 102class ParamDesc(object): 103 file_ext = 'ptype' 104 105 def __init__(self, ptype_str, ptype, *args, **kwargs): 106 self.ptype_str = ptype_str 107 # remember ptype only if it is provided 108 if ptype != None: 109 self.ptype = ptype 110 111 if args: 112 if len(args) == 1: 113 self.desc = args[0] 114 elif len(args) == 2: 115 self.default = args[0] 116 self.desc = args[1] 117 else: 118 raise TypeError, 'too many arguments' 119 120 if kwargs.has_key('desc'): 121 assert(not hasattr(self, 'desc')) 122 self.desc = kwargs['desc'] 123 del kwargs['desc'] 124 125 if kwargs.has_key('default'): 126 assert(not hasattr(self, 'default')) 127 self.default = kwargs['default'] 128 del kwargs['default'] 129 130 if kwargs: 131 raise TypeError, 'extra unknown kwargs %s' % kwargs 132 133 if not hasattr(self, 'desc'): 134 raise TypeError, 'desc attribute missing' 135 136 def __getattr__(self, attr): 137 if attr == 'ptype': 138 ptype = SimObject.allClasses[self.ptype_str] 139 assert isSimObjectClass(ptype) 140 self.ptype = ptype 141 return ptype 142 143 raise AttributeError, "'%s' object has no attribute '%s'" % \ 144 (type(self).__name__, attr) 145 146 def convert(self, value): 147 if isinstance(value, proxy.BaseProxy): 148 value.set_param_desc(self) 149 return value 150 if not hasattr(self, 'ptype') and isNullPointer(value): 151 # deferred evaluation of SimObject; continue to defer if 152 # we're just assigning a null pointer 153 return value 154 if isinstance(value, self.ptype): 155 return value 156 if isNullPointer(value) and isSimObjectClass(self.ptype): 157 return value 158 return self.ptype(value) 159 160 def cxx_predecls(self, code): 161 self.ptype.cxx_predecls(code) 162 163 def swig_predecls(self, code): 164 self.ptype.swig_predecls(code) 165 166 def cxx_decl(self, code): 167 code('${{self.ptype.cxx_type}} ${{self.name}};') 168 169# Vector-valued parameter description. Just like ParamDesc, except 170# that the value is a vector (list) of the specified type instead of a 171# single value. 172 173class VectorParamValue(list): 174 __metaclass__ = MetaParamValue 175 def __setattr__(self, attr, value): 176 raise AttributeError, \ 177 "Not allowed to set %s on '%s'" % (attr, type(self).__name__) 178 179 def ini_str(self): 180 return ' '.join([v.ini_str() for v in self]) 181 182 def getValue(self): 183 return [ v.getValue() for v in self ] 184 185 def unproxy(self, base): 186 return [v.unproxy(base) for v in self] 187 188class SimObjectVector(VectorParamValue): 189 # support clone operation 190 def __call__(self, **kwargs): 191 return SimObjectVector([v(**kwargs) for v in self]) 192 193 def clear_parent(self, old_parent): 194 for v in self: 195 v.clear_parent(old_parent) 196 197 def set_parent(self, parent, name): 198 if len(self) == 1: 199 self[0].set_parent(parent, name) 200 else: 201 width = int(math.ceil(math.log(len(self))/math.log(10))) 202 for i,v in enumerate(self): 203 v.set_parent(parent, "%s%0*d" % (name, width, i)) 204 205 def get_parent(self): 206 parent_set = set(v._parent for v in self) 207 if len(parent_set) != 1: 208 raise RuntimeError, \ 209 "SimObjectVector elements have inconsistent parent value." 210 return parent_set.pop() 211 212 # return 'cpu0 cpu1' etc. for print_ini() 213 def get_name(self): 214 return ' '.join([v._name for v in self]) 215 216 # By iterating through the constituent members of the vector here 217 # we can nicely handle iterating over all a SimObject's children 218 # without having to provide lots of special functions on 219 # SimObjectVector directly. 220 def descendants(self): 221 for v in self: 222 for obj in v.descendants(): 223 yield obj 224 225class VectorParamDesc(ParamDesc): 226 file_ext = 'vptype' 227 228 # Convert assigned value to appropriate type. If the RHS is not a 229 # list or tuple, it generates a single-element list. 230 def convert(self, value): 231 if isinstance(value, (list, tuple)): 232 # list: coerce each element into new list 233 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 234 else: 235 # singleton: coerce to a single-element list 236 tmp_list = [ ParamDesc.convert(self, value) ] 237 238 if isSimObjectSequence(tmp_list): 239 return SimObjectVector(tmp_list) 240 else: 241 return VectorParamValue(tmp_list) 242 243 def swig_predecls(self, code): 244 code('%import "vptype_${{self.ptype_str}}.i"') 245 246 def swig_decl(self, code): 247 code('%{') 248 self.ptype.cxx_predecls(code) 249 code('%}') 250 code() 251 self.ptype.swig_predecls(code) 252 code() 253 code('%include "std_vector.i"') 254 code() 255 256 ptype = self.ptype_str 257 cxx_type = self.ptype.cxx_type 258 259 code('''\ 260%typemap(in) std::vector< $cxx_type >::value_type { 261 if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) { 262 if (SWIG_ConvertPtr($$input, (void **)&$$1, 263 $$descriptor($cxx_type), 0) == -1) { 264 return NULL; 265 } 266 } 267} 268 269%typemap(in) std::vector< $cxx_type >::value_type * { 270 if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) { 271 if (SWIG_ConvertPtr($$input, (void **)&$$1, 272 $$descriptor($cxx_type *), 0) == -1) { 273 return NULL; 274 } 275 } 276} 277''') 278 279 code('%template(vector_$ptype) std::vector< $cxx_type >;') 280 281 def cxx_predecls(self, code): 282 code('#include <vector>') 283 self.ptype.cxx_predecls(code) 284 285 def cxx_decl(self, code): 286 code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};') 287 288class ParamFactory(object): 289 def __init__(self, param_desc_class, ptype_str = None): 290 self.param_desc_class = param_desc_class 291 self.ptype_str = ptype_str 292 293 def __getattr__(self, attr): 294 if self.ptype_str: 295 attr = self.ptype_str + '.' + attr 296 return ParamFactory(self.param_desc_class, attr) 297 298 # E.g., Param.Int(5, "number of widgets") 299 def __call__(self, *args, **kwargs): 300 ptype = None 301 try: 302 ptype = allParams[self.ptype_str] 303 except KeyError: 304 # if name isn't defined yet, assume it's a SimObject, and 305 # try to resolve it later 306 pass 307 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 308 309Param = ParamFactory(ParamDesc) 310VectorParam = ParamFactory(VectorParamDesc) 311 312##################################################################### 313# 314# Parameter Types 315# 316# Though native Python types could be used to specify parameter types 317# (the 'ptype' field of the Param and VectorParam classes), it's more 318# flexible to define our own set of types. This gives us more control 319# over how Python expressions are converted to values (via the 320# __init__() constructor) and how these values are printed out (via 321# the __str__() conversion method). 322# 323##################################################################### 324 325# String-valued parameter. Just mixin the ParamValue class with the 326# built-in str class. 327class String(ParamValue,str): 328 cxx_type = 'std::string' 329 330 @classmethod 331 def cxx_predecls(self, code): 332 code('#include <string>') 333 334 @classmethod 335 def swig_predecls(cls, code): 336 code('%include "std_string.i"') 337 338 def getValue(self): 339 return self 340 341# superclass for "numeric" parameter values, to emulate math 342# operations in a type-safe way. e.g., a Latency times an int returns 343# a new Latency object. 344class NumericParamValue(ParamValue): 345 def __str__(self): 346 return str(self.value) 347 348 def __float__(self): 349 return float(self.value) 350 351 def __long__(self): 352 return long(self.value) 353 354 def __int__(self): 355 return int(self.value) 356 357 # hook for bounds checking 358 def _check(self): 359 return 360 361 def __mul__(self, other): 362 newobj = self.__class__(self) 363 newobj.value *= other 364 newobj._check() 365 return newobj 366 367 __rmul__ = __mul__ 368 369 def __div__(self, other): 370 newobj = self.__class__(self) 371 newobj.value /= other 372 newobj._check() 373 return newobj 374 375 def __sub__(self, other): 376 newobj = self.__class__(self) 377 newobj.value -= other 378 newobj._check() 379 return newobj 380 381# Metaclass for bounds-checked integer parameters. See CheckedInt. 382class CheckedIntType(MetaParamValue): 383 def __init__(cls, name, bases, dict): 384 super(CheckedIntType, cls).__init__(name, bases, dict) 385 386 # CheckedInt is an abstract base class, so we actually don't 387 # want to do any processing on it... the rest of this code is 388 # just for classes that derive from CheckedInt. 389 if name == 'CheckedInt': 390 return 391 392 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 393 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 394 panic("CheckedInt subclass %s must define either\n" \ 395 " 'min' and 'max' or 'size' and 'unsigned'\n", 396 name); 397 if cls.unsigned: 398 cls.min = 0 399 cls.max = 2 ** cls.size - 1 400 else: 401 cls.min = -(2 ** (cls.size - 1)) 402 cls.max = (2 ** (cls.size - 1)) - 1 403 404# Abstract superclass for bounds-checked integer parameters. This 405# class is subclassed to generate parameter classes with specific 406# bounds. Initialization of the min and max bounds is done in the 407# metaclass CheckedIntType.__init__. 408class CheckedInt(NumericParamValue): 409 __metaclass__ = CheckedIntType 410 411 def _check(self): 412 if not self.min <= self.value <= self.max: 413 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 414 (self.min, self.value, self.max) 415 416 def __init__(self, value): 417 if isinstance(value, str): 418 self.value = convert.toInteger(value) 419 elif isinstance(value, (int, long, float, NumericParamValue)): 420 self.value = long(value) 421 else: 422 raise TypeError, "Can't convert object of type %s to CheckedInt" \ 423 % type(value).__name__ 424 self._check() 425 426 @classmethod 427 def cxx_predecls(cls, code): 428 # most derived types require this, so we just do it here once 429 code('#include "base/types.hh"') 430 431 @classmethod 432 def swig_predecls(cls, code): 433 # most derived types require this, so we just do it here once 434 code('%import "stdint.i"') 435 code('%import "base/types.hh"') 436 437 def getValue(self): 438 return long(self.value) 439 440class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False 441class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True 442 443class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False 444class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True 445class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False 446class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 447class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False 448class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True 449class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False 450class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True 451 452class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True 453class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True 454class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 455class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True 456 457class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 458 459class Float(ParamValue, float): 460 cxx_type = 'double' 461 462 def __init__(self, value): 463 if isinstance(value, (int, long, float, NumericParamValue, Float)): 464 self.value = float(value) 465 else: 466 raise TypeError, "Can't convert object of type %s to Float" \ 467 % type(value).__name__ 468 469 def getValue(self): 470 return float(self.value) 471 472class MemorySize(CheckedInt): 473 cxx_type = 'uint64_t' 474 size = 64 475 unsigned = True 476 def __init__(self, value): 477 if isinstance(value, MemorySize): 478 self.value = value.value 479 else: 480 self.value = convert.toMemorySize(value) 481 self._check() 482 483class MemorySize32(CheckedInt): 484 cxx_type = 'uint32_t' 485 size = 32 486 unsigned = True 487 def __init__(self, value): 488 if isinstance(value, MemorySize): 489 self.value = value.value 490 else: 491 self.value = convert.toMemorySize(value) 492 self._check() 493 494class Addr(CheckedInt): 495 cxx_type = 'Addr' 496 size = 64 497 unsigned = True 498 def __init__(self, value): 499 if isinstance(value, Addr): 500 self.value = value.value 501 else: 502 try: 503 self.value = convert.toMemorySize(value) 504 except TypeError: 505 self.value = long(value) 506 self._check() 507 def __add__(self, other): 508 if isinstance(other, Addr): 509 return self.value + other.value 510 else: 511 return self.value + other 512 513 514class MetaRange(MetaParamValue): 515 def __init__(cls, name, bases, dict): 516 super(MetaRange, cls).__init__(name, bases, dict) 517 if name == 'Range': 518 return 519 cls.cxx_type = 'Range< %s >' % cls.type.cxx_type 520 521class Range(ParamValue): 522 __metaclass__ = MetaRange 523 type = Int # default; can be overridden in subclasses 524 def __init__(self, *args, **kwargs): 525 def handle_kwargs(self, kwargs): 526 if 'end' in kwargs: 527 self.second = self.type(kwargs.pop('end')) 528 elif 'size' in kwargs: 529 self.second = self.first + self.type(kwargs.pop('size')) - 1 530 else: 531 raise TypeError, "Either end or size must be specified" 532 533 if len(args) == 0: 534 self.first = self.type(kwargs.pop('start')) 535 handle_kwargs(self, kwargs) 536 537 elif len(args) == 1: 538 if kwargs: 539 self.first = self.type(args[0]) 540 handle_kwargs(self, kwargs) 541 elif isinstance(args[0], Range): 542 self.first = self.type(args[0].first) 543 self.second = self.type(args[0].second) 544 elif isinstance(args[0], (list, tuple)): 545 self.first = self.type(args[0][0]) 546 self.second = self.type(args[0][1]) 547 else: 548 self.first = self.type(0) 549 self.second = self.type(args[0]) - 1 550 551 elif len(args) == 2: 552 self.first = self.type(args[0]) 553 self.second = self.type(args[1]) 554 else: 555 raise TypeError, "Too many arguments specified" 556 557 if kwargs: 558 raise TypeError, "too many keywords: %s" % kwargs.keys() 559 560 def __str__(self): 561 return '%s:%s' % (self.first, self.second) 562 563 @classmethod 564 def cxx_predecls(cls, code): 565 cls.type.cxx_predecls(code) 566 code('#include "base/range.hh"') 567 568 @classmethod 569 def swig_predecls(cls, code): 570 cls.type.swig_predecls(code) 571 code('%import "python/swig/range.i"') 572 573class AddrRange(Range): 574 type = Addr 575 576 def getValue(self): 577 from m5.internal.range import AddrRange 578 579 value = AddrRange() 580 value.start = long(self.first) 581 value.end = long(self.second) 582 return value 583 584class TickRange(Range): 585 type = Tick 586 587 def getValue(self): 588 from m5.internal.range import TickRange 589 590 value = TickRange() 591 value.start = long(self.first) 592 value.end = long(self.second) 593 return value 594 595# Boolean parameter type. Python doesn't let you subclass bool, since 596# it doesn't want to let you create multiple instances of True and 597# False. Thus this is a little more complicated than String. 598class Bool(ParamValue): 599 cxx_type = 'bool' 600 def __init__(self, value): 601 try: 602 self.value = convert.toBool(value) 603 except TypeError: 604 self.value = bool(value) 605 606 def getValue(self): 607 return bool(self.value) 608 609 def __str__(self): 610 return str(self.value) 611 612 def ini_str(self): 613 if self.value: 614 return 'true' 615 return 'false' 616 617def IncEthernetAddr(addr, val = 1): 618 bytes = map(lambda x: int(x, 16), addr.split(':')) 619 bytes[5] += val 620 for i in (5, 4, 3, 2, 1): 621 val,rem = divmod(bytes[i], 256) 622 bytes[i] = rem 623 if val == 0: 624 break 625 bytes[i - 1] += val 626 assert(bytes[0] <= 255) 627 return ':'.join(map(lambda x: '%02x' % x, bytes)) 628 629_NextEthernetAddr = "00:90:00:00:00:01" 630def NextEthernetAddr(): 631 global _NextEthernetAddr 632 633 value = _NextEthernetAddr 634 _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) 635 return value 636 637class EthernetAddr(ParamValue): 638 cxx_type = 'Net::EthAddr' 639 640 @classmethod 641 def cxx_predecls(cls, code): 642 code('#include "base/inet.hh"') 643 644 @classmethod 645 def swig_predecls(cls, code): 646 code('%include "python/swig/inet.i"') 647 648 def __init__(self, value): 649 if value == NextEthernetAddr: 650 self.value = value 651 return 652 653 if not isinstance(value, str): 654 raise TypeError, "expected an ethernet address and didn't get one" 655 656 bytes = value.split(':') 657 if len(bytes) != 6: 658 raise TypeError, 'invalid ethernet address %s' % value 659 660 for byte in bytes: 661 if not 0 <= int(byte) <= 256: 662 raise TypeError, 'invalid ethernet address %s' % value 663 664 self.value = value 665 666 def unproxy(self, base): 667 if self.value == NextEthernetAddr: 668 return EthernetAddr(self.value()) 669 return self 670 671 def getValue(self): 672 from m5.internal.params import EthAddr 673 return EthAddr(self.value) 674 675 def ini_str(self): 676 return self.value 677 678time_formats = [ "%a %b %d %H:%M:%S %Z %Y", 679 "%a %b %d %H:%M:%S %Z %Y", 680 "%Y/%m/%d %H:%M:%S", 681 "%Y/%m/%d %H:%M", 682 "%Y/%m/%d", 683 "%m/%d/%Y %H:%M:%S", 684 "%m/%d/%Y %H:%M", 685 "%m/%d/%Y", 686 "%m/%d/%y %H:%M:%S", 687 "%m/%d/%y %H:%M", 688 "%m/%d/%y"] 689 690 691def parse_time(value): 692 from time import gmtime, strptime, struct_time, time 693 from datetime import datetime, date 694 695 if isinstance(value, struct_time): 696 return value 697 698 if isinstance(value, (int, long)): 699 return gmtime(value) 700 701 if isinstance(value, (datetime, date)): 702 return value.timetuple() 703 704 if isinstance(value, str): 705 if value in ('Now', 'Today'): 706 return time.gmtime(time.time()) 707 708 for format in time_formats: 709 try: 710 return strptime(value, format) 711 except ValueError: 712 pass 713 714 raise ValueError, "Could not parse '%s' as a time" % value 715 716class Time(ParamValue): 717 cxx_type = 'tm' 718 719 @classmethod 720 def cxx_predecls(cls, code): 721 code('#include <time.h>') 722 723 @classmethod 724 def swig_predecls(cls, code): 725 code('%include "python/swig/time.i"') 726 727 def __init__(self, value): 728 self.value = parse_time(value) 729 730 def getValue(self): 731 from m5.internal.params import tm 732 733 c_time = tm() 734 py_time = self.value 735 736 # UNIX is years since 1900 737 c_time.tm_year = py_time.tm_year - 1900; 738 739 # Python starts at 1, UNIX starts at 0 740 c_time.tm_mon = py_time.tm_mon - 1; 741 c_time.tm_mday = py_time.tm_mday; 742 c_time.tm_hour = py_time.tm_hour; 743 c_time.tm_min = py_time.tm_min; 744 c_time.tm_sec = py_time.tm_sec; 745 746 # Python has 0 as Monday, UNIX is 0 as sunday 747 c_time.tm_wday = py_time.tm_wday + 1 748 if c_time.tm_wday > 6: 749 c_time.tm_wday -= 7; 750 751 # Python starts at 1, Unix starts at 0 752 c_time.tm_yday = py_time.tm_yday - 1; 753 754 return c_time 755 756 def __str__(self): 757 return time.asctime(self.value) 758 759 def ini_str(self): 760 return str(self) 761 762# Enumerated types are a little more complex. The user specifies the 763# type as Enum(foo) where foo is either a list or dictionary of 764# alternatives (typically strings, but not necessarily so). (In the 765# long run, the integer value of the parameter will be the list index 766# or the corresponding dictionary value. For now, since we only check 767# that the alternative is valid and then spit it into a .ini file, 768# there's not much point in using the dictionary.) 769 770# What Enum() must do is generate a new type encapsulating the 771# provided list/dictionary so that specific values of the parameter 772# can be instances of that type. We define two hidden internal 773# classes (_ListEnum and _DictEnum) to serve as base classes, then 774# derive the new type from the appropriate base class on the fly. 775 776allEnums = {} 777# Metaclass for Enum types 778class MetaEnum(MetaParamValue): 779 def __new__(mcls, name, bases, dict): 780 assert name not in allEnums 781 782 cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) 783 allEnums[name] = cls 784 return cls 785 786 def __init__(cls, name, bases, init_dict): 787 if init_dict.has_key('map'): 788 if not isinstance(cls.map, dict): 789 raise TypeError, "Enum-derived class attribute 'map' " \ 790 "must be of type dict" 791 # build list of value strings from map 792 cls.vals = cls.map.keys() 793 cls.vals.sort() 794 elif init_dict.has_key('vals'): 795 if not isinstance(cls.vals, list): 796 raise TypeError, "Enum-derived class attribute 'vals' " \ 797 "must be of type list" 798 # build string->value map from vals sequence 799 cls.map = {} 800 for idx,val in enumerate(cls.vals): 801 cls.map[val] = idx 802 else: 803 raise TypeError, "Enum-derived class must define "\ 804 "attribute 'map' or 'vals'" 805 806 cls.cxx_type = 'Enums::%s' % name 807 808 super(MetaEnum, cls).__init__(name, bases, init_dict) 809 810 # Generate C++ class declaration for this enum type. 811 # Note that we wrap the enum in a class/struct to act as a namespace, 812 # so that the enum strings can be brief w/o worrying about collisions. 813 def cxx_decl(cls, code): 814 name = cls.__name__ 815 code('''\ 816#ifndef __ENUM__${name}__ 817#define __ENUM__${name}__ 818 819namespace Enums { 820 enum $name { 821''') 822 code.indent(2) 823 for val in cls.vals: 824 code('$val = ${{cls.map[val]}},') 825 code('Num_$name = ${{len(cls.vals)}},') 826 code.dedent(2) 827 code('''\ 828 }; 829extern const char *${name}Strings[Num_${name}]; 830} 831 832#endif // __ENUM__${name}__ 833''') 834 835 def cxx_def(cls, code): 836 name = cls.__name__ 837 code('''\ 838#include "enums/$name.hh" 839namespace Enums { 840 const char *${name}Strings[Num_${name}] = 841 { 842''') 843 code.indent(2) 844 for val in cls.vals: 845 code('"$val",') 846 code.dedent(2) 847 code(''' 848 }; 849/* namespace Enums */ } 850''') 851 852# Base class for enum types. 853class Enum(ParamValue): 854 __metaclass__ = MetaEnum 855 vals = [] 856 857 def __init__(self, value): 858 if value not in self.map: 859 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 860 % (value, self.vals) 861 self.value = value 862 863 @classmethod 864 def cxx_predecls(cls, code): 865 code('#include "enums/$0.hh"', cls.__name__) 866 867 @classmethod 868 def swig_predecls(cls, code): 869 code('%import "python/m5/internal/enum_$0.i"', cls.__name__) 870 871 def getValue(self): 872 return int(self.map[self.value]) 873 874 def __str__(self): 875 return self.value 876 877# how big does a rounding error need to be before we warn about it? 878frequency_tolerance = 0.001 # 0.1% 879 880class TickParamValue(NumericParamValue): 881 cxx_type = 'Tick' 882 883 @classmethod 884 def cxx_predecls(cls, code): 885 code('#include "base/types.hh"') 886 887 @classmethod 888 def swig_predecls(cls, code): 889 code('%import "stdint.i"') 890 code('%import "base/types.hh"') 891 892 def getValue(self): 893 return long(self.value) 894 895class Latency(TickParamValue): 896 def __init__(self, value): 897 if isinstance(value, (Latency, Clock)): 898 self.ticks = value.ticks 899 self.value = value.value 900 elif isinstance(value, Frequency): 901 self.ticks = value.ticks 902 self.value = 1.0 / value.value 903 elif value.endswith('t'): 904 self.ticks = True 905 self.value = int(value[:-1]) 906 else: 907 self.ticks = False 908 self.value = convert.toLatency(value) 909 910 def __getattr__(self, attr): 911 if attr in ('latency', 'period'): 912 return self 913 if attr == 'frequency': 914 return Frequency(self) 915 raise AttributeError, "Latency object has no attribute '%s'" % attr 916 917 def getValue(self): 918 if self.ticks or self.value == 0: 919 value = self.value 920 else: 921 value = ticks.fromSeconds(self.value) 922 return long(value) 923 924 # convert latency to ticks 925 def ini_str(self): 926 return '%d' % self.getValue() 927 928class Frequency(TickParamValue): 929 def __init__(self, value): 930 if isinstance(value, (Latency, Clock)): 931 if value.value == 0: 932 self.value = 0 933 else: 934 self.value = 1.0 / value.value 935 self.ticks = value.ticks 936 elif isinstance(value, Frequency): 937 self.value = value.value 938 self.ticks = value.ticks 939 else: 940 self.ticks = False 941 self.value = convert.toFrequency(value) 942 943 def __getattr__(self, attr): 944 if attr == 'frequency': 945 return self 946 if attr in ('latency', 'period'): 947 return Latency(self) 948 raise AttributeError, "Frequency object has no attribute '%s'" % attr 949 950 # convert latency to ticks 951 def getValue(self): 952 if self.ticks or self.value == 0: 953 value = self.value 954 else: 955 value = ticks.fromSeconds(1.0 / self.value) 956 return long(value) 957 958 def ini_str(self): 959 return '%d' % self.getValue() 960 961# A generic frequency and/or Latency value. Value is stored as a latency, 962# but to avoid ambiguity this object does not support numeric ops (* or /). 963# An explicit conversion to a Latency or Frequency must be made first. 964class Clock(ParamValue): 965 cxx_type = 'Tick' 966 967 @classmethod 968 def cxx_predecls(cls, code): 969 code('#include "base/types.hh"') 970 971 @classmethod 972 def swig_predecls(cls, code): 973 code('%import "stdint.i"') 974 code('%import "base/types.hh"') 975 976 def __init__(self, value): 977 if isinstance(value, (Latency, Clock)): 978 self.ticks = value.ticks 979 self.value = value.value 980 elif isinstance(value, Frequency): 981 self.ticks = value.ticks 982 self.value = 1.0 / value.value 983 elif value.endswith('t'): 984 self.ticks = True 985 self.value = int(value[:-1]) 986 else: 987 self.ticks = False 988 self.value = convert.anyToLatency(value) 989 990 def __getattr__(self, attr): 991 if attr == 'frequency': 992 return Frequency(self) 993 if attr in ('latency', 'period'): 994 return Latency(self) 995 raise AttributeError, "Frequency object has no attribute '%s'" % attr 996 997 def getValue(self): 998 return self.period.getValue() 999 1000 def ini_str(self): 1001 return self.period.ini_str() 1002 1003class NetworkBandwidth(float,ParamValue): 1004 cxx_type = 'float' 1005 def __new__(cls, value): 1006 # convert to bits per second 1007 val = convert.toNetworkBandwidth(value) 1008 return super(cls, NetworkBandwidth).__new__(cls, val) 1009 1010 def __str__(self): 1011 return str(self.val) 1012 1013 def getValue(self): 1014 # convert to seconds per byte 1015 value = 8.0 / float(self) 1016 # convert to ticks per byte 1017 value = ticks.fromSeconds(value) 1018 return float(value) 1019 1020 def ini_str(self): 1021 return '%f' % self.getValue() 1022 1023class MemoryBandwidth(float,ParamValue): 1024 cxx_type = 'float' 1025 def __new__(cls, value): 1026 # we want the number of ticks per byte of data 1027 val = convert.toMemoryBandwidth(value) 1028 return super(cls, MemoryBandwidth).__new__(cls, val) 1029 1030 def __str__(self): 1031 return str(self.val) 1032 1033 def getValue(self): 1034 # convert to seconds per byte 1035 value = float(self) 1036 if value: 1037 value = 1.0 / float(self) 1038 # convert to ticks per byte 1039 value = ticks.fromSeconds(value) 1040 return float(value) 1041 1042 def ini_str(self): 1043 return '%f' % self.getValue() 1044 1045# 1046# "Constants"... handy aliases for various values. 1047# 1048 1049# Special class for NULL pointers. Note the special check in 1050# make_param_value() above that lets these be assigned where a 1051# SimObject is required. 1052# only one copy of a particular node 1053class NullSimObject(object): 1054 __metaclass__ = Singleton 1055 1056 def __call__(cls): 1057 return cls 1058 1059 def _instantiate(self, parent = None, path = ''): 1060 pass 1061 1062 def ini_str(self): 1063 return 'Null' 1064 1065 def unproxy(self, base): 1066 return self 1067 1068 def set_path(self, parent, name): 1069 pass 1070 1071 def __str__(self): 1072 return 'Null' 1073 1074 def getValue(self): 1075 return None 1076 1077# The only instance you'll ever need... 1078NULL = NullSimObject() 1079 1080def isNullPointer(value): 1081 return isinstance(value, NullSimObject) 1082 1083# Some memory range specifications use this as a default upper bound. 1084MaxAddr = Addr.max 1085MaxTick = Tick.max 1086AllMemory = AddrRange(0, MaxAddr) 1087 1088 1089##################################################################### 1090# 1091# Port objects 1092# 1093# Ports are used to interconnect objects in the memory system. 1094# 1095##################################################################### 1096 1097# Port reference: encapsulates a reference to a particular port on a 1098# particular SimObject. 1099class PortRef(object): 1100 def __init__(self, simobj, name): 1101 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1102 self.simobj = simobj 1103 self.name = name 1104 self.peer = None # not associated with another port yet 1105 self.ccConnected = False # C++ port connection done? 1106 self.index = -1 # always -1 for non-vector ports 1107 1108 def __str__(self): 1109 return '%s.%s' % (self.simobj, self.name) 1110 1111 # for config.ini, print peer's name (not ours) 1112 def ini_str(self): 1113 return str(self.peer) 1114 1115 def __getattr__(self, attr): 1116 if attr == 'peerObj': 1117 # shorthand for proxies 1118 return self.peer.simobj 1119 raise AttributeError, "'%s' object has no attribute '%s'" % \ 1120 (self.__class__.__name__, attr) 1121 1122 # Full connection is symmetric (both ways). Called via 1123 # SimObject.__setattr__ as a result of a port assignment, e.g., 1124 # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, 1125 # e.g., "obj1.portA[3] = obj2.portB". 1126 def connect(self, other): 1127 if isinstance(other, VectorPortRef): 1128 # reference to plain VectorPort is implicit append 1129 other = other._get_next() 1130 if self.peer and not proxy.isproxy(self.peer): 1131 print "warning: overwriting port", self, \ 1132 "value", self.peer, "with", other 1133 self.peer.peer = None 1134 self.peer = other 1135 if proxy.isproxy(other): 1136 other.set_param_desc(PortParamDesc()) 1137 elif isinstance(other, PortRef): 1138 if other.peer is not self: 1139 other.connect(self) 1140 else: 1141 raise TypeError, \ 1142 "assigning non-port reference '%s' to port '%s'" \ 1143 % (other, self) 1144 1145 def clone(self, simobj, memo): 1146 if memo.has_key(self): 1147 return memo[self] 1148 newRef = copy.copy(self) 1149 memo[self] = newRef 1150 newRef.simobj = simobj 1151 assert(isSimObject(newRef.simobj)) 1152 if self.peer and not proxy.isproxy(self.peer): 1153 peerObj = self.peer.simobj(_memo=memo) 1154 newRef.peer = self.peer.clone(peerObj, memo) 1155 assert(not isinstance(newRef.peer, VectorPortRef)) 1156 return newRef 1157 1158 def unproxy(self, simobj): 1159 assert(simobj is self.simobj) 1160 if proxy.isproxy(self.peer): 1161 try: 1162 realPeer = self.peer.unproxy(self.simobj) 1163 except: 1164 print "Error in unproxying port '%s' of %s" % \ 1165 (self.name, self.simobj.path()) 1166 raise 1167 self.connect(realPeer) 1168 1169 # Call C++ to create corresponding port connection between C++ objects 1170 def ccConnect(self): 1171 from m5.internal.params import connectPorts 1172 1173 if self.ccConnected: # already done this 1174 return 1175 peer = self.peer 1176 if not self.peer: # nothing to connect to 1177 return 1178 try: 1179 connectPorts(self.simobj.getCCObject(), self.name, self.index, 1180 peer.simobj.getCCObject(), peer.name, peer.index) 1181 except: 1182 print "Error connecting port %s.%s to %s.%s" % \ 1183 (self.simobj.path(), self.name, 1184 peer.simobj.path(), peer.name) 1185 raise 1186 self.ccConnected = True 1187 peer.ccConnected = True 1188 1189# A reference to an individual element of a VectorPort... much like a 1190# PortRef, but has an index. 1191class VectorPortElementRef(PortRef): 1192 def __init__(self, simobj, name, index): 1193 PortRef.__init__(self, simobj, name) 1194 self.index = index 1195 1196 def __str__(self): 1197 return '%s.%s[%d]' % (self.simobj, self.name, self.index) 1198 1199# A reference to a complete vector-valued port (not just a single element). 1200# Can be indexed to retrieve individual VectorPortElementRef instances. 1201class VectorPortRef(object): 1202 def __init__(self, simobj, name): 1203 assert(isSimObject(simobj) or isSimObjectClass(simobj)) 1204 self.simobj = simobj 1205 self.name = name 1206 self.elements = [] 1207 1208 def __str__(self): 1209 return '%s.%s[:]' % (self.simobj, self.name) 1210 1211 # for config.ini, print peer's name (not ours) 1212 def ini_str(self): 1213 return ' '.join([el.ini_str() for el in self.elements]) 1214 1215 def __getitem__(self, key): 1216 if not isinstance(key, int): 1217 raise TypeError, "VectorPort index must be integer" 1218 if key >= len(self.elements): 1219 # need to extend list 1220 ext = [VectorPortElementRef(self.simobj, self.name, i) 1221 for i in range(len(self.elements), key+1)] 1222 self.elements.extend(ext) 1223 return self.elements[key] 1224 1225 def _get_next(self): 1226 return self[len(self.elements)] 1227 1228 def __setitem__(self, key, value): 1229 if not isinstance(key, int): 1230 raise TypeError, "VectorPort index must be integer" 1231 self[key].connect(value) 1232 1233 def connect(self, other): 1234 if isinstance(other, (list, tuple)): 1235 # Assign list of port refs to vector port. 1236 # For now, append them... not sure if that's the right semantics 1237 # or if it should replace the current vector. 1238 for ref in other: 1239 self._get_next().connect(ref) 1240 else: 1241 # scalar assignment to plain VectorPort is implicit append 1242 self._get_next().connect(other) 1243 1244 def clone(self, simobj, memo): 1245 if memo.has_key(self): 1246 return memo[self] 1247 newRef = copy.copy(self) 1248 memo[self] = newRef 1249 newRef.simobj = simobj 1250 assert(isSimObject(newRef.simobj)) 1251 newRef.elements = [el.clone(simobj, memo) for el in self.elements] 1252 return newRef 1253 1254 def unproxy(self, simobj): 1255 [el.unproxy(simobj) for el in self.elements] 1256 1257 def ccConnect(self): 1258 [el.ccConnect() for el in self.elements] 1259 1260# Port description object. Like a ParamDesc object, this represents a 1261# logical port in the SimObject class, not a particular port on a 1262# SimObject instance. The latter are represented by PortRef objects. 1263class Port(object): 1264 # Port("description") or Port(default, "description") 1265 def __init__(self, *args): 1266 if len(args) == 1: 1267 self.desc = args[0] 1268 elif len(args) == 2: 1269 self.default = args[0] 1270 self.desc = args[1] 1271 else: 1272 raise TypeError, 'wrong number of arguments' 1273 # self.name is set by SimObject class on assignment 1274 # e.g., pio_port = Port("blah") sets self.name to 'pio_port' 1275 1276 # Generate a PortRef for this port on the given SimObject with the 1277 # given name 1278 def makeRef(self, simobj): 1279 return PortRef(simobj, self.name) 1280 1281 # Connect an instance of this port (on the given SimObject with 1282 # the given name) with the port described by the supplied PortRef 1283 def connect(self, simobj, ref): 1284 self.makeRef(simobj).connect(ref) 1285 1286# VectorPort description object. Like Port, but represents a vector 1287# of connections (e.g., as on a Bus). 1288class VectorPort(Port): 1289 def __init__(self, *args): 1290 Port.__init__(self, *args) 1291 self.isVec = True 1292 1293 def makeRef(self, simobj): 1294 return VectorPortRef(simobj, self.name) 1295 1296# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of 1297# proxy objects (via set_param_desc()) so that proxy error messages 1298# make sense. 1299class PortParamDesc(object): 1300 __metaclass__ = Singleton 1301 1302 ptype_str = 'Port' 1303 ptype = Port 1304 1305baseEnums = allEnums.copy() 1306baseParams = allParams.copy() 1307 1308def clear(): 1309 global allEnums, allParams 1310 1311 allEnums = baseEnums.copy() 1312 allParams = baseParams.copy() 1313 1314__all__ = ['Param', 'VectorParam', 1315 'Enum', 'Bool', 'String', 'Float', 1316 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1317 'Int32', 'UInt32', 'Int64', 'UInt64', 1318 'Counter', 'Addr', 'Tick', 'Percent', 1319 'TcpPort', 'UdpPort', 'EthernetAddr', 1320 'MemorySize', 'MemorySize32', 1321 'Latency', 'Frequency', 'Clock', 1322 'NetworkBandwidth', 'MemoryBandwidth', 1323 'Range', 'AddrRange', 'TickRange', 1324 'MaxAddr', 'MaxTick', 'AllMemory', 1325 'Time', 1326 'NextEthernetAddr', 'NULL', 1327 'Port', 'VectorPort'] 1328 1329import SimObject 1330