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