params.py revision 13891:b92919e5fb16
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, is_source):
1838        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1839        self.simobj = simobj
1840        self.name = name
1841        self.role = role
1842        self.is_source = is_source
1843        self.peer = None   # not associated with another port yet
1844        self.ccConnected = False # C++ port connection done?
1845        self.index = -1  # always -1 for non-vector ports
1846
1847    def __str__(self):
1848        return '%s.%s' % (self.simobj, self.name)
1849
1850    def __len__(self):
1851        # Return the number of connected ports, i.e. 0 is we have no
1852        # peer and 1 if we do.
1853        return int(self.peer != None)
1854
1855    # for config.ini, print peer's name (not ours)
1856    def ini_str(self):
1857        return str(self.peer)
1858
1859    # for config.json
1860    def get_config_as_dict(self):
1861        return {'role' : self.role, 'peer' : str(self.peer),
1862                'is_source' : str(self.is_source)}
1863
1864    def __getattr__(self, attr):
1865        if attr == 'peerObj':
1866            # shorthand for proxies
1867            return self.peer.simobj
1868        raise AttributeError("'%s' object has no attribute '%s'" % \
1869              (self.__class__.__name__, attr))
1870
1871    # Full connection is symmetric (both ways).  Called via
1872    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1873    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1874    # e.g., "obj1.portA[3] = obj2.portB".
1875    def connect(self, other):
1876        if isinstance(other, VectorPortRef):
1877            # reference to plain VectorPort is implicit append
1878            other = other._get_next()
1879        if self.peer and not proxy.isproxy(self.peer):
1880            fatal("Port %s is already connected to %s, cannot connect %s\n",
1881                  self, self.peer, other);
1882        self.peer = other
1883
1884        if proxy.isproxy(other):
1885            other.set_param_desc(PortParamDesc())
1886            return
1887        elif not isinstance(other, PortRef):
1888            raise TypeError("assigning non-port reference '%s' to port '%s'" \
1889                  % (other, self))
1890
1891        if not Port.is_compat(self, other):
1892            fatal("Ports %s and %s with roles '%s' and '%s' "
1893                    "are not compatible", self, other, self.role, other.role)
1894
1895        if other.peer is not self:
1896            other.connect(self)
1897
1898    # Allow a compatible port pair to be spliced between a port and its
1899    # connected peer. Useful operation for connecting instrumentation
1900    # structures into a system when it is necessary to connect the
1901    # instrumentation after the full system has been constructed.
1902    def splice(self, new_1, new_2):
1903        if not self.peer or proxy.isproxy(self.peer):
1904            fatal("Port %s not connected, cannot splice in new peers\n", self)
1905
1906        if not isinstance(new_1, PortRef) or not isinstance(new_2, PortRef):
1907            raise TypeError(
1908                  "Splicing non-port references '%s','%s' to port '%s'" % \
1909                  (new_1, new_2, self))
1910
1911        old_peer = self.peer
1912
1913        if Port.is_compat(old_peer, new_1) and Port.is_compat(self, new_2):
1914            old_peer.peer = new_1
1915            new_1.peer = old_peer
1916            self.peer = new_2
1917            new_2.peer = self
1918        elif Port.is_compat(old_peer, new_2) and Port.is_compat(self, new_1):
1919            old_peer.peer = new_2
1920            new_2.peer = old_peer
1921            self.peer = new_1
1922            new_1.peer = self
1923        else:
1924            fatal("Ports %s(%s) and %s(%s) can't be compatibly spliced with "
1925                    "%s(%s) and %s(%s)", self, self.role,
1926                    old_peer, old_peer.role, new_1, new_1.role,
1927                    new_2, new_2.role)
1928
1929    def clone(self, simobj, memo):
1930        if self in memo:
1931            return memo[self]
1932        newRef = copy.copy(self)
1933        memo[self] = newRef
1934        newRef.simobj = simobj
1935        assert(isSimObject(newRef.simobj))
1936        if self.peer and not proxy.isproxy(self.peer):
1937            peerObj = self.peer.simobj(_memo=memo)
1938            newRef.peer = self.peer.clone(peerObj, memo)
1939            assert(not isinstance(newRef.peer, VectorPortRef))
1940        return newRef
1941
1942    def unproxy(self, simobj):
1943        assert(simobj is self.simobj)
1944        if proxy.isproxy(self.peer):
1945            try:
1946                realPeer = self.peer.unproxy(self.simobj)
1947            except:
1948                print("Error in unproxying port '%s' of %s" %
1949                      (self.name, self.simobj.path()))
1950                raise
1951            self.connect(realPeer)
1952
1953    # Call C++ to create corresponding port connection between C++ objects
1954    def ccConnect(self):
1955        if self.ccConnected: # already done this
1956            return
1957
1958        peer = self.peer
1959        if not self.peer: # nothing to connect to
1960            return
1961
1962        port = self.simobj.getPort(self.name, self.index)
1963        peer_port = peer.simobj.getPort(peer.name, peer.index)
1964        port.bind(peer_port)
1965
1966        self.ccConnected = True
1967
1968# A reference to an individual element of a VectorPort... much like a
1969# PortRef, but has an index.
1970class VectorPortElementRef(PortRef):
1971    def __init__(self, simobj, name, role, is_source, index):
1972        PortRef.__init__(self, simobj, name, role, is_source)
1973        self.index = index
1974
1975    def __str__(self):
1976        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1977
1978# A reference to a complete vector-valued port (not just a single element).
1979# Can be indexed to retrieve individual VectorPortElementRef instances.
1980class VectorPortRef(object):
1981    def __init__(self, simobj, name, role, is_source):
1982        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1983        self.simobj = simobj
1984        self.name = name
1985        self.role = role
1986        self.is_source = is_source
1987        self.elements = []
1988
1989    def __str__(self):
1990        return '%s.%s[:]' % (self.simobj, self.name)
1991
1992    def __len__(self):
1993        # Return the number of connected peers, corresponding the the
1994        # length of the elements.
1995        return len(self.elements)
1996
1997    # for config.ini, print peer's name (not ours)
1998    def ini_str(self):
1999        return ' '.join([el.ini_str() for el in self.elements])
2000
2001    # for config.json
2002    def get_config_as_dict(self):
2003        return {'role' : self.role,
2004                'peer' : [el.ini_str() for el in self.elements],
2005                'is_source' : str(self.is_source)}
2006
2007    def __getitem__(self, key):
2008        if not isinstance(key, int):
2009            raise TypeError("VectorPort index must be integer")
2010        if key >= len(self.elements):
2011            # need to extend list
2012            ext = [VectorPortElementRef(
2013                    self.simobj, self.name, self.role, self.is_source, i)
2014                   for i in range(len(self.elements), key+1)]
2015            self.elements.extend(ext)
2016        return self.elements[key]
2017
2018    def _get_next(self):
2019        return self[len(self.elements)]
2020
2021    def __setitem__(self, key, value):
2022        if not isinstance(key, int):
2023            raise TypeError("VectorPort index must be integer")
2024        self[key].connect(value)
2025
2026    def connect(self, other):
2027        if isinstance(other, (list, tuple)):
2028            # Assign list of port refs to vector port.
2029            # For now, append them... not sure if that's the right semantics
2030            # or if it should replace the current vector.
2031            for ref in other:
2032                self._get_next().connect(ref)
2033        else:
2034            # scalar assignment to plain VectorPort is implicit append
2035            self._get_next().connect(other)
2036
2037    def clone(self, simobj, memo):
2038        if self in memo:
2039            return memo[self]
2040        newRef = copy.copy(self)
2041        memo[self] = newRef
2042        newRef.simobj = simobj
2043        assert(isSimObject(newRef.simobj))
2044        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
2045        return newRef
2046
2047    def unproxy(self, simobj):
2048        [el.unproxy(simobj) for el in self.elements]
2049
2050    def ccConnect(self):
2051        [el.ccConnect() for el in self.elements]
2052
2053# Port description object.  Like a ParamDesc object, this represents a
2054# logical port in the SimObject class, not a particular port on a
2055# SimObject instance.  The latter are represented by PortRef objects.
2056class Port(object):
2057    # Port("role", "description")
2058
2059    _compat_dict = { }
2060
2061    @classmethod
2062    def compat(cls, role, peer):
2063        cls._compat_dict.setdefault(role, set()).add(peer)
2064        cls._compat_dict.setdefault(peer, set()).add(role)
2065
2066    @classmethod
2067    def is_compat(cls, one, two):
2068        for port in one, two:
2069            if not port.role in Port._compat_dict:
2070                fatal("Unrecognized role '%s' for port %s\n", port.role, port)
2071        return one.role in Port._compat_dict[two.role]
2072
2073    def __init__(self, role, desc, is_source=False):
2074        self.desc = desc
2075        self.role = role
2076        self.is_source = is_source
2077
2078    # Generate a PortRef for this port on the given SimObject with the
2079    # given name
2080    def makeRef(self, simobj):
2081        return PortRef(simobj, self.name, self.role, self.is_source)
2082
2083    # Connect an instance of this port (on the given SimObject with
2084    # the given name) with the port described by the supplied PortRef
2085    def connect(self, simobj, ref):
2086        self.makeRef(simobj).connect(ref)
2087
2088    # No need for any pre-declarations at the moment as we merely rely
2089    # on an unsigned int.
2090    def cxx_predecls(self, code):
2091        pass
2092
2093    def pybind_predecls(self, code):
2094        cls.cxx_predecls(self, code)
2095
2096    # Declare an unsigned int with the same name as the port, that
2097    # will eventually hold the number of connected ports (and thus the
2098    # number of elements for a VectorPort).
2099    def cxx_decl(self, code):
2100        code('unsigned int port_${{self.name}}_connection_count;')
2101
2102Port.compat('GEM5 REQUESTER', 'GEM5 RESPONDER')
2103
2104class RequestPort(Port):
2105    # RequestPort("description")
2106    def __init__(self, desc):
2107        super(RequestPort, self).__init__(
2108                'GEM5 REQUESTER', desc, is_source=True)
2109
2110class ResponsePort(Port):
2111    # ResponsePort("description")
2112    def __init__(self, desc):
2113        super(ResponsePort, self).__init__('GEM5 RESPONDER', desc)
2114
2115# VectorPort description object.  Like Port, but represents a vector
2116# of connections (e.g., as on a XBar).
2117class VectorPort(Port):
2118    def makeRef(self, simobj):
2119        return VectorPortRef(simobj, self.name, self.role, self.is_source)
2120
2121class VectorRequestPort(VectorPort):
2122    # VectorRequestPort("description")
2123    def __init__(self, desc):
2124        super(VectorRequestPort, self).__init__(
2125                'GEM5 REQUESTER', desc, is_source=True)
2126
2127class VectorResponsePort(VectorPort):
2128    # VectorResponsePort("description")
2129    def __init__(self, desc):
2130        super(VectorResponsePort, self).__init__('GEM5 RESPONDER', desc)
2131
2132# Old names, maintained for compatibility.
2133MasterPort = RequestPort
2134SlavePort = ResponsePort
2135VectorMasterPort = VectorRequestPort
2136VectorSlavePort = VectorResponsePort
2137
2138# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2139# proxy objects (via set_param_desc()) so that proxy error messages
2140# make sense.
2141class PortParamDesc(object):
2142    __metaclass__ = Singleton
2143
2144    ptype_str = 'Port'
2145    ptype = Port
2146
2147baseEnums = allEnums.copy()
2148baseParams = allParams.copy()
2149
2150def clear():
2151    global allEnums, allParams
2152
2153    allEnums = baseEnums.copy()
2154    allParams = baseParams.copy()
2155
2156__all__ = ['Param', 'VectorParam',
2157           'Enum', 'ScopedEnum', 'Bool', 'String', 'Float',
2158           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2159           'Int32', 'UInt32', 'Int64', 'UInt64',
2160           'Counter', 'Addr', 'Tick', 'Percent',
2161           'TcpPort', 'UdpPort', 'EthernetAddr',
2162           'IpAddress', 'IpNetmask', 'IpWithPort',
2163           'MemorySize', 'MemorySize32',
2164           'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2165           'NetworkBandwidth', 'MemoryBandwidth',
2166           'AddrRange',
2167           'MaxAddr', 'MaxTick', 'AllMemory',
2168           'Time',
2169           'NextEthernetAddr', 'NULL',
2170           'Port', 'RequestPort', 'ResponsePort', 'MasterPort', 'SlavePort',
2171           'VectorPort', 'VectorRequestPort', 'VectorResponsePort',
2172           'VectorMasterPort', 'VectorSlavePort']
2173