params.py revision 9232
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 = 'Range<Addr>'
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/range.hh"')
598
599    @classmethod
600    def swig_predecls(cls, code):
601        Addr.swig_predecls(code)
602        code('%import "python/swig/range.i"')
603
604    def getValue(self):
605        from m5.internal.range import AddrRange
606
607        value = AddrRange()
608        value.start = long(self.start)
609        value.end = long(self.end)
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    # implement truth value testing for Bool parameters so that these params
630    # evaluate correctly during the python configuration phase
631    def __nonzero__(self):
632        return bool(self.value)
633
634    def ini_str(self):
635        if self.value:
636            return 'true'
637        return 'false'
638
639def IncEthernetAddr(addr, val = 1):
640    bytes = map(lambda x: int(x, 16), addr.split(':'))
641    bytes[5] += val
642    for i in (5, 4, 3, 2, 1):
643        val,rem = divmod(bytes[i], 256)
644        bytes[i] = rem
645        if val == 0:
646            break
647        bytes[i - 1] += val
648    assert(bytes[0] <= 255)
649    return ':'.join(map(lambda x: '%02x' % x, bytes))
650
651_NextEthernetAddr = "00:90:00:00:00:01"
652def NextEthernetAddr():
653    global _NextEthernetAddr
654
655    value = _NextEthernetAddr
656    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
657    return value
658
659class EthernetAddr(ParamValue):
660    cxx_type = 'Net::EthAddr'
661
662    @classmethod
663    def cxx_predecls(cls, code):
664        code('#include "base/inet.hh"')
665
666    @classmethod
667    def swig_predecls(cls, code):
668        code('%include "python/swig/inet.i"')
669
670    def __init__(self, value):
671        if value == NextEthernetAddr:
672            self.value = value
673            return
674
675        if not isinstance(value, str):
676            raise TypeError, "expected an ethernet address and didn't get one"
677
678        bytes = value.split(':')
679        if len(bytes) != 6:
680            raise TypeError, 'invalid ethernet address %s' % value
681
682        for byte in bytes:
683            if not 0 <= int(byte) <= 0xff:
684                raise TypeError, 'invalid ethernet address %s' % value
685
686        self.value = value
687
688    def unproxy(self, base):
689        if self.value == NextEthernetAddr:
690            return EthernetAddr(self.value())
691        return self
692
693    def getValue(self):
694        from m5.internal.params import EthAddr
695        return EthAddr(self.value)
696
697    def ini_str(self):
698        return self.value
699
700# When initializing an IpAddress, pass in an existing IpAddress, a string of
701# the form "a.b.c.d", or an integer representing an IP.
702class IpAddress(ParamValue):
703    cxx_type = 'Net::IpAddress'
704
705    @classmethod
706    def cxx_predecls(cls, code):
707        code('#include "base/inet.hh"')
708
709    @classmethod
710    def swig_predecls(cls, code):
711        code('%include "python/swig/inet.i"')
712
713    def __init__(self, value):
714        if isinstance(value, IpAddress):
715            self.ip = value.ip
716        else:
717            try:
718                self.ip = convert.toIpAddress(value)
719            except TypeError:
720                self.ip = long(value)
721        self.verifyIp()
722
723    def __str__(self):
724        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
725        return '%d.%d.%d.%d' % tuple(tup)
726
727    def __eq__(self, other):
728        if isinstance(other, IpAddress):
729            return self.ip == other.ip
730        elif isinstance(other, str):
731            try:
732                return self.ip == convert.toIpAddress(other)
733            except:
734                return False
735        else:
736            return self.ip == other
737
738    def __ne__(self, other):
739        return not (self == other)
740
741    def verifyIp(self):
742        if self.ip < 0 or self.ip >= (1 << 32):
743            raise TypeError, "invalid ip address %#08x" % self.ip
744
745    def getValue(self):
746        from m5.internal.params import IpAddress
747        return IpAddress(self.ip)
748
749# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
750# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
751# positional or keyword arguments.
752class IpNetmask(IpAddress):
753    cxx_type = 'Net::IpNetmask'
754
755    @classmethod
756    def cxx_predecls(cls, code):
757        code('#include "base/inet.hh"')
758
759    @classmethod
760    def swig_predecls(cls, code):
761        code('%include "python/swig/inet.i"')
762
763    def __init__(self, *args, **kwargs):
764        def handle_kwarg(self, kwargs, key, elseVal = None):
765            if key in kwargs:
766                setattr(self, key, kwargs.pop(key))
767            elif elseVal:
768                setattr(self, key, elseVal)
769            else:
770                raise TypeError, "No value set for %s" % key
771
772        if len(args) == 0:
773            handle_kwarg(self, kwargs, 'ip')
774            handle_kwarg(self, kwargs, 'netmask')
775
776        elif len(args) == 1:
777            if kwargs:
778                if not 'ip' in kwargs and not 'netmask' in kwargs:
779                    raise TypeError, "Invalid arguments"
780                handle_kwarg(self, kwargs, 'ip', args[0])
781                handle_kwarg(self, kwargs, 'netmask', args[0])
782            elif isinstance(args[0], IpNetmask):
783                self.ip = args[0].ip
784                self.netmask = args[0].netmask
785            else:
786                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
787
788        elif len(args) == 2:
789            self.ip = args[0]
790            self.netmask = args[1]
791        else:
792            raise TypeError, "Too many arguments specified"
793
794        if kwargs:
795            raise TypeError, "Too many keywords: %s" % kwargs.keys()
796
797        self.verify()
798
799    def __str__(self):
800        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
801
802    def __eq__(self, other):
803        if isinstance(other, IpNetmask):
804            return self.ip == other.ip and self.netmask == other.netmask
805        elif isinstance(other, str):
806            try:
807                return (self.ip, self.netmask) == convert.toIpNetmask(other)
808            except:
809                return False
810        else:
811            return False
812
813    def verify(self):
814        self.verifyIp()
815        if self.netmask < 0 or self.netmask > 32:
816            raise TypeError, "invalid netmask %d" % netmask
817
818    def getValue(self):
819        from m5.internal.params import IpNetmask
820        return IpNetmask(self.ip, self.netmask)
821
822# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
823# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
824class IpWithPort(IpAddress):
825    cxx_type = 'Net::IpWithPort'
826
827    @classmethod
828    def cxx_predecls(cls, code):
829        code('#include "base/inet.hh"')
830
831    @classmethod
832    def swig_predecls(cls, code):
833        code('%include "python/swig/inet.i"')
834
835    def __init__(self, *args, **kwargs):
836        def handle_kwarg(self, kwargs, key, elseVal = None):
837            if key in kwargs:
838                setattr(self, key, kwargs.pop(key))
839            elif elseVal:
840                setattr(self, key, elseVal)
841            else:
842                raise TypeError, "No value set for %s" % key
843
844        if len(args) == 0:
845            handle_kwarg(self, kwargs, 'ip')
846            handle_kwarg(self, kwargs, 'port')
847
848        elif len(args) == 1:
849            if kwargs:
850                if not 'ip' in kwargs and not 'port' in kwargs:
851                    raise TypeError, "Invalid arguments"
852                handle_kwarg(self, kwargs, 'ip', args[0])
853                handle_kwarg(self, kwargs, 'port', args[0])
854            elif isinstance(args[0], IpWithPort):
855                self.ip = args[0].ip
856                self.port = args[0].port
857            else:
858                (self.ip, self.port) = convert.toIpWithPort(args[0])
859
860        elif len(args) == 2:
861            self.ip = args[0]
862            self.port = args[1]
863        else:
864            raise TypeError, "Too many arguments specified"
865
866        if kwargs:
867            raise TypeError, "Too many keywords: %s" % kwargs.keys()
868
869        self.verify()
870
871    def __str__(self):
872        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
873
874    def __eq__(self, other):
875        if isinstance(other, IpWithPort):
876            return self.ip == other.ip and self.port == other.port
877        elif isinstance(other, str):
878            try:
879                return (self.ip, self.port) == convert.toIpWithPort(other)
880            except:
881                return False
882        else:
883            return False
884
885    def verify(self):
886        self.verifyIp()
887        if self.port < 0 or self.port > 0xffff:
888            raise TypeError, "invalid port %d" % self.port
889
890    def getValue(self):
891        from m5.internal.params import IpWithPort
892        return IpWithPort(self.ip, self.port)
893
894time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
895                 "%a %b %d %H:%M:%S %Z %Y",
896                 "%Y/%m/%d %H:%M:%S",
897                 "%Y/%m/%d %H:%M",
898                 "%Y/%m/%d",
899                 "%m/%d/%Y %H:%M:%S",
900                 "%m/%d/%Y %H:%M",
901                 "%m/%d/%Y",
902                 "%m/%d/%y %H:%M:%S",
903                 "%m/%d/%y %H:%M",
904                 "%m/%d/%y"]
905
906
907def parse_time(value):
908    from time import gmtime, strptime, struct_time, time
909    from datetime import datetime, date
910
911    if isinstance(value, struct_time):
912        return value
913
914    if isinstance(value, (int, long)):
915        return gmtime(value)
916
917    if isinstance(value, (datetime, date)):
918        return value.timetuple()
919
920    if isinstance(value, str):
921        if value in ('Now', 'Today'):
922            return time.gmtime(time.time())
923
924        for format in time_formats:
925            try:
926                return strptime(value, format)
927            except ValueError:
928                pass
929
930    raise ValueError, "Could not parse '%s' as a time" % value
931
932class Time(ParamValue):
933    cxx_type = 'tm'
934
935    @classmethod
936    def cxx_predecls(cls, code):
937        code('#include <time.h>')
938
939    @classmethod
940    def swig_predecls(cls, code):
941        code('%include "python/swig/time.i"')
942
943    def __init__(self, value):
944        self.value = parse_time(value)
945
946    def getValue(self):
947        from m5.internal.params import tm
948
949        c_time = tm()
950        py_time = self.value
951
952        # UNIX is years since 1900
953        c_time.tm_year = py_time.tm_year - 1900;
954
955        # Python starts at 1, UNIX starts at 0
956        c_time.tm_mon =  py_time.tm_mon - 1;
957        c_time.tm_mday = py_time.tm_mday;
958        c_time.tm_hour = py_time.tm_hour;
959        c_time.tm_min = py_time.tm_min;
960        c_time.tm_sec = py_time.tm_sec;
961
962        # Python has 0 as Monday, UNIX is 0 as sunday
963        c_time.tm_wday = py_time.tm_wday + 1
964        if c_time.tm_wday > 6:
965            c_time.tm_wday -= 7;
966
967        # Python starts at 1, Unix starts at 0
968        c_time.tm_yday = py_time.tm_yday - 1;
969
970        return c_time
971
972    def __str__(self):
973        return time.asctime(self.value)
974
975    def ini_str(self):
976        return str(self)
977
978    def get_config_as_dict(self):
979        return str(self)
980
981# Enumerated types are a little more complex.  The user specifies the
982# type as Enum(foo) where foo is either a list or dictionary of
983# alternatives (typically strings, but not necessarily so).  (In the
984# long run, the integer value of the parameter will be the list index
985# or the corresponding dictionary value.  For now, since we only check
986# that the alternative is valid and then spit it into a .ini file,
987# there's not much point in using the dictionary.)
988
989# What Enum() must do is generate a new type encapsulating the
990# provided list/dictionary so that specific values of the parameter
991# can be instances of that type.  We define two hidden internal
992# classes (_ListEnum and _DictEnum) to serve as base classes, then
993# derive the new type from the appropriate base class on the fly.
994
995allEnums = {}
996# Metaclass for Enum types
997class MetaEnum(MetaParamValue):
998    def __new__(mcls, name, bases, dict):
999        assert name not in allEnums
1000
1001        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1002        allEnums[name] = cls
1003        return cls
1004
1005    def __init__(cls, name, bases, init_dict):
1006        if init_dict.has_key('map'):
1007            if not isinstance(cls.map, dict):
1008                raise TypeError, "Enum-derived class attribute 'map' " \
1009                      "must be of type dict"
1010            # build list of value strings from map
1011            cls.vals = cls.map.keys()
1012            cls.vals.sort()
1013        elif init_dict.has_key('vals'):
1014            if not isinstance(cls.vals, list):
1015                raise TypeError, "Enum-derived class attribute 'vals' " \
1016                      "must be of type list"
1017            # build string->value map from vals sequence
1018            cls.map = {}
1019            for idx,val in enumerate(cls.vals):
1020                cls.map[val] = idx
1021        else:
1022            raise TypeError, "Enum-derived class must define "\
1023                  "attribute 'map' or 'vals'"
1024
1025        cls.cxx_type = 'Enums::%s' % name
1026
1027        super(MetaEnum, cls).__init__(name, bases, init_dict)
1028
1029    # Generate C++ class declaration for this enum type.
1030    # Note that we wrap the enum in a class/struct to act as a namespace,
1031    # so that the enum strings can be brief w/o worrying about collisions.
1032    def cxx_decl(cls, code):
1033        name = cls.__name__
1034        code('''\
1035#ifndef __ENUM__${name}__
1036#define __ENUM__${name}__
1037
1038namespace Enums {
1039    enum $name {
1040''')
1041        code.indent(2)
1042        for val in cls.vals:
1043            code('$val = ${{cls.map[val]}},')
1044        code('Num_$name = ${{len(cls.vals)}}')
1045        code.dedent(2)
1046        code('''\
1047    };
1048extern const char *${name}Strings[Num_${name}];
1049}
1050
1051#endif // __ENUM__${name}__
1052''')
1053
1054    def cxx_def(cls, code):
1055        name = cls.__name__
1056        code('''\
1057#include "enums/$name.hh"
1058namespace Enums {
1059    const char *${name}Strings[Num_${name}] =
1060    {
1061''')
1062        code.indent(2)
1063        for val in cls.vals:
1064            code('"$val",')
1065        code.dedent(2)
1066        code('''
1067    };
1068} // namespace Enums
1069''')
1070
1071    def swig_decl(cls, code):
1072        name = cls.__name__
1073        code('''\
1074%module(package="m5.internal") enum_$name
1075
1076%{
1077#include "enums/$name.hh"
1078%}
1079
1080%include "enums/$name.hh"
1081''')
1082
1083
1084# Base class for enum types.
1085class Enum(ParamValue):
1086    __metaclass__ = MetaEnum
1087    vals = []
1088
1089    def __init__(self, value):
1090        if value not in self.map:
1091            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1092                  % (value, self.vals)
1093        self.value = value
1094
1095    @classmethod
1096    def cxx_predecls(cls, code):
1097        code('#include "enums/$0.hh"', cls.__name__)
1098
1099    @classmethod
1100    def swig_predecls(cls, code):
1101        code('%import "python/m5/internal/enum_$0.i"', cls.__name__)
1102
1103    def getValue(self):
1104        return int(self.map[self.value])
1105
1106    def __str__(self):
1107        return self.value
1108
1109# how big does a rounding error need to be before we warn about it?
1110frequency_tolerance = 0.001  # 0.1%
1111
1112class TickParamValue(NumericParamValue):
1113    cxx_type = 'Tick'
1114
1115    @classmethod
1116    def cxx_predecls(cls, code):
1117        code('#include "base/types.hh"')
1118
1119    @classmethod
1120    def swig_predecls(cls, code):
1121        code('%import "stdint.i"')
1122        code('%import "base/types.hh"')
1123
1124    def getValue(self):
1125        return long(self.value)
1126
1127class Latency(TickParamValue):
1128    def __init__(self, value):
1129        if isinstance(value, (Latency, Clock)):
1130            self.ticks = value.ticks
1131            self.value = value.value
1132        elif isinstance(value, Frequency):
1133            self.ticks = value.ticks
1134            self.value = 1.0 / value.value
1135        elif value.endswith('t'):
1136            self.ticks = True
1137            self.value = int(value[:-1])
1138        else:
1139            self.ticks = False
1140            self.value = convert.toLatency(value)
1141
1142    def __getattr__(self, attr):
1143        if attr in ('latency', 'period'):
1144            return self
1145        if attr == 'frequency':
1146            return Frequency(self)
1147        raise AttributeError, "Latency object has no attribute '%s'" % attr
1148
1149    def getValue(self):
1150        if self.ticks or self.value == 0:
1151            value = self.value
1152        else:
1153            value = ticks.fromSeconds(self.value)
1154        return long(value)
1155
1156    # convert latency to ticks
1157    def ini_str(self):
1158        return '%d' % self.getValue()
1159
1160class Frequency(TickParamValue):
1161    def __init__(self, value):
1162        if isinstance(value, (Latency, Clock)):
1163            if value.value == 0:
1164                self.value = 0
1165            else:
1166                self.value = 1.0 / value.value
1167            self.ticks = value.ticks
1168        elif isinstance(value, Frequency):
1169            self.value = value.value
1170            self.ticks = value.ticks
1171        else:
1172            self.ticks = False
1173            self.value = convert.toFrequency(value)
1174
1175    def __getattr__(self, attr):
1176        if attr == 'frequency':
1177            return self
1178        if attr in ('latency', 'period'):
1179            return Latency(self)
1180        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1181
1182    # convert latency to ticks
1183    def getValue(self):
1184        if self.ticks or self.value == 0:
1185            value = self.value
1186        else:
1187            value = ticks.fromSeconds(1.0 / self.value)
1188        return long(value)
1189
1190    def ini_str(self):
1191        return '%d' % self.getValue()
1192
1193# A generic frequency and/or Latency value.  Value is stored as a latency,
1194# but to avoid ambiguity this object does not support numeric ops (* or /).
1195# An explicit conversion to a Latency or Frequency must be made first.
1196class Clock(ParamValue):
1197    cxx_type = 'Tick'
1198
1199    @classmethod
1200    def cxx_predecls(cls, code):
1201        code('#include "base/types.hh"')
1202
1203    @classmethod
1204    def swig_predecls(cls, code):
1205        code('%import "stdint.i"')
1206        code('%import "base/types.hh"')
1207
1208    def __init__(self, value):
1209        if isinstance(value, (Latency, Clock)):
1210            self.ticks = value.ticks
1211            self.value = value.value
1212        elif isinstance(value, Frequency):
1213            self.ticks = value.ticks
1214            self.value = 1.0 / value.value
1215        elif value.endswith('t'):
1216            self.ticks = True
1217            self.value = int(value[:-1])
1218        else:
1219            self.ticks = False
1220            self.value = convert.anyToLatency(value)
1221
1222    def __getattr__(self, attr):
1223        if attr == 'frequency':
1224            return Frequency(self)
1225        if attr in ('latency', 'period'):
1226            return Latency(self)
1227        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1228
1229    def getValue(self):
1230        return self.period.getValue()
1231
1232    def ini_str(self):
1233        return self.period.ini_str()
1234
1235class NetworkBandwidth(float,ParamValue):
1236    cxx_type = 'float'
1237    def __new__(cls, value):
1238        # convert to bits per second
1239        val = convert.toNetworkBandwidth(value)
1240        return super(cls, NetworkBandwidth).__new__(cls, val)
1241
1242    def __str__(self):
1243        return str(self.val)
1244
1245    def getValue(self):
1246        # convert to seconds per byte
1247        value = 8.0 / float(self)
1248        # convert to ticks per byte
1249        value = ticks.fromSeconds(value)
1250        return float(value)
1251
1252    def ini_str(self):
1253        return '%f' % self.getValue()
1254
1255class MemoryBandwidth(float,ParamValue):
1256    cxx_type = 'float'
1257    def __new__(cls, value):
1258        # convert to bytes per second
1259        val = convert.toMemoryBandwidth(value)
1260        return super(cls, MemoryBandwidth).__new__(cls, val)
1261
1262    def __str__(self):
1263        return str(self.val)
1264
1265    def getValue(self):
1266        # convert to seconds per byte
1267        value = float(self)
1268        if value:
1269            value = 1.0 / float(self)
1270        # convert to ticks per byte
1271        value = ticks.fromSeconds(value)
1272        return float(value)
1273
1274    def ini_str(self):
1275        return '%f' % self.getValue()
1276
1277#
1278# "Constants"... handy aliases for various values.
1279#
1280
1281# Special class for NULL pointers.  Note the special check in
1282# make_param_value() above that lets these be assigned where a
1283# SimObject is required.
1284# only one copy of a particular node
1285class NullSimObject(object):
1286    __metaclass__ = Singleton
1287
1288    def __call__(cls):
1289        return cls
1290
1291    def _instantiate(self, parent = None, path = ''):
1292        pass
1293
1294    def ini_str(self):
1295        return 'Null'
1296
1297    def unproxy(self, base):
1298        return self
1299
1300    def set_path(self, parent, name):
1301        pass
1302
1303    def __str__(self):
1304        return 'Null'
1305
1306    def getValue(self):
1307        return None
1308
1309# The only instance you'll ever need...
1310NULL = NullSimObject()
1311
1312def isNullPointer(value):
1313    return isinstance(value, NullSimObject)
1314
1315# Some memory range specifications use this as a default upper bound.
1316MaxAddr = Addr.max
1317MaxTick = Tick.max
1318AllMemory = AddrRange(0, MaxAddr)
1319
1320
1321#####################################################################
1322#
1323# Port objects
1324#
1325# Ports are used to interconnect objects in the memory system.
1326#
1327#####################################################################
1328
1329# Port reference: encapsulates a reference to a particular port on a
1330# particular SimObject.
1331class PortRef(object):
1332    def __init__(self, simobj, name, role):
1333        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1334        self.simobj = simobj
1335        self.name = name
1336        self.role = role
1337        self.peer = None   # not associated with another port yet
1338        self.ccConnected = False # C++ port connection done?
1339        self.index = -1  # always -1 for non-vector ports
1340
1341    def __str__(self):
1342        return '%s.%s' % (self.simobj, self.name)
1343
1344    def __len__(self):
1345        # Return the number of connected ports, i.e. 0 is we have no
1346        # peer and 1 if we do.
1347        return int(self.peer != None)
1348
1349    # for config.ini, print peer's name (not ours)
1350    def ini_str(self):
1351        return str(self.peer)
1352
1353    # for config.json
1354    def get_config_as_dict(self):
1355        return {'role' : self.role, 'peer' : str(self.peer)}
1356
1357    def __getattr__(self, attr):
1358        if attr == 'peerObj':
1359            # shorthand for proxies
1360            return self.peer.simobj
1361        raise AttributeError, "'%s' object has no attribute '%s'" % \
1362              (self.__class__.__name__, attr)
1363
1364    # Full connection is symmetric (both ways).  Called via
1365    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1366    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1367    # e.g., "obj1.portA[3] = obj2.portB".
1368    def connect(self, other):
1369        if isinstance(other, VectorPortRef):
1370            # reference to plain VectorPort is implicit append
1371            other = other._get_next()
1372        if self.peer and not proxy.isproxy(self.peer):
1373            fatal("Port %s is already connected to %s, cannot connect %s\n",
1374                  self, self.peer, other);
1375        self.peer = other
1376        if proxy.isproxy(other):
1377            other.set_param_desc(PortParamDesc())
1378        elif isinstance(other, PortRef):
1379            if other.peer is not self:
1380                other.connect(self)
1381        else:
1382            raise TypeError, \
1383                  "assigning non-port reference '%s' to port '%s'" \
1384                  % (other, self)
1385
1386    def clone(self, simobj, memo):
1387        if memo.has_key(self):
1388            return memo[self]
1389        newRef = copy.copy(self)
1390        memo[self] = newRef
1391        newRef.simobj = simobj
1392        assert(isSimObject(newRef.simobj))
1393        if self.peer and not proxy.isproxy(self.peer):
1394            peerObj = self.peer.simobj(_memo=memo)
1395            newRef.peer = self.peer.clone(peerObj, memo)
1396            assert(not isinstance(newRef.peer, VectorPortRef))
1397        return newRef
1398
1399    def unproxy(self, simobj):
1400        assert(simobj is self.simobj)
1401        if proxy.isproxy(self.peer):
1402            try:
1403                realPeer = self.peer.unproxy(self.simobj)
1404            except:
1405                print "Error in unproxying port '%s' of %s" % \
1406                      (self.name, self.simobj.path())
1407                raise
1408            self.connect(realPeer)
1409
1410    # Call C++ to create corresponding port connection between C++ objects
1411    def ccConnect(self):
1412        from m5.internal.pyobject import connectPorts
1413
1414        if self.role == 'SLAVE':
1415            # do nothing and let the master take care of it
1416            return
1417
1418        if self.ccConnected: # already done this
1419            return
1420        peer = self.peer
1421        if not self.peer: # nothing to connect to
1422            return
1423
1424        # check that we connect a master to a slave
1425        if self.role == peer.role:
1426            raise TypeError, \
1427                "cannot connect '%s' and '%s' due to identical role '%s'" \
1428                % (peer, self, self.role)
1429
1430        try:
1431            # self is always the master and peer the slave
1432            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1433                         peer.simobj.getCCObject(), peer.name, peer.index)
1434        except:
1435            print "Error connecting port %s.%s to %s.%s" % \
1436                  (self.simobj.path(), self.name,
1437                   peer.simobj.path(), peer.name)
1438            raise
1439        self.ccConnected = True
1440        peer.ccConnected = True
1441
1442# A reference to an individual element of a VectorPort... much like a
1443# PortRef, but has an index.
1444class VectorPortElementRef(PortRef):
1445    def __init__(self, simobj, name, role, index):
1446        PortRef.__init__(self, simobj, name, role)
1447        self.index = index
1448
1449    def __str__(self):
1450        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1451
1452# A reference to a complete vector-valued port (not just a single element).
1453# Can be indexed to retrieve individual VectorPortElementRef instances.
1454class VectorPortRef(object):
1455    def __init__(self, simobj, name, role):
1456        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1457        self.simobj = simobj
1458        self.name = name
1459        self.role = role
1460        self.elements = []
1461
1462    def __str__(self):
1463        return '%s.%s[:]' % (self.simobj, self.name)
1464
1465    def __len__(self):
1466        # Return the number of connected peers, corresponding the the
1467        # length of the elements.
1468        return len(self.elements)
1469
1470    # for config.ini, print peer's name (not ours)
1471    def ini_str(self):
1472        return ' '.join([el.ini_str() for el in self.elements])
1473
1474    # for config.json
1475    def get_config_as_dict(self):
1476        return {'role' : self.role,
1477                'peer' : [el.ini_str() for el in self.elements]}
1478
1479    def __getitem__(self, key):
1480        if not isinstance(key, int):
1481            raise TypeError, "VectorPort index must be integer"
1482        if key >= len(self.elements):
1483            # need to extend list
1484            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1485                   for i in range(len(self.elements), key+1)]
1486            self.elements.extend(ext)
1487        return self.elements[key]
1488
1489    def _get_next(self):
1490        return self[len(self.elements)]
1491
1492    def __setitem__(self, key, value):
1493        if not isinstance(key, int):
1494            raise TypeError, "VectorPort index must be integer"
1495        self[key].connect(value)
1496
1497    def connect(self, other):
1498        if isinstance(other, (list, tuple)):
1499            # Assign list of port refs to vector port.
1500            # For now, append them... not sure if that's the right semantics
1501            # or if it should replace the current vector.
1502            for ref in other:
1503                self._get_next().connect(ref)
1504        else:
1505            # scalar assignment to plain VectorPort is implicit append
1506            self._get_next().connect(other)
1507
1508    def clone(self, simobj, memo):
1509        if memo.has_key(self):
1510            return memo[self]
1511        newRef = copy.copy(self)
1512        memo[self] = newRef
1513        newRef.simobj = simobj
1514        assert(isSimObject(newRef.simobj))
1515        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1516        return newRef
1517
1518    def unproxy(self, simobj):
1519        [el.unproxy(simobj) for el in self.elements]
1520
1521    def ccConnect(self):
1522        [el.ccConnect() for el in self.elements]
1523
1524# Port description object.  Like a ParamDesc object, this represents a
1525# logical port in the SimObject class, not a particular port on a
1526# SimObject instance.  The latter are represented by PortRef objects.
1527class Port(object):
1528    # Generate a PortRef for this port on the given SimObject with the
1529    # given name
1530    def makeRef(self, simobj):
1531        return PortRef(simobj, self.name, self.role)
1532
1533    # Connect an instance of this port (on the given SimObject with
1534    # the given name) with the port described by the supplied PortRef
1535    def connect(self, simobj, ref):
1536        self.makeRef(simobj).connect(ref)
1537
1538    # No need for any pre-declarations at the moment as we merely rely
1539    # on an unsigned int.
1540    def cxx_predecls(self, code):
1541        pass
1542
1543    # Declare an unsigned int with the same name as the port, that
1544    # will eventually hold the number of connected ports (and thus the
1545    # number of elements for a VectorPort).
1546    def cxx_decl(self, code):
1547        code('unsigned int port_${{self.name}}_connection_count;')
1548
1549class MasterPort(Port):
1550    # MasterPort("description")
1551    def __init__(self, *args):
1552        if len(args) == 1:
1553            self.desc = args[0]
1554            self.role = 'MASTER'
1555        else:
1556            raise TypeError, 'wrong number of arguments'
1557
1558class SlavePort(Port):
1559    # SlavePort("description")
1560    def __init__(self, *args):
1561        if len(args) == 1:
1562            self.desc = args[0]
1563            self.role = 'SLAVE'
1564        else:
1565            raise TypeError, 'wrong number of arguments'
1566
1567# VectorPort description object.  Like Port, but represents a vector
1568# of connections (e.g., as on a Bus).
1569class VectorPort(Port):
1570    def __init__(self, *args):
1571        self.isVec = True
1572
1573    def makeRef(self, simobj):
1574        return VectorPortRef(simobj, self.name, self.role)
1575
1576class VectorMasterPort(VectorPort):
1577    # VectorMasterPort("description")
1578    def __init__(self, *args):
1579        if len(args) == 1:
1580            self.desc = args[0]
1581            self.role = 'MASTER'
1582            VectorPort.__init__(self, *args)
1583        else:
1584            raise TypeError, 'wrong number of arguments'
1585
1586class VectorSlavePort(VectorPort):
1587    # VectorSlavePort("description")
1588    def __init__(self, *args):
1589        if len(args) == 1:
1590            self.desc = args[0]
1591            self.role = 'SLAVE'
1592            VectorPort.__init__(self, *args)
1593        else:
1594            raise TypeError, 'wrong number of arguments'
1595
1596# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1597# proxy objects (via set_param_desc()) so that proxy error messages
1598# make sense.
1599class PortParamDesc(object):
1600    __metaclass__ = Singleton
1601
1602    ptype_str = 'Port'
1603    ptype = Port
1604
1605baseEnums = allEnums.copy()
1606baseParams = allParams.copy()
1607
1608def clear():
1609    global allEnums, allParams
1610
1611    allEnums = baseEnums.copy()
1612    allParams = baseParams.copy()
1613
1614__all__ = ['Param', 'VectorParam',
1615           'Enum', 'Bool', 'String', 'Float',
1616           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1617           'Int32', 'UInt32', 'Int64', 'UInt64',
1618           'Counter', 'Addr', 'Tick', 'Percent',
1619           'TcpPort', 'UdpPort', 'EthernetAddr',
1620           'IpAddress', 'IpNetmask', 'IpWithPort',
1621           'MemorySize', 'MemorySize32',
1622           'Latency', 'Frequency', 'Clock',
1623           'NetworkBandwidth', 'MemoryBandwidth',
1624           'AddrRange',
1625           'MaxAddr', 'MaxTick', 'AllMemory',
1626           'Time',
1627           'NextEthernetAddr', 'NULL',
1628           'MasterPort', 'SlavePort',
1629           'VectorMasterPort', 'VectorSlavePort']
1630
1631import SimObject
1632