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