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