73dd0ea357
pass the tuple of python's struct_time and interpret that. Fixes a problem where the local timezone leaked into the time calculation. Also fix things so that the unix, python, and RTC data sheets all get the right time. Provide both years since 1900 and BCD two digit year. Put the date back at 1/1/2006 for now. --HG-- extra : convert_revision : 473244572f468de2cb579a3dd7ae296a6f81f5d7
1039 lines
35 KiB
Python
1039 lines
35 KiB
Python
# 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 inspect
|
|
import sys
|
|
import time
|
|
|
|
import convert
|
|
from util import *
|
|
|
|
# Dummy base class to identify types that are legitimate for SimObject
|
|
# parameters.
|
|
class ParamValue(object):
|
|
|
|
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':
|
|
try:
|
|
ptype = eval(self.ptype_str, objects.__dict__)
|
|
if not isinstance(ptype, type):
|
|
raise NameError
|
|
self.ptype = ptype
|
|
return ptype
|
|
except NameError:
|
|
raise TypeError, \
|
|
"Param qualifier '%s' is not a type" % self.ptype_str
|
|
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):
|
|
def ini_str(self):
|
|
return ' '.join([v.ini_str() for v in self])
|
|
|
|
def unproxy(self, base):
|
|
return [v.unproxy(base) for v in self]
|
|
|
|
class SimObjVector(VectorParamValue):
|
|
def print_ini(self):
|
|
for v in self:
|
|
v.print_ini()
|
|
|
|
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 ]
|
|
if isSimObjectSequence(tmp_list):
|
|
return SimObjVector(tmp_list)
|
|
else:
|
|
return VectorParamValue(tmp_list)
|
|
else:
|
|
# singleton: leave it be (could coerce to a single-element
|
|
# list here, but for some historical reason we don't...
|
|
return ParamDesc.convert(self, value)
|
|
|
|
def cxx_predecls(self):
|
|
return ['#include <vector>'] + self.ptype.cxx_predecls
|
|
|
|
def swig_predecls(self):
|
|
return ['%include "std_vector.i"'] + self.ptype.swig_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):
|
|
caller_frame = inspect.currentframe().f_back
|
|
ptype = None
|
|
try:
|
|
ptype = eval(self.ptype_str,
|
|
caller_frame.f_globals, caller_frame.f_locals)
|
|
if not isinstance(ptype, type):
|
|
raise TypeError, \
|
|
"Param qualifier is not a type: %s" % ptype
|
|
except NameError:
|
|
# 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 *};']
|
|
pass
|
|
|
|
# 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(type):
|
|
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 "sim/host.hh"']
|
|
|
|
if not cls.swig_predecls:
|
|
# most derived types require this, so we just do it here once
|
|
cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
|
|
'%import "sim/host.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()
|
|
|
|
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):
|
|
pass
|
|
|
|
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):
|
|
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'
|
|
cxx_predecls = ['#include "targetarch/isa_traits.hh"']
|
|
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(type):
|
|
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)
|
|
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
|
|
|
|
class TickRange(Range):
|
|
type = Tick
|
|
|
|
# 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 __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))
|
|
|
|
class NextEthernetAddr(object):
|
|
addr = "00:90:00:00:00:01"
|
|
|
|
def __init__(self, inc = 1):
|
|
self.value = NextEthernetAddr.addr
|
|
NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
|
|
|
|
class EthernetAddr(ParamValue):
|
|
cxx_type = 'Net::EthAddr'
|
|
cxx_predecls = ['#include "base/inet.hh"']
|
|
swig_predecls = ['class Net::EthAddr;']
|
|
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:
|
|
self.addr = self.value().value
|
|
return self
|
|
|
|
def __str__(self):
|
|
if self.value == NextEthernetAddr:
|
|
if hasattr(self, 'addr'):
|
|
return self.addr
|
|
else:
|
|
return "NextEthernetAddr (unresolved)"
|
|
else:
|
|
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 = 'time_t'
|
|
def __init__(self, value):
|
|
self.value = parse_time(value)
|
|
|
|
def __str__(self):
|
|
tm = self.value
|
|
return ' '.join([ str(tm[i]) for i in xrange(8)])
|
|
|
|
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.
|
|
|
|
|
|
# Metaclass for Enum types
|
|
class MetaEnum(type):
|
|
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 = name + '::Enum'
|
|
|
|
super(MetaEnum, cls).__init__(name, bases, init_dict)
|
|
|
|
# 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):
|
|
s = 'struct %s {\n enum Enum {\n ' % cls.__name__
|
|
s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
|
|
s += '\n };\n};\n'
|
|
return s
|
|
|
|
# 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 __str__(self):
|
|
return self.value
|
|
|
|
ticks_per_sec = None
|
|
|
|
# how big does a rounding error need to be before we warn about it?
|
|
frequency_tolerance = 0.001 # 0.1%
|
|
|
|
# convert a floting-point # of ticks to integer, and warn if rounding
|
|
# discards too much precision
|
|
def tick_check(float_ticks):
|
|
if float_ticks == 0:
|
|
return 0
|
|
int_ticks = int(round(float_ticks))
|
|
err = (float_ticks - int_ticks) / float_ticks
|
|
if err > frequency_tolerance:
|
|
print >> sys.stderr, "Warning: rounding error > tolerance"
|
|
print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
|
|
#raise ValueError
|
|
return int_ticks
|
|
|
|
def getLatency(value):
|
|
if isinstance(value, Latency) or isinstance(value, Clock):
|
|
return value.value
|
|
elif isinstance(value, Frequency) or isinstance(value, RootClock):
|
|
return 1 / value.value
|
|
elif isinstance(value, str):
|
|
try:
|
|
return convert.toLatency(value)
|
|
except ValueError:
|
|
try:
|
|
return 1 / convert.toFrequency(value)
|
|
except ValueError:
|
|
pass # fall through
|
|
raise ValueError, "Invalid Frequency/Latency value '%s'" % value
|
|
|
|
|
|
class Latency(NumericParamValue):
|
|
cxx_type = 'Tick'
|
|
cxx_predecls = ['#include "sim/host.hh"']
|
|
swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
|
|
'%import "sim/host.hh"']
|
|
def __init__(self, value):
|
|
self.value = getLatency(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
|
|
|
|
# convert latency to ticks
|
|
def ini_str(self):
|
|
return str(tick_check(self.value * ticks_per_sec))
|
|
|
|
class Frequency(NumericParamValue):
|
|
cxx_type = 'Tick'
|
|
cxx_predecls = ['#include "sim/host.hh"']
|
|
swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
|
|
'%import "sim/host.hh"']
|
|
def __init__(self, value):
|
|
self.value = 1 / getLatency(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 frequency to ticks per period
|
|
def ini_str(self):
|
|
return self.period.ini_str()
|
|
|
|
# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
|
|
# We can't inherit from Frequency because we don't want it to be directly
|
|
# assignable to a regular Frequency parameter.
|
|
class RootClock(ParamValue):
|
|
cxx_type = 'Tick'
|
|
cxx_predecls = ['#include "sim/host.hh"']
|
|
swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
|
|
'%import "sim/host.hh"']
|
|
def __init__(self, value):
|
|
self.value = 1 / getLatency(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 ini_str(self):
|
|
return str(tick_check(self.value))
|
|
|
|
# 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 "sim/host.hh"']
|
|
swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
|
|
'%import "sim/host.hh"']
|
|
def __init__(self, value):
|
|
self.value = getLatency(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 ini_str(self):
|
|
return self.period.ini_str()
|
|
|
|
class NetworkBandwidth(float,ParamValue):
|
|
cxx_type = 'float'
|
|
def __new__(cls, value):
|
|
val = convert.toNetworkBandwidth(value) / 8.0
|
|
return super(cls, NetworkBandwidth).__new__(cls, val)
|
|
|
|
def __str__(self):
|
|
return str(self.val)
|
|
|
|
def ini_str(self):
|
|
return '%f' % (ticks_per_sec / float(self))
|
|
|
|
class MemoryBandwidth(float,ParamValue):
|
|
cxx_type = 'float'
|
|
def __new__(self, value):
|
|
val = convert.toMemoryBandwidth(value)
|
|
return super(cls, MemoryBandwidth).__new__(cls, val)
|
|
|
|
def __str__(self):
|
|
return str(self.val)
|
|
|
|
def ini_str(self):
|
|
return '%f' % (ticks_per_sec / float(self))
|
|
|
|
#
|
|
# "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'
|
|
|
|
# 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 = 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):
|
|
if self.ccConnected: # already done this
|
|
return
|
|
peer = self.peer
|
|
internal.main.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', 'RootClock', 'Clock',
|
|
'NetworkBandwidth', 'MemoryBandwidth',
|
|
'Range', 'AddrRange', 'TickRange',
|
|
'MaxAddr', 'MaxTick', 'AllMemory',
|
|
'Time',
|
|
'NextEthernetAddr', 'NULL',
|
|
'Port', 'VectorPort']
|
|
|
|
# see comment on imports at end of __init__.py.
|
|
from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
|
|
import proxy
|
|
import objects
|
|
import internal
|