params.py revision 13719:74853963ddcf
1# Copyright (c) 2012-2014, 2017, 2018 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.intlvHighBit = 0
761        self.xorHighBit = 0
762        self.intlvBits = 0
763        self.intlvMatch = 0
764
765        def handle_kwargs(self, kwargs):
766            # An address range needs to have an upper limit, specified
767            # either explicitly with an end, or as an offset using the
768            # size keyword.
769            if 'end' in kwargs:
770                self.end = Addr(kwargs.pop('end'))
771            elif 'size' in kwargs:
772                self.end = self.start + Addr(kwargs.pop('size')) - 1
773            else:
774                raise TypeError("Either end or size must be specified")
775
776            # Now on to the optional bit
777            if 'intlvHighBit' in kwargs:
778                self.intlvHighBit = int(kwargs.pop('intlvHighBit'))
779            if 'xorHighBit' in kwargs:
780                self.xorHighBit = int(kwargs.pop('xorHighBit'))
781            if 'intlvBits' in kwargs:
782                self.intlvBits = int(kwargs.pop('intlvBits'))
783            if 'intlvMatch' in kwargs:
784                self.intlvMatch = int(kwargs.pop('intlvMatch'))
785
786        if len(args) == 0:
787            self.start = Addr(kwargs.pop('start'))
788            handle_kwargs(self, kwargs)
789
790        elif len(args) == 1:
791            if kwargs:
792                self.start = Addr(args[0])
793                handle_kwargs(self, kwargs)
794            elif isinstance(args[0], (list, tuple)):
795                self.start = Addr(args[0][0])
796                self.end = Addr(args[0][1])
797            else:
798                self.start = Addr(0)
799                self.end = Addr(args[0]) - 1
800
801        elif len(args) == 2:
802            self.start = Addr(args[0])
803            self.end = Addr(args[1])
804        else:
805            raise TypeError("Too many arguments specified")
806
807        if kwargs:
808            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
809
810    def __str__(self):
811        return '%s:%s:%s:%s:%s:%s' \
812            % (self.start, self.end, self.intlvHighBit, self.xorHighBit,\
813               self.intlvBits, self.intlvMatch)
814
815    def size(self):
816        # Divide the size by the size of the interleaving slice
817        return (long(self.end) - long(self.start) + 1) >> self.intlvBits
818
819    @classmethod
820    def cxx_predecls(cls, code):
821        Addr.cxx_predecls(code)
822        code('#include "base/addr_range.hh"')
823
824    @classmethod
825    def pybind_predecls(cls, code):
826        Addr.pybind_predecls(code)
827        code('#include "base/addr_range.hh"')
828
829    @classmethod
830    def cxx_ini_predecls(cls, code):
831        code('#include <sstream>')
832
833    @classmethod
834    def cxx_ini_parse(cls, code, src, dest, ret):
835        code('uint64_t _start, _end, _intlvHighBit = 0, _xorHighBit = 0;')
836        code('uint64_t _intlvBits = 0, _intlvMatch = 0;')
837        code('char _sep;')
838        code('std::istringstream _stream(${src});')
839        code('_stream >> _start;')
840        code('_stream.get(_sep);')
841        code('_stream >> _end;')
842        code('if (!_stream.fail() && !_stream.eof()) {')
843        code('    _stream.get(_sep);')
844        code('    _stream >> _intlvHighBit;')
845        code('    _stream.get(_sep);')
846        code('    _stream >> _xorHighBit;')
847        code('    _stream.get(_sep);')
848        code('    _stream >> _intlvBits;')
849        code('    _stream.get(_sep);')
850        code('    _stream >> _intlvMatch;')
851        code('}')
852        code('bool _ret = !_stream.fail() &&'
853            '_stream.eof() && _sep == \':\';')
854        code('if (_ret)')
855        code('   ${dest} = AddrRange(_start, _end, _intlvHighBit, \
856                _xorHighBit, _intlvBits, _intlvMatch);')
857        code('${ret} _ret;')
858
859    def getValue(self):
860        # Go from the Python class to the wrapped C++ class
861        from _m5.range import AddrRange
862
863        return AddrRange(long(self.start), long(self.end),
864                         int(self.intlvHighBit), int(self.xorHighBit),
865                         int(self.intlvBits), int(self.intlvMatch))
866
867# Boolean parameter type.  Python doesn't let you subclass bool, since
868# it doesn't want to let you create multiple instances of True and
869# False.  Thus this is a little more complicated than String.
870class Bool(ParamValue):
871    cxx_type = 'bool'
872    cmd_line_settable = True
873
874    def __init__(self, value):
875        try:
876            self.value = convert.toBool(value)
877        except TypeError:
878            self.value = bool(value)
879
880    def __call__(self, value):
881        self.__init__(value)
882        return value
883
884    def getValue(self):
885        return bool(self.value)
886
887    def __str__(self):
888        return str(self.value)
889
890    # implement truth value testing for Bool parameters so that these params
891    # evaluate correctly during the python configuration phase
892    def __bool__(self):
893        return bool(self.value)
894
895    # Python 2.7 uses __nonzero__ instead of __bool__
896    __nonzero__ = __bool__
897
898    def ini_str(self):
899        if self.value:
900            return 'true'
901        return 'false'
902
903    def config_value(self):
904        return self.value
905
906    @classmethod
907    def cxx_ini_predecls(cls, code):
908        # Assume that base/str.hh will be included anyway
909        # code('#include "base/str.hh"')
910        pass
911
912    @classmethod
913    def cxx_ini_parse(cls, code, src, dest, ret):
914        code('%s to_bool(%s, %s);' % (ret, src, dest))
915
916def IncEthernetAddr(addr, val = 1):
917    bytes = [ int(x, 16) for x in addr.split(':') ]
918    bytes[5] += val
919    for i in (5, 4, 3, 2, 1):
920        val,rem = divmod(bytes[i], 256)
921        bytes[i] = rem
922        if val == 0:
923            break
924        bytes[i - 1] += val
925    assert(bytes[0] <= 255)
926    return ':'.join(map(lambda x: '%02x' % x, bytes))
927
928_NextEthernetAddr = "00:90:00:00:00:01"
929def NextEthernetAddr():
930    global _NextEthernetAddr
931
932    value = _NextEthernetAddr
933    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
934    return value
935
936class EthernetAddr(ParamValue):
937    cxx_type = 'Net::EthAddr'
938    ex_str = "00:90:00:00:00:01"
939    cmd_line_settable = True
940
941    @classmethod
942    def cxx_predecls(cls, code):
943        code('#include "base/inet.hh"')
944
945    def __init__(self, value):
946        if value == NextEthernetAddr:
947            self.value = value
948            return
949
950        if not isinstance(value, str):
951            raise TypeError("expected an ethernet address and didn't get one")
952
953        bytes = value.split(':')
954        if len(bytes) != 6:
955            raise TypeError('invalid ethernet address %s' % value)
956
957        for byte in bytes:
958            if not 0 <= int(byte, base=16) <= 0xff:
959                raise TypeError('invalid ethernet address %s' % value)
960
961        self.value = value
962
963    def __call__(self, value):
964        self.__init__(value)
965        return value
966
967    def unproxy(self, base):
968        if self.value == NextEthernetAddr:
969            return EthernetAddr(self.value())
970        return self
971
972    def getValue(self):
973        from _m5.net import EthAddr
974        return EthAddr(self.value)
975
976    def __str__(self):
977        return self.value
978
979    def ini_str(self):
980        return self.value
981
982    @classmethod
983    def cxx_ini_parse(self, code, src, dest, ret):
984        code('%s = Net::EthAddr(%s);' % (dest, src))
985        code('%s true;' % ret)
986
987# When initializing an IpAddress, pass in an existing IpAddress, a string of
988# the form "a.b.c.d", or an integer representing an IP.
989class IpAddress(ParamValue):
990    cxx_type = 'Net::IpAddress'
991    ex_str = "127.0.0.1"
992    cmd_line_settable = True
993
994    @classmethod
995    def cxx_predecls(cls, code):
996        code('#include "base/inet.hh"')
997
998    def __init__(self, value):
999        if isinstance(value, IpAddress):
1000            self.ip = value.ip
1001        else:
1002            try:
1003                self.ip = convert.toIpAddress(value)
1004            except TypeError:
1005                self.ip = long(value)
1006        self.verifyIp()
1007
1008    def __call__(self, value):
1009        self.__init__(value)
1010        return value
1011
1012    def __str__(self):
1013        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
1014        return '%d.%d.%d.%d' % tuple(tup)
1015
1016    def __eq__(self, other):
1017        if isinstance(other, IpAddress):
1018            return self.ip == other.ip
1019        elif isinstance(other, str):
1020            try:
1021                return self.ip == convert.toIpAddress(other)
1022            except:
1023                return False
1024        else:
1025            return self.ip == other
1026
1027    def __ne__(self, other):
1028        return not (self == other)
1029
1030    def verifyIp(self):
1031        if self.ip < 0 or self.ip >= (1 << 32):
1032            raise TypeError("invalid ip address %#08x" % self.ip)
1033
1034    def getValue(self):
1035        from _m5.net import IpAddress
1036        return IpAddress(self.ip)
1037
1038# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
1039# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
1040# positional or keyword arguments.
1041class IpNetmask(IpAddress):
1042    cxx_type = 'Net::IpNetmask'
1043    ex_str = "127.0.0.0/24"
1044    cmd_line_settable = True
1045
1046    @classmethod
1047    def cxx_predecls(cls, code):
1048        code('#include "base/inet.hh"')
1049
1050    def __init__(self, *args, **kwargs):
1051        def handle_kwarg(self, kwargs, key, elseVal = None):
1052            if key in kwargs:
1053                setattr(self, key, kwargs.pop(key))
1054            elif elseVal:
1055                setattr(self, key, elseVal)
1056            else:
1057                raise TypeError("No value set for %s" % key)
1058
1059        if len(args) == 0:
1060            handle_kwarg(self, kwargs, 'ip')
1061            handle_kwarg(self, kwargs, 'netmask')
1062
1063        elif len(args) == 1:
1064            if kwargs:
1065                if not 'ip' in kwargs and not 'netmask' in kwargs:
1066                    raise TypeError("Invalid arguments")
1067                handle_kwarg(self, kwargs, 'ip', args[0])
1068                handle_kwarg(self, kwargs, 'netmask', args[0])
1069            elif isinstance(args[0], IpNetmask):
1070                self.ip = args[0].ip
1071                self.netmask = args[0].netmask
1072            else:
1073                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
1074
1075        elif len(args) == 2:
1076            self.ip = args[0]
1077            self.netmask = args[1]
1078        else:
1079            raise TypeError("Too many arguments specified")
1080
1081        if kwargs:
1082            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1083
1084        self.verify()
1085
1086    def __call__(self, value):
1087        self.__init__(value)
1088        return value
1089
1090    def __str__(self):
1091        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
1092
1093    def __eq__(self, other):
1094        if isinstance(other, IpNetmask):
1095            return self.ip == other.ip and self.netmask == other.netmask
1096        elif isinstance(other, str):
1097            try:
1098                return (self.ip, self.netmask) == convert.toIpNetmask(other)
1099            except:
1100                return False
1101        else:
1102            return False
1103
1104    def verify(self):
1105        self.verifyIp()
1106        if self.netmask < 0 or self.netmask > 32:
1107            raise TypeError("invalid netmask %d" % netmask)
1108
1109    def getValue(self):
1110        from _m5.net import IpNetmask
1111        return IpNetmask(self.ip, self.netmask)
1112
1113# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1114# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1115class IpWithPort(IpAddress):
1116    cxx_type = 'Net::IpWithPort'
1117    ex_str = "127.0.0.1:80"
1118    cmd_line_settable = True
1119
1120    @classmethod
1121    def cxx_predecls(cls, code):
1122        code('#include "base/inet.hh"')
1123
1124    def __init__(self, *args, **kwargs):
1125        def handle_kwarg(self, kwargs, key, elseVal = None):
1126            if key in kwargs:
1127                setattr(self, key, kwargs.pop(key))
1128            elif elseVal:
1129                setattr(self, key, elseVal)
1130            else:
1131                raise TypeError("No value set for %s" % key)
1132
1133        if len(args) == 0:
1134            handle_kwarg(self, kwargs, 'ip')
1135            handle_kwarg(self, kwargs, 'port')
1136
1137        elif len(args) == 1:
1138            if kwargs:
1139                if not 'ip' in kwargs and not 'port' in kwargs:
1140                    raise TypeError("Invalid arguments")
1141                handle_kwarg(self, kwargs, 'ip', args[0])
1142                handle_kwarg(self, kwargs, 'port', args[0])
1143            elif isinstance(args[0], IpWithPort):
1144                self.ip = args[0].ip
1145                self.port = args[0].port
1146            else:
1147                (self.ip, self.port) = convert.toIpWithPort(args[0])
1148
1149        elif len(args) == 2:
1150            self.ip = args[0]
1151            self.port = args[1]
1152        else:
1153            raise TypeError("Too many arguments specified")
1154
1155        if kwargs:
1156            raise TypeError("Too many keywords: %s" % list(kwargs.keys()))
1157
1158        self.verify()
1159
1160    def __call__(self, value):
1161        self.__init__(value)
1162        return value
1163
1164    def __str__(self):
1165        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1166
1167    def __eq__(self, other):
1168        if isinstance(other, IpWithPort):
1169            return self.ip == other.ip and self.port == other.port
1170        elif isinstance(other, str):
1171            try:
1172                return (self.ip, self.port) == convert.toIpWithPort(other)
1173            except:
1174                return False
1175        else:
1176            return False
1177
1178    def verify(self):
1179        self.verifyIp()
1180        if self.port < 0 or self.port > 0xffff:
1181            raise TypeError("invalid port %d" % self.port)
1182
1183    def getValue(self):
1184        from _m5.net import IpWithPort
1185        return IpWithPort(self.ip, self.port)
1186
1187time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1188                 "%a %b %d %H:%M:%S %Y",
1189                 "%Y/%m/%d %H:%M:%S",
1190                 "%Y/%m/%d %H:%M",
1191                 "%Y/%m/%d",
1192                 "%m/%d/%Y %H:%M:%S",
1193                 "%m/%d/%Y %H:%M",
1194                 "%m/%d/%Y",
1195                 "%m/%d/%y %H:%M:%S",
1196                 "%m/%d/%y %H:%M",
1197                 "%m/%d/%y"]
1198
1199
1200def parse_time(value):
1201    from time import gmtime, strptime, struct_time, time
1202    from datetime import datetime, date
1203
1204    if isinstance(value, struct_time):
1205        return value
1206
1207    if isinstance(value, (int, long)):
1208        return gmtime(value)
1209
1210    if isinstance(value, (datetime, date)):
1211        return value.timetuple()
1212
1213    if isinstance(value, str):
1214        if value in ('Now', 'Today'):
1215            return time.gmtime(time.time())
1216
1217        for format in time_formats:
1218            try:
1219                return strptime(value, format)
1220            except ValueError:
1221                pass
1222
1223    raise ValueError("Could not parse '%s' as a time" % value)
1224
1225class Time(ParamValue):
1226    cxx_type = 'tm'
1227
1228    @classmethod
1229    def cxx_predecls(cls, code):
1230        code('#include <time.h>')
1231
1232    def __init__(self, value):
1233        self.value = parse_time(value)
1234
1235    def __call__(self, value):
1236        self.__init__(value)
1237        return value
1238
1239    def getValue(self):
1240        from _m5.core import tm
1241        import calendar
1242
1243        return tm.gmtime(calendar.timegm(self.value))
1244
1245    def __str__(self):
1246        return time.asctime(self.value)
1247
1248    def ini_str(self):
1249        return str(self)
1250
1251    def get_config_as_dict(self):
1252        assert false
1253        return str(self)
1254
1255    @classmethod
1256    def cxx_ini_predecls(cls, code):
1257        code('#include <time.h>')
1258
1259    @classmethod
1260    def cxx_ini_parse(cls, code, src, dest, ret):
1261        code('char *_parse_ret = strptime((${src}).c_str(),')
1262        code('    "%a %b %d %H:%M:%S %Y", &(${dest}));')
1263        code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1264
1265# Enumerated types are a little more complex.  The user specifies the
1266# type as Enum(foo) where foo is either a list or dictionary of
1267# alternatives (typically strings, but not necessarily so).  (In the
1268# long run, the integer value of the parameter will be the list index
1269# or the corresponding dictionary value.  For now, since we only check
1270# that the alternative is valid and then spit it into a .ini file,
1271# there's not much point in using the dictionary.)
1272
1273# What Enum() must do is generate a new type encapsulating the
1274# provided list/dictionary so that specific values of the parameter
1275# can be instances of that type.  We define two hidden internal
1276# classes (_ListEnum and _DictEnum) to serve as base classes, then
1277# derive the new type from the appropriate base class on the fly.
1278
1279allEnums = {}
1280# Metaclass for Enum types
1281class MetaEnum(MetaParamValue):
1282    def __new__(mcls, name, bases, dict):
1283        assert name not in allEnums
1284
1285        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1286        allEnums[name] = cls
1287        return cls
1288
1289    def __init__(cls, name, bases, init_dict):
1290        if 'map' in init_dict:
1291            if not isinstance(cls.map, dict):
1292                raise TypeError("Enum-derived class attribute 'map' " \
1293                      "must be of type dict")
1294            # build list of value strings from map
1295            cls.vals = list(cls.map.keys())
1296            cls.vals.sort()
1297        elif 'vals' in init_dict:
1298            if not isinstance(cls.vals, list):
1299                raise TypeError("Enum-derived class attribute 'vals' " \
1300                      "must be of type list")
1301            # build string->value map from vals sequence
1302            cls.map = {}
1303            for idx,val in enumerate(cls.vals):
1304                cls.map[val] = idx
1305        else:
1306            raise TypeError("Enum-derived class must define "\
1307                  "attribute 'map' or 'vals'")
1308
1309        if cls.is_class:
1310            cls.cxx_type = '%s' % name
1311        else:
1312            cls.cxx_type = 'Enums::%s' % name
1313
1314        super(MetaEnum, cls).__init__(name, bases, init_dict)
1315
1316    # Generate C++ class declaration for this enum type.
1317    # Note that we wrap the enum in a class/struct to act as a namespace,
1318    # so that the enum strings can be brief w/o worrying about collisions.
1319    def cxx_decl(cls, code):
1320        wrapper_name = cls.wrapper_name
1321        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1322        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1323        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1324
1325        code('''\
1326#ifndef $idem_macro
1327#define $idem_macro
1328
1329''')
1330        if cls.is_class:
1331            code('''\
1332enum class $name {
1333''')
1334        else:
1335            code('''\
1336$wrapper $wrapper_name {
1337    enum $name {
1338''')
1339            code.indent(1)
1340        code.indent(1)
1341        for val in cls.vals:
1342            code('$val = ${{cls.map[val]}},')
1343        code('Num_$name = ${{len(cls.vals)}}')
1344        code.dedent(1)
1345        code('};')
1346
1347        if cls.is_class:
1348            code('''\
1349extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
1350''')
1351        elif cls.wrapper_is_struct:
1352            code('static const char *${name}Strings[Num_${name}];')
1353        else:
1354            code('extern const char *${name}Strings[Num_${name}];')
1355
1356        if not cls.is_class:
1357            code.dedent(1)
1358            code('};')
1359
1360        code()
1361        code('#endif // $idem_macro')
1362
1363    def cxx_def(cls, code):
1364        wrapper_name = cls.wrapper_name
1365        file_name = cls.__name__
1366        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1367
1368        code('#include "enums/$file_name.hh"')
1369        if cls.wrapper_is_struct:
1370            code('const char *${wrapper_name}::${name}Strings'
1371                '[Num_${name}] =')
1372        else:
1373            if cls.is_class:
1374                code('''\
1375const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
1376''')
1377            else:
1378                code('namespace Enums {')
1379                code.indent(1)
1380                code('const char *${name}Strings[Num_${name}] =')
1381
1382        code('{')
1383        code.indent(1)
1384        for val in cls.vals:
1385            code('"$val",')
1386        code.dedent(1)
1387        code('};')
1388
1389        if not cls.wrapper_is_struct and not cls.is_class:
1390            code.dedent(1)
1391            code('} // namespace $wrapper_name')
1392
1393
1394    def pybind_def(cls, code):
1395        name = cls.__name__
1396        enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
1397        wrapper_name = enum_name if cls.is_class else cls.wrapper_name
1398
1399        code('''#include "pybind11/pybind11.h"
1400#include "pybind11/stl.h"
1401
1402#include <sim/init.hh>
1403
1404namespace py = pybind11;
1405
1406static void
1407module_init(py::module &m_internal)
1408{
1409    py::module m = m_internal.def_submodule("enum_${name}");
1410
1411''')
1412        if cls.is_class:
1413            code('py::enum_<${enum_name}>(m, "enum_${name}")')
1414        else:
1415            code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
1416
1417        code.indent()
1418        code.indent()
1419        for val in cls.vals:
1420            code('.value("${val}", ${wrapper_name}::${val})')
1421        code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1422        code('.export_values()')
1423        code(';')
1424        code.dedent()
1425
1426        code('}')
1427        code.dedent()
1428        code()
1429        code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1430
1431
1432# Base class for enum types.
1433class Enum(ParamValue):
1434    __metaclass__ = MetaEnum
1435    vals = []
1436    cmd_line_settable = True
1437
1438    # The name of the wrapping namespace or struct
1439    wrapper_name = 'Enums'
1440
1441    # If true, the enum is wrapped in a struct rather than a namespace
1442    wrapper_is_struct = False
1443
1444    is_class = False
1445
1446    # If not None, use this as the enum name rather than this class name
1447    enum_name = None
1448
1449    def __init__(self, value):
1450        if value not in self.map:
1451            raise TypeError("Enum param got bad value '%s' (not in %s)" \
1452                  % (value, self.vals))
1453        self.value = value
1454
1455    def __call__(self, value):
1456        self.__init__(value)
1457        return value
1458
1459    @classmethod
1460    def cxx_predecls(cls, code):
1461        code('#include "enums/$0.hh"', cls.__name__)
1462
1463    @classmethod
1464    def cxx_ini_parse(cls, code, src, dest, ret):
1465        code('if (false) {')
1466        for elem_name in cls.map.keys():
1467            code('} else if (%s == "%s") {' % (src, elem_name))
1468            code.indent()
1469            code('%s = Enums::%s;' % (dest, elem_name))
1470            code('%s true;' % ret)
1471            code.dedent()
1472        code('} else {')
1473        code('    %s false;' % ret)
1474        code('}')
1475
1476    def getValue(self):
1477        import m5.internal.params
1478        e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
1479        return e(self.map[self.value])
1480
1481    def __str__(self):
1482        return self.value
1483
1484# This param will generate a scoped c++ enum and its python bindings.
1485class ScopedEnum(Enum):
1486    __metaclass__ = MetaEnum
1487    vals = []
1488    cmd_line_settable = True
1489
1490    # The name of the wrapping namespace or struct
1491    wrapper_name = None
1492
1493    # If true, the enum is wrapped in a struct rather than a namespace
1494    wrapper_is_struct = False
1495
1496    # If true, the generated enum is a scoped enum
1497    is_class = True
1498
1499    # If not None, use this as the enum name rather than this class name
1500    enum_name = None
1501
1502# how big does a rounding error need to be before we warn about it?
1503frequency_tolerance = 0.001  # 0.1%
1504
1505class TickParamValue(NumericParamValue):
1506    cxx_type = 'Tick'
1507    ex_str = "1MHz"
1508    cmd_line_settable = True
1509
1510    @classmethod
1511    def cxx_predecls(cls, code):
1512        code('#include "base/types.hh"')
1513
1514    def __call__(self, value):
1515        self.__init__(value)
1516        return value
1517
1518    def getValue(self):
1519        return long(self.value)
1520
1521    @classmethod
1522    def cxx_ini_predecls(cls, code):
1523        code('#include <sstream>')
1524
1525    # Ticks are expressed in seconds in JSON files and in plain
1526    # Ticks in .ini files.  Switch based on a config flag
1527    @classmethod
1528    def cxx_ini_parse(self, code, src, dest, ret):
1529        code('${ret} to_number(${src}, ${dest});')
1530
1531class Latency(TickParamValue):
1532    ex_str = "100ns"
1533
1534    def __init__(self, value):
1535        if isinstance(value, (Latency, Clock)):
1536            self.ticks = value.ticks
1537            self.value = value.value
1538        elif isinstance(value, Frequency):
1539            self.ticks = value.ticks
1540            self.value = 1.0 / value.value
1541        elif value.endswith('t'):
1542            self.ticks = True
1543            self.value = int(value[:-1])
1544        else:
1545            self.ticks = False
1546            self.value = convert.toLatency(value)
1547
1548    def __call__(self, value):
1549        self.__init__(value)
1550        return value
1551
1552    def __getattr__(self, attr):
1553        if attr in ('latency', 'period'):
1554            return self
1555        if attr == 'frequency':
1556            return Frequency(self)
1557        raise AttributeError("Latency object has no attribute '%s'" % attr)
1558
1559    def getValue(self):
1560        if self.ticks or self.value == 0:
1561            value = self.value
1562        else:
1563            value = ticks.fromSeconds(self.value)
1564        return long(value)
1565
1566    def config_value(self):
1567        return self.getValue()
1568
1569    # convert latency to ticks
1570    def ini_str(self):
1571        return '%d' % self.getValue()
1572
1573class Frequency(TickParamValue):
1574    ex_str = "1GHz"
1575
1576    def __init__(self, value):
1577        if isinstance(value, (Latency, Clock)):
1578            if value.value == 0:
1579                self.value = 0
1580            else:
1581                self.value = 1.0 / value.value
1582            self.ticks = value.ticks
1583        elif isinstance(value, Frequency):
1584            self.value = value.value
1585            self.ticks = value.ticks
1586        else:
1587            self.ticks = False
1588            self.value = convert.toFrequency(value)
1589
1590    def __call__(self, value):
1591        self.__init__(value)
1592        return value
1593
1594    def __getattr__(self, attr):
1595        if attr == 'frequency':
1596            return self
1597        if attr in ('latency', 'period'):
1598            return Latency(self)
1599        raise AttributeError("Frequency object has no attribute '%s'" % attr)
1600
1601    # convert latency to ticks
1602    def getValue(self):
1603        if self.ticks or self.value == 0:
1604            value = self.value
1605        else:
1606            value = ticks.fromSeconds(1.0 / self.value)
1607        return long(value)
1608
1609    def config_value(self):
1610        return self.getValue()
1611
1612    def ini_str(self):
1613        return '%d' % self.getValue()
1614
1615# A generic Frequency and/or Latency value. Value is stored as a
1616# latency, just like Latency and Frequency.
1617class Clock(TickParamValue):
1618    def __init__(self, value):
1619        if isinstance(value, (Latency, Clock)):
1620            self.ticks = value.ticks
1621            self.value = value.value
1622        elif isinstance(value, Frequency):
1623            self.ticks = value.ticks
1624            self.value = 1.0 / value.value
1625        elif value.endswith('t'):
1626            self.ticks = True
1627            self.value = int(value[:-1])
1628        else:
1629            self.ticks = False
1630            self.value = convert.anyToLatency(value)
1631
1632    def __call__(self, value):
1633        self.__init__(value)
1634        return value
1635
1636    def __str__(self):
1637        return "%s" % Latency(self)
1638
1639    def __getattr__(self, attr):
1640        if attr == 'frequency':
1641            return Frequency(self)
1642        if attr in ('latency', 'period'):
1643            return Latency(self)
1644        raise AttributeError("Frequency object has no attribute '%s'" % attr)
1645
1646    def getValue(self):
1647        return self.period.getValue()
1648
1649    def config_value(self):
1650        return self.period.config_value()
1651
1652    def ini_str(self):
1653        return self.period.ini_str()
1654
1655class Voltage(Float):
1656    ex_str = "1V"
1657
1658    def __new__(cls, value):
1659        value = convert.toVoltage(value)
1660        return super(cls, Voltage).__new__(cls, value)
1661
1662    def __init__(self, value):
1663        value = convert.toVoltage(value)
1664        super(Voltage, self).__init__(value)
1665
1666class Current(Float):
1667    ex_str = "1mA"
1668
1669    def __new__(cls, value):
1670        value = convert.toCurrent(value)
1671        return super(cls, Current).__new__(cls, value)
1672
1673    def __init__(self, value):
1674        value = convert.toCurrent(value)
1675        super(Current, self).__init__(value)
1676
1677class Energy(Float):
1678    ex_str = "1pJ"
1679
1680    def __new__(cls, value):
1681        value = convert.toEnergy(value)
1682        return super(cls, Energy).__new__(cls, value)
1683
1684    def __init__(self, value):
1685        value = convert.toEnergy(value)
1686        super(Energy, self).__init__(value)
1687
1688class NetworkBandwidth(float,ParamValue):
1689    cxx_type = 'float'
1690    ex_str = "1Gbps"
1691    cmd_line_settable = True
1692
1693    def __new__(cls, value):
1694        # convert to bits per second
1695        val = convert.toNetworkBandwidth(value)
1696        return super(cls, NetworkBandwidth).__new__(cls, val)
1697
1698    def __str__(self):
1699        return str(self.val)
1700
1701    def __call__(self, value):
1702        val = convert.toNetworkBandwidth(value)
1703        self.__init__(val)
1704        return value
1705
1706    def getValue(self):
1707        # convert to seconds per byte
1708        value = 8.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
1727class MemoryBandwidth(float,ParamValue):
1728    cxx_type = 'float'
1729    ex_str = "1GB/s"
1730    cmd_line_settable = True
1731
1732    def __new__(cls, value):
1733        # convert to bytes per second
1734        val = convert.toMemoryBandwidth(value)
1735        return super(cls, MemoryBandwidth).__new__(cls, val)
1736
1737    def __call__(self, value):
1738        val = convert.toMemoryBandwidth(value)
1739        self.__init__(val)
1740        return value
1741
1742    def getValue(self):
1743        # convert to seconds per byte
1744        value = float(self)
1745        if value:
1746            value = 1.0 / float(self)
1747        # convert to ticks per byte
1748        value = ticks.fromSeconds(value)
1749        return float(value)
1750
1751    def ini_str(self):
1752        return '%f' % self.getValue()
1753
1754    def config_value(self):
1755        return '%f' % self.getValue()
1756
1757    @classmethod
1758    def cxx_ini_predecls(cls, code):
1759        code('#include <sstream>')
1760
1761    @classmethod
1762    def cxx_ini_parse(self, code, src, dest, ret):
1763        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1764
1765#
1766# "Constants"... handy aliases for various values.
1767#
1768
1769# Special class for NULL pointers.  Note the special check in
1770# make_param_value() above that lets these be assigned where a
1771# SimObject is required.
1772# only one copy of a particular node
1773class NullSimObject(object):
1774    __metaclass__ = Singleton
1775    _name = 'Null'
1776
1777    def __call__(cls):
1778        return cls
1779
1780    def _instantiate(self, parent = None, path = ''):
1781        pass
1782
1783    def ini_str(self):
1784        return 'Null'
1785
1786    def unproxy(self, base):
1787        return self
1788
1789    def set_path(self, parent, name):
1790        pass
1791
1792    def set_parent(self, parent, name):
1793        pass
1794
1795    def clear_parent(self, old_parent):
1796        pass
1797
1798    def descendants(self):
1799        return
1800        yield None
1801
1802    def get_config_as_dict(self):
1803        return {}
1804
1805    def __str__(self):
1806        return self._name
1807
1808    def config_value(self):
1809        return None
1810
1811    def getValue(self):
1812        return None
1813
1814# The only instance you'll ever need...
1815NULL = NullSimObject()
1816
1817def isNullPointer(value):
1818    return isinstance(value, NullSimObject)
1819
1820# Some memory range specifications use this as a default upper bound.
1821MaxAddr = Addr.max
1822MaxTick = Tick.max
1823AllMemory = AddrRange(0, MaxAddr)
1824
1825
1826#####################################################################
1827#
1828# Port objects
1829#
1830# Ports are used to interconnect objects in the memory system.
1831#
1832#####################################################################
1833
1834# Port reference: encapsulates a reference to a particular port on a
1835# particular SimObject.
1836class PortRef(object):
1837    def __init__(self, simobj, name, role):
1838        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1839        self.simobj = simobj
1840        self.name = name
1841        self.role = role
1842        self.peer = None   # not associated with another port yet
1843        self.ccConnected = False # C++ port connection done?
1844        self.index = -1  # always -1 for non-vector ports
1845
1846    def __str__(self):
1847        return '%s.%s' % (self.simobj, self.name)
1848
1849    def __len__(self):
1850        # Return the number of connected ports, i.e. 0 is we have no
1851        # peer and 1 if we do.
1852        return int(self.peer != None)
1853
1854    # for config.ini, print peer's name (not ours)
1855    def ini_str(self):
1856        return str(self.peer)
1857
1858    # for config.json
1859    def get_config_as_dict(self):
1860        return {'role' : self.role, 'peer' : str(self.peer)}
1861
1862    def __getattr__(self, attr):
1863        if attr == 'peerObj':
1864            # shorthand for proxies
1865            return self.peer.simobj
1866        raise AttributeError("'%s' object has no attribute '%s'" % \
1867              (self.__class__.__name__, attr))
1868
1869    # Full connection is symmetric (both ways).  Called via
1870    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1871    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1872    # e.g., "obj1.portA[3] = obj2.portB".
1873    def connect(self, other):
1874        if isinstance(other, VectorPortRef):
1875            # reference to plain VectorPort is implicit append
1876            other = other._get_next()
1877        if self.peer and not proxy.isproxy(self.peer):
1878            fatal("Port %s is already connected to %s, cannot connect %s\n",
1879                  self, self.peer, other);
1880        self.peer = other
1881        if proxy.isproxy(other):
1882            other.set_param_desc(PortParamDesc())
1883        elif isinstance(other, PortRef):
1884            if other.peer is not self:
1885                other.connect(self)
1886        else:
1887            raise TypeError("assigning non-port reference '%s' to port '%s'" \
1888                  % (other, self))
1889
1890    # Allow a master/slave port pair to be spliced between
1891    # a port and its connected peer. Useful operation for connecting
1892    # instrumentation structures into a system when it is necessary
1893    # to connect the instrumentation after the full system has been
1894    # constructed.
1895    def splice(self, new_master_peer, new_slave_peer):
1896        if not self.peer or proxy.isproxy(self.peer):
1897            fatal("Port %s not connected, cannot splice in new peers\n", self)
1898
1899        if not isinstance(new_master_peer, PortRef) or \
1900           not isinstance(new_slave_peer, PortRef):
1901            raise TypeError(
1902                  "Splicing non-port references '%s','%s' to port '%s'" % \
1903                  (new_master_peer, new_slave_peer, self))
1904
1905        old_peer = self.peer
1906        if self.role == 'SLAVE':
1907            self.peer = new_master_peer
1908            old_peer.peer = new_slave_peer
1909            new_master_peer.connect(self)
1910            new_slave_peer.connect(old_peer)
1911        elif self.role == 'MASTER':
1912            self.peer = new_slave_peer
1913            old_peer.peer = new_master_peer
1914            new_slave_peer.connect(self)
1915            new_master_peer.connect(old_peer)
1916        else:
1917            panic("Port %s has unknown role, "+\
1918                  "cannot splice in new peers\n", self)
1919
1920    def clone(self, simobj, memo):
1921        if self in memo:
1922            return memo[self]
1923        newRef = copy.copy(self)
1924        memo[self] = newRef
1925        newRef.simobj = simobj
1926        assert(isSimObject(newRef.simobj))
1927        if self.peer and not proxy.isproxy(self.peer):
1928            peerObj = self.peer.simobj(_memo=memo)
1929            newRef.peer = self.peer.clone(peerObj, memo)
1930            assert(not isinstance(newRef.peer, VectorPortRef))
1931        return newRef
1932
1933    def unproxy(self, simobj):
1934        assert(simobj is self.simobj)
1935        if proxy.isproxy(self.peer):
1936            try:
1937                realPeer = self.peer.unproxy(self.simobj)
1938            except:
1939                print("Error in unproxying port '%s' of %s" %
1940                      (self.name, self.simobj.path()))
1941                raise
1942            self.connect(realPeer)
1943
1944    # Call C++ to create corresponding port connection between C++ objects
1945    def ccConnect(self):
1946        from _m5.pyobject import connectPorts
1947
1948        if self.ccConnected: # already done this
1949            return
1950
1951        peer = self.peer
1952        if not self.peer: # nothing to connect to
1953            return
1954
1955        # check that we connect a master to a slave
1956        if self.role == peer.role:
1957            raise TypeError(
1958                "cannot connect '%s' and '%s' due to identical role '%s'" % \
1959                (peer, self, self.role))
1960
1961        if self.role == 'SLAVE':
1962            # do nothing and let the master take care of it
1963            return
1964
1965        try:
1966            # self is always the master and peer the slave
1967            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1968                         peer.simobj.getCCObject(), peer.name, peer.index)
1969        except:
1970            print("Error connecting port %s.%s to %s.%s" %
1971                  (self.simobj.path(), self.name,
1972                   peer.simobj.path(), peer.name))
1973            raise
1974        self.ccConnected = True
1975        peer.ccConnected = True
1976
1977# A reference to an individual element of a VectorPort... much like a
1978# PortRef, but has an index.
1979class VectorPortElementRef(PortRef):
1980    def __init__(self, simobj, name, role, index):
1981        PortRef.__init__(self, simobj, name, role)
1982        self.index = index
1983
1984    def __str__(self):
1985        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1986
1987# A reference to a complete vector-valued port (not just a single element).
1988# Can be indexed to retrieve individual VectorPortElementRef instances.
1989class VectorPortRef(object):
1990    def __init__(self, simobj, name, role):
1991        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1992        self.simobj = simobj
1993        self.name = name
1994        self.role = role
1995        self.elements = []
1996
1997    def __str__(self):
1998        return '%s.%s[:]' % (self.simobj, self.name)
1999
2000    def __len__(self):
2001        # Return the number of connected peers, corresponding the the
2002        # length of the elements.
2003        return len(self.elements)
2004
2005    # for config.ini, print peer's name (not ours)
2006    def ini_str(self):
2007        return ' '.join([el.ini_str() for el in self.elements])
2008
2009    # for config.json
2010    def get_config_as_dict(self):
2011        return {'role' : self.role,
2012                'peer' : [el.ini_str() for el in self.elements]}
2013
2014    def __getitem__(self, key):
2015        if not isinstance(key, int):
2016            raise TypeError("VectorPort index must be integer")
2017        if key >= len(self.elements):
2018            # need to extend list
2019            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
2020                   for i in range(len(self.elements), key+1)]
2021            self.elements.extend(ext)
2022        return self.elements[key]
2023
2024    def _get_next(self):
2025        return self[len(self.elements)]
2026
2027    def __setitem__(self, key, value):
2028        if not isinstance(key, int):
2029            raise TypeError("VectorPort index must be integer")
2030        self[key].connect(value)
2031
2032    def connect(self, other):
2033        if isinstance(other, (list, tuple)):
2034            # Assign list of port refs to vector port.
2035            # For now, append them... not sure if that's the right semantics
2036            # or if it should replace the current vector.
2037            for ref in other:
2038                self._get_next().connect(ref)
2039        else:
2040            # scalar assignment to plain VectorPort is implicit append
2041            self._get_next().connect(other)
2042
2043    def clone(self, simobj, memo):
2044        if self in memo:
2045            return memo[self]
2046        newRef = copy.copy(self)
2047        memo[self] = newRef
2048        newRef.simobj = simobj
2049        assert(isSimObject(newRef.simobj))
2050        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
2051        return newRef
2052
2053    def unproxy(self, simobj):
2054        [el.unproxy(simobj) for el in self.elements]
2055
2056    def ccConnect(self):
2057        [el.ccConnect() for el in self.elements]
2058
2059# Port description object.  Like a ParamDesc object, this represents a
2060# logical port in the SimObject class, not a particular port on a
2061# SimObject instance.  The latter are represented by PortRef objects.
2062class Port(object):
2063    # Generate a PortRef for this port on the given SimObject with the
2064    # given name
2065    def makeRef(self, simobj):
2066        return PortRef(simobj, self.name, self.role)
2067
2068    # Connect an instance of this port (on the given SimObject with
2069    # the given name) with the port described by the supplied PortRef
2070    def connect(self, simobj, ref):
2071        self.makeRef(simobj).connect(ref)
2072
2073    # No need for any pre-declarations at the moment as we merely rely
2074    # on an unsigned int.
2075    def cxx_predecls(self, code):
2076        pass
2077
2078    def pybind_predecls(self, code):
2079        cls.cxx_predecls(self, code)
2080
2081    # Declare an unsigned int with the same name as the port, that
2082    # will eventually hold the number of connected ports (and thus the
2083    # number of elements for a VectorPort).
2084    def cxx_decl(self, code):
2085        code('unsigned int port_${{self.name}}_connection_count;')
2086
2087class MasterPort(Port):
2088    # MasterPort("description")
2089    def __init__(self, *args):
2090        if len(args) == 1:
2091            self.desc = args[0]
2092            self.role = 'MASTER'
2093        else:
2094            raise TypeError('wrong number of arguments')
2095
2096class SlavePort(Port):
2097    # SlavePort("description")
2098    def __init__(self, *args):
2099        if len(args) == 1:
2100            self.desc = args[0]
2101            self.role = 'SLAVE'
2102        else:
2103            raise TypeError('wrong number of arguments')
2104
2105# VectorPort description object.  Like Port, but represents a vector
2106# of connections (e.g., as on a XBar).
2107class VectorPort(Port):
2108    def __init__(self, *args):
2109        self.isVec = True
2110
2111    def makeRef(self, simobj):
2112        return VectorPortRef(simobj, self.name, self.role)
2113
2114class VectorMasterPort(VectorPort):
2115    # VectorMasterPort("description")
2116    def __init__(self, *args):
2117        if len(args) == 1:
2118            self.desc = args[0]
2119            self.role = 'MASTER'
2120            VectorPort.__init__(self, *args)
2121        else:
2122            raise TypeError('wrong number of arguments')
2123
2124class VectorSlavePort(VectorPort):
2125    # VectorSlavePort("description")
2126    def __init__(self, *args):
2127        if len(args) == 1:
2128            self.desc = args[0]
2129            self.role = 'SLAVE'
2130            VectorPort.__init__(self, *args)
2131        else:
2132            raise TypeError('wrong number of arguments')
2133
2134# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2135# proxy objects (via set_param_desc()) so that proxy error messages
2136# make sense.
2137class PortParamDesc(object):
2138    __metaclass__ = Singleton
2139
2140    ptype_str = 'Port'
2141    ptype = Port
2142
2143baseEnums = allEnums.copy()
2144baseParams = allParams.copy()
2145
2146def clear():
2147    global allEnums, allParams
2148
2149    allEnums = baseEnums.copy()
2150    allParams = baseParams.copy()
2151
2152__all__ = ['Param', 'VectorParam',
2153           'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
2154           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2155           'Int32', 'UInt32', 'Int64', 'UInt64',
2156           'Counter', 'Addr', 'Tick', 'Percent',
2157           'TcpPort', 'UdpPort', 'EthernetAddr',
2158           'IpAddress', 'IpNetmask', 'IpWithPort',
2159           'MemorySize', 'MemorySize32',
2160           'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2161           'NetworkBandwidth', 'MemoryBandwidth',
2162           'AddrRange',
2163           'MaxAddr', 'MaxTick', 'AllMemory',
2164           'Time',
2165           'NextEthernetAddr', 'NULL',
2166           'MasterPort', 'SlavePort',
2167           'VectorMasterPort', 'VectorSlavePort']
2168