| # Copyright (c) 2004-2006 The Regents of The University of Michigan |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer; |
| # redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution; |
| # neither the name of the copyright holders nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| # |
| # Authors: Steve Reinhardt |
| # Nathan Binkert |
| |
| ##################################################################### |
| # |
| # Parameter description classes |
| # |
| # The _params dictionary in each class maps parameter names to either |
| # a Param or a VectorParam object. These objects contain the |
| # parameter description string, the parameter type, and the default |
| # value (if any). The convert() method on these objects is used to |
| # force whatever value is assigned to the parameter to the appropriate |
| # type. |
| # |
| # Note that the default values are loaded into the class's attribute |
| # space when the parameter dictionary is initialized (in |
| # MetaSimObject._new_param()); after that point they aren't used. |
| # |
| ##################################################################### |
| |
| import copy |
| import datetime |
| import re |
| import sys |
| import time |
| |
| import convert |
| import proxy |
| import ticks |
| from util import * |
| |
| import SimObject |
| |
| def isSimObject(*args, **kwargs): |
| return SimObject.isSimObject(*args, **kwargs) |
| |
| def isSimObjectSequence(*args, **kwargs): |
| return SimObject.isSimObjectSequence(*args, **kwargs) |
| |
| def isSimObjectClass(*args, **kwargs): |
| return SimObject.isSimObjectClass(*args, **kwargs) |
| |
| allParams = {} |
| |
| class MetaParamValue(type): |
| def __new__(mcls, name, bases, dct): |
| cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct) |
| assert name not in allParams |
| allParams[name] = cls |
| return cls |
| |
| |
| # Dummy base class to identify types that are legitimate for SimObject |
| # parameters. |
| class ParamValue(object): |
| __metaclass__ = MetaParamValue |
| |
| cxx_predecls = [] |
| swig_predecls = [] |
| |
| # default for printing to .ini file is regular string conversion. |
| # will be overridden in some cases |
| def ini_str(self): |
| return str(self) |
| |
| # allows us to blithely call unproxy() on things without checking |
| # if they're really proxies or not |
| def unproxy(self, base): |
| return self |
| |
| # Regular parameter description. |
| class ParamDesc(object): |
| def __init__(self, ptype_str, ptype, *args, **kwargs): |
| self.ptype_str = ptype_str |
| # remember ptype only if it is provided |
| if ptype != None: |
| self.ptype = ptype |
| |
| if args: |
| if len(args) == 1: |
| self.desc = args[0] |
| elif len(args) == 2: |
| self.default = args[0] |
| self.desc = args[1] |
| else: |
| raise TypeError, 'too many arguments' |
| |
| if kwargs.has_key('desc'): |
| assert(not hasattr(self, 'desc')) |
| self.desc = kwargs['desc'] |
| del kwargs['desc'] |
| |
| if kwargs.has_key('default'): |
| assert(not hasattr(self, 'default')) |
| self.default = kwargs['default'] |
| del kwargs['default'] |
| |
| if kwargs: |
| raise TypeError, 'extra unknown kwargs %s' % kwargs |
| |
| if not hasattr(self, 'desc'): |
| raise TypeError, 'desc attribute missing' |
| |
| def __getattr__(self, attr): |
| if attr == 'ptype': |
| ptype = SimObject.allClasses[self.ptype_str] |
| assert issubclass(ptype, SimObject.SimObject) |
| self.ptype = ptype |
| return ptype |
| |
| raise AttributeError, "'%s' object has no attribute '%s'" % \ |
| (type(self).__name__, attr) |
| |
| def convert(self, value): |
| if isinstance(value, proxy.BaseProxy): |
| value.set_param_desc(self) |
| return value |
| if not hasattr(self, 'ptype') and isNullPointer(value): |
| # deferred evaluation of SimObject; continue to defer if |
| # we're just assigning a null pointer |
| return value |
| if isinstance(value, self.ptype): |
| return value |
| if isNullPointer(value) and isSimObjectClass(self.ptype): |
| return value |
| return self.ptype(value) |
| |
| def cxx_predecls(self): |
| return self.ptype.cxx_predecls |
| |
| def swig_predecls(self): |
| return self.ptype.swig_predecls |
| |
| def cxx_decl(self): |
| return '%s %s;' % (self.ptype.cxx_type, self.name) |
| |
| # Vector-valued parameter description. Just like ParamDesc, except |
| # that the value is a vector (list) of the specified type instead of a |
| # single value. |
| |
| class VectorParamValue(list): |
| __metaclass__ = MetaParamValue |
| def __setattr__(self, attr, value): |
| raise AttributeError, \ |
| "Not allowed to set %s on '%s'" % (attr, type(self).__name__) |
| |
| def ini_str(self): |
| return ' '.join([v.ini_str() for v in self]) |
| |
| def getValue(self): |
| return [ v.getValue() for v in self ] |
| |
| def unproxy(self, base): |
| return [v.unproxy(base) for v in self] |
| |
| class SimObjVector(VectorParamValue): |
| def print_ini(self, ini_file): |
| for v in self: |
| v.print_ini(ini_file) |
| |
| class VectorParamDesc(ParamDesc): |
| # Convert assigned value to appropriate type. If the RHS is not a |
| # list or tuple, it generates a single-element list. |
| def convert(self, value): |
| if isinstance(value, (list, tuple)): |
| # list: coerce each element into new list |
| tmp_list = [ ParamDesc.convert(self, v) for v in value ] |
| else: |
| # singleton: coerce to a single-element list |
| tmp_list = [ ParamDesc.convert(self, value) ] |
| |
| if isSimObjectSequence(tmp_list): |
| return SimObjVector(tmp_list) |
| else: |
| return VectorParamValue(tmp_list) |
| |
| def swig_predecls(self): |
| return ['%%include "%s_vptype.i"' % self.ptype_str] |
| |
| def swig_decl(self): |
| cxx_type = re.sub('std::', '', self.ptype.cxx_type) |
| vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \ |
| (self.ptype_str, cxx_type) |
| return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl] |
| |
| def cxx_predecls(self): |
| return ['#include <vector>'] + self.ptype.cxx_predecls |
| |
| def cxx_decl(self): |
| return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) |
| |
| class ParamFactory(object): |
| def __init__(self, param_desc_class, ptype_str = None): |
| self.param_desc_class = param_desc_class |
| self.ptype_str = ptype_str |
| |
| def __getattr__(self, attr): |
| if self.ptype_str: |
| attr = self.ptype_str + '.' + attr |
| return ParamFactory(self.param_desc_class, attr) |
| |
| # E.g., Param.Int(5, "number of widgets") |
| def __call__(self, *args, **kwargs): |
| ptype = None |
| try: |
| ptype = allParams[self.ptype_str] |
| except KeyError: |
| # if name isn't defined yet, assume it's a SimObject, and |
| # try to resolve it later |
| pass |
| return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) |
| |
| Param = ParamFactory(ParamDesc) |
| VectorParam = ParamFactory(VectorParamDesc) |
| |
| ##################################################################### |
| # |
| # Parameter Types |
| # |
| # Though native Python types could be used to specify parameter types |
| # (the 'ptype' field of the Param and VectorParam classes), it's more |
| # flexible to define our own set of types. This gives us more control |
| # over how Python expressions are converted to values (via the |
| # __init__() constructor) and how these values are printed out (via |
| # the __str__() conversion method). |
| # |
| ##################################################################### |
| |
| # String-valued parameter. Just mixin the ParamValue class with the |
| # built-in str class. |
| class String(ParamValue,str): |
| cxx_type = 'std::string' |
| cxx_predecls = ['#include <string>'] |
| swig_predecls = ['%include "std_string.i"\n' + |
| '%apply const std::string& {std::string *};'] |
| swig_predecls = ['%include "std_string.i"' ] |
| |
| def getValue(self): |
| return self |
| |
| # superclass for "numeric" parameter values, to emulate math |
| # operations in a type-safe way. e.g., a Latency times an int returns |
| # a new Latency object. |
| class NumericParamValue(ParamValue): |
| def __str__(self): |
| return str(self.value) |
| |
| def __float__(self): |
| return float(self.value) |
| |
| def __long__(self): |
| return long(self.value) |
| |
| def __int__(self): |
| return int(self.value) |
| |
| # hook for bounds checking |
| def _check(self): |
| return |
| |
| def __mul__(self, other): |
| newobj = self.__class__(self) |
| newobj.value *= other |
| newobj._check() |
| return newobj |
| |
| __rmul__ = __mul__ |
| |
| def __div__(self, other): |
| newobj = self.__class__(self) |
| newobj.value /= other |
| newobj._check() |
| return newobj |
| |
| def __sub__(self, other): |
| newobj = self.__class__(self) |
| newobj.value -= other |
| newobj._check() |
| return newobj |
| |
| # Metaclass for bounds-checked integer parameters. See CheckedInt. |
| class CheckedIntType(MetaParamValue): |
| def __init__(cls, name, bases, dict): |
| super(CheckedIntType, cls).__init__(name, bases, dict) |
| |
| # CheckedInt is an abstract base class, so we actually don't |
| # want to do any processing on it... the rest of this code is |
| # just for classes that derive from CheckedInt. |
| if name == 'CheckedInt': |
| return |
| |
| if not cls.cxx_predecls: |
| # most derived types require this, so we just do it here once |
| cls.cxx_predecls = ['#include "base/types.hh"'] |
| |
| if not cls.swig_predecls: |
| # most derived types require this, so we just do it here once |
| cls.swig_predecls = ['%import "stdint.i"\n' + |
| '%import "base/types.hh"'] |
| |
| if not (hasattr(cls, 'min') and hasattr(cls, 'max')): |
| if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): |
| panic("CheckedInt subclass %s must define either\n" \ |
| " 'min' and 'max' or 'size' and 'unsigned'\n", |
| name); |
| if cls.unsigned: |
| cls.min = 0 |
| cls.max = 2 ** cls.size - 1 |
| else: |
| cls.min = -(2 ** (cls.size - 1)) |
| cls.max = (2 ** (cls.size - 1)) - 1 |
| |
| # Abstract superclass for bounds-checked integer parameters. This |
| # class is subclassed to generate parameter classes with specific |
| # bounds. Initialization of the min and max bounds is done in the |
| # metaclass CheckedIntType.__init__. |
| class CheckedInt(NumericParamValue): |
| __metaclass__ = CheckedIntType |
| |
| def _check(self): |
| if not self.min <= self.value <= self.max: |
| raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ |
| (self.min, self.value, self.max) |
| |
| def __init__(self, value): |
| if isinstance(value, str): |
| self.value = convert.toInteger(value) |
| elif isinstance(value, (int, long, float, NumericParamValue)): |
| self.value = long(value) |
| else: |
| raise TypeError, "Can't convert object of type %s to CheckedInt" \ |
| % type(value).__name__ |
| self._check() |
| |
| def getValue(self): |
| return long(self.value) |
| |
| class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False |
| class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True |
| |
| class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False |
| class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True |
| class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False |
| class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True |
| class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False |
| class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True |
| class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False |
| class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True |
| |
| class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True |
| class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True |
| class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True |
| class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True |
| |
| class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 |
| |
| class Float(ParamValue, float): |
| cxx_type = 'double' |
| |
| def __init__(self, value): |
| if isinstance(value, (int, long, float, NumericParamValue, Float)): |
| self.value = float(value) |
| else: |
| raise TypeError, "Can't convert object of type %s to Float" \ |
| % type(value).__name__ |
| |
| def getValue(self): |
| return float(self.value) |
| |
| class MemorySize(CheckedInt): |
| cxx_type = 'uint64_t' |
| size = 64 |
| unsigned = True |
| def __init__(self, value): |
| if isinstance(value, MemorySize): |
| self.value = value.value |
| else: |
| self.value = convert.toMemorySize(value) |
| self._check() |
| |
| class MemorySize32(CheckedInt): |
| cxx_type = 'uint32_t' |
| size = 32 |
| unsigned = True |
| def __init__(self, value): |
| if isinstance(value, MemorySize): |
| self.value = value.value |
| else: |
| self.value = convert.toMemorySize(value) |
| self._check() |
| |
| class Addr(CheckedInt): |
| cxx_type = 'Addr' |
| size = 64 |
| unsigned = True |
| def __init__(self, value): |
| if isinstance(value, Addr): |
| self.value = value.value |
| else: |
| try: |
| self.value = convert.toMemorySize(value) |
| except TypeError: |
| self.value = long(value) |
| self._check() |
| def __add__(self, other): |
| if isinstance(other, Addr): |
| return self.value + other.value |
| else: |
| return self.value + other |
| |
| |
| class MetaRange(MetaParamValue): |
| def __init__(cls, name, bases, dict): |
| super(MetaRange, cls).__init__(name, bases, dict) |
| if name == 'Range': |
| return |
| cls.cxx_type = 'Range< %s >' % cls.type.cxx_type |
| cls.cxx_predecls = \ |
| ['#include "base/range.hh"'] + cls.type.cxx_predecls |
| |
| class Range(ParamValue): |
| __metaclass__ = MetaRange |
| type = Int # default; can be overridden in subclasses |
| def __init__(self, *args, **kwargs): |
| def handle_kwargs(self, kwargs): |
| if 'end' in kwargs: |
| self.second = self.type(kwargs.pop('end')) |
| elif 'size' in kwargs: |
| self.second = self.first + self.type(kwargs.pop('size')) - 1 |
| else: |
| raise TypeError, "Either end or size must be specified" |
| |
| if len(args) == 0: |
| self.first = self.type(kwargs.pop('start')) |
| handle_kwargs(self, kwargs) |
| |
| elif len(args) == 1: |
| if kwargs: |
| self.first = self.type(args[0]) |
| handle_kwargs(self, kwargs) |
| elif isinstance(args[0], Range): |
| self.first = self.type(args[0].first) |
| self.second = self.type(args[0].second) |
| elif isinstance(args[0], (list, tuple)): |
| self.first = self.type(args[0][0]) |
| self.second = self.type(args[0][1]) |
| else: |
| self.first = self.type(0) |
| self.second = self.type(args[0]) - 1 |
| |
| elif len(args) == 2: |
| self.first = self.type(args[0]) |
| self.second = self.type(args[1]) |
| else: |
| raise TypeError, "Too many arguments specified" |
| |
| if kwargs: |
| raise TypeError, "too many keywords: %s" % kwargs.keys() |
| |
| def __str__(self): |
| return '%s:%s' % (self.first, self.second) |
| |
| class AddrRange(Range): |
| type = Addr |
| swig_predecls = ['%include "python/swig/range.i"'] |
| |
| def getValue(self): |
| from m5.objects.params import AddrRange |
| |
| value = AddrRange() |
| value.start = long(self.first) |
| value.end = long(self.second) |
| return value |
| |
| class TickRange(Range): |
| type = Tick |
| swig_predecls = ['%include "python/swig/range.i"'] |
| |
| def getValue(self): |
| from m5.objects.params import TickRange |
| |
| value = TickRange() |
| value.start = long(self.first) |
| value.end = long(self.second) |
| return value |
| |
| # Boolean parameter type. Python doesn't let you subclass bool, since |
| # it doesn't want to let you create multiple instances of True and |
| # False. Thus this is a little more complicated than String. |
| class Bool(ParamValue): |
| cxx_type = 'bool' |
| def __init__(self, value): |
| try: |
| self.value = convert.toBool(value) |
| except TypeError: |
| self.value = bool(value) |
| |
| def getValue(self): |
| return bool(self.value) |
| |
| def __str__(self): |
| return str(self.value) |
| |
| def ini_str(self): |
| if self.value: |
| return 'true' |
| return 'false' |
| |
| def IncEthernetAddr(addr, val = 1): |
| bytes = map(lambda x: int(x, 16), addr.split(':')) |
| bytes[5] += val |
| for i in (5, 4, 3, 2, 1): |
| val,rem = divmod(bytes[i], 256) |
| bytes[i] = rem |
| if val == 0: |
| break |
| bytes[i - 1] += val |
| assert(bytes[0] <= 255) |
| return ':'.join(map(lambda x: '%02x' % x, bytes)) |
| |
| _NextEthernetAddr = "00:90:00:00:00:01" |
| def NextEthernetAddr(): |
| global _NextEthernetAddr |
| |
| value = _NextEthernetAddr |
| _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1) |
| return value |
| |
| class EthernetAddr(ParamValue): |
| cxx_type = 'Net::EthAddr' |
| cxx_predecls = ['#include "base/inet.hh"'] |
| swig_predecls = ['%include "python/swig/inet.i"'] |
| def __init__(self, value): |
| if value == NextEthernetAddr: |
| self.value = value |
| return |
| |
| if not isinstance(value, str): |
| raise TypeError, "expected an ethernet address and didn't get one" |
| |
| bytes = value.split(':') |
| if len(bytes) != 6: |
| raise TypeError, 'invalid ethernet address %s' % value |
| |
| for byte in bytes: |
| if not 0 <= int(byte) <= 256: |
| raise TypeError, 'invalid ethernet address %s' % value |
| |
| self.value = value |
| |
| def unproxy(self, base): |
| if self.value == NextEthernetAddr: |
| return EthernetAddr(self.value()) |
| return self |
| |
| def getValue(self): |
| from m5.objects.params import EthAddr |
| return EthAddr(self.value) |
| |
| def ini_str(self): |
| return self.value |
| |
| time_formats = [ "%a %b %d %H:%M:%S %Z %Y", |
| "%a %b %d %H:%M:%S %Z %Y", |
| "%Y/%m/%d %H:%M:%S", |
| "%Y/%m/%d %H:%M", |
| "%Y/%m/%d", |
| "%m/%d/%Y %H:%M:%S", |
| "%m/%d/%Y %H:%M", |
| "%m/%d/%Y", |
| "%m/%d/%y %H:%M:%S", |
| "%m/%d/%y %H:%M", |
| "%m/%d/%y"] |
| |
| |
| def parse_time(value): |
| from time import gmtime, strptime, struct_time, time |
| from datetime import datetime, date |
| |
| if isinstance(value, struct_time): |
| return value |
| |
| if isinstance(value, (int, long)): |
| return gmtime(value) |
| |
| if isinstance(value, (datetime, date)): |
| return value.timetuple() |
| |
| if isinstance(value, str): |
| if value in ('Now', 'Today'): |
| return time.gmtime(time.time()) |
| |
| for format in time_formats: |
| try: |
| return strptime(value, format) |
| except ValueError: |
| pass |
| |
| raise ValueError, "Could not parse '%s' as a time" % value |
| |
| class Time(ParamValue): |
| cxx_type = 'tm' |
| cxx_predecls = [ '#include <time.h>' ] |
| swig_predecls = [ '%include "python/swig/time.i"' ] |
| def __init__(self, value): |
| self.value = parse_time(value) |
| |
| def getValue(self): |
| from m5.objects.params import tm |
| |
| c_time = tm() |
| py_time = self.value |
| |
| # UNIX is years since 1900 |
| c_time.tm_year = py_time.tm_year - 1900; |
| |
| # Python starts at 1, UNIX starts at 0 |
| c_time.tm_mon = py_time.tm_mon - 1; |
| c_time.tm_mday = py_time.tm_mday; |
| c_time.tm_hour = py_time.tm_hour; |
| c_time.tm_min = py_time.tm_min; |
| c_time.tm_sec = py_time.tm_sec; |
| |
| # Python has 0 as Monday, UNIX is 0 as sunday |
| c_time.tm_wday = py_time.tm_wday + 1 |
| if c_time.tm_wday > 6: |
| c_time.tm_wday -= 7; |
| |
| # Python starts at 1, Unix starts at 0 |
| c_time.tm_yday = py_time.tm_yday - 1; |
| |
| return c_time |
| |
| def __str__(self): |
| return time.asctime(self.value) |
| |
| def ini_str(self): |
| return str(self) |
| |
| # Enumerated types are a little more complex. The user specifies the |
| # type as Enum(foo) where foo is either a list or dictionary of |
| # alternatives (typically strings, but not necessarily so). (In the |
| # long run, the integer value of the parameter will be the list index |
| # or the corresponding dictionary value. For now, since we only check |
| # that the alternative is valid and then spit it into a .ini file, |
| # there's not much point in using the dictionary.) |
| |
| # What Enum() must do is generate a new type encapsulating the |
| # provided list/dictionary so that specific values of the parameter |
| # can be instances of that type. We define two hidden internal |
| # classes (_ListEnum and _DictEnum) to serve as base classes, then |
| # derive the new type from the appropriate base class on the fly. |
| |
| allEnums = {} |
| # Metaclass for Enum types |
| class MetaEnum(MetaParamValue): |
| def __new__(mcls, name, bases, dict): |
| assert name not in allEnums |
| |
| cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict) |
| allEnums[name] = cls |
| return cls |
| |
| def __init__(cls, name, bases, init_dict): |
| if init_dict.has_key('map'): |
| if not isinstance(cls.map, dict): |
| raise TypeError, "Enum-derived class attribute 'map' " \ |
| "must be of type dict" |
| # build list of value strings from map |
| cls.vals = cls.map.keys() |
| cls.vals.sort() |
| elif init_dict.has_key('vals'): |
| if not isinstance(cls.vals, list): |
| raise TypeError, "Enum-derived class attribute 'vals' " \ |
| "must be of type list" |
| # build string->value map from vals sequence |
| cls.map = {} |
| for idx,val in enumerate(cls.vals): |
| cls.map[val] = idx |
| else: |
| raise TypeError, "Enum-derived class must define "\ |
| "attribute 'map' or 'vals'" |
| |
| cls.cxx_type = 'Enums::%s' % name |
| |
| super(MetaEnum, cls).__init__(name, bases, init_dict) |
| |
| def __str__(cls): |
| return cls.__name__ |
| |
| # Generate C++ class declaration for this enum type. |
| # Note that we wrap the enum in a class/struct to act as a namespace, |
| # so that the enum strings can be brief w/o worrying about collisions. |
| def cxx_decl(cls): |
| code = "#ifndef __ENUM__%s\n" % cls |
| code += '#define __ENUM__%s\n' % cls |
| code += '\n' |
| code += 'namespace Enums {\n' |
| code += ' enum %s {\n' % cls |
| for val in cls.vals: |
| code += ' %s = %d,\n' % (val, cls.map[val]) |
| code += ' Num_%s = %d,\n' % (cls, len(cls.vals)) |
| code += ' };\n' |
| code += ' extern const char *%sStrings[Num_%s];\n' % (cls, cls) |
| code += '}\n' |
| code += '\n' |
| code += '#endif\n' |
| return code |
| |
| def cxx_def(cls): |
| code = '#include "enums/%s.hh"\n' % cls |
| code += 'namespace Enums {\n' |
| code += ' const char *%sStrings[Num_%s] =\n' % (cls, cls) |
| code += ' {\n' |
| for val in cls.vals: |
| code += ' "%s",\n' % val |
| code += ' };\n' |
| code += '}\n' |
| return code |
| |
| # Base class for enum types. |
| class Enum(ParamValue): |
| __metaclass__ = MetaEnum |
| vals = [] |
| |
| def __init__(self, value): |
| if value not in self.map: |
| raise TypeError, "Enum param got bad value '%s' (not in %s)" \ |
| % (value, self.vals) |
| self.value = value |
| |
| def getValue(self): |
| return int(self.map[self.value]) |
| |
| def __str__(self): |
| return self.value |
| |
| # how big does a rounding error need to be before we warn about it? |
| frequency_tolerance = 0.001 # 0.1% |
| |
| class TickParamValue(NumericParamValue): |
| cxx_type = 'Tick' |
| cxx_predecls = ['#include "base/types.hh"'] |
| swig_predecls = ['%import "stdint.i"\n' + |
| '%import "base/types.hh"'] |
| |
| def getValue(self): |
| return long(self.value) |
| |
| class Latency(TickParamValue): |
| def __init__(self, value): |
| if isinstance(value, (Latency, Clock)): |
| self.ticks = value.ticks |
| self.value = value.value |
| elif isinstance(value, Frequency): |
| self.ticks = value.ticks |
| self.value = 1.0 / value.value |
| elif value.endswith('t'): |
| self.ticks = True |
| self.value = int(value[:-1]) |
| else: |
| self.ticks = False |
| self.value = convert.toLatency(value) |
| |
| def __getattr__(self, attr): |
| if attr in ('latency', 'period'): |
| return self |
| if attr == 'frequency': |
| return Frequency(self) |
| raise AttributeError, "Latency object has no attribute '%s'" % attr |
| |
| def getValue(self): |
| if self.ticks or self.value == 0: |
| value = self.value |
| else: |
| value = ticks.fromSeconds(self.value) |
| return long(value) |
| |
| # convert latency to ticks |
| def ini_str(self): |
| return '%d' % self.getValue() |
| |
| class Frequency(TickParamValue): |
| def __init__(self, value): |
| if isinstance(value, (Latency, Clock)): |
| if value.value == 0: |
| self.value = 0 |
| else: |
| self.value = 1.0 / value.value |
| self.ticks = value.ticks |
| elif isinstance(value, Frequency): |
| self.value = value.value |
| self.ticks = value.ticks |
| else: |
| self.ticks = False |
| self.value = convert.toFrequency(value) |
| |
| def __getattr__(self, attr): |
| if attr == 'frequency': |
| return self |
| if attr in ('latency', 'period'): |
| return Latency(self) |
| raise AttributeError, "Frequency object has no attribute '%s'" % attr |
| |
| # convert latency to ticks |
| def getValue(self): |
| if self.ticks or self.value == 0: |
| value = self.value |
| else: |
| value = ticks.fromSeconds(1.0 / self.value) |
| return long(value) |
| |
| def ini_str(self): |
| return '%d' % self.getValue() |
| |
| # A generic frequency and/or Latency value. Value is stored as a latency, |
| # but to avoid ambiguity this object does not support numeric ops (* or /). |
| # An explicit conversion to a Latency or Frequency must be made first. |
| class Clock(ParamValue): |
| cxx_type = 'Tick' |
| cxx_predecls = ['#include "base/types.hh"'] |
| swig_predecls = ['%import "stdint.i"\n' + |
| '%import "base/types.hh"'] |
| def __init__(self, value): |
| if isinstance(value, (Latency, Clock)): |
| self.ticks = value.ticks |
| self.value = value.value |
| elif isinstance(value, Frequency): |
| self.ticks = value.ticks |
| self.value = 1.0 / value.value |
| elif value.endswith('t'): |
| self.ticks = True |
| self.value = int(value[:-1]) |
| else: |
| self.ticks = False |
| self.value = convert.anyToLatency(value) |
| |
| def __getattr__(self, attr): |
| if attr == 'frequency': |
| return Frequency(self) |
| if attr in ('latency', 'period'): |
| return Latency(self) |
| raise AttributeError, "Frequency object has no attribute '%s'" % attr |
| |
| def getValue(self): |
| return self.period.getValue() |
| |
| def ini_str(self): |
| return self.period.ini_str() |
| |
| class NetworkBandwidth(float,ParamValue): |
| cxx_type = 'float' |
| def __new__(cls, value): |
| # convert to bits per second |
| val = convert.toNetworkBandwidth(value) |
| return super(cls, NetworkBandwidth).__new__(cls, val) |
| |
| def __str__(self): |
| return str(self.val) |
| |
| def getValue(self): |
| # convert to seconds per byte |
| value = 8.0 / float(self) |
| # convert to ticks per byte |
| value = ticks.fromSeconds(value) |
| return float(value) |
| |
| def ini_str(self): |
| return '%f' % self.getValue() |
| |
| class MemoryBandwidth(float,ParamValue): |
| cxx_type = 'float' |
| def __new__(cls, value): |
| # we want the number of ticks per byte of data |
| val = convert.toMemoryBandwidth(value) |
| return super(cls, MemoryBandwidth).__new__(cls, val) |
| |
| def __str__(self): |
| return str(self.val) |
| |
| def getValue(self): |
| # convert to seconds per byte |
| value = float(self) |
| if value: |
| value = 1.0 / float(self) |
| # convert to ticks per byte |
| value = ticks.fromSeconds(value) |
| return float(value) |
| |
| def ini_str(self): |
| return '%f' % self.getValue() |
| |
| # |
| # "Constants"... handy aliases for various values. |
| # |
| |
| # Special class for NULL pointers. Note the special check in |
| # make_param_value() above that lets these be assigned where a |
| # SimObject is required. |
| # only one copy of a particular node |
| class NullSimObject(object): |
| __metaclass__ = Singleton |
| |
| def __call__(cls): |
| return cls |
| |
| def _instantiate(self, parent = None, path = ''): |
| pass |
| |
| def ini_str(self): |
| return 'Null' |
| |
| def unproxy(self, base): |
| return self |
| |
| def set_path(self, parent, name): |
| pass |
| |
| def __str__(self): |
| return 'Null' |
| |
| def getValue(self): |
| return None |
| |
| # The only instance you'll ever need... |
| NULL = NullSimObject() |
| |
| def isNullPointer(value): |
| return isinstance(value, NullSimObject) |
| |
| # Some memory range specifications use this as a default upper bound. |
| MaxAddr = Addr.max |
| MaxTick = Tick.max |
| AllMemory = AddrRange(0, MaxAddr) |
| |
| |
| ##################################################################### |
| # |
| # Port objects |
| # |
| # Ports are used to interconnect objects in the memory system. |
| # |
| ##################################################################### |
| |
| # Port reference: encapsulates a reference to a particular port on a |
| # particular SimObject. |
| class PortRef(object): |
| def __init__(self, simobj, name): |
| assert(isSimObject(simobj) or isSimObjectClass(simobj)) |
| self.simobj = simobj |
| self.name = name |
| self.peer = None # not associated with another port yet |
| self.ccConnected = False # C++ port connection done? |
| self.index = -1 # always -1 for non-vector ports |
| |
| def __str__(self): |
| return '%s.%s' % (self.simobj, self.name) |
| |
| # for config.ini, print peer's name (not ours) |
| def ini_str(self): |
| return str(self.peer) |
| |
| def __getattr__(self, attr): |
| if attr == 'peerObj': |
| # shorthand for proxies |
| return self.peer.simobj |
| raise AttributeError, "'%s' object has no attribute '%s'" % \ |
| (self.__class__.__name__, attr) |
| |
| # Full connection is symmetric (both ways). Called via |
| # SimObject.__setattr__ as a result of a port assignment, e.g., |
| # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, |
| # e.g., "obj1.portA[3] = obj2.portB". |
| def connect(self, other): |
| if isinstance(other, VectorPortRef): |
| # reference to plain VectorPort is implicit append |
| other = other._get_next() |
| if self.peer and not proxy.isproxy(self.peer): |
| print "warning: overwriting port", self, \ |
| "value", self.peer, "with", other |
| self.peer.peer = None |
| self.peer = other |
| if proxy.isproxy(other): |
| other.set_param_desc(PortParamDesc()) |
| elif isinstance(other, PortRef): |
| if other.peer is not self: |
| other.connect(self) |
| else: |
| raise TypeError, \ |
| "assigning non-port reference '%s' to port '%s'" \ |
| % (other, self) |
| |
| def clone(self, simobj, memo): |
| if memo.has_key(self): |
| return memo[self] |
| newRef = copy.copy(self) |
| memo[self] = newRef |
| newRef.simobj = simobj |
| assert(isSimObject(newRef.simobj)) |
| if self.peer and not proxy.isproxy(self.peer): |
| peerObj = self.peer.simobj(_memo=memo) |
| newRef.peer = self.peer.clone(peerObj, memo) |
| assert(not isinstance(newRef.peer, VectorPortRef)) |
| return newRef |
| |
| def unproxy(self, simobj): |
| assert(simobj is self.simobj) |
| if proxy.isproxy(self.peer): |
| try: |
| realPeer = self.peer.unproxy(self.simobj) |
| except: |
| print "Error in unproxying port '%s' of %s" % \ |
| (self.name, self.simobj.path()) |
| raise |
| self.connect(realPeer) |
| |
| # Call C++ to create corresponding port connection between C++ objects |
| def ccConnect(self): |
| from m5.objects.params import connectPorts |
| |
| if self.ccConnected: # already done this |
| return |
| peer = self.peer |
| if not self.peer: # nothing to connect to |
| return |
| connectPorts(self.simobj.getCCObject(), self.name, self.index, |
| peer.simobj.getCCObject(), peer.name, peer.index) |
| self.ccConnected = True |
| peer.ccConnected = True |
| |
| # A reference to an individual element of a VectorPort... much like a |
| # PortRef, but has an index. |
| class VectorPortElementRef(PortRef): |
| def __init__(self, simobj, name, index): |
| PortRef.__init__(self, simobj, name) |
| self.index = index |
| |
| def __str__(self): |
| return '%s.%s[%d]' % (self.simobj, self.name, self.index) |
| |
| # A reference to a complete vector-valued port (not just a single element). |
| # Can be indexed to retrieve individual VectorPortElementRef instances. |
| class VectorPortRef(object): |
| def __init__(self, simobj, name): |
| assert(isSimObject(simobj) or isSimObjectClass(simobj)) |
| self.simobj = simobj |
| self.name = name |
| self.elements = [] |
| |
| def __str__(self): |
| return '%s.%s[:]' % (self.simobj, self.name) |
| |
| # for config.ini, print peer's name (not ours) |
| def ini_str(self): |
| return ' '.join([el.ini_str() for el in self.elements]) |
| |
| def __getitem__(self, key): |
| if not isinstance(key, int): |
| raise TypeError, "VectorPort index must be integer" |
| if key >= len(self.elements): |
| # need to extend list |
| ext = [VectorPortElementRef(self.simobj, self.name, i) |
| for i in range(len(self.elements), key+1)] |
| self.elements.extend(ext) |
| return self.elements[key] |
| |
| def _get_next(self): |
| return self[len(self.elements)] |
| |
| def __setitem__(self, key, value): |
| if not isinstance(key, int): |
| raise TypeError, "VectorPort index must be integer" |
| self[key].connect(value) |
| |
| def connect(self, other): |
| if isinstance(other, (list, tuple)): |
| # Assign list of port refs to vector port. |
| # For now, append them... not sure if that's the right semantics |
| # or if it should replace the current vector. |
| for ref in other: |
| self._get_next().connect(ref) |
| else: |
| # scalar assignment to plain VectorPort is implicit append |
| self._get_next().connect(other) |
| |
| def clone(self, simobj, memo): |
| if memo.has_key(self): |
| return memo[self] |
| newRef = copy.copy(self) |
| memo[self] = newRef |
| newRef.simobj = simobj |
| assert(isSimObject(newRef.simobj)) |
| newRef.elements = [el.clone(simobj, memo) for el in self.elements] |
| return newRef |
| |
| def unproxy(self, simobj): |
| [el.unproxy(simobj) for el in self.elements] |
| |
| def ccConnect(self): |
| [el.ccConnect() for el in self.elements] |
| |
| # Port description object. Like a ParamDesc object, this represents a |
| # logical port in the SimObject class, not a particular port on a |
| # SimObject instance. The latter are represented by PortRef objects. |
| class Port(object): |
| # Port("description") or Port(default, "description") |
| def __init__(self, *args): |
| if len(args) == 1: |
| self.desc = args[0] |
| elif len(args) == 2: |
| self.default = args[0] |
| self.desc = args[1] |
| else: |
| raise TypeError, 'wrong number of arguments' |
| # self.name is set by SimObject class on assignment |
| # e.g., pio_port = Port("blah") sets self.name to 'pio_port' |
| |
| # Generate a PortRef for this port on the given SimObject with the |
| # given name |
| def makeRef(self, simobj): |
| return PortRef(simobj, self.name) |
| |
| # Connect an instance of this port (on the given SimObject with |
| # the given name) with the port described by the supplied PortRef |
| def connect(self, simobj, ref): |
| self.makeRef(simobj).connect(ref) |
| |
| # VectorPort description object. Like Port, but represents a vector |
| # of connections (e.g., as on a Bus). |
| class VectorPort(Port): |
| def __init__(self, *args): |
| Port.__init__(self, *args) |
| self.isVec = True |
| |
| def makeRef(self, simobj): |
| return VectorPortRef(simobj, self.name) |
| |
| # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of |
| # proxy objects (via set_param_desc()) so that proxy error messages |
| # make sense. |
| class PortParamDesc(object): |
| __metaclass__ = Singleton |
| |
| ptype_str = 'Port' |
| ptype = Port |
| |
| __all__ = ['Param', 'VectorParam', |
| 'Enum', 'Bool', 'String', 'Float', |
| 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', |
| 'Int32', 'UInt32', 'Int64', 'UInt64', |
| 'Counter', 'Addr', 'Tick', 'Percent', |
| 'TcpPort', 'UdpPort', 'EthernetAddr', |
| 'MemorySize', 'MemorySize32', |
| 'Latency', 'Frequency', 'Clock', |
| 'NetworkBandwidth', 'MemoryBandwidth', |
| 'Range', 'AddrRange', 'TickRange', |
| 'MaxAddr', 'MaxTick', 'AllMemory', |
| 'Time', |
| 'NextEthernetAddr', 'NULL', |
| 'Port', 'VectorPort'] |