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