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