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