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