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