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