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