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