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