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