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