params.py revision 9014:e22ded364548
110259SAndrew.Bardsley@arm.com# Copyright (c) 2012 ARM Limited
210259SAndrew.Bardsley@arm.com# All rights reserved.
310259SAndrew.Bardsley@arm.com#
410259SAndrew.Bardsley@arm.com# The license below extends only to copyright in the software and shall
510259SAndrew.Bardsley@arm.com# not be construed as granting a license to any other intellectual
610259SAndrew.Bardsley@arm.com# property including but not limited to intellectual property relating
710259SAndrew.Bardsley@arm.com# to a hardware implementation of the functionality of the software
810259SAndrew.Bardsley@arm.com# licensed hereunder.  You may use the software subject to the license
910259SAndrew.Bardsley@arm.com# terms below provided that you ensure that this notice is replicated
1010259SAndrew.Bardsley@arm.com# unmodified and in its entirety in all distributions of the software,
1110259SAndrew.Bardsley@arm.com# modified or unmodified, in source code or in binary form.
1210259SAndrew.Bardsley@arm.com#
1310259SAndrew.Bardsley@arm.com# Copyright (c) 2004-2006 The Regents of The University of Michigan
1410259SAndrew.Bardsley@arm.com# Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
1510259SAndrew.Bardsley@arm.com# All rights reserved.
1610259SAndrew.Bardsley@arm.com#
1710259SAndrew.Bardsley@arm.com# Redistribution and use in source and binary forms, with or without
1810259SAndrew.Bardsley@arm.com# modification, are permitted provided that the following conditions are
1910259SAndrew.Bardsley@arm.com# met: redistributions of source code must retain the above copyright
2010259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer;
2110259SAndrew.Bardsley@arm.com# redistributions in binary form must reproduce the above copyright
2210259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer in the
2310259SAndrew.Bardsley@arm.com# documentation and/or other materials provided with the distribution;
2410259SAndrew.Bardsley@arm.com# neither the name of the copyright holders nor the names of its
2510259SAndrew.Bardsley@arm.com# contributors may be used to endorse or promote products derived from
2610259SAndrew.Bardsley@arm.com# this software without specific prior written permission.
2710259SAndrew.Bardsley@arm.com#
2810259SAndrew.Bardsley@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2910259SAndrew.Bardsley@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3010259SAndrew.Bardsley@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3110259SAndrew.Bardsley@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3210259SAndrew.Bardsley@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3310259SAndrew.Bardsley@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3410259SAndrew.Bardsley@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3510259SAndrew.Bardsley@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3610259SAndrew.Bardsley@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3710259SAndrew.Bardsley@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3810259SAndrew.Bardsley@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3910259SAndrew.Bardsley@arm.com#
4010259SAndrew.Bardsley@arm.com# Authors: Steve Reinhardt
4110259SAndrew.Bardsley@arm.com#          Nathan Binkert
4210259SAndrew.Bardsley@arm.com#          Gabe Black
4310259SAndrew.Bardsley@arm.com#          Andreas Hansson
4410259SAndrew.Bardsley@arm.com
4510259SAndrew.Bardsley@arm.com#####################################################################
4610259SAndrew.Bardsley@arm.com#
4710259SAndrew.Bardsley@arm.com# Parameter description classes
4810259SAndrew.Bardsley@arm.com#
4910259SAndrew.Bardsley@arm.com# The _params dictionary in each class maps parameter names to either
5010259SAndrew.Bardsley@arm.com# a Param or a VectorParam object.  These objects contain the
5110259SAndrew.Bardsley@arm.com# parameter description string, the parameter type, and the default
5210259SAndrew.Bardsley@arm.com# value (if any).  The convert() method on these objects is used to
5310259SAndrew.Bardsley@arm.com# force whatever value is assigned to the parameter to the appropriate
5410259SAndrew.Bardsley@arm.com# type.
5510259SAndrew.Bardsley@arm.com#
5610259SAndrew.Bardsley@arm.com# Note that the default values are loaded into the class's attribute
5710259SAndrew.Bardsley@arm.com# space when the parameter dictionary is initialized (in
5810259SAndrew.Bardsley@arm.com# MetaSimObject._new_param()); after that point they aren't used.
5910259SAndrew.Bardsley@arm.com#
6010259SAndrew.Bardsley@arm.com#####################################################################
6110259SAndrew.Bardsley@arm.com
6210259SAndrew.Bardsley@arm.comimport copy
6310259SAndrew.Bardsley@arm.comimport datetime
6410259SAndrew.Bardsley@arm.comimport re
6510259SAndrew.Bardsley@arm.comimport sys
6610259SAndrew.Bardsley@arm.comimport time
6710259SAndrew.Bardsley@arm.comimport math
6810259SAndrew.Bardsley@arm.com
6910259SAndrew.Bardsley@arm.comimport proxy
7010259SAndrew.Bardsley@arm.comimport ticks
7110259SAndrew.Bardsley@arm.comfrom util import *
7210259SAndrew.Bardsley@arm.com
7310259SAndrew.Bardsley@arm.comdef isSimObject(*args, **kwargs):
7410259SAndrew.Bardsley@arm.com    return SimObject.isSimObject(*args, **kwargs)
7510259SAndrew.Bardsley@arm.com
7610259SAndrew.Bardsley@arm.comdef isSimObjectSequence(*args, **kwargs):
7710259SAndrew.Bardsley@arm.com    return SimObject.isSimObjectSequence(*args, **kwargs)
7810259SAndrew.Bardsley@arm.com
7910259SAndrew.Bardsley@arm.comdef isSimObjectClass(*args, **kwargs):
8010259SAndrew.Bardsley@arm.com    return SimObject.isSimObjectClass(*args, **kwargs)
8110259SAndrew.Bardsley@arm.com
8210259SAndrew.Bardsley@arm.comallParams = {}
8310259SAndrew.Bardsley@arm.com
8410259SAndrew.Bardsley@arm.comclass MetaParamValue(type):
8510259SAndrew.Bardsley@arm.com    def __new__(mcls, name, bases, dct):
8610259SAndrew.Bardsley@arm.com        cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
8710259SAndrew.Bardsley@arm.com        assert name not in allParams
8810259SAndrew.Bardsley@arm.com        allParams[name] = cls
8910259SAndrew.Bardsley@arm.com        return cls
9010259SAndrew.Bardsley@arm.com
9110259SAndrew.Bardsley@arm.com
9210259SAndrew.Bardsley@arm.com# Dummy base class to identify types that are legitimate for SimObject
9310259SAndrew.Bardsley@arm.com# parameters.
9410259SAndrew.Bardsley@arm.comclass ParamValue(object):
9510259SAndrew.Bardsley@arm.com    __metaclass__ = MetaParamValue
9610259SAndrew.Bardsley@arm.com
9710259SAndrew.Bardsley@arm.com
9810259SAndrew.Bardsley@arm.com    # Generate the code needed as a prerequisite for declaring a C++
9910259SAndrew.Bardsley@arm.com    # object of this type.  Typically generates one or more #include
10010259SAndrew.Bardsley@arm.com    # statements.  Used when declaring parameters of this type.
10110259SAndrew.Bardsley@arm.com    @classmethod
10210259SAndrew.Bardsley@arm.com    def cxx_predecls(cls, code):
10310259SAndrew.Bardsley@arm.com        pass
10410259SAndrew.Bardsley@arm.com
10510259SAndrew.Bardsley@arm.com    # Generate the code needed as a prerequisite for including a
10610259SAndrew.Bardsley@arm.com    # reference to a C++ object of this type in a SWIG .i file.
10710259SAndrew.Bardsley@arm.com    # Typically generates one or more %import or %include statements.
10810259SAndrew.Bardsley@arm.com    @classmethod
10910259SAndrew.Bardsley@arm.com    def swig_predecls(cls, code):
11010259SAndrew.Bardsley@arm.com        pass
11110259SAndrew.Bardsley@arm.com
11210259SAndrew.Bardsley@arm.com    # default for printing to .ini file is regular string conversion.
11310259SAndrew.Bardsley@arm.com    # will be overridden in some cases
11410259SAndrew.Bardsley@arm.com    def ini_str(self):
11510259SAndrew.Bardsley@arm.com        return str(self)
11610259SAndrew.Bardsley@arm.com
11710259SAndrew.Bardsley@arm.com    # allows us to blithely call unproxy() on things without checking
11810259SAndrew.Bardsley@arm.com    # if they're really proxies or not
11910259SAndrew.Bardsley@arm.com    def unproxy(self, base):
12010259SAndrew.Bardsley@arm.com        return self
12110259SAndrew.Bardsley@arm.com
12210259SAndrew.Bardsley@arm.com# Regular parameter description.
12310259SAndrew.Bardsley@arm.comclass ParamDesc(object):
12410259SAndrew.Bardsley@arm.com    def __init__(self, ptype_str, ptype, *args, **kwargs):
12510259SAndrew.Bardsley@arm.com        self.ptype_str = ptype_str
12610259SAndrew.Bardsley@arm.com        # remember ptype only if it is provided
12710259SAndrew.Bardsley@arm.com        if ptype != None:
12810259SAndrew.Bardsley@arm.com            self.ptype = ptype
12910259SAndrew.Bardsley@arm.com
13010259SAndrew.Bardsley@arm.com        if args:
13110259SAndrew.Bardsley@arm.com            if len(args) == 1:
13210259SAndrew.Bardsley@arm.com                self.desc = args[0]
13310259SAndrew.Bardsley@arm.com            elif len(args) == 2:
13410259SAndrew.Bardsley@arm.com                self.default = args[0]
13510259SAndrew.Bardsley@arm.com                self.desc = args[1]
13610259SAndrew.Bardsley@arm.com            else:
13710259SAndrew.Bardsley@arm.com                raise TypeError, 'too many arguments'
13810259SAndrew.Bardsley@arm.com
13910259SAndrew.Bardsley@arm.com        if kwargs.has_key('desc'):
14010259SAndrew.Bardsley@arm.com            assert(not hasattr(self, 'desc'))
14110259SAndrew.Bardsley@arm.com            self.desc = kwargs['desc']
14210259SAndrew.Bardsley@arm.com            del kwargs['desc']
14310259SAndrew.Bardsley@arm.com
14410259SAndrew.Bardsley@arm.com        if kwargs.has_key('default'):
14510259SAndrew.Bardsley@arm.com            assert(not hasattr(self, 'default'))
14610259SAndrew.Bardsley@arm.com            self.default = kwargs['default']
14710259SAndrew.Bardsley@arm.com            del kwargs['default']
14810259SAndrew.Bardsley@arm.com
14910259SAndrew.Bardsley@arm.com        if kwargs:
15010259SAndrew.Bardsley@arm.com            raise TypeError, 'extra unknown kwargs %s' % kwargs
15110259SAndrew.Bardsley@arm.com
15210259SAndrew.Bardsley@arm.com        if not hasattr(self, 'desc'):
15310259SAndrew.Bardsley@arm.com            raise TypeError, 'desc attribute missing'
15410259SAndrew.Bardsley@arm.com
15510259SAndrew.Bardsley@arm.com    def __getattr__(self, attr):
15610259SAndrew.Bardsley@arm.com        if attr == 'ptype':
15710259SAndrew.Bardsley@arm.com            ptype = SimObject.allClasses[self.ptype_str]
15810259SAndrew.Bardsley@arm.com            assert isSimObjectClass(ptype)
15910259SAndrew.Bardsley@arm.com            self.ptype = ptype
16010259SAndrew.Bardsley@arm.com            return ptype
16110259SAndrew.Bardsley@arm.com
16210259SAndrew.Bardsley@arm.com        raise AttributeError, "'%s' object has no attribute '%s'" % \
16310259SAndrew.Bardsley@arm.com              (type(self).__name__, attr)
16410259SAndrew.Bardsley@arm.com
16510259SAndrew.Bardsley@arm.com    def convert(self, value):
16610259SAndrew.Bardsley@arm.com        if isinstance(value, proxy.BaseProxy):
16710259SAndrew.Bardsley@arm.com            value.set_param_desc(self)
16810259SAndrew.Bardsley@arm.com            return value
16910259SAndrew.Bardsley@arm.com        if not hasattr(self, 'ptype') and isNullPointer(value):
17010259SAndrew.Bardsley@arm.com            # deferred evaluation of SimObject; continue to defer if
17110259SAndrew.Bardsley@arm.com            # we're just assigning a null pointer
17210259SAndrew.Bardsley@arm.com            return value
17310259SAndrew.Bardsley@arm.com        if isinstance(value, self.ptype):
17410259SAndrew.Bardsley@arm.com            return value
17510259SAndrew.Bardsley@arm.com        if isNullPointer(value) and isSimObjectClass(self.ptype):
17610259SAndrew.Bardsley@arm.com            return value
17710259SAndrew.Bardsley@arm.com        return self.ptype(value)
17810259SAndrew.Bardsley@arm.com
17910259SAndrew.Bardsley@arm.com    def cxx_predecls(self, code):
18010259SAndrew.Bardsley@arm.com        code('#include <cstddef>')
18110259SAndrew.Bardsley@arm.com        self.ptype.cxx_predecls(code)
18210259SAndrew.Bardsley@arm.com
18310259SAndrew.Bardsley@arm.com    def swig_predecls(self, code):
18410259SAndrew.Bardsley@arm.com        self.ptype.swig_predecls(code)
18510259SAndrew.Bardsley@arm.com
18610259SAndrew.Bardsley@arm.com    def cxx_decl(self, code):
18710259SAndrew.Bardsley@arm.com        code('${{self.ptype.cxx_type}} ${{self.name}};')
18810259SAndrew.Bardsley@arm.com
18910259SAndrew.Bardsley@arm.com# Vector-valued parameter description.  Just like ParamDesc, except
19010259SAndrew.Bardsley@arm.com# that the value is a vector (list) of the specified type instead of a
19110259SAndrew.Bardsley@arm.com# single value.
19210259SAndrew.Bardsley@arm.com
19310259SAndrew.Bardsley@arm.comclass VectorParamValue(list):
19410259SAndrew.Bardsley@arm.com    __metaclass__ = MetaParamValue
19510259SAndrew.Bardsley@arm.com    def __setattr__(self, attr, value):
19610259SAndrew.Bardsley@arm.com        raise AttributeError, \
19710259SAndrew.Bardsley@arm.com              "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
19810259SAndrew.Bardsley@arm.com
19910259SAndrew.Bardsley@arm.com    def ini_str(self):
20010259SAndrew.Bardsley@arm.com        return ' '.join([v.ini_str() for v in self])
20110259SAndrew.Bardsley@arm.com
20210259SAndrew.Bardsley@arm.com    def getValue(self):
20310259SAndrew.Bardsley@arm.com        return [ v.getValue() for v in self ]
20410259SAndrew.Bardsley@arm.com
20510259SAndrew.Bardsley@arm.com    def unproxy(self, base):
20610259SAndrew.Bardsley@arm.com        if len(self) == 1 and isinstance(self[0], proxy.AllProxy):
20710259SAndrew.Bardsley@arm.com            return self[0].unproxy(base)
20810259SAndrew.Bardsley@arm.com        else:
20910259SAndrew.Bardsley@arm.com             return [v.unproxy(base) for v in self]
21010259SAndrew.Bardsley@arm.com
21110259SAndrew.Bardsley@arm.comclass SimObjectVector(VectorParamValue):
21210259SAndrew.Bardsley@arm.com    # support clone operation
21310259SAndrew.Bardsley@arm.com    def __call__(self, **kwargs):
21410259SAndrew.Bardsley@arm.com        return SimObjectVector([v(**kwargs) for v in self])
21510259SAndrew.Bardsley@arm.com
21610259SAndrew.Bardsley@arm.com    def clear_parent(self, old_parent):
21710259SAndrew.Bardsley@arm.com        for v in self:
21810259SAndrew.Bardsley@arm.com            v.clear_parent(old_parent)
21910259SAndrew.Bardsley@arm.com
22010259SAndrew.Bardsley@arm.com    def set_parent(self, parent, name):
22110259SAndrew.Bardsley@arm.com        if len(self) == 1:
22212104Snathanael.premillieu@arm.com            self[0].set_parent(parent, name)
22310259SAndrew.Bardsley@arm.com        else:
22410259SAndrew.Bardsley@arm.com            width = int(math.ceil(math.log(len(self))/math.log(10)))
22510259SAndrew.Bardsley@arm.com            for i,v in enumerate(self):
22610259SAndrew.Bardsley@arm.com                v.set_parent(parent, "%s%0*d" % (name, width, i))
22710259SAndrew.Bardsley@arm.com
22810259SAndrew.Bardsley@arm.com    def has_parent(self):
22910259SAndrew.Bardsley@arm.com        return reduce(lambda x,y: x and y, [v.has_parent() for v in self])
23010259SAndrew.Bardsley@arm.com
23110259SAndrew.Bardsley@arm.com    # return 'cpu0 cpu1' etc. for print_ini()
23212420Sgabeblack@google.com    def get_name(self):
23310259SAndrew.Bardsley@arm.com        return ' '.join([v._name for v in self])
23410259SAndrew.Bardsley@arm.com
23510259SAndrew.Bardsley@arm.com    # By iterating through the constituent members of the vector here
23610259SAndrew.Bardsley@arm.com    # we can nicely handle iterating over all a SimObject's children
23710259SAndrew.Bardsley@arm.com    # without having to provide lots of special functions on
23810259SAndrew.Bardsley@arm.com    # SimObjectVector directly.
23910259SAndrew.Bardsley@arm.com    def descendants(self):
24010259SAndrew.Bardsley@arm.com        for v in self:
24110259SAndrew.Bardsley@arm.com            for obj in v.descendants():
24210259SAndrew.Bardsley@arm.com                yield obj
24310259SAndrew.Bardsley@arm.com
24410259SAndrew.Bardsley@arm.com    def get_config_as_dict(self):
24510259SAndrew.Bardsley@arm.com        a = []
24610259SAndrew.Bardsley@arm.com        for v in self:
24710259SAndrew.Bardsley@arm.com            a.append(v.get_config_as_dict())
24810259SAndrew.Bardsley@arm.com        return a
24910259SAndrew.Bardsley@arm.com
25010259SAndrew.Bardsley@arm.comclass VectorParamDesc(ParamDesc):
25110259SAndrew.Bardsley@arm.com    # Convert assigned value to appropriate type.  If the RHS is not a
25210259SAndrew.Bardsley@arm.com    # list or tuple, it generates a single-element list.
25310259SAndrew.Bardsley@arm.com    def convert(self, value):
25410259SAndrew.Bardsley@arm.com        if isinstance(value, (list, tuple)):
25510259SAndrew.Bardsley@arm.com            # list: coerce each element into new list
25610259SAndrew.Bardsley@arm.com            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
25710259SAndrew.Bardsley@arm.com        else:
25810259SAndrew.Bardsley@arm.com            # singleton: coerce to a single-element list
25910259SAndrew.Bardsley@arm.com            tmp_list = [ ParamDesc.convert(self, value) ]
26010259SAndrew.Bardsley@arm.com
26110259SAndrew.Bardsley@arm.com        if isSimObjectSequence(tmp_list):
26210259SAndrew.Bardsley@arm.com            return SimObjectVector(tmp_list)
26310259SAndrew.Bardsley@arm.com        else:
26410259SAndrew.Bardsley@arm.com            return VectorParamValue(tmp_list)
26510259SAndrew.Bardsley@arm.com
26610259SAndrew.Bardsley@arm.com    def swig_module_name(self):
26710259SAndrew.Bardsley@arm.com        return "%s_vector" % self.ptype_str
26810259SAndrew.Bardsley@arm.com
26910259SAndrew.Bardsley@arm.com    def swig_predecls(self, code):
27010259SAndrew.Bardsley@arm.com        code('%import "${{self.swig_module_name()}}.i"')
27110259SAndrew.Bardsley@arm.com
27210259SAndrew.Bardsley@arm.com    def swig_decl(self, code):
27310259SAndrew.Bardsley@arm.com        code('%module(package="m5.internal") ${{self.swig_module_name()}}')
27410259SAndrew.Bardsley@arm.com        code('%{')
27510259SAndrew.Bardsley@arm.com        self.ptype.cxx_predecls(code)
27610259SAndrew.Bardsley@arm.com        code('%}')
27710259SAndrew.Bardsley@arm.com        code()
278        # Make sure the SWIGPY_SLICE_ARG is defined through this inclusion
279        code('%include "std_container.i"')
280        code()
281        self.ptype.swig_predecls(code)
282        code()
283        code('%include "std_vector.i"')
284        code()
285
286        ptype = self.ptype_str
287        cxx_type = self.ptype.cxx_type
288
289        code('''\
290%typemap(in) std::vector< $cxx_type >::value_type {
291    if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) {
292        if (SWIG_ConvertPtr($$input, (void **)&$$1,
293                            $$descriptor($cxx_type), 0) == -1) {
294            return NULL;
295        }
296    }
297}
298
299%typemap(in) std::vector< $cxx_type >::value_type * {
300    if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) {
301        if (SWIG_ConvertPtr($$input, (void **)&$$1,
302                            $$descriptor($cxx_type *), 0) == -1) {
303            return NULL;
304        }
305    }
306}
307''')
308
309        code('%template(vector_$ptype) std::vector< $cxx_type >;')
310
311    def cxx_predecls(self, code):
312        code('#include <vector>')
313        self.ptype.cxx_predecls(code)
314
315    def cxx_decl(self, code):
316        code('std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};')
317
318class ParamFactory(object):
319    def __init__(self, param_desc_class, ptype_str = None):
320        self.param_desc_class = param_desc_class
321        self.ptype_str = ptype_str
322
323    def __getattr__(self, attr):
324        if self.ptype_str:
325            attr = self.ptype_str + '.' + attr
326        return ParamFactory(self.param_desc_class, attr)
327
328    # E.g., Param.Int(5, "number of widgets")
329    def __call__(self, *args, **kwargs):
330        ptype = None
331        try:
332            ptype = allParams[self.ptype_str]
333        except KeyError:
334            # if name isn't defined yet, assume it's a SimObject, and
335            # try to resolve it later
336            pass
337        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
338
339Param = ParamFactory(ParamDesc)
340VectorParam = ParamFactory(VectorParamDesc)
341
342#####################################################################
343#
344# Parameter Types
345#
346# Though native Python types could be used to specify parameter types
347# (the 'ptype' field of the Param and VectorParam classes), it's more
348# flexible to define our own set of types.  This gives us more control
349# over how Python expressions are converted to values (via the
350# __init__() constructor) and how these values are printed out (via
351# the __str__() conversion method).
352#
353#####################################################################
354
355# String-valued parameter.  Just mixin the ParamValue class with the
356# built-in str class.
357class String(ParamValue,str):
358    cxx_type = 'std::string'
359
360    @classmethod
361    def cxx_predecls(self, code):
362        code('#include <string>')
363
364    @classmethod
365    def swig_predecls(cls, code):
366        code('%include "std_string.i"')
367
368    def getValue(self):
369        return self
370
371# superclass for "numeric" parameter values, to emulate math
372# operations in a type-safe way.  e.g., a Latency times an int returns
373# a new Latency object.
374class NumericParamValue(ParamValue):
375    def __str__(self):
376        return str(self.value)
377
378    def __float__(self):
379        return float(self.value)
380
381    def __long__(self):
382        return long(self.value)
383
384    def __int__(self):
385        return int(self.value)
386
387    # hook for bounds checking
388    def _check(self):
389        return
390
391    def __mul__(self, other):
392        newobj = self.__class__(self)
393        newobj.value *= other
394        newobj._check()
395        return newobj
396
397    __rmul__ = __mul__
398
399    def __div__(self, other):
400        newobj = self.__class__(self)
401        newobj.value /= other
402        newobj._check()
403        return newobj
404
405    def __sub__(self, other):
406        newobj = self.__class__(self)
407        newobj.value -= other
408        newobj._check()
409        return newobj
410
411# Metaclass for bounds-checked integer parameters.  See CheckedInt.
412class CheckedIntType(MetaParamValue):
413    def __init__(cls, name, bases, dict):
414        super(CheckedIntType, cls).__init__(name, bases, dict)
415
416        # CheckedInt is an abstract base class, so we actually don't
417        # want to do any processing on it... the rest of this code is
418        # just for classes that derive from CheckedInt.
419        if name == 'CheckedInt':
420            return
421
422        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
423            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
424                panic("CheckedInt subclass %s must define either\n" \
425                      "    'min' and 'max' or 'size' and 'unsigned'\n",
426                      name);
427            if cls.unsigned:
428                cls.min = 0
429                cls.max = 2 ** cls.size - 1
430            else:
431                cls.min = -(2 ** (cls.size - 1))
432                cls.max = (2 ** (cls.size - 1)) - 1
433
434# Abstract superclass for bounds-checked integer parameters.  This
435# class is subclassed to generate parameter classes with specific
436# bounds.  Initialization of the min and max bounds is done in the
437# metaclass CheckedIntType.__init__.
438class CheckedInt(NumericParamValue):
439    __metaclass__ = CheckedIntType
440
441    def _check(self):
442        if not self.min <= self.value <= self.max:
443            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
444                  (self.min, self.value, self.max)
445
446    def __init__(self, value):
447        if isinstance(value, str):
448            self.value = convert.toInteger(value)
449        elif isinstance(value, (int, long, float, NumericParamValue)):
450            self.value = long(value)
451        else:
452            raise TypeError, "Can't convert object of type %s to CheckedInt" \
453                  % type(value).__name__
454        self._check()
455
456    @classmethod
457    def cxx_predecls(cls, code):
458        # most derived types require this, so we just do it here once
459        code('#include "base/types.hh"')
460
461    @classmethod
462    def swig_predecls(cls, code):
463        # most derived types require this, so we just do it here once
464        code('%import "stdint.i"')
465        code('%import "base/types.hh"')
466
467    def getValue(self):
468        return long(self.value)
469
470class Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
471class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
472
473class Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
474class UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
475class Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
476class UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
477class Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
478class UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
479class Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
480class UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
481
482class Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
483class Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
484class TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
485class UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
486
487class Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
488
489class Float(ParamValue, float):
490    cxx_type = 'double'
491
492    def __init__(self, value):
493        if isinstance(value, (int, long, float, NumericParamValue, Float)):
494            self.value = float(value)
495        else:
496            raise TypeError, "Can't convert object of type %s to Float" \
497                  % type(value).__name__
498
499    def getValue(self):
500        return float(self.value)
501
502class MemorySize(CheckedInt):
503    cxx_type = 'uint64_t'
504    size = 64
505    unsigned = True
506    def __init__(self, value):
507        if isinstance(value, MemorySize):
508            self.value = value.value
509        else:
510            self.value = convert.toMemorySize(value)
511        self._check()
512
513class MemorySize32(CheckedInt):
514    cxx_type = 'uint32_t'
515    size = 32
516    unsigned = True
517    def __init__(self, value):
518        if isinstance(value, MemorySize):
519            self.value = value.value
520        else:
521            self.value = convert.toMemorySize(value)
522        self._check()
523
524class Addr(CheckedInt):
525    cxx_type = 'Addr'
526    size = 64
527    unsigned = True
528    def __init__(self, value):
529        if isinstance(value, Addr):
530            self.value = value.value
531        else:
532            try:
533                self.value = convert.toMemorySize(value)
534            except TypeError:
535                self.value = long(value)
536        self._check()
537    def __add__(self, other):
538        if isinstance(other, Addr):
539            return self.value + other.value
540        else:
541            return self.value + other
542
543
544class MetaRange(MetaParamValue):
545    def __init__(cls, name, bases, dict):
546        super(MetaRange, cls).__init__(name, bases, dict)
547        if name == 'Range':
548            return
549        cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
550
551class Range(ParamValue):
552    __metaclass__ = MetaRange
553    type = Int # default; can be overridden in subclasses
554    def __init__(self, *args, **kwargs):
555        def handle_kwargs(self, kwargs):
556            if 'end' in kwargs:
557                self.second = self.type(kwargs.pop('end'))
558            elif 'size' in kwargs:
559                self.second = self.first + self.type(kwargs.pop('size')) - 1
560            else:
561                raise TypeError, "Either end or size must be specified"
562
563        if len(args) == 0:
564            self.first = self.type(kwargs.pop('start'))
565            handle_kwargs(self, kwargs)
566
567        elif len(args) == 1:
568            if kwargs:
569                self.first = self.type(args[0])
570                handle_kwargs(self, kwargs)
571            elif isinstance(args[0], Range):
572                self.first = self.type(args[0].first)
573                self.second = self.type(args[0].second)
574            elif isinstance(args[0], (list, tuple)):
575                self.first = self.type(args[0][0])
576                self.second = self.type(args[0][1])
577            else:
578                self.first = self.type(0)
579                self.second = self.type(args[0]) - 1
580
581        elif len(args) == 2:
582            self.first = self.type(args[0])
583            self.second = self.type(args[1])
584        else:
585            raise TypeError, "Too many arguments specified"
586
587        if kwargs:
588            raise TypeError, "too many keywords: %s" % kwargs.keys()
589
590    def __str__(self):
591        return '%s:%s' % (self.first, self.second)
592
593    @classmethod
594    def cxx_predecls(cls, code):
595        cls.type.cxx_predecls(code)
596        code('#include "base/range.hh"')
597
598    @classmethod
599    def swig_predecls(cls, code):
600        cls.type.swig_predecls(code)
601        code('%import "python/swig/range.i"')
602
603class AddrRange(Range):
604    type = Addr
605
606    def getValue(self):
607        from m5.internal.range import AddrRange
608
609        value = AddrRange()
610        value.start = long(self.first)
611        value.end = long(self.second)
612        return value
613
614class TickRange(Range):
615    type = Tick
616
617    def getValue(self):
618        from m5.internal.range import TickRange
619
620        value = TickRange()
621        value.start = long(self.first)
622        value.end = long(self.second)
623        return value
624
625# Boolean parameter type.  Python doesn't let you subclass bool, since
626# it doesn't want to let you create multiple instances of True and
627# False.  Thus this is a little more complicated than String.
628class Bool(ParamValue):
629    cxx_type = 'bool'
630    def __init__(self, value):
631        try:
632            self.value = convert.toBool(value)
633        except TypeError:
634            self.value = bool(value)
635
636    def getValue(self):
637        return bool(self.value)
638
639    def __str__(self):
640        return str(self.value)
641
642    # implement truth value testing for Bool parameters so that these params
643    # evaluate correctly during the python configuration phase
644    def __nonzero__(self):
645        return bool(self.value)
646
647    def ini_str(self):
648        if self.value:
649            return 'true'
650        return 'false'
651
652def IncEthernetAddr(addr, val = 1):
653    bytes = map(lambda x: int(x, 16), addr.split(':'))
654    bytes[5] += val
655    for i in (5, 4, 3, 2, 1):
656        val,rem = divmod(bytes[i], 256)
657        bytes[i] = rem
658        if val == 0:
659            break
660        bytes[i - 1] += val
661    assert(bytes[0] <= 255)
662    return ':'.join(map(lambda x: '%02x' % x, bytes))
663
664_NextEthernetAddr = "00:90:00:00:00:01"
665def NextEthernetAddr():
666    global _NextEthernetAddr
667
668    value = _NextEthernetAddr
669    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
670    return value
671
672class EthernetAddr(ParamValue):
673    cxx_type = 'Net::EthAddr'
674
675    @classmethod
676    def cxx_predecls(cls, code):
677        code('#include "base/inet.hh"')
678
679    @classmethod
680    def swig_predecls(cls, code):
681        code('%include "python/swig/inet.i"')
682
683    def __init__(self, value):
684        if value == NextEthernetAddr:
685            self.value = value
686            return
687
688        if not isinstance(value, str):
689            raise TypeError, "expected an ethernet address and didn't get one"
690
691        bytes = value.split(':')
692        if len(bytes) != 6:
693            raise TypeError, 'invalid ethernet address %s' % value
694
695        for byte in bytes:
696            if not 0 <= int(byte) <= 0xff:
697                raise TypeError, 'invalid ethernet address %s' % value
698
699        self.value = value
700
701    def unproxy(self, base):
702        if self.value == NextEthernetAddr:
703            return EthernetAddr(self.value())
704        return self
705
706    def getValue(self):
707        from m5.internal.params import EthAddr
708        return EthAddr(self.value)
709
710    def ini_str(self):
711        return self.value
712
713# When initializing an IpAddress, pass in an existing IpAddress, a string of
714# the form "a.b.c.d", or an integer representing an IP.
715class IpAddress(ParamValue):
716    cxx_type = 'Net::IpAddress'
717
718    @classmethod
719    def cxx_predecls(cls, code):
720        code('#include "base/inet.hh"')
721
722    @classmethod
723    def swig_predecls(cls, code):
724        code('%include "python/swig/inet.i"')
725
726    def __init__(self, value):
727        if isinstance(value, IpAddress):
728            self.ip = value.ip
729        else:
730            try:
731                self.ip = convert.toIpAddress(value)
732            except TypeError:
733                self.ip = long(value)
734        self.verifyIp()
735
736    def __str__(self):
737        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
738        return '%d.%d.%d.%d' % tuple(tup)
739
740    def __eq__(self, other):
741        if isinstance(other, IpAddress):
742            return self.ip == other.ip
743        elif isinstance(other, str):
744            try:
745                return self.ip == convert.toIpAddress(other)
746            except:
747                return False
748        else:
749            return self.ip == other
750
751    def __ne__(self, other):
752        return not (self == other)
753
754    def verifyIp(self):
755        if self.ip < 0 or self.ip >= (1 << 32):
756            raise TypeError, "invalid ip address %#08x" % self.ip
757
758    def getValue(self):
759        from m5.internal.params import IpAddress
760        return IpAddress(self.ip)
761
762# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
763# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
764# positional or keyword arguments.
765class IpNetmask(IpAddress):
766    cxx_type = 'Net::IpNetmask'
767
768    @classmethod
769    def cxx_predecls(cls, code):
770        code('#include "base/inet.hh"')
771
772    @classmethod
773    def swig_predecls(cls, code):
774        code('%include "python/swig/inet.i"')
775
776    def __init__(self, *args, **kwargs):
777        def handle_kwarg(self, kwargs, key, elseVal = None):
778            if key in kwargs:
779                setattr(self, key, kwargs.pop(key))
780            elif elseVal:
781                setattr(self, key, elseVal)
782            else:
783                raise TypeError, "No value set for %s" % key
784
785        if len(args) == 0:
786            handle_kwarg(self, kwargs, 'ip')
787            handle_kwarg(self, kwargs, 'netmask')
788
789        elif len(args) == 1:
790            if kwargs:
791                if not 'ip' in kwargs and not 'netmask' in kwargs:
792                    raise TypeError, "Invalid arguments"
793                handle_kwarg(self, kwargs, 'ip', args[0])
794                handle_kwarg(self, kwargs, 'netmask', args[0])
795            elif isinstance(args[0], IpNetmask):
796                self.ip = args[0].ip
797                self.netmask = args[0].netmask
798            else:
799                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
800
801        elif len(args) == 2:
802            self.ip = args[0]
803            self.netmask = args[1]
804        else:
805            raise TypeError, "Too many arguments specified"
806
807        if kwargs:
808            raise TypeError, "Too many keywords: %s" % kwargs.keys()
809
810        self.verify()
811
812    def __str__(self):
813        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
814
815    def __eq__(self, other):
816        if isinstance(other, IpNetmask):
817            return self.ip == other.ip and self.netmask == other.netmask
818        elif isinstance(other, str):
819            try:
820                return (self.ip, self.netmask) == convert.toIpNetmask(other)
821            except:
822                return False
823        else:
824            return False
825
826    def verify(self):
827        self.verifyIp()
828        if self.netmask < 0 or self.netmask > 32:
829            raise TypeError, "invalid netmask %d" % netmask
830
831    def getValue(self):
832        from m5.internal.params import IpNetmask
833        return IpNetmask(self.ip, self.netmask)
834
835# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
836# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
837class IpWithPort(IpAddress):
838    cxx_type = 'Net::IpWithPort'
839
840    @classmethod
841    def cxx_predecls(cls, code):
842        code('#include "base/inet.hh"')
843
844    @classmethod
845    def swig_predecls(cls, code):
846        code('%include "python/swig/inet.i"')
847
848    def __init__(self, *args, **kwargs):
849        def handle_kwarg(self, kwargs, key, elseVal = None):
850            if key in kwargs:
851                setattr(self, key, kwargs.pop(key))
852            elif elseVal:
853                setattr(self, key, elseVal)
854            else:
855                raise TypeError, "No value set for %s" % key
856
857        if len(args) == 0:
858            handle_kwarg(self, kwargs, 'ip')
859            handle_kwarg(self, kwargs, 'port')
860
861        elif len(args) == 1:
862            if kwargs:
863                if not 'ip' in kwargs and not 'port' in kwargs:
864                    raise TypeError, "Invalid arguments"
865                handle_kwarg(self, kwargs, 'ip', args[0])
866                handle_kwarg(self, kwargs, 'port', args[0])
867            elif isinstance(args[0], IpWithPort):
868                self.ip = args[0].ip
869                self.port = args[0].port
870            else:
871                (self.ip, self.port) = convert.toIpWithPort(args[0])
872
873        elif len(args) == 2:
874            self.ip = args[0]
875            self.port = args[1]
876        else:
877            raise TypeError, "Too many arguments specified"
878
879        if kwargs:
880            raise TypeError, "Too many keywords: %s" % kwargs.keys()
881
882        self.verify()
883
884    def __str__(self):
885        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
886
887    def __eq__(self, other):
888        if isinstance(other, IpWithPort):
889            return self.ip == other.ip and self.port == other.port
890        elif isinstance(other, str):
891            try:
892                return (self.ip, self.port) == convert.toIpWithPort(other)
893            except:
894                return False
895        else:
896            return False
897
898    def verify(self):
899        self.verifyIp()
900        if self.port < 0 or self.port > 0xffff:
901            raise TypeError, "invalid port %d" % self.port
902
903    def getValue(self):
904        from m5.internal.params import IpWithPort
905        return IpWithPort(self.ip, self.port)
906
907time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
908                 "%a %b %d %H:%M:%S %Z %Y",
909                 "%Y/%m/%d %H:%M:%S",
910                 "%Y/%m/%d %H:%M",
911                 "%Y/%m/%d",
912                 "%m/%d/%Y %H:%M:%S",
913                 "%m/%d/%Y %H:%M",
914                 "%m/%d/%Y",
915                 "%m/%d/%y %H:%M:%S",
916                 "%m/%d/%y %H:%M",
917                 "%m/%d/%y"]
918
919
920def parse_time(value):
921    from time import gmtime, strptime, struct_time, time
922    from datetime import datetime, date
923
924    if isinstance(value, struct_time):
925        return value
926
927    if isinstance(value, (int, long)):
928        return gmtime(value)
929
930    if isinstance(value, (datetime, date)):
931        return value.timetuple()
932
933    if isinstance(value, str):
934        if value in ('Now', 'Today'):
935            return time.gmtime(time.time())
936
937        for format in time_formats:
938            try:
939                return strptime(value, format)
940            except ValueError:
941                pass
942
943    raise ValueError, "Could not parse '%s' as a time" % value
944
945class Time(ParamValue):
946    cxx_type = 'tm'
947
948    @classmethod
949    def cxx_predecls(cls, code):
950        code('#include <time.h>')
951
952    @classmethod
953    def swig_predecls(cls, code):
954        code('%include "python/swig/time.i"')
955
956    def __init__(self, value):
957        self.value = parse_time(value)
958
959    def getValue(self):
960        from m5.internal.params import tm
961
962        c_time = tm()
963        py_time = self.value
964
965        # UNIX is years since 1900
966        c_time.tm_year = py_time.tm_year - 1900;
967
968        # Python starts at 1, UNIX starts at 0
969        c_time.tm_mon =  py_time.tm_mon - 1;
970        c_time.tm_mday = py_time.tm_mday;
971        c_time.tm_hour = py_time.tm_hour;
972        c_time.tm_min = py_time.tm_min;
973        c_time.tm_sec = py_time.tm_sec;
974
975        # Python has 0 as Monday, UNIX is 0 as sunday
976        c_time.tm_wday = py_time.tm_wday + 1
977        if c_time.tm_wday > 6:
978            c_time.tm_wday -= 7;
979
980        # Python starts at 1, Unix starts at 0
981        c_time.tm_yday = py_time.tm_yday - 1;
982
983        return c_time
984
985    def __str__(self):
986        return time.asctime(self.value)
987
988    def ini_str(self):
989        return str(self)
990
991    def get_config_as_dict(self):
992        return str(self)
993
994# Enumerated types are a little more complex.  The user specifies the
995# type as Enum(foo) where foo is either a list or dictionary of
996# alternatives (typically strings, but not necessarily so).  (In the
997# long run, the integer value of the parameter will be the list index
998# or the corresponding dictionary value.  For now, since we only check
999# that the alternative is valid and then spit it into a .ini file,
1000# there's not much point in using the dictionary.)
1001
1002# What Enum() must do is generate a new type encapsulating the
1003# provided list/dictionary so that specific values of the parameter
1004# can be instances of that type.  We define two hidden internal
1005# classes (_ListEnum and _DictEnum) to serve as base classes, then
1006# derive the new type from the appropriate base class on the fly.
1007
1008allEnums = {}
1009# Metaclass for Enum types
1010class MetaEnum(MetaParamValue):
1011    def __new__(mcls, name, bases, dict):
1012        assert name not in allEnums
1013
1014        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1015        allEnums[name] = cls
1016        return cls
1017
1018    def __init__(cls, name, bases, init_dict):
1019        if init_dict.has_key('map'):
1020            if not isinstance(cls.map, dict):
1021                raise TypeError, "Enum-derived class attribute 'map' " \
1022                      "must be of type dict"
1023            # build list of value strings from map
1024            cls.vals = cls.map.keys()
1025            cls.vals.sort()
1026        elif init_dict.has_key('vals'):
1027            if not isinstance(cls.vals, list):
1028                raise TypeError, "Enum-derived class attribute 'vals' " \
1029                      "must be of type list"
1030            # build string->value map from vals sequence
1031            cls.map = {}
1032            for idx,val in enumerate(cls.vals):
1033                cls.map[val] = idx
1034        else:
1035            raise TypeError, "Enum-derived class must define "\
1036                  "attribute 'map' or 'vals'"
1037
1038        cls.cxx_type = 'Enums::%s' % name
1039
1040        super(MetaEnum, cls).__init__(name, bases, init_dict)
1041
1042    # Generate C++ class declaration for this enum type.
1043    # Note that we wrap the enum in a class/struct to act as a namespace,
1044    # so that the enum strings can be brief w/o worrying about collisions.
1045    def cxx_decl(cls, code):
1046        name = cls.__name__
1047        code('''\
1048#ifndef __ENUM__${name}__
1049#define __ENUM__${name}__
1050
1051namespace Enums {
1052    enum $name {
1053''')
1054        code.indent(2)
1055        for val in cls.vals:
1056            code('$val = ${{cls.map[val]}},')
1057        code('Num_$name = ${{len(cls.vals)}}')
1058        code.dedent(2)
1059        code('''\
1060    };
1061extern const char *${name}Strings[Num_${name}];
1062}
1063
1064#endif // __ENUM__${name}__
1065''')
1066
1067    def cxx_def(cls, code):
1068        name = cls.__name__
1069        code('''\
1070#include "enums/$name.hh"
1071namespace Enums {
1072    const char *${name}Strings[Num_${name}] =
1073    {
1074''')
1075        code.indent(2)
1076        for val in cls.vals:
1077            code('"$val",')
1078        code.dedent(2)
1079        code('''
1080    };
1081} // namespace Enums
1082''')
1083
1084    def swig_decl(cls, code):
1085        name = cls.__name__
1086        code('''\
1087%module(package="m5.internal") enum_$name
1088
1089%{
1090#include "enums/$name.hh"
1091%}
1092
1093%include "enums/$name.hh"
1094''')
1095
1096
1097# Base class for enum types.
1098class Enum(ParamValue):
1099    __metaclass__ = MetaEnum
1100    vals = []
1101
1102    def __init__(self, value):
1103        if value not in self.map:
1104            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1105                  % (value, self.vals)
1106        self.value = value
1107
1108    @classmethod
1109    def cxx_predecls(cls, code):
1110        code('#include "enums/$0.hh"', cls.__name__)
1111
1112    @classmethod
1113    def swig_predecls(cls, code):
1114        code('%import "python/m5/internal/enum_$0.i"', cls.__name__)
1115
1116    def getValue(self):
1117        return int(self.map[self.value])
1118
1119    def __str__(self):
1120        return self.value
1121
1122# how big does a rounding error need to be before we warn about it?
1123frequency_tolerance = 0.001  # 0.1%
1124
1125class TickParamValue(NumericParamValue):
1126    cxx_type = 'Tick'
1127
1128    @classmethod
1129    def cxx_predecls(cls, code):
1130        code('#include "base/types.hh"')
1131
1132    @classmethod
1133    def swig_predecls(cls, code):
1134        code('%import "stdint.i"')
1135        code('%import "base/types.hh"')
1136
1137    def getValue(self):
1138        return long(self.value)
1139
1140class Latency(TickParamValue):
1141    def __init__(self, value):
1142        if isinstance(value, (Latency, Clock)):
1143            self.ticks = value.ticks
1144            self.value = value.value
1145        elif isinstance(value, Frequency):
1146            self.ticks = value.ticks
1147            self.value = 1.0 / value.value
1148        elif value.endswith('t'):
1149            self.ticks = True
1150            self.value = int(value[:-1])
1151        else:
1152            self.ticks = False
1153            self.value = convert.toLatency(value)
1154
1155    def __getattr__(self, attr):
1156        if attr in ('latency', 'period'):
1157            return self
1158        if attr == 'frequency':
1159            return Frequency(self)
1160        raise AttributeError, "Latency object has no attribute '%s'" % attr
1161
1162    def getValue(self):
1163        if self.ticks or self.value == 0:
1164            value = self.value
1165        else:
1166            value = ticks.fromSeconds(self.value)
1167        return long(value)
1168
1169    # convert latency to ticks
1170    def ini_str(self):
1171        return '%d' % self.getValue()
1172
1173class Frequency(TickParamValue):
1174    def __init__(self, value):
1175        if isinstance(value, (Latency, Clock)):
1176            if value.value == 0:
1177                self.value = 0
1178            else:
1179                self.value = 1.0 / value.value
1180            self.ticks = value.ticks
1181        elif isinstance(value, Frequency):
1182            self.value = value.value
1183            self.ticks = value.ticks
1184        else:
1185            self.ticks = False
1186            self.value = convert.toFrequency(value)
1187
1188    def __getattr__(self, attr):
1189        if attr == 'frequency':
1190            return self
1191        if attr in ('latency', 'period'):
1192            return Latency(self)
1193        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1194
1195    # convert latency to ticks
1196    def getValue(self):
1197        if self.ticks or self.value == 0:
1198            value = self.value
1199        else:
1200            value = ticks.fromSeconds(1.0 / self.value)
1201        return long(value)
1202
1203    def ini_str(self):
1204        return '%d' % self.getValue()
1205
1206# A generic frequency and/or Latency value.  Value is stored as a latency,
1207# but to avoid ambiguity this object does not support numeric ops (* or /).
1208# An explicit conversion to a Latency or Frequency must be made first.
1209class Clock(ParamValue):
1210    cxx_type = 'Tick'
1211
1212    @classmethod
1213    def cxx_predecls(cls, code):
1214        code('#include "base/types.hh"')
1215
1216    @classmethod
1217    def swig_predecls(cls, code):
1218        code('%import "stdint.i"')
1219        code('%import "base/types.hh"')
1220
1221    def __init__(self, value):
1222        if isinstance(value, (Latency, Clock)):
1223            self.ticks = value.ticks
1224            self.value = value.value
1225        elif isinstance(value, Frequency):
1226            self.ticks = value.ticks
1227            self.value = 1.0 / value.value
1228        elif value.endswith('t'):
1229            self.ticks = True
1230            self.value = int(value[:-1])
1231        else:
1232            self.ticks = False
1233            self.value = convert.anyToLatency(value)
1234
1235    def __getattr__(self, attr):
1236        if attr == 'frequency':
1237            return Frequency(self)
1238        if attr in ('latency', 'period'):
1239            return Latency(self)
1240        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1241
1242    def getValue(self):
1243        return self.period.getValue()
1244
1245    def ini_str(self):
1246        return self.period.ini_str()
1247
1248class NetworkBandwidth(float,ParamValue):
1249    cxx_type = 'float'
1250    def __new__(cls, value):
1251        # convert to bits per second
1252        val = convert.toNetworkBandwidth(value)
1253        return super(cls, NetworkBandwidth).__new__(cls, val)
1254
1255    def __str__(self):
1256        return str(self.val)
1257
1258    def getValue(self):
1259        # convert to seconds per byte
1260        value = 8.0 / float(self)
1261        # convert to ticks per byte
1262        value = ticks.fromSeconds(value)
1263        return float(value)
1264
1265    def ini_str(self):
1266        return '%f' % self.getValue()
1267
1268class MemoryBandwidth(float,ParamValue):
1269    cxx_type = 'float'
1270    def __new__(cls, value):
1271        # convert to bytes per second
1272        val = convert.toMemoryBandwidth(value)
1273        return super(cls, MemoryBandwidth).__new__(cls, val)
1274
1275    def __str__(self):
1276        return str(self.val)
1277
1278    def getValue(self):
1279        # convert to seconds per byte
1280        value = float(self)
1281        if value:
1282            value = 1.0 / float(self)
1283        # convert to ticks per byte
1284        value = ticks.fromSeconds(value)
1285        return float(value)
1286
1287    def ini_str(self):
1288        return '%f' % self.getValue()
1289
1290#
1291# "Constants"... handy aliases for various values.
1292#
1293
1294# Special class for NULL pointers.  Note the special check in
1295# make_param_value() above that lets these be assigned where a
1296# SimObject is required.
1297# only one copy of a particular node
1298class NullSimObject(object):
1299    __metaclass__ = Singleton
1300
1301    def __call__(cls):
1302        return cls
1303
1304    def _instantiate(self, parent = None, path = ''):
1305        pass
1306
1307    def ini_str(self):
1308        return 'Null'
1309
1310    def unproxy(self, base):
1311        return self
1312
1313    def set_path(self, parent, name):
1314        pass
1315
1316    def __str__(self):
1317        return 'Null'
1318
1319    def getValue(self):
1320        return None
1321
1322# The only instance you'll ever need...
1323NULL = NullSimObject()
1324
1325def isNullPointer(value):
1326    return isinstance(value, NullSimObject)
1327
1328# Some memory range specifications use this as a default upper bound.
1329MaxAddr = Addr.max
1330MaxTick = Tick.max
1331AllMemory = AddrRange(0, MaxAddr)
1332
1333
1334#####################################################################
1335#
1336# Port objects
1337#
1338# Ports are used to interconnect objects in the memory system.
1339#
1340#####################################################################
1341
1342# Port reference: encapsulates a reference to a particular port on a
1343# particular SimObject.
1344class PortRef(object):
1345    def __init__(self, simobj, name, role):
1346        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1347        self.simobj = simobj
1348        self.name = name
1349        self.role = role
1350        self.peer = None   # not associated with another port yet
1351        self.ccConnected = False # C++ port connection done?
1352        self.index = -1  # always -1 for non-vector ports
1353
1354    def __str__(self):
1355        return '%s.%s' % (self.simobj, self.name)
1356
1357    def __len__(self):
1358        # Return the number of connected ports, i.e. 0 is we have no
1359        # peer and 1 if we do.
1360        return int(self.peer != None)
1361
1362    # for config.ini, print peer's name (not ours)
1363    def ini_str(self):
1364        return str(self.peer)
1365
1366    def __getattr__(self, attr):
1367        if attr == 'peerObj':
1368            # shorthand for proxies
1369            return self.peer.simobj
1370        raise AttributeError, "'%s' object has no attribute '%s'" % \
1371              (self.__class__.__name__, attr)
1372
1373    # Full connection is symmetric (both ways).  Called via
1374    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1375    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1376    # e.g., "obj1.portA[3] = obj2.portB".
1377    def connect(self, other):
1378        if isinstance(other, VectorPortRef):
1379            # reference to plain VectorPort is implicit append
1380            other = other._get_next()
1381        if self.peer and not proxy.isproxy(self.peer):
1382            fatal("Port %s is already connected to %s, cannot connect %s\n",
1383                  self, self.peer, other);
1384        self.peer = other
1385        if proxy.isproxy(other):
1386            other.set_param_desc(PortParamDesc())
1387        elif isinstance(other, PortRef):
1388            if other.peer is not self:
1389                other.connect(self)
1390        else:
1391            raise TypeError, \
1392                  "assigning non-port reference '%s' to port '%s'" \
1393                  % (other, self)
1394
1395    def clone(self, simobj, memo):
1396        if memo.has_key(self):
1397            return memo[self]
1398        newRef = copy.copy(self)
1399        memo[self] = newRef
1400        newRef.simobj = simobj
1401        assert(isSimObject(newRef.simobj))
1402        if self.peer and not proxy.isproxy(self.peer):
1403            peerObj = self.peer.simobj(_memo=memo)
1404            newRef.peer = self.peer.clone(peerObj, memo)
1405            assert(not isinstance(newRef.peer, VectorPortRef))
1406        return newRef
1407
1408    def unproxy(self, simobj):
1409        assert(simobj is self.simobj)
1410        if proxy.isproxy(self.peer):
1411            try:
1412                realPeer = self.peer.unproxy(self.simobj)
1413            except:
1414                print "Error in unproxying port '%s' of %s" % \
1415                      (self.name, self.simobj.path())
1416                raise
1417            self.connect(realPeer)
1418
1419    # Call C++ to create corresponding port connection between C++ objects
1420    def ccConnect(self):
1421        from m5.internal.pyobject import connectPorts
1422
1423        if self.role == 'SLAVE':
1424            # do nothing and let the master take care of it
1425            return
1426
1427        if self.ccConnected: # already done this
1428            return
1429        peer = self.peer
1430        if not self.peer: # nothing to connect to
1431            return
1432
1433        # check that we connect a master to a slave
1434        if self.role == peer.role:
1435            raise TypeError, \
1436                "cannot connect '%s' and '%s' due to identical role '%s'" \
1437                % (peer, self, self.role)
1438
1439        try:
1440            # self is always the master and peer the slave
1441            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1442                         peer.simobj.getCCObject(), peer.name, peer.index)
1443        except:
1444            print "Error connecting port %s.%s to %s.%s" % \
1445                  (self.simobj.path(), self.name,
1446                   peer.simobj.path(), peer.name)
1447            raise
1448        self.ccConnected = True
1449        peer.ccConnected = True
1450
1451# A reference to an individual element of a VectorPort... much like a
1452# PortRef, but has an index.
1453class VectorPortElementRef(PortRef):
1454    def __init__(self, simobj, name, role, index):
1455        PortRef.__init__(self, simobj, name, role)
1456        self.index = index
1457
1458    def __str__(self):
1459        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1460
1461# A reference to a complete vector-valued port (not just a single element).
1462# Can be indexed to retrieve individual VectorPortElementRef instances.
1463class VectorPortRef(object):
1464    def __init__(self, simobj, name, role):
1465        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1466        self.simobj = simobj
1467        self.name = name
1468        self.role = role
1469        self.elements = []
1470
1471    def __str__(self):
1472        return '%s.%s[:]' % (self.simobj, self.name)
1473
1474    def __len__(self):
1475        # Return the number of connected peers, corresponding the the
1476        # length of the elements.
1477        return len(self.elements)
1478
1479    # for config.ini, print peer's name (not ours)
1480    def ini_str(self):
1481        return ' '.join([el.ini_str() for el in self.elements])
1482
1483    def __getitem__(self, key):
1484        if not isinstance(key, int):
1485            raise TypeError, "VectorPort index must be integer"
1486        if key >= len(self.elements):
1487            # need to extend list
1488            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1489                   for i in range(len(self.elements), key+1)]
1490            self.elements.extend(ext)
1491        return self.elements[key]
1492
1493    def _get_next(self):
1494        return self[len(self.elements)]
1495
1496    def __setitem__(self, key, value):
1497        if not isinstance(key, int):
1498            raise TypeError, "VectorPort index must be integer"
1499        self[key].connect(value)
1500
1501    def connect(self, other):
1502        if isinstance(other, (list, tuple)):
1503            # Assign list of port refs to vector port.
1504            # For now, append them... not sure if that's the right semantics
1505            # or if it should replace the current vector.
1506            for ref in other:
1507                self._get_next().connect(ref)
1508        else:
1509            # scalar assignment to plain VectorPort is implicit append
1510            self._get_next().connect(other)
1511
1512    def clone(self, simobj, memo):
1513        if memo.has_key(self):
1514            return memo[self]
1515        newRef = copy.copy(self)
1516        memo[self] = newRef
1517        newRef.simobj = simobj
1518        assert(isSimObject(newRef.simobj))
1519        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1520        return newRef
1521
1522    def unproxy(self, simobj):
1523        [el.unproxy(simobj) for el in self.elements]
1524
1525    def ccConnect(self):
1526        [el.ccConnect() for el in self.elements]
1527
1528# Port description object.  Like a ParamDesc object, this represents a
1529# logical port in the SimObject class, not a particular port on a
1530# SimObject instance.  The latter are represented by PortRef objects.
1531class Port(object):
1532    # Generate a PortRef for this port on the given SimObject with the
1533    # given name
1534    def makeRef(self, simobj):
1535        return PortRef(simobj, self.name, self.role)
1536
1537    # Connect an instance of this port (on the given SimObject with
1538    # the given name) with the port described by the supplied PortRef
1539    def connect(self, simobj, ref):
1540        self.makeRef(simobj).connect(ref)
1541
1542    # No need for any pre-declarations at the moment as we merely rely
1543    # on an unsigned int.
1544    def cxx_predecls(self, code):
1545        pass
1546
1547    # Declare an unsigned int with the same name as the port, that
1548    # will eventually hold the number of connected ports (and thus the
1549    # number of elements for a VectorPort).
1550    def cxx_decl(self, code):
1551        code('unsigned int port_${{self.name}}_connection_count;')
1552
1553class MasterPort(Port):
1554    # MasterPort("description")
1555    def __init__(self, *args):
1556        if len(args) == 1:
1557            self.desc = args[0]
1558            self.role = 'MASTER'
1559        else:
1560            raise TypeError, 'wrong number of arguments'
1561
1562class SlavePort(Port):
1563    # SlavePort("description")
1564    def __init__(self, *args):
1565        if len(args) == 1:
1566            self.desc = args[0]
1567            self.role = 'SLAVE'
1568        else:
1569            raise TypeError, 'wrong number of arguments'
1570
1571# VectorPort description object.  Like Port, but represents a vector
1572# of connections (e.g., as on a Bus).
1573class VectorPort(Port):
1574    def __init__(self, *args):
1575        self.isVec = True
1576
1577    def makeRef(self, simobj):
1578        return VectorPortRef(simobj, self.name, self.role)
1579
1580class VectorMasterPort(VectorPort):
1581    # VectorMasterPort("description")
1582    def __init__(self, *args):
1583        if len(args) == 1:
1584            self.desc = args[0]
1585            self.role = 'MASTER'
1586            VectorPort.__init__(self, *args)
1587        else:
1588            raise TypeError, 'wrong number of arguments'
1589
1590class VectorSlavePort(VectorPort):
1591    # VectorSlavePort("description")
1592    def __init__(self, *args):
1593        if len(args) == 1:
1594            self.desc = args[0]
1595            self.role = 'SLAVE'
1596            VectorPort.__init__(self, *args)
1597        else:
1598            raise TypeError, 'wrong number of arguments'
1599
1600# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1601# proxy objects (via set_param_desc()) so that proxy error messages
1602# make sense.
1603class PortParamDesc(object):
1604    __metaclass__ = Singleton
1605
1606    ptype_str = 'Port'
1607    ptype = Port
1608
1609baseEnums = allEnums.copy()
1610baseParams = allParams.copy()
1611
1612def clear():
1613    global allEnums, allParams
1614
1615    allEnums = baseEnums.copy()
1616    allParams = baseParams.copy()
1617
1618__all__ = ['Param', 'VectorParam',
1619           'Enum', 'Bool', 'String', 'Float',
1620           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1621           'Int32', 'UInt32', 'Int64', 'UInt64',
1622           'Counter', 'Addr', 'Tick', 'Percent',
1623           'TcpPort', 'UdpPort', 'EthernetAddr',
1624           'IpAddress', 'IpNetmask', 'IpWithPort',
1625           'MemorySize', 'MemorySize32',
1626           'Latency', 'Frequency', 'Clock',
1627           'NetworkBandwidth', 'MemoryBandwidth',
1628           'Range', 'AddrRange', 'TickRange',
1629           'MaxAddr', 'MaxTick', 'AllMemory',
1630           'Time',
1631           'NextEthernetAddr', 'NULL',
1632           'MasterPort', 'SlavePort',
1633           'VectorMasterPort', 'VectorSlavePort']
1634
1635import SimObject
1636