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