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