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