params.py revision 10019:a14f92150d3f
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, just like Latency and Frequency.
1225class Clock(TickParamValue):
1226    def __init__(self, value):
1227        if isinstance(value, (Latency, Clock)):
1228            self.ticks = value.ticks
1229            self.value = value.value
1230        elif isinstance(value, Frequency):
1231            self.ticks = value.ticks
1232            self.value = 1.0 / value.value
1233        elif value.endswith('t'):
1234            self.ticks = True
1235            self.value = int(value[:-1])
1236        else:
1237            self.ticks = False
1238            self.value = convert.anyToLatency(value)
1239
1240    def __getattr__(self, attr):
1241        if attr == 'frequency':
1242            return Frequency(self)
1243        if attr in ('latency', 'period'):
1244            return Latency(self)
1245        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1246
1247    def getValue(self):
1248        return self.period.getValue()
1249
1250    def ini_str(self):
1251        return self.period.ini_str()
1252
1253class Voltage(float,ParamValue):
1254    cxx_type = 'double'
1255    def __new__(cls, value):
1256        # convert to voltage
1257        val = convert.toVoltage(value)
1258        return super(cls, Voltage).__new__(cls, val)
1259
1260    def __str__(self):
1261        return str(self.val)
1262
1263    def getValue(self):
1264        value = float(self)
1265        return value
1266
1267    def ini_str(self):
1268        return '%f' % self.getValue()
1269
1270class NetworkBandwidth(float,ParamValue):
1271    cxx_type = 'float'
1272    def __new__(cls, value):
1273        # convert to bits per second
1274        val = convert.toNetworkBandwidth(value)
1275        return super(cls, NetworkBandwidth).__new__(cls, val)
1276
1277    def __str__(self):
1278        return str(self.val)
1279
1280    def getValue(self):
1281        # convert to seconds per byte
1282        value = 8.0 / float(self)
1283        # convert to ticks per byte
1284        value = ticks.fromSeconds(value)
1285        return float(value)
1286
1287    def ini_str(self):
1288        return '%f' % self.getValue()
1289
1290class MemoryBandwidth(float,ParamValue):
1291    cxx_type = 'float'
1292    def __new__(cls, value):
1293        # convert to bytes per second
1294        val = convert.toMemoryBandwidth(value)
1295        return super(cls, MemoryBandwidth).__new__(cls, val)
1296
1297    def __str__(self):
1298        return str(self.val)
1299
1300    def getValue(self):
1301        # convert to seconds per byte
1302        value = float(self)
1303        if value:
1304            value = 1.0 / float(self)
1305        # convert to ticks per byte
1306        value = ticks.fromSeconds(value)
1307        return float(value)
1308
1309    def ini_str(self):
1310        return '%f' % self.getValue()
1311
1312#
1313# "Constants"... handy aliases for various values.
1314#
1315
1316# Special class for NULL pointers.  Note the special check in
1317# make_param_value() above that lets these be assigned where a
1318# SimObject is required.
1319# only one copy of a particular node
1320class NullSimObject(object):
1321    __metaclass__ = Singleton
1322
1323    def __call__(cls):
1324        return cls
1325
1326    def _instantiate(self, parent = None, path = ''):
1327        pass
1328
1329    def ini_str(self):
1330        return 'Null'
1331
1332    def unproxy(self, base):
1333        return self
1334
1335    def set_path(self, parent, name):
1336        pass
1337
1338    def __str__(self):
1339        return 'Null'
1340
1341    def getValue(self):
1342        return None
1343
1344# The only instance you'll ever need...
1345NULL = NullSimObject()
1346
1347def isNullPointer(value):
1348    return isinstance(value, NullSimObject)
1349
1350# Some memory range specifications use this as a default upper bound.
1351MaxAddr = Addr.max
1352MaxTick = Tick.max
1353AllMemory = AddrRange(0, MaxAddr)
1354
1355
1356#####################################################################
1357#
1358# Port objects
1359#
1360# Ports are used to interconnect objects in the memory system.
1361#
1362#####################################################################
1363
1364# Port reference: encapsulates a reference to a particular port on a
1365# particular SimObject.
1366class PortRef(object):
1367    def __init__(self, simobj, name, role):
1368        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1369        self.simobj = simobj
1370        self.name = name
1371        self.role = role
1372        self.peer = None   # not associated with another port yet
1373        self.ccConnected = False # C++ port connection done?
1374        self.index = -1  # always -1 for non-vector ports
1375
1376    def __str__(self):
1377        return '%s.%s' % (self.simobj, self.name)
1378
1379    def __len__(self):
1380        # Return the number of connected ports, i.e. 0 is we have no
1381        # peer and 1 if we do.
1382        return int(self.peer != None)
1383
1384    # for config.ini, print peer's name (not ours)
1385    def ini_str(self):
1386        return str(self.peer)
1387
1388    # for config.json
1389    def get_config_as_dict(self):
1390        return {'role' : self.role, 'peer' : str(self.peer)}
1391
1392    def __getattr__(self, attr):
1393        if attr == 'peerObj':
1394            # shorthand for proxies
1395            return self.peer.simobj
1396        raise AttributeError, "'%s' object has no attribute '%s'" % \
1397              (self.__class__.__name__, attr)
1398
1399    # Full connection is symmetric (both ways).  Called via
1400    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1401    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1402    # e.g., "obj1.portA[3] = obj2.portB".
1403    def connect(self, other):
1404        if isinstance(other, VectorPortRef):
1405            # reference to plain VectorPort is implicit append
1406            other = other._get_next()
1407        if self.peer and not proxy.isproxy(self.peer):
1408            fatal("Port %s is already connected to %s, cannot connect %s\n",
1409                  self, self.peer, other);
1410        self.peer = other
1411        if proxy.isproxy(other):
1412            other.set_param_desc(PortParamDesc())
1413        elif isinstance(other, PortRef):
1414            if other.peer is not self:
1415                other.connect(self)
1416        else:
1417            raise TypeError, \
1418                  "assigning non-port reference '%s' to port '%s'" \
1419                  % (other, self)
1420
1421    def clone(self, simobj, memo):
1422        if memo.has_key(self):
1423            return memo[self]
1424        newRef = copy.copy(self)
1425        memo[self] = newRef
1426        newRef.simobj = simobj
1427        assert(isSimObject(newRef.simobj))
1428        if self.peer and not proxy.isproxy(self.peer):
1429            peerObj = self.peer.simobj(_memo=memo)
1430            newRef.peer = self.peer.clone(peerObj, memo)
1431            assert(not isinstance(newRef.peer, VectorPortRef))
1432        return newRef
1433
1434    def unproxy(self, simobj):
1435        assert(simobj is self.simobj)
1436        if proxy.isproxy(self.peer):
1437            try:
1438                realPeer = self.peer.unproxy(self.simobj)
1439            except:
1440                print "Error in unproxying port '%s' of %s" % \
1441                      (self.name, self.simobj.path())
1442                raise
1443            self.connect(realPeer)
1444
1445    # Call C++ to create corresponding port connection between C++ objects
1446    def ccConnect(self):
1447        from m5.internal.pyobject import connectPorts
1448
1449        if self.role == 'SLAVE':
1450            # do nothing and let the master take care of it
1451            return
1452
1453        if self.ccConnected: # already done this
1454            return
1455        peer = self.peer
1456        if not self.peer: # nothing to connect to
1457            return
1458
1459        # check that we connect a master to a slave
1460        if self.role == peer.role:
1461            raise TypeError, \
1462                "cannot connect '%s' and '%s' due to identical role '%s'" \
1463                % (peer, self, self.role)
1464
1465        try:
1466            # self is always the master and peer the slave
1467            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1468                         peer.simobj.getCCObject(), peer.name, peer.index)
1469        except:
1470            print "Error connecting port %s.%s to %s.%s" % \
1471                  (self.simobj.path(), self.name,
1472                   peer.simobj.path(), peer.name)
1473            raise
1474        self.ccConnected = True
1475        peer.ccConnected = True
1476
1477# A reference to an individual element of a VectorPort... much like a
1478# PortRef, but has an index.
1479class VectorPortElementRef(PortRef):
1480    def __init__(self, simobj, name, role, index):
1481        PortRef.__init__(self, simobj, name, role)
1482        self.index = index
1483
1484    def __str__(self):
1485        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1486
1487# A reference to a complete vector-valued port (not just a single element).
1488# Can be indexed to retrieve individual VectorPortElementRef instances.
1489class VectorPortRef(object):
1490    def __init__(self, simobj, name, role):
1491        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1492        self.simobj = simobj
1493        self.name = name
1494        self.role = role
1495        self.elements = []
1496
1497    def __str__(self):
1498        return '%s.%s[:]' % (self.simobj, self.name)
1499
1500    def __len__(self):
1501        # Return the number of connected peers, corresponding the the
1502        # length of the elements.
1503        return len(self.elements)
1504
1505    # for config.ini, print peer's name (not ours)
1506    def ini_str(self):
1507        return ' '.join([el.ini_str() for el in self.elements])
1508
1509    # for config.json
1510    def get_config_as_dict(self):
1511        return {'role' : self.role,
1512                'peer' : [el.ini_str() for el in self.elements]}
1513
1514    def __getitem__(self, key):
1515        if not isinstance(key, int):
1516            raise TypeError, "VectorPort index must be integer"
1517        if key >= len(self.elements):
1518            # need to extend list
1519            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1520                   for i in range(len(self.elements), key+1)]
1521            self.elements.extend(ext)
1522        return self.elements[key]
1523
1524    def _get_next(self):
1525        return self[len(self.elements)]
1526
1527    def __setitem__(self, key, value):
1528        if not isinstance(key, int):
1529            raise TypeError, "VectorPort index must be integer"
1530        self[key].connect(value)
1531
1532    def connect(self, other):
1533        if isinstance(other, (list, tuple)):
1534            # Assign list of port refs to vector port.
1535            # For now, append them... not sure if that's the right semantics
1536            # or if it should replace the current vector.
1537            for ref in other:
1538                self._get_next().connect(ref)
1539        else:
1540            # scalar assignment to plain VectorPort is implicit append
1541            self._get_next().connect(other)
1542
1543    def clone(self, simobj, memo):
1544        if memo.has_key(self):
1545            return memo[self]
1546        newRef = copy.copy(self)
1547        memo[self] = newRef
1548        newRef.simobj = simobj
1549        assert(isSimObject(newRef.simobj))
1550        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1551        return newRef
1552
1553    def unproxy(self, simobj):
1554        [el.unproxy(simobj) for el in self.elements]
1555
1556    def ccConnect(self):
1557        [el.ccConnect() for el in self.elements]
1558
1559# Port description object.  Like a ParamDesc object, this represents a
1560# logical port in the SimObject class, not a particular port on a
1561# SimObject instance.  The latter are represented by PortRef objects.
1562class Port(object):
1563    # Generate a PortRef for this port on the given SimObject with the
1564    # given name
1565    def makeRef(self, simobj):
1566        return PortRef(simobj, self.name, self.role)
1567
1568    # Connect an instance of this port (on the given SimObject with
1569    # the given name) with the port described by the supplied PortRef
1570    def connect(self, simobj, ref):
1571        self.makeRef(simobj).connect(ref)
1572
1573    # No need for any pre-declarations at the moment as we merely rely
1574    # on an unsigned int.
1575    def cxx_predecls(self, code):
1576        pass
1577
1578    # Declare an unsigned int with the same name as the port, that
1579    # will eventually hold the number of connected ports (and thus the
1580    # number of elements for a VectorPort).
1581    def cxx_decl(self, code):
1582        code('unsigned int port_${{self.name}}_connection_count;')
1583
1584class MasterPort(Port):
1585    # MasterPort("description")
1586    def __init__(self, *args):
1587        if len(args) == 1:
1588            self.desc = args[0]
1589            self.role = 'MASTER'
1590        else:
1591            raise TypeError, 'wrong number of arguments'
1592
1593class SlavePort(Port):
1594    # SlavePort("description")
1595    def __init__(self, *args):
1596        if len(args) == 1:
1597            self.desc = args[0]
1598            self.role = 'SLAVE'
1599        else:
1600            raise TypeError, 'wrong number of arguments'
1601
1602# VectorPort description object.  Like Port, but represents a vector
1603# of connections (e.g., as on a Bus).
1604class VectorPort(Port):
1605    def __init__(self, *args):
1606        self.isVec = True
1607
1608    def makeRef(self, simobj):
1609        return VectorPortRef(simobj, self.name, self.role)
1610
1611class VectorMasterPort(VectorPort):
1612    # VectorMasterPort("description")
1613    def __init__(self, *args):
1614        if len(args) == 1:
1615            self.desc = args[0]
1616            self.role = 'MASTER'
1617            VectorPort.__init__(self, *args)
1618        else:
1619            raise TypeError, 'wrong number of arguments'
1620
1621class VectorSlavePort(VectorPort):
1622    # VectorSlavePort("description")
1623    def __init__(self, *args):
1624        if len(args) == 1:
1625            self.desc = args[0]
1626            self.role = 'SLAVE'
1627            VectorPort.__init__(self, *args)
1628        else:
1629            raise TypeError, 'wrong number of arguments'
1630
1631# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1632# proxy objects (via set_param_desc()) so that proxy error messages
1633# make sense.
1634class PortParamDesc(object):
1635    __metaclass__ = Singleton
1636
1637    ptype_str = 'Port'
1638    ptype = Port
1639
1640baseEnums = allEnums.copy()
1641baseParams = allParams.copy()
1642
1643def clear():
1644    global allEnums, allParams
1645
1646    allEnums = baseEnums.copy()
1647    allParams = baseParams.copy()
1648
1649__all__ = ['Param', 'VectorParam',
1650           'Enum', 'Bool', 'String', 'Float',
1651           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1652           'Int32', 'UInt32', 'Int64', 'UInt64',
1653           'Counter', 'Addr', 'Tick', 'Percent',
1654           'TcpPort', 'UdpPort', 'EthernetAddr',
1655           'IpAddress', 'IpNetmask', 'IpWithPort',
1656           'MemorySize', 'MemorySize32',
1657           'Latency', 'Frequency', 'Clock', 'Voltage',
1658           'NetworkBandwidth', 'MemoryBandwidth',
1659           'AddrRange',
1660           'MaxAddr', 'MaxTick', 'AllMemory',
1661           'Time',
1662           'NextEthernetAddr', 'NULL',
1663           'MasterPort', 'SlavePort',
1664           'VectorMasterPort', 'VectorSlavePort']
1665
1666import SimObject
1667