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