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