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