params.py revision 4446
14680Sgblack@eecs.umich.edu# Copyright (c) 2004-2006 The Regents of The University of Michigan
25442Sgblack@eecs.umich.edu# All rights reserved.
34680Sgblack@eecs.umich.edu#
44680Sgblack@eecs.umich.edu# Redistribution and use in source and binary forms, with or without
54680Sgblack@eecs.umich.edu# modification, are permitted provided that the following conditions are
64680Sgblack@eecs.umich.edu# met: redistributions of source code must retain the above copyright
74680Sgblack@eecs.umich.edu# notice, this list of conditions and the following disclaimer;
84680Sgblack@eecs.umich.edu# redistributions in binary form must reproduce the above copyright
94680Sgblack@eecs.umich.edu# notice, this list of conditions and the following disclaimer in the
104680Sgblack@eecs.umich.edu# documentation and/or other materials provided with the distribution;
114680Sgblack@eecs.umich.edu# neither the name of the copyright holders nor the names of its
124680Sgblack@eecs.umich.edu# contributors may be used to endorse or promote products derived from
134680Sgblack@eecs.umich.edu# this software without specific prior written permission.
144680Sgblack@eecs.umich.edu#
154680Sgblack@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
164680Sgblack@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
174680Sgblack@eecs.umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
184680Sgblack@eecs.umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
194680Sgblack@eecs.umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
204680Sgblack@eecs.umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
214680Sgblack@eecs.umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
224680Sgblack@eecs.umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
234680Sgblack@eecs.umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
244680Sgblack@eecs.umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
254680Sgblack@eecs.umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
264680Sgblack@eecs.umich.edu#
274680Sgblack@eecs.umich.edu# Authors: Steve Reinhardt
284680Sgblack@eecs.umich.edu#          Nathan Binkert
294680Sgblack@eecs.umich.edu
304680Sgblack@eecs.umich.edu#####################################################################
314680Sgblack@eecs.umich.edu#
324680Sgblack@eecs.umich.edu# Parameter description classes
334680Sgblack@eecs.umich.edu#
344680Sgblack@eecs.umich.edu# The _params dictionary in each class maps parameter names to either
356215Snate@binkert.org# a Param or a VectorParam object.  These objects contain the
364680Sgblack@eecs.umich.edu# parameter description string, the parameter type, and the default
375543Ssaidi@eecs.umich.edu# value (if any).  The convert() method on these objects is used to
384680Sgblack@eecs.umich.edu# force whatever value is assigned to the parameter to the appropriate
394680Sgblack@eecs.umich.edu# type.
404680Sgblack@eecs.umich.edu#
414680Sgblack@eecs.umich.edu# Note that the default values are loaded into the class's attribute
424680Sgblack@eecs.umich.edu# space when the parameter dictionary is initialized (in
434680Sgblack@eecs.umich.edu# MetaSimObject._new_param()); after that point they aren't used.
444680Sgblack@eecs.umich.edu#
454680Sgblack@eecs.umich.edu#####################################################################
464680Sgblack@eecs.umich.edu
474680Sgblack@eecs.umich.eduimport copy
484680Sgblack@eecs.umich.eduimport datetime
494680Sgblack@eecs.umich.eduimport inspect
504680Sgblack@eecs.umich.eduimport sys
514680Sgblack@eecs.umich.eduimport time
524680Sgblack@eecs.umich.edu
534680Sgblack@eecs.umich.eduimport convert
544680Sgblack@eecs.umich.eduimport proxy
554680Sgblack@eecs.umich.eduimport ticks
564680Sgblack@eecs.umich.edufrom util import *
574680Sgblack@eecs.umich.edu
584680Sgblack@eecs.umich.edu# Dummy base class to identify types that are legitimate for SimObject
594680Sgblack@eecs.umich.edu# parameters.
604680Sgblack@eecs.umich.educlass ParamValue(object):
614680Sgblack@eecs.umich.edu
624680Sgblack@eecs.umich.edu    cxx_predecls = []
634680Sgblack@eecs.umich.edu    swig_predecls = []
644680Sgblack@eecs.umich.edu
654680Sgblack@eecs.umich.edu    # default for printing to .ini file is regular string conversion.
664680Sgblack@eecs.umich.edu    # will be overridden in some cases
674680Sgblack@eecs.umich.edu    def ini_str(self):
684680Sgblack@eecs.umich.edu        return str(self)
694680Sgblack@eecs.umich.edu
704680Sgblack@eecs.umich.edu    # allows us to blithely call unproxy() on things without checking
714680Sgblack@eecs.umich.edu    # if they're really proxies or not
724680Sgblack@eecs.umich.edu    def unproxy(self, base):
734680Sgblack@eecs.umich.edu        return self
744680Sgblack@eecs.umich.edu
754680Sgblack@eecs.umich.edu# Regular parameter description.
764680Sgblack@eecs.umich.educlass ParamDesc(object):
774680Sgblack@eecs.umich.edu    def __init__(self, ptype_str, ptype, *args, **kwargs):
784680Sgblack@eecs.umich.edu        self.ptype_str = ptype_str
794680Sgblack@eecs.umich.edu        # remember ptype only if it is provided
804680Sgblack@eecs.umich.edu        if ptype != None:
814680Sgblack@eecs.umich.edu            self.ptype = ptype
824680Sgblack@eecs.umich.edu
834680Sgblack@eecs.umich.edu        if args:
844680Sgblack@eecs.umich.edu            if len(args) == 1:
854680Sgblack@eecs.umich.edu                self.desc = args[0]
864680Sgblack@eecs.umich.edu            elif len(args) == 2:
874680Sgblack@eecs.umich.edu                self.default = args[0]
8810289SAndreas.Sandberg@ARM.com                self.desc = args[1]
8910289SAndreas.Sandberg@ARM.com            else:
9010289SAndreas.Sandberg@ARM.com                raise TypeError, 'too many arguments'
914680Sgblack@eecs.umich.edu
926249Sgblack@eecs.umich.edu        if kwargs.has_key('desc'):
934680Sgblack@eecs.umich.edu            assert(not hasattr(self, 'desc'))
944680Sgblack@eecs.umich.edu            self.desc = kwargs['desc']
954680Sgblack@eecs.umich.edu            del kwargs['desc']
964680Sgblack@eecs.umich.edu
974680Sgblack@eecs.umich.edu        if kwargs.has_key('default'):
984680Sgblack@eecs.umich.edu            assert(not hasattr(self, 'default'))
994680Sgblack@eecs.umich.edu            self.default = kwargs['default']
1004680Sgblack@eecs.umich.edu            del kwargs['default']
1014680Sgblack@eecs.umich.edu
1024680Sgblack@eecs.umich.edu        if kwargs:
1034680Sgblack@eecs.umich.edu            raise TypeError, 'extra unknown kwargs %s' % kwargs
1044680Sgblack@eecs.umich.edu
1054680Sgblack@eecs.umich.edu        if not hasattr(self, 'desc'):
1064680Sgblack@eecs.umich.edu            raise TypeError, 'desc attribute missing'
1074680Sgblack@eecs.umich.edu
1084680Sgblack@eecs.umich.edu    def __getattr__(self, attr):
1094680Sgblack@eecs.umich.edu        if attr == 'ptype':
1104680Sgblack@eecs.umich.edu            try:
1114680Sgblack@eecs.umich.edu                ptype = eval(self.ptype_str, objects.__dict__)
1124680Sgblack@eecs.umich.edu                if not isinstance(ptype, type):
1134680Sgblack@eecs.umich.edu                    raise NameError
1144680Sgblack@eecs.umich.edu                self.ptype = ptype
1154680Sgblack@eecs.umich.edu                return ptype
1164680Sgblack@eecs.umich.edu            except NameError:
1174680Sgblack@eecs.umich.edu                raise TypeError, \
1184680Sgblack@eecs.umich.edu                      "Param qualifier '%s' is not a type" % self.ptype_str
1194680Sgblack@eecs.umich.edu        raise AttributeError, "'%s' object has no attribute '%s'" % \
1204680Sgblack@eecs.umich.edu              (type(self).__name__, attr)
1214680Sgblack@eecs.umich.edu
1224680Sgblack@eecs.umich.edu    def convert(self, value):
1236249Sgblack@eecs.umich.edu        if isinstance(value, proxy.BaseProxy):
1244680Sgblack@eecs.umich.edu            value.set_param_desc(self)
1254680Sgblack@eecs.umich.edu            return value
1264680Sgblack@eecs.umich.edu        if not hasattr(self, 'ptype') and isNullPointer(value):
1274680Sgblack@eecs.umich.edu            # deferred evaluation of SimObject; continue to defer if
1284680Sgblack@eecs.umich.edu            # we're just assigning a null pointer
1294680Sgblack@eecs.umich.edu            return value
1304680Sgblack@eecs.umich.edu        if isinstance(value, self.ptype):
1314680Sgblack@eecs.umich.edu            return value
1324680Sgblack@eecs.umich.edu        if isNullPointer(value) and isSimObjectClass(self.ptype):
1334680Sgblack@eecs.umich.edu            return value
1344680Sgblack@eecs.umich.edu        return self.ptype(value)
1354680Sgblack@eecs.umich.edu
1364680Sgblack@eecs.umich.edu    def cxx_predecls(self):
1374680Sgblack@eecs.umich.edu        return self.ptype.cxx_predecls
1384680Sgblack@eecs.umich.edu
1394680Sgblack@eecs.umich.edu    def swig_predecls(self):
1404680Sgblack@eecs.umich.edu        return self.ptype.swig_predecls
1414680Sgblack@eecs.umich.edu
1426249Sgblack@eecs.umich.edu    def cxx_decl(self):
1434680Sgblack@eecs.umich.edu        return '%s %s;' % (self.ptype.cxx_type, self.name)
1444680Sgblack@eecs.umich.edu
1454680Sgblack@eecs.umich.edu# Vector-valued parameter description.  Just like ParamDesc, except
1464680Sgblack@eecs.umich.edu# that the value is a vector (list) of the specified type instead of a
1474680Sgblack@eecs.umich.edu# single value.
1484680Sgblack@eecs.umich.edu
1494680Sgblack@eecs.umich.educlass VectorParamValue(list):
1504680Sgblack@eecs.umich.edu    def ini_str(self):
1514680Sgblack@eecs.umich.edu        return ' '.join([v.ini_str() for v in self])
1524680Sgblack@eecs.umich.edu
1534680Sgblack@eecs.umich.edu    def unproxy(self, base):
1544680Sgblack@eecs.umich.edu        return [v.unproxy(base) for v in self]
1554680Sgblack@eecs.umich.edu
1564680Sgblack@eecs.umich.educlass SimObjVector(VectorParamValue):
1574680Sgblack@eecs.umich.edu    def print_ini(self):
1584680Sgblack@eecs.umich.edu        for v in self:
1594680Sgblack@eecs.umich.edu            v.print_ini()
1604680Sgblack@eecs.umich.edu
1614680Sgblack@eecs.umich.educlass VectorParamDesc(ParamDesc):
1624680Sgblack@eecs.umich.edu    # Convert assigned value to appropriate type.  If the RHS is not a
1634680Sgblack@eecs.umich.edu    # list or tuple, it generates a single-element list.
1644680Sgblack@eecs.umich.edu    def convert(self, value):
1654680Sgblack@eecs.umich.edu        if isinstance(value, (list, tuple)):
1664680Sgblack@eecs.umich.edu            # list: coerce each element into new list
1674680Sgblack@eecs.umich.edu            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
1684680Sgblack@eecs.umich.edu            if isSimObjectSequence(tmp_list):
1694680Sgblack@eecs.umich.edu                return SimObjVector(tmp_list)
1704680Sgblack@eecs.umich.edu            else:
1714680Sgblack@eecs.umich.edu                return VectorParamValue(tmp_list)
1724680Sgblack@eecs.umich.edu        else:
1736249Sgblack@eecs.umich.edu            # singleton: leave it be (could coerce to a single-element
1744680Sgblack@eecs.umich.edu            # list here, but for some historical reason we don't...
1754680Sgblack@eecs.umich.edu            return ParamDesc.convert(self, value)
1764680Sgblack@eecs.umich.edu
1774680Sgblack@eecs.umich.edu    def cxx_predecls(self):
1784680Sgblack@eecs.umich.edu        return ['#include <vector>'] + self.ptype.cxx_predecls
1794680Sgblack@eecs.umich.edu
1804680Sgblack@eecs.umich.edu    def swig_predecls(self):
1814680Sgblack@eecs.umich.edu        return ['%include "std_vector.i"'] + self.ptype.swig_predecls
1824680Sgblack@eecs.umich.edu
1834680Sgblack@eecs.umich.edu    def cxx_decl(self):
1844680Sgblack@eecs.umich.edu        return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
1854680Sgblack@eecs.umich.edu
1864680Sgblack@eecs.umich.educlass ParamFactory(object):
1874680Sgblack@eecs.umich.edu    def __init__(self, param_desc_class, ptype_str = None):
1884680Sgblack@eecs.umich.edu        self.param_desc_class = param_desc_class
1894680Sgblack@eecs.umich.edu        self.ptype_str = ptype_str
1904680Sgblack@eecs.umich.edu
1914680Sgblack@eecs.umich.edu    def __getattr__(self, attr):
1924680Sgblack@eecs.umich.edu        if self.ptype_str:
1934680Sgblack@eecs.umich.edu            attr = self.ptype_str + '.' + attr
1944680Sgblack@eecs.umich.edu        return ParamFactory(self.param_desc_class, attr)
1954680Sgblack@eecs.umich.edu
1964680Sgblack@eecs.umich.edu    # E.g., Param.Int(5, "number of widgets")
1974680Sgblack@eecs.umich.edu    def __call__(self, *args, **kwargs):
1984680Sgblack@eecs.umich.edu        caller_frame = inspect.currentframe().f_back
1995136Sgblack@eecs.umich.edu        ptype = None
2004681Sgblack@eecs.umich.edu        try:
2014681Sgblack@eecs.umich.edu            ptype = eval(self.ptype_str,
2024681Sgblack@eecs.umich.edu                         caller_frame.f_globals, caller_frame.f_locals)
2034681Sgblack@eecs.umich.edu            if not isinstance(ptype, type):
2044681Sgblack@eecs.umich.edu                raise TypeError, \
2054681Sgblack@eecs.umich.edu                      "Param qualifier is not a type: %s" % ptype
2066249Sgblack@eecs.umich.edu        except NameError:
2074680Sgblack@eecs.umich.edu            # if name isn't defined yet, assume it's a SimObject, and
2084680Sgblack@eecs.umich.edu            # try to resolve it later
2094680Sgblack@eecs.umich.edu            pass
2104680Sgblack@eecs.umich.edu        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
2114680Sgblack@eecs.umich.edu
2125136Sgblack@eecs.umich.eduParam = ParamFactory(ParamDesc)
2134680Sgblack@eecs.umich.eduVectorParam = ParamFactory(VectorParamDesc)
2144680Sgblack@eecs.umich.edu
2154680Sgblack@eecs.umich.edu#####################################################################
2164680Sgblack@eecs.umich.edu#
2174680Sgblack@eecs.umich.edu# Parameter Types
2184680Sgblack@eecs.umich.edu#
2195136Sgblack@eecs.umich.edu# Though native Python types could be used to specify parameter types
2204680Sgblack@eecs.umich.edu# (the 'ptype' field of the Param and VectorParam classes), it's more
2214680Sgblack@eecs.umich.edu# flexible to define our own set of types.  This gives us more control
2224680Sgblack@eecs.umich.edu# over how Python expressions are converted to values (via the
2234680Sgblack@eecs.umich.edu# __init__() constructor) and how these values are printed out (via
2244680Sgblack@eecs.umich.edu# the __str__() conversion method).
2255136Sgblack@eecs.umich.edu#
2264680Sgblack@eecs.umich.edu#####################################################################
2274680Sgblack@eecs.umich.edu
2284680Sgblack@eecs.umich.edu# String-valued parameter.  Just mixin the ParamValue class with the
2294680Sgblack@eecs.umich.edu# built-in str class.
2304680Sgblack@eecs.umich.educlass String(ParamValue,str):
2314680Sgblack@eecs.umich.edu    cxx_type = 'std::string'
2324680Sgblack@eecs.umich.edu    cxx_predecls = ['#include <string>']
2334680Sgblack@eecs.umich.edu    swig_predecls = ['%include "std_string.i"\n' +
2344680Sgblack@eecs.umich.edu                     '%apply const std::string& {std::string *};']
2354680Sgblack@eecs.umich.edu    pass
2364680Sgblack@eecs.umich.edu
2374680Sgblack@eecs.umich.edu# superclass for "numeric" parameter values, to emulate math
2384680Sgblack@eecs.umich.edu# operations in a type-safe way.  e.g., a Latency times an int returns
2394680Sgblack@eecs.umich.edu# a new Latency object.
2404680Sgblack@eecs.umich.educlass NumericParamValue(ParamValue):
2414680Sgblack@eecs.umich.edu    def __str__(self):
2424680Sgblack@eecs.umich.edu        return str(self.value)
2434680Sgblack@eecs.umich.edu
2444680Sgblack@eecs.umich.edu    def __float__(self):
2454680Sgblack@eecs.umich.edu        return float(self.value)
2464680Sgblack@eecs.umich.edu
2474680Sgblack@eecs.umich.edu    def __long__(self):
2485442Sgblack@eecs.umich.edu        return long(self.value)
2494680Sgblack@eecs.umich.edu
2504680Sgblack@eecs.umich.edu    def __int__(self):
2514680Sgblack@eecs.umich.edu        return int(self.value)
2524680Sgblack@eecs.umich.edu
2534680Sgblack@eecs.umich.edu    # hook for bounds checking
2544680Sgblack@eecs.umich.edu    def _check(self):
2554680Sgblack@eecs.umich.edu        return
2564680Sgblack@eecs.umich.edu
2574680Sgblack@eecs.umich.edu    def __mul__(self, other):
2584680Sgblack@eecs.umich.edu        newobj = self.__class__(self)
2594680Sgblack@eecs.umich.edu        newobj.value *= other
2604680Sgblack@eecs.umich.edu        newobj._check()
2614680Sgblack@eecs.umich.edu        return newobj
2624680Sgblack@eecs.umich.edu
2634680Sgblack@eecs.umich.edu    __rmul__ = __mul__
2645442Sgblack@eecs.umich.edu
2655442Sgblack@eecs.umich.edu    def __div__(self, other):
2664680Sgblack@eecs.umich.edu        newobj = self.__class__(self)
2674680Sgblack@eecs.umich.edu        newobj.value /= other
2684680Sgblack@eecs.umich.edu        newobj._check()
2694680Sgblack@eecs.umich.edu        return newobj
2704680Sgblack@eecs.umich.edu
2714680Sgblack@eecs.umich.edu    def __sub__(self, other):
2724680Sgblack@eecs.umich.edu        newobj = self.__class__(self)
2734680Sgblack@eecs.umich.edu        newobj.value -= other
2744680Sgblack@eecs.umich.edu        newobj._check()
2754680Sgblack@eecs.umich.edu        return newobj
2764680Sgblack@eecs.umich.edu
2774680Sgblack@eecs.umich.edu# Metaclass for bounds-checked integer parameters.  See CheckedInt.
2784680Sgblack@eecs.umich.educlass CheckedIntType(type):
2794680Sgblack@eecs.umich.edu    def __init__(cls, name, bases, dict):
2804680Sgblack@eecs.umich.edu        super(CheckedIntType, cls).__init__(name, bases, dict)
2814680Sgblack@eecs.umich.edu
2824680Sgblack@eecs.umich.edu        # CheckedInt is an abstract base class, so we actually don't
2834680Sgblack@eecs.umich.edu        # want to do any processing on it... the rest of this code is
2844680Sgblack@eecs.umich.edu        # just for classes that derive from CheckedInt.
2854680Sgblack@eecs.umich.edu        if name == 'CheckedInt':
2866249Sgblack@eecs.umich.edu            return
2874680Sgblack@eecs.umich.edu
2884680Sgblack@eecs.umich.edu        if not cls.cxx_predecls:
2894680Sgblack@eecs.umich.edu            # most derived types require this, so we just do it here once
2904698Sgblack@eecs.umich.edu            cls.cxx_predecls = ['#include "sim/host.hh"']
2914680Sgblack@eecs.umich.edu
2924680Sgblack@eecs.umich.edu        if not cls.swig_predecls:
2934680Sgblack@eecs.umich.edu            # most derived types require this, so we just do it here once
2944680Sgblack@eecs.umich.edu            cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
2954680Sgblack@eecs.umich.edu                                 '%import "sim/host.hh"']
2964680Sgblack@eecs.umich.edu
2974680Sgblack@eecs.umich.edu        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
2984680Sgblack@eecs.umich.edu            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
2994680Sgblack@eecs.umich.edu                panic("CheckedInt subclass %s must define either\n" \
3004680Sgblack@eecs.umich.edu                      "    'min' and 'max' or 'size' and 'unsigned'\n" \
3014680Sgblack@eecs.umich.edu                      % name);
3024680Sgblack@eecs.umich.edu            if cls.unsigned:
3034680Sgblack@eecs.umich.edu                cls.min = 0
3044680Sgblack@eecs.umich.edu                cls.max = 2 ** cls.size - 1
3054680Sgblack@eecs.umich.edu            else:
3064680Sgblack@eecs.umich.edu                cls.min = -(2 ** (cls.size - 1))
3074680Sgblack@eecs.umich.edu                cls.max = (2 ** (cls.size - 1)) - 1
3084680Sgblack@eecs.umich.edu
3094680Sgblack@eecs.umich.edu# Abstract superclass for bounds-checked integer parameters.  This
3104680Sgblack@eecs.umich.edu# class is subclassed to generate parameter classes with specific
3114680Sgblack@eecs.umich.edu# bounds.  Initialization of the min and max bounds is done in the
3124680Sgblack@eecs.umich.edu# metaclass CheckedIntType.__init__.
313class CheckedInt(NumericParamValue):
314    __metaclass__ = CheckedIntType
315
316    def _check(self):
317        if not self.min <= self.value <= self.max:
318            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
319                  (self.min, self.value, self.max)
320
321    def __init__(self, value):
322        if isinstance(value, str):
323            self.value = convert.toInteger(value)
324        elif isinstance(value, (int, long, float, NumericParamValue)):
325            self.value = long(value)
326        else:
327            raise TypeError, "Can't convert object of type %s to CheckedInt" \
328                  % type(value).__name__
329        self._check()
330
331class Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
332class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
333
334class Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
335class UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
336class Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
337class UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
338class Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
339class UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
340class Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
341class UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
342
343class Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
344class Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
345class TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
346class UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
347
348class Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
349
350class Float(ParamValue, float):
351    cxx_type = 'double'
352
353class MemorySize(CheckedInt):
354    cxx_type = 'uint64_t'
355    size = 64
356    unsigned = True
357    def __init__(self, value):
358        if isinstance(value, MemorySize):
359            self.value = value.value
360        else:
361            self.value = convert.toMemorySize(value)
362        self._check()
363
364class MemorySize32(CheckedInt):
365    cxx_type = 'uint32_t'
366    size = 32
367    unsigned = True
368    def __init__(self, value):
369        if isinstance(value, MemorySize):
370            self.value = value.value
371        else:
372            self.value = convert.toMemorySize(value)
373        self._check()
374
375class Addr(CheckedInt):
376    cxx_type = 'Addr'
377    cxx_predecls = ['#include "targetarch/isa_traits.hh"']
378    size = 64
379    unsigned = True
380    def __init__(self, value):
381        if isinstance(value, Addr):
382            self.value = value.value
383        else:
384            try:
385                self.value = convert.toMemorySize(value)
386            except TypeError:
387                self.value = long(value)
388        self._check()
389    def __add__(self, other):
390        if isinstance(other, Addr):
391            return self.value + other.value
392        else:
393            return self.value + other
394
395
396class MetaRange(type):
397    def __init__(cls, name, bases, dict):
398        super(MetaRange, cls).__init__(name, bases, dict)
399        if name == 'Range':
400            return
401        cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
402        cls.cxx_predecls = \
403                       ['#include "base/range.hh"'] + cls.type.cxx_predecls
404
405class Range(ParamValue):
406    __metaclass__ = MetaRange
407    type = Int # default; can be overridden in subclasses
408    def __init__(self, *args, **kwargs):
409        def handle_kwargs(self, kwargs):
410            if 'end' in kwargs:
411                self.second = self.type(kwargs.pop('end'))
412            elif 'size' in kwargs:
413                self.second = self.first + self.type(kwargs.pop('size')) - 1
414            else:
415                raise TypeError, "Either end or size must be specified"
416
417        if len(args) == 0:
418            self.first = self.type(kwargs.pop('start'))
419            handle_kwargs(self, kwargs)
420
421        elif len(args) == 1:
422            if kwargs:
423                self.first = self.type(args[0])
424                handle_kwargs(self, kwargs)
425            elif isinstance(args[0], Range):
426                self.first = self.type(args[0].first)
427                self.second = self.type(args[0].second)
428            else:
429                self.first = self.type(0)
430                self.second = self.type(args[0]) - 1
431
432        elif len(args) == 2:
433            self.first = self.type(args[0])
434            self.second = self.type(args[1])
435        else:
436            raise TypeError, "Too many arguments specified"
437
438        if kwargs:
439            raise TypeError, "too many keywords: %s" % kwargs.keys()
440
441    def __str__(self):
442        return '%s:%s' % (self.first, self.second)
443
444class AddrRange(Range):
445    type = Addr
446
447class TickRange(Range):
448    type = Tick
449
450# Boolean parameter type.  Python doesn't let you subclass bool, since
451# it doesn't want to let you create multiple instances of True and
452# False.  Thus this is a little more complicated than String.
453class Bool(ParamValue):
454    cxx_type = 'bool'
455    def __init__(self, value):
456        try:
457            self.value = convert.toBool(value)
458        except TypeError:
459            self.value = bool(value)
460
461    def __str__(self):
462        return str(self.value)
463
464    def ini_str(self):
465        if self.value:
466            return 'true'
467        return 'false'
468
469def IncEthernetAddr(addr, val = 1):
470    bytes = map(lambda x: int(x, 16), addr.split(':'))
471    bytes[5] += val
472    for i in (5, 4, 3, 2, 1):
473        val,rem = divmod(bytes[i], 256)
474        bytes[i] = rem
475        if val == 0:
476            break
477        bytes[i - 1] += val
478    assert(bytes[0] <= 255)
479    return ':'.join(map(lambda x: '%02x' % x, bytes))
480
481_NextEthernetAddr = "00:90:00:00:00:01"
482def NextEthernetAddr():
483    global _NextEthernetAddr
484
485    value = _NextEthernetAddr
486    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
487    return value
488
489class EthernetAddr(ParamValue):
490    cxx_type = 'Net::EthAddr'
491    cxx_predecls = ['#include "base/inet.hh"']
492    swig_predecls = ['class Net::EthAddr;']
493    def __init__(self, value):
494        if value == NextEthernetAddr:
495            self.value = value
496            return
497
498        if not isinstance(value, str):
499            raise TypeError, "expected an ethernet address and didn't get one"
500
501        bytes = value.split(':')
502        if len(bytes) != 6:
503            raise TypeError, 'invalid ethernet address %s' % value
504
505        for byte in bytes:
506            if not 0 <= int(byte) <= 256:
507                raise TypeError, 'invalid ethernet address %s' % value
508
509        self.value = value
510
511    def unproxy(self, base):
512        if self.value == NextEthernetAddr:
513            return EthernetAddr(self.value())
514        return self
515
516    def ini_str(self):
517        return self.value
518
519time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
520                 "%a %b %d %H:%M:%S %Z %Y",
521                 "%Y/%m/%d %H:%M:%S",
522                 "%Y/%m/%d %H:%M",
523                 "%Y/%m/%d",
524                 "%m/%d/%Y %H:%M:%S",
525                 "%m/%d/%Y %H:%M",
526                 "%m/%d/%Y",
527                 "%m/%d/%y %H:%M:%S",
528                 "%m/%d/%y %H:%M",
529                 "%m/%d/%y"]
530
531
532def parse_time(value):
533    from time import gmtime, strptime, struct_time, time
534    from datetime import datetime, date
535
536    if isinstance(value, struct_time):
537        return value
538
539    if isinstance(value, (int, long)):
540        return gmtime(value)
541
542    if isinstance(value, (datetime, date)):
543        return value.timetuple()
544
545    if isinstance(value, str):
546        if value in ('Now', 'Today'):
547            return time.gmtime(time.time())
548
549        for format in time_formats:
550            try:
551                return strptime(value, format)
552            except ValueError:
553                pass
554
555    raise ValueError, "Could not parse '%s' as a time" % value
556
557class Time(ParamValue):
558    cxx_type = 'time_t'
559    def __init__(self, value):
560        self.value = parse_time(value)
561
562    def __str__(self):
563        tm = self.value
564        return ' '.join([ str(tm[i]) for i in xrange(8)])
565
566    def ini_str(self):
567        return str(self)
568
569# Enumerated types are a little more complex.  The user specifies the
570# type as Enum(foo) where foo is either a list or dictionary of
571# alternatives (typically strings, but not necessarily so).  (In the
572# long run, the integer value of the parameter will be the list index
573# or the corresponding dictionary value.  For now, since we only check
574# that the alternative is valid and then spit it into a .ini file,
575# there's not much point in using the dictionary.)
576
577# What Enum() must do is generate a new type encapsulating the
578# provided list/dictionary so that specific values of the parameter
579# can be instances of that type.  We define two hidden internal
580# classes (_ListEnum and _DictEnum) to serve as base classes, then
581# derive the new type from the appropriate base class on the fly.
582
583
584# Metaclass for Enum types
585class MetaEnum(type):
586    def __init__(cls, name, bases, init_dict):
587        if init_dict.has_key('map'):
588            if not isinstance(cls.map, dict):
589                raise TypeError, "Enum-derived class attribute 'map' " \
590                      "must be of type dict"
591            # build list of value strings from map
592            cls.vals = cls.map.keys()
593            cls.vals.sort()
594        elif init_dict.has_key('vals'):
595            if not isinstance(cls.vals, list):
596                raise TypeError, "Enum-derived class attribute 'vals' " \
597                      "must be of type list"
598            # build string->value map from vals sequence
599            cls.map = {}
600            for idx,val in enumerate(cls.vals):
601                cls.map[val] = idx
602        else:
603            raise TypeError, "Enum-derived class must define "\
604                  "attribute 'map' or 'vals'"
605
606        cls.cxx_type = name + '::Enum'
607
608        super(MetaEnum, cls).__init__(name, bases, init_dict)
609
610    # Generate C++ class declaration for this enum type.
611    # Note that we wrap the enum in a class/struct to act as a namespace,
612    # so that the enum strings can be brief w/o worrying about collisions.
613    def cxx_decl(cls):
614        s = 'struct %s {\n  enum Enum {\n    ' % cls.__name__
615        s += ',\n    '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
616        s += '\n  };\n};\n'
617        return s
618
619# Base class for enum types.
620class Enum(ParamValue):
621    __metaclass__ = MetaEnum
622    vals = []
623
624    def __init__(self, value):
625        if value not in self.map:
626            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
627                  % (value, self.vals)
628        self.value = value
629
630    def __str__(self):
631        return self.value
632
633# how big does a rounding error need to be before we warn about it?
634frequency_tolerance = 0.001  # 0.1%
635
636class TickParamValue(NumericParamValue):
637    cxx_type = 'Tick'
638    cxx_predecls = ['#include "sim/host.hh"']
639    swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
640                     '%import "sim/host.hh"']
641
642class Latency(TickParamValue):
643    def __init__(self, value):
644        if isinstance(value, (Latency, Clock)):
645            self.ticks = value.ticks
646            self.value = value.value
647        elif isinstance(value, Frequency):
648            self.ticks = value.ticks
649            self.value = 1.0 / value.value
650        elif value.endswith('t'):
651            self.ticks = True
652            self.value = int(value[:-1])
653        else:
654            self.ticks = False
655            self.value = convert.toLatency(value)
656
657    def __getattr__(self, attr):
658        if attr in ('latency', 'period'):
659            return self
660        if attr == 'frequency':
661            return Frequency(self)
662        raise AttributeError, "Latency object has no attribute '%s'" % attr
663
664    # convert latency to ticks
665    def ini_str(self):
666        if self.ticks or self.value == 0:
667            return '%d' % self.value
668        else:
669            return '%d' % (ticks.fromSeconds(self.value))
670
671class Frequency(TickParamValue):
672    def __init__(self, value):
673        if isinstance(value, (Latency, Clock)):
674            if value.value == 0:
675                self.value = 0
676            else:
677                self.value = 1.0 / value.value
678            self.ticks = value.ticks
679        elif isinstance(value, Frequency):
680            self.value = value.value
681            self.ticks = value.ticks
682        else:
683            self.ticks = False
684            self.value = convert.toFrequency(value)
685
686    def __getattr__(self, attr):
687        if attr == 'frequency':
688            return self
689        if attr in ('latency', 'period'):
690            return Latency(self)
691        raise AttributeError, "Frequency object has no attribute '%s'" % attr
692
693    # convert latency to ticks
694    def ini_str(self):
695        if self.ticks or self.value == 0:
696            return '%d' % self.value
697        else:
698            return '%d' % (ticks.fromSeconds(1.0 / self.value))
699
700# A generic frequency and/or Latency value.  Value is stored as a latency,
701# but to avoid ambiguity this object does not support numeric ops (* or /).
702# An explicit conversion to a Latency or Frequency must be made first.
703class Clock(ParamValue):
704    cxx_type = 'Tick'
705    cxx_predecls = ['#include "sim/host.hh"']
706    swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
707                     '%import "sim/host.hh"']
708    def __init__(self, value):
709        if isinstance(value, (Latency, Clock)):
710            self.ticks = value.ticks
711            self.value = value.value
712        elif isinstance(value, Frequency):
713            self.ticks = value.ticks
714            self.value = 1.0 / value.value
715        elif value.endswith('t'):
716            self.ticks = True
717            self.value = int(value[:-1])
718        else:
719            self.ticks = False
720            self.value = convert.anyToLatency(value)
721
722    def __getattr__(self, attr):
723        if attr == 'frequency':
724            return Frequency(self)
725        if attr in ('latency', 'period'):
726            return Latency(self)
727        raise AttributeError, "Frequency object has no attribute '%s'" % attr
728
729    def ini_str(self):
730        return self.period.ini_str()
731
732class NetworkBandwidth(float,ParamValue):
733    cxx_type = 'float'
734    def __new__(cls, value):
735        # convert to bits per second
736        val = convert.toNetworkBandwidth(value)
737        return super(cls, NetworkBandwidth).__new__(cls, val)
738
739    def __str__(self):
740        return str(self.val)
741
742    def ini_str(self):
743        # convert to seconds per byte
744        value = 8.0 / float(self)
745        # convert to ticks per byte
746        return '%f' % (ticks.fromSeconds(value))
747
748class MemoryBandwidth(float,ParamValue):
749    cxx_type = 'float'
750    def __new__(self, value):
751        # we want the number of ticks per byte of data
752        val = convert.toMemoryBandwidth(value)
753        return super(cls, MemoryBandwidth).__new__(cls, val)
754
755    def __str__(self):
756        return str(self.val)
757
758    def ini_str(self):
759        # convert to seconds per byte
760        value = 1.0 / float(self)
761        # convert to ticks per byte
762        return '%f' % (ticks.fromSeconds(value))
763
764#
765# "Constants"... handy aliases for various values.
766#
767
768# Special class for NULL pointers.  Note the special check in
769# make_param_value() above that lets these be assigned where a
770# SimObject is required.
771# only one copy of a particular node
772class NullSimObject(object):
773    __metaclass__ = Singleton
774
775    def __call__(cls):
776        return cls
777
778    def _instantiate(self, parent = None, path = ''):
779        pass
780
781    def ini_str(self):
782        return 'Null'
783
784    def unproxy(self, base):
785        return self
786
787    def set_path(self, parent, name):
788        pass
789    def __str__(self):
790        return 'Null'
791
792# The only instance you'll ever need...
793NULL = NullSimObject()
794
795def isNullPointer(value):
796    return isinstance(value, NullSimObject)
797
798# Some memory range specifications use this as a default upper bound.
799MaxAddr = Addr.max
800MaxTick = Tick.max
801AllMemory = AddrRange(0, MaxAddr)
802
803
804#####################################################################
805#
806# Port objects
807#
808# Ports are used to interconnect objects in the memory system.
809#
810#####################################################################
811
812# Port reference: encapsulates a reference to a particular port on a
813# particular SimObject.
814class PortRef(object):
815    def __init__(self, simobj, name):
816        assert(isSimObject(simobj) or isSimObjectClass(simobj))
817        self.simobj = simobj
818        self.name = name
819        self.peer = None   # not associated with another port yet
820        self.ccConnected = False # C++ port connection done?
821        self.index = -1  # always -1 for non-vector ports
822
823    def __str__(self):
824        return '%s.%s' % (self.simobj, self.name)
825
826    # for config.ini, print peer's name (not ours)
827    def ini_str(self):
828        return str(self.peer)
829
830    def __getattr__(self, attr):
831        if attr == 'peerObj':
832            # shorthand for proxies
833            return self.peer.simobj
834        raise AttributeError, "'%s' object has no attribute '%s'" % \
835              (self.__class__.__name__, attr)
836
837    # Full connection is symmetric (both ways).  Called via
838    # SimObject.__setattr__ as a result of a port assignment, e.g.,
839    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
840    # e.g., "obj1.portA[3] = obj2.portB".
841    def connect(self, other):
842        if isinstance(other, VectorPortRef):
843            # reference to plain VectorPort is implicit append
844            other = other._get_next()
845        if self.peer and not proxy.isproxy(self.peer):
846            print "warning: overwriting port", self, \
847                  "value", self.peer, "with", other
848        self.peer = other
849        if proxy.isproxy(other):
850            other.set_param_desc(PortParamDesc())
851        elif isinstance(other, PortRef):
852            if other.peer is not self:
853                other.connect(self)
854        else:
855            raise TypeError, \
856                  "assigning non-port reference '%s' to port '%s'" \
857                  % (other, self)
858
859    def clone(self, simobj, memo):
860        if memo.has_key(self):
861            return memo[self]
862        newRef = copy.copy(self)
863        memo[self] = newRef
864        newRef.simobj = simobj
865        assert(isSimObject(newRef.simobj))
866        if self.peer and not proxy.isproxy(self.peer):
867            peerObj = self.peer.simobj(_memo=memo)
868            newRef.peer = self.peer.clone(peerObj, memo)
869            assert(not isinstance(newRef.peer, VectorPortRef))
870        return newRef
871
872    def unproxy(self, simobj):
873        assert(simobj is self.simobj)
874        if proxy.isproxy(self.peer):
875            try:
876                realPeer = self.peer.unproxy(self.simobj)
877            except:
878                print "Error in unproxying port '%s' of %s" % \
879                      (self.name, self.simobj.path())
880                raise
881            self.connect(realPeer)
882
883    # Call C++ to create corresponding port connection between C++ objects
884    def ccConnect(self):
885        if self.ccConnected: # already done this
886            return
887        peer = self.peer
888        internal.sim_object.connectPorts(self.simobj.getCCObject(), self.name,
889            self.index, peer.simobj.getCCObject(), peer.name, peer.index)
890        self.ccConnected = True
891        peer.ccConnected = True
892
893# A reference to an individual element of a VectorPort... much like a
894# PortRef, but has an index.
895class VectorPortElementRef(PortRef):
896    def __init__(self, simobj, name, index):
897        PortRef.__init__(self, simobj, name)
898        self.index = index
899
900    def __str__(self):
901        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
902
903# A reference to a complete vector-valued port (not just a single element).
904# Can be indexed to retrieve individual VectorPortElementRef instances.
905class VectorPortRef(object):
906    def __init__(self, simobj, name):
907        assert(isSimObject(simobj) or isSimObjectClass(simobj))
908        self.simobj = simobj
909        self.name = name
910        self.elements = []
911
912    def __str__(self):
913        return '%s.%s[:]' % (self.simobj, self.name)
914
915    # for config.ini, print peer's name (not ours)
916    def ini_str(self):
917        return ' '.join([el.ini_str() for el in self.elements])
918
919    def __getitem__(self, key):
920        if not isinstance(key, int):
921            raise TypeError, "VectorPort index must be integer"
922        if key >= len(self.elements):
923            # need to extend list
924            ext = [VectorPortElementRef(self.simobj, self.name, i)
925                   for i in range(len(self.elements), key+1)]
926            self.elements.extend(ext)
927        return self.elements[key]
928
929    def _get_next(self):
930        return self[len(self.elements)]
931
932    def __setitem__(self, key, value):
933        if not isinstance(key, int):
934            raise TypeError, "VectorPort index must be integer"
935        self[key].connect(value)
936
937    def connect(self, other):
938        if isinstance(other, (list, tuple)):
939            # Assign list of port refs to vector port.
940            # For now, append them... not sure if that's the right semantics
941            # or if it should replace the current vector.
942            for ref in other:
943                self._get_next().connect(ref)
944        else:
945            # scalar assignment to plain VectorPort is implicit append
946            self._get_next().connect(other)
947
948    def clone(self, simobj, memo):
949        if memo.has_key(self):
950            return memo[self]
951        newRef = copy.copy(self)
952        memo[self] = newRef
953        newRef.simobj = simobj
954        assert(isSimObject(newRef.simobj))
955        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
956        return newRef
957
958    def unproxy(self, simobj):
959        [el.unproxy(simobj) for el in self.elements]
960
961    def ccConnect(self):
962        [el.ccConnect() for el in self.elements]
963
964# Port description object.  Like a ParamDesc object, this represents a
965# logical port in the SimObject class, not a particular port on a
966# SimObject instance.  The latter are represented by PortRef objects.
967class Port(object):
968    # Port("description") or Port(default, "description")
969    def __init__(self, *args):
970        if len(args) == 1:
971            self.desc = args[0]
972        elif len(args) == 2:
973            self.default = args[0]
974            self.desc = args[1]
975        else:
976            raise TypeError, 'wrong number of arguments'
977        # self.name is set by SimObject class on assignment
978        # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
979
980    # Generate a PortRef for this port on the given SimObject with the
981    # given name
982    def makeRef(self, simobj):
983        return PortRef(simobj, self.name)
984
985    # Connect an instance of this port (on the given SimObject with
986    # the given name) with the port described by the supplied PortRef
987    def connect(self, simobj, ref):
988        self.makeRef(simobj).connect(ref)
989
990# VectorPort description object.  Like Port, but represents a vector
991# of connections (e.g., as on a Bus).
992class VectorPort(Port):
993    def __init__(self, *args):
994        Port.__init__(self, *args)
995        self.isVec = True
996
997    def makeRef(self, simobj):
998        return VectorPortRef(simobj, self.name)
999
1000# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1001# proxy objects (via set_param_desc()) so that proxy error messages
1002# make sense.
1003class PortParamDesc(object):
1004    __metaclass__ = Singleton
1005
1006    ptype_str = 'Port'
1007    ptype = Port
1008
1009
1010__all__ = ['Param', 'VectorParam',
1011           'Enum', 'Bool', 'String', 'Float',
1012           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1013           'Int32', 'UInt32', 'Int64', 'UInt64',
1014           'Counter', 'Addr', 'Tick', 'Percent',
1015           'TcpPort', 'UdpPort', 'EthernetAddr',
1016           'MemorySize', 'MemorySize32',
1017           'Latency', 'Frequency', 'Clock',
1018           'NetworkBandwidth', 'MemoryBandwidth',
1019           'Range', 'AddrRange', 'TickRange',
1020           'MaxAddr', 'MaxTick', 'AllMemory',
1021           'Time',
1022           'NextEthernetAddr', 'NULL',
1023           'Port', 'VectorPort']
1024
1025# see comment on imports at end of __init__.py.
1026from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
1027import objects
1028import internal
1029