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