params.py revision 10181:6270235e0585
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        name = cls.__name__
1044        code('''\
1045#ifndef __ENUM__${name}__
1046#define __ENUM__${name}__
1047
1048namespace Enums {
1049    enum $name {
1050''')
1051        code.indent(2)
1052        for val in cls.vals:
1053            code('$val = ${{cls.map[val]}},')
1054        code('Num_$name = ${{len(cls.vals)}}')
1055        code.dedent(2)
1056        code('''\
1057    };
1058extern const char *${name}Strings[Num_${name}];
1059}
1060
1061#endif // __ENUM__${name}__
1062''')
1063
1064    def cxx_def(cls, code):
1065        name = cls.__name__
1066        code('''\
1067#include "enums/$name.hh"
1068namespace Enums {
1069    const char *${name}Strings[Num_${name}] =
1070    {
1071''')
1072        code.indent(2)
1073        for val in cls.vals:
1074            code('"$val",')
1075        code.dedent(2)
1076        code('''
1077    };
1078} // namespace Enums
1079''')
1080
1081    def swig_decl(cls, code):
1082        name = cls.__name__
1083        code('''\
1084%module(package="m5.internal") enum_$name
1085
1086%{
1087#include "enums/$name.hh"
1088%}
1089
1090%include "enums/$name.hh"
1091''')
1092
1093
1094# Base class for enum types.
1095class Enum(ParamValue):
1096    __metaclass__ = MetaEnum
1097    vals = []
1098
1099    def __init__(self, value):
1100        if value not in self.map:
1101            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1102                  % (value, self.vals)
1103        self.value = value
1104
1105    @classmethod
1106    def cxx_predecls(cls, code):
1107        code('#include "enums/$0.hh"', cls.__name__)
1108
1109    @classmethod
1110    def swig_predecls(cls, code):
1111        code('%import "python/m5/internal/enum_$0.i"', cls.__name__)
1112
1113    def getValue(self):
1114        return int(self.map[self.value])
1115
1116    def __str__(self):
1117        return self.value
1118
1119# how big does a rounding error need to be before we warn about it?
1120frequency_tolerance = 0.001  # 0.1%
1121
1122class TickParamValue(NumericParamValue):
1123    cxx_type = 'Tick'
1124
1125    @classmethod
1126    def cxx_predecls(cls, code):
1127        code('#include "base/types.hh"')
1128
1129    @classmethod
1130    def swig_predecls(cls, code):
1131        code('%import "stdint.i"')
1132        code('%import "base/types.hh"')
1133
1134    def getValue(self):
1135        return long(self.value)
1136
1137class Latency(TickParamValue):
1138    def __init__(self, value):
1139        if isinstance(value, (Latency, Clock)):
1140            self.ticks = value.ticks
1141            self.value = value.value
1142        elif isinstance(value, Frequency):
1143            self.ticks = value.ticks
1144            self.value = 1.0 / value.value
1145        elif value.endswith('t'):
1146            self.ticks = True
1147            self.value = int(value[:-1])
1148        else:
1149            self.ticks = False
1150            self.value = convert.toLatency(value)
1151
1152    def __getattr__(self, attr):
1153        if attr in ('latency', 'period'):
1154            return self
1155        if attr == 'frequency':
1156            return Frequency(self)
1157        raise AttributeError, "Latency object has no attribute '%s'" % attr
1158
1159    def getValue(self):
1160        if self.ticks or self.value == 0:
1161            value = self.value
1162        else:
1163            value = ticks.fromSeconds(self.value)
1164        return long(value)
1165
1166    # convert latency to ticks
1167    def ini_str(self):
1168        return '%d' % self.getValue()
1169
1170class Frequency(TickParamValue):
1171    def __init__(self, value):
1172        if isinstance(value, (Latency, Clock)):
1173            if value.value == 0:
1174                self.value = 0
1175            else:
1176                self.value = 1.0 / value.value
1177            self.ticks = value.ticks
1178        elif isinstance(value, Frequency):
1179            self.value = value.value
1180            self.ticks = value.ticks
1181        else:
1182            self.ticks = False
1183            self.value = convert.toFrequency(value)
1184
1185    def __getattr__(self, attr):
1186        if attr == 'frequency':
1187            return self
1188        if attr in ('latency', 'period'):
1189            return Latency(self)
1190        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1191
1192    # convert latency to ticks
1193    def getValue(self):
1194        if self.ticks or self.value == 0:
1195            value = self.value
1196        else:
1197            value = ticks.fromSeconds(1.0 / self.value)
1198        return long(value)
1199
1200    def ini_str(self):
1201        return '%d' % self.getValue()
1202
1203# A generic Frequency and/or Latency value. Value is stored as a
1204# latency, just like Latency and Frequency.
1205class Clock(TickParamValue):
1206    def __init__(self, value):
1207        if isinstance(value, (Latency, Clock)):
1208            self.ticks = value.ticks
1209            self.value = value.value
1210        elif isinstance(value, Frequency):
1211            self.ticks = value.ticks
1212            self.value = 1.0 / value.value
1213        elif value.endswith('t'):
1214            self.ticks = True
1215            self.value = int(value[:-1])
1216        else:
1217            self.ticks = False
1218            self.value = convert.anyToLatency(value)
1219
1220    def __getattr__(self, attr):
1221        if attr == 'frequency':
1222            return Frequency(self)
1223        if attr in ('latency', 'period'):
1224            return Latency(self)
1225        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1226
1227    def getValue(self):
1228        return self.period.getValue()
1229
1230    def ini_str(self):
1231        return self.period.ini_str()
1232
1233class Voltage(float,ParamValue):
1234    cxx_type = 'double'
1235    def __new__(cls, value):
1236        # convert to voltage
1237        val = convert.toVoltage(value)
1238        return super(cls, Voltage).__new__(cls, val)
1239
1240    def __str__(self):
1241        return str(self.val)
1242
1243    def getValue(self):
1244        value = float(self)
1245        return value
1246
1247    def ini_str(self):
1248        return '%f' % self.getValue()
1249
1250class NetworkBandwidth(float,ParamValue):
1251    cxx_type = 'float'
1252    def __new__(cls, value):
1253        # convert to bits per second
1254        val = convert.toNetworkBandwidth(value)
1255        return super(cls, NetworkBandwidth).__new__(cls, val)
1256
1257    def __str__(self):
1258        return str(self.val)
1259
1260    def getValue(self):
1261        # convert to seconds per byte
1262        value = 8.0 / float(self)
1263        # convert to ticks per byte
1264        value = ticks.fromSeconds(value)
1265        return float(value)
1266
1267    def ini_str(self):
1268        return '%f' % self.getValue()
1269
1270class MemoryBandwidth(float,ParamValue):
1271    cxx_type = 'float'
1272    def __new__(cls, value):
1273        # convert to bytes per second
1274        val = convert.toMemoryBandwidth(value)
1275        return super(cls, MemoryBandwidth).__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 = float(self)
1283        if value:
1284            value = 1.0 / float(self)
1285        # convert to ticks per byte
1286        value = ticks.fromSeconds(value)
1287        return float(value)
1288
1289    def ini_str(self):
1290        return '%f' % self.getValue()
1291
1292#
1293# "Constants"... handy aliases for various values.
1294#
1295
1296# Special class for NULL pointers.  Note the special check in
1297# make_param_value() above that lets these be assigned where a
1298# SimObject is required.
1299# only one copy of a particular node
1300class NullSimObject(object):
1301    __metaclass__ = Singleton
1302
1303    def __call__(cls):
1304        return cls
1305
1306    def _instantiate(self, parent = None, path = ''):
1307        pass
1308
1309    def ini_str(self):
1310        return 'Null'
1311
1312    def unproxy(self, base):
1313        return self
1314
1315    def set_path(self, parent, name):
1316        pass
1317
1318    def __str__(self):
1319        return 'Null'
1320
1321    def getValue(self):
1322        return None
1323
1324# The only instance you'll ever need...
1325NULL = NullSimObject()
1326
1327def isNullPointer(value):
1328    return isinstance(value, NullSimObject)
1329
1330# Some memory range specifications use this as a default upper bound.
1331MaxAddr = Addr.max
1332MaxTick = Tick.max
1333AllMemory = AddrRange(0, MaxAddr)
1334
1335
1336#####################################################################
1337#
1338# Port objects
1339#
1340# Ports are used to interconnect objects in the memory system.
1341#
1342#####################################################################
1343
1344# Port reference: encapsulates a reference to a particular port on a
1345# particular SimObject.
1346class PortRef(object):
1347    def __init__(self, simobj, name, role):
1348        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1349        self.simobj = simobj
1350        self.name = name
1351        self.role = role
1352        self.peer = None   # not associated with another port yet
1353        self.ccConnected = False # C++ port connection done?
1354        self.index = -1  # always -1 for non-vector ports
1355
1356    def __str__(self):
1357        return '%s.%s' % (self.simobj, self.name)
1358
1359    def __len__(self):
1360        # Return the number of connected ports, i.e. 0 is we have no
1361        # peer and 1 if we do.
1362        return int(self.peer != None)
1363
1364    # for config.ini, print peer's name (not ours)
1365    def ini_str(self):
1366        return str(self.peer)
1367
1368    # for config.json
1369    def get_config_as_dict(self):
1370        return {'role' : self.role, 'peer' : str(self.peer)}
1371
1372    def __getattr__(self, attr):
1373        if attr == 'peerObj':
1374            # shorthand for proxies
1375            return self.peer.simobj
1376        raise AttributeError, "'%s' object has no attribute '%s'" % \
1377              (self.__class__.__name__, attr)
1378
1379    # Full connection is symmetric (both ways).  Called via
1380    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1381    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1382    # e.g., "obj1.portA[3] = obj2.portB".
1383    def connect(self, other):
1384        if isinstance(other, VectorPortRef):
1385            # reference to plain VectorPort is implicit append
1386            other = other._get_next()
1387        if self.peer and not proxy.isproxy(self.peer):
1388            fatal("Port %s is already connected to %s, cannot connect %s\n",
1389                  self, self.peer, other);
1390        self.peer = other
1391        if proxy.isproxy(other):
1392            other.set_param_desc(PortParamDesc())
1393        elif isinstance(other, PortRef):
1394            if other.peer is not self:
1395                other.connect(self)
1396        else:
1397            raise TypeError, \
1398                  "assigning non-port reference '%s' to port '%s'" \
1399                  % (other, self)
1400
1401    def clone(self, simobj, memo):
1402        if memo.has_key(self):
1403            return memo[self]
1404        newRef = copy.copy(self)
1405        memo[self] = newRef
1406        newRef.simobj = simobj
1407        assert(isSimObject(newRef.simobj))
1408        if self.peer and not proxy.isproxy(self.peer):
1409            peerObj = self.peer.simobj(_memo=memo)
1410            newRef.peer = self.peer.clone(peerObj, memo)
1411            assert(not isinstance(newRef.peer, VectorPortRef))
1412        return newRef
1413
1414    def unproxy(self, simobj):
1415        assert(simobj is self.simobj)
1416        if proxy.isproxy(self.peer):
1417            try:
1418                realPeer = self.peer.unproxy(self.simobj)
1419            except:
1420                print "Error in unproxying port '%s' of %s" % \
1421                      (self.name, self.simobj.path())
1422                raise
1423            self.connect(realPeer)
1424
1425    # Call C++ to create corresponding port connection between C++ objects
1426    def ccConnect(self):
1427        from m5.internal.pyobject import connectPorts
1428
1429        if self.role == 'SLAVE':
1430            # do nothing and let the master take care of it
1431            return
1432
1433        if self.ccConnected: # already done this
1434            return
1435        peer = self.peer
1436        if not self.peer: # nothing to connect to
1437            return
1438
1439        # check that we connect a master to a slave
1440        if self.role == peer.role:
1441            raise TypeError, \
1442                "cannot connect '%s' and '%s' due to identical role '%s'" \
1443                % (peer, self, self.role)
1444
1445        try:
1446            # self is always the master and peer the slave
1447            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1448                         peer.simobj.getCCObject(), peer.name, peer.index)
1449        except:
1450            print "Error connecting port %s.%s to %s.%s" % \
1451                  (self.simobj.path(), self.name,
1452                   peer.simobj.path(), peer.name)
1453            raise
1454        self.ccConnected = True
1455        peer.ccConnected = True
1456
1457# A reference to an individual element of a VectorPort... much like a
1458# PortRef, but has an index.
1459class VectorPortElementRef(PortRef):
1460    def __init__(self, simobj, name, role, index):
1461        PortRef.__init__(self, simobj, name, role)
1462        self.index = index
1463
1464    def __str__(self):
1465        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1466
1467# A reference to a complete vector-valued port (not just a single element).
1468# Can be indexed to retrieve individual VectorPortElementRef instances.
1469class VectorPortRef(object):
1470    def __init__(self, simobj, name, role):
1471        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1472        self.simobj = simobj
1473        self.name = name
1474        self.role = role
1475        self.elements = []
1476
1477    def __str__(self):
1478        return '%s.%s[:]' % (self.simobj, self.name)
1479
1480    def __len__(self):
1481        # Return the number of connected peers, corresponding the the
1482        # length of the elements.
1483        return len(self.elements)
1484
1485    # for config.ini, print peer's name (not ours)
1486    def ini_str(self):
1487        return ' '.join([el.ini_str() for el in self.elements])
1488
1489    # for config.json
1490    def get_config_as_dict(self):
1491        return {'role' : self.role,
1492                'peer' : [el.ini_str() for el in self.elements]}
1493
1494    def __getitem__(self, key):
1495        if not isinstance(key, int):
1496            raise TypeError, "VectorPort index must be integer"
1497        if key >= len(self.elements):
1498            # need to extend list
1499            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1500                   for i in range(len(self.elements), key+1)]
1501            self.elements.extend(ext)
1502        return self.elements[key]
1503
1504    def _get_next(self):
1505        return self[len(self.elements)]
1506
1507    def __setitem__(self, key, value):
1508        if not isinstance(key, int):
1509            raise TypeError, "VectorPort index must be integer"
1510        self[key].connect(value)
1511
1512    def connect(self, other):
1513        if isinstance(other, (list, tuple)):
1514            # Assign list of port refs to vector port.
1515            # For now, append them... not sure if that's the right semantics
1516            # or if it should replace the current vector.
1517            for ref in other:
1518                self._get_next().connect(ref)
1519        else:
1520            # scalar assignment to plain VectorPort is implicit append
1521            self._get_next().connect(other)
1522
1523    def clone(self, simobj, memo):
1524        if memo.has_key(self):
1525            return memo[self]
1526        newRef = copy.copy(self)
1527        memo[self] = newRef
1528        newRef.simobj = simobj
1529        assert(isSimObject(newRef.simobj))
1530        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1531        return newRef
1532
1533    def unproxy(self, simobj):
1534        [el.unproxy(simobj) for el in self.elements]
1535
1536    def ccConnect(self):
1537        [el.ccConnect() for el in self.elements]
1538
1539# Port description object.  Like a ParamDesc object, this represents a
1540# logical port in the SimObject class, not a particular port on a
1541# SimObject instance.  The latter are represented by PortRef objects.
1542class Port(object):
1543    # Generate a PortRef for this port on the given SimObject with the
1544    # given name
1545    def makeRef(self, simobj):
1546        return PortRef(simobj, self.name, self.role)
1547
1548    # Connect an instance of this port (on the given SimObject with
1549    # the given name) with the port described by the supplied PortRef
1550    def connect(self, simobj, ref):
1551        self.makeRef(simobj).connect(ref)
1552
1553    # No need for any pre-declarations at the moment as we merely rely
1554    # on an unsigned int.
1555    def cxx_predecls(self, code):
1556        pass
1557
1558    # Declare an unsigned int with the same name as the port, that
1559    # will eventually hold the number of connected ports (and thus the
1560    # number of elements for a VectorPort).
1561    def cxx_decl(self, code):
1562        code('unsigned int port_${{self.name}}_connection_count;')
1563
1564class MasterPort(Port):
1565    # MasterPort("description")
1566    def __init__(self, *args):
1567        if len(args) == 1:
1568            self.desc = args[0]
1569            self.role = 'MASTER'
1570        else:
1571            raise TypeError, 'wrong number of arguments'
1572
1573class SlavePort(Port):
1574    # SlavePort("description")
1575    def __init__(self, *args):
1576        if len(args) == 1:
1577            self.desc = args[0]
1578            self.role = 'SLAVE'
1579        else:
1580            raise TypeError, 'wrong number of arguments'
1581
1582# VectorPort description object.  Like Port, but represents a vector
1583# of connections (e.g., as on a Bus).
1584class VectorPort(Port):
1585    def __init__(self, *args):
1586        self.isVec = True
1587
1588    def makeRef(self, simobj):
1589        return VectorPortRef(simobj, self.name, self.role)
1590
1591class VectorMasterPort(VectorPort):
1592    # VectorMasterPort("description")
1593    def __init__(self, *args):
1594        if len(args) == 1:
1595            self.desc = args[0]
1596            self.role = 'MASTER'
1597            VectorPort.__init__(self, *args)
1598        else:
1599            raise TypeError, 'wrong number of arguments'
1600
1601class VectorSlavePort(VectorPort):
1602    # VectorSlavePort("description")
1603    def __init__(self, *args):
1604        if len(args) == 1:
1605            self.desc = args[0]
1606            self.role = 'SLAVE'
1607            VectorPort.__init__(self, *args)
1608        else:
1609            raise TypeError, 'wrong number of arguments'
1610
1611# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1612# proxy objects (via set_param_desc()) so that proxy error messages
1613# make sense.
1614class PortParamDesc(object):
1615    __metaclass__ = Singleton
1616
1617    ptype_str = 'Port'
1618    ptype = Port
1619
1620baseEnums = allEnums.copy()
1621baseParams = allParams.copy()
1622
1623def clear():
1624    global allEnums, allParams
1625
1626    allEnums = baseEnums.copy()
1627    allParams = baseParams.copy()
1628
1629__all__ = ['Param', 'VectorParam',
1630           'Enum', 'Bool', 'String', 'Float',
1631           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1632           'Int32', 'UInt32', 'Int64', 'UInt64',
1633           'Counter', 'Addr', 'Tick', 'Percent',
1634           'TcpPort', 'UdpPort', 'EthernetAddr',
1635           'IpAddress', 'IpNetmask', 'IpWithPort',
1636           'MemorySize', 'MemorySize32',
1637           'Latency', 'Frequency', 'Clock', 'Voltage',
1638           'NetworkBandwidth', 'MemoryBandwidth',
1639           'AddrRange',
1640           'MaxAddr', 'MaxTick', 'AllMemory',
1641           'Time',
1642           'NextEthernetAddr', 'NULL',
1643           'MasterPort', 'SlavePort',
1644           'VectorMasterPort', 'VectorSlavePort']
1645
1646import SimObject
1647