# Copyright (c) 2004-2006 The Regents of The University of Michigan # Copyright (c) 2010-2011 Advanced Micro Devices, Inc. # 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 # Gabe Black ##################################################################### # # 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 math import proxy import ticks from util import * 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 # Generate the code needed as a prerequisite for declaring a C++ # object of this type. Typically generates one or more #include # statements. Used when declaring parameters of this type. @classmethod def cxx_predecls(cls, code): pass # Generate the code needed as a prerequisite for including a # reference to a C++ object of this type in a SWIG .i file. # Typically generates one or more %import or %include statements. @classmethod def swig_predecls(cls, code): pass # 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 isSimObjectClass(ptype) 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, code): code('#include ') self.ptype.cxx_predecls(code) def swig_predecls(self, code): self.ptype.swig_predecls(code) def cxx_decl(self, code): code('${{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): if len(self) == 1 and isinstance(self[0], proxy.AllProxy): return self[0].unproxy(base) else: return [v.unproxy(base) for v in self] class SimObjectVector(VectorParamValue): # support clone operation def __call__(self, **kwargs): return SimObjectVector([v(**kwargs) for v in self]) def clear_parent(self, old_parent): for v in self: v.clear_parent(old_parent) def set_parent(self, parent, name): if len(self) == 1: self[0].set_parent(parent, name) else: width = int(math.ceil(math.log(len(self))/math.log(10))) for i,v in enumerate(self): v.set_parent(parent, "%s%0*d" % (name, width, i)) def has_parent(self): return reduce(lambda x,y: x and y, [v.has_parent() for v in self]) # return 'cpu0 cpu1' etc. for print_ini() def get_name(self): return ' '.join([v._name for v in self]) # By iterating through the constituent members of the vector here # we can nicely handle iterating over all a SimObject's children # without having to provide lots of special functions on # SimObjectVector directly. def descendants(self): for v in self: for obj in v.descendants(): yield obj 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 SimObjectVector(tmp_list) else: return VectorParamValue(tmp_list) def swig_module_name(self): return "%s_vector" % self.ptype_str def swig_predecls(self, code): code('%import "${{self.swig_module_name()}}.i"') def swig_decl(self, code): code('%module(package="m5.internal") ${{self.swig_module_name()}}') code('%{') self.ptype.cxx_predecls(code) code('%}') code() self.ptype.swig_predecls(code) code() code('%include "std_vector.i"') code() ptype = self.ptype_str cxx_type = self.ptype.cxx_type code('''\ %typemap(in) std::vector< $cxx_type >::value_type { if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) { if (SWIG_ConvertPtr($$input, (void **)&$$1, $$descriptor($cxx_type), 0) == -1) { return NULL; } } } %typemap(in) std::vector< $cxx_type >::value_type * { if (SWIG_ConvertPtr($$input, (void **)&$$1, $$1_descriptor, 0) == -1) { if (SWIG_ConvertPtr($$input, (void **)&$$1, $$descriptor($cxx_type *), 0) == -1) { return NULL; } } } ''') code('%template(vector_$ptype) std::vector< $cxx_type >;') def cxx_predecls(self, code): code('#include ') self.ptype.cxx_predecls(code) def cxx_decl(self, code): code('std::vector< ${{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' @classmethod def cxx_predecls(self, code): code('#include ') @classmethod def swig_predecls(cls, code): code('%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 (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() @classmethod def cxx_predecls(cls, code): # most derived types require this, so we just do it here once code('#include "base/types.hh"') @classmethod def swig_predecls(cls, code): # most derived types require this, so we just do it here once code('%import "stdint.i"') code('%import "base/types.hh"') 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 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) @classmethod def cxx_predecls(cls, code): cls.type.cxx_predecls(code) code('#include "base/range.hh"') @classmethod def swig_predecls(cls, code): cls.type.swig_predecls(code) code('%import "python/swig/range.i"') class AddrRange(Range): type = Addr def getValue(self): from m5.internal.range import AddrRange value = AddrRange() value.start = long(self.first) value.end = long(self.second) return value class TickRange(Range): type = Tick def getValue(self): from m5.internal.range 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' @classmethod def cxx_predecls(cls, code): code('#include "base/inet.hh"') @classmethod def swig_predecls(cls, code): code('%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) <= 0xff: 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.internal.params import EthAddr return EthAddr(self.value) def ini_str(self): return self.value # When initializing an IpAddress, pass in an existing IpAddress, a string of # the form "a.b.c.d", or an integer representing an IP. class IpAddress(ParamValue): cxx_type = 'Net::IpAddress' @classmethod def cxx_predecls(cls, code): code('#include "base/inet.hh"') @classmethod def swig_predecls(cls, code): code('%include "python/swig/inet.i"') def __init__(self, value): if isinstance(value, IpAddress): self.ip = value.ip else: try: self.ip = convert.toIpAddress(value) except TypeError: self.ip = long(value) self.verifyIp() def __str__(self): tup = [(self.ip >> i) & 0xff for i in (24, 16, 8, 0)] return '%d.%d.%d.%d' % tuple(tup) def __eq__(self, other): if isinstance(other, IpAddress): return self.ip == other.ip elif isinstance(other, str): try: return self.ip == convert.toIpAddress(other) except: return False else: return self.ip == other def __ne__(self, other): return not (self == other) def verifyIp(self): if self.ip < 0 or self.ip >= (1 << 32): raise TypeError, "invalid ip address %#08x" % self.ip def getValue(self): from m5.internal.params import IpAddress return IpAddress(self.ip) # When initializing an IpNetmask, pass in an existing IpNetmask, a string of # the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as # positional or keyword arguments. class IpNetmask(IpAddress): cxx_type = 'Net::IpNetmask' @classmethod def cxx_predecls(cls, code): code('#include "base/inet.hh"') @classmethod def swig_predecls(cls, code): code('%include "python/swig/inet.i"') def __init__(self, *args, **kwargs): def handle_kwarg(self, kwargs, key, elseVal = None): if key in kwargs: setattr(self, key, kwargs.pop(key)) elif elseVal: setattr(self, key, elseVal) else: raise TypeError, "No value set for %s" % key if len(args) == 0: handle_kwarg(self, kwargs, 'ip') handle_kwarg(self, kwargs, 'netmask') elif len(args) == 1: if kwargs: if not 'ip' in kwargs and not 'netmask' in kwargs: raise TypeError, "Invalid arguments" handle_kwarg(self, kwargs, 'ip', args[0]) handle_kwarg(self, kwargs, 'netmask', args[0]) elif isinstance(args[0], IpNetmask): self.ip = args[0].ip self.netmask = args[0].netmask else: (self.ip, self.netmask) = convert.toIpNetmask(args[0]) elif len(args) == 2: self.ip = args[0] self.netmask = args[1] else: raise TypeError, "Too many arguments specified" if kwargs: raise TypeError, "Too many keywords: %s" % kwargs.keys() self.verify() def __str__(self): return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask) def __eq__(self, other): if isinstance(other, IpNetmask): return self.ip == other.ip and self.netmask == other.netmask elif isinstance(other, str): try: return (self.ip, self.netmask) == convert.toIpNetmask(other) except: return False else: return False def verify(self): self.verifyIp() if self.netmask < 0 or self.netmask > 32: raise TypeError, "invalid netmask %d" % netmask def getValue(self): from m5.internal.params import IpNetmask return IpNetmask(self.ip, self.netmask) # When initializing an IpWithPort, pass in an existing IpWithPort, a string of # the form "a.b.c.d:p", or an ip and port as positional or keyword arguments. class IpWithPort(IpAddress): cxx_type = 'Net::IpWithPort' @classmethod def cxx_predecls(cls, code): code('#include "base/inet.hh"') @classmethod def swig_predecls(cls, code): code('%include "python/swig/inet.i"') def __init__(self, *args, **kwargs): def handle_kwarg(self, kwargs, key, elseVal = None): if key in kwargs: setattr(self, key, kwargs.pop(key)) elif elseVal: setattr(self, key, elseVal) else: raise TypeError, "No value set for %s" % key if len(args) == 0: handle_kwarg(self, kwargs, 'ip') handle_kwarg(self, kwargs, 'port') elif len(args) == 1: if kwargs: if not 'ip' in kwargs and not 'port' in kwargs: raise TypeError, "Invalid arguments" handle_kwarg(self, kwargs, 'ip', args[0]) handle_kwarg(self, kwargs, 'port', args[0]) elif isinstance(args[0], IpWithPort): self.ip = args[0].ip self.port = args[0].port else: (self.ip, self.port) = convert.toIpWithPort(args[0]) elif len(args) == 2: self.ip = args[0] self.port = args[1] else: raise TypeError, "Too many arguments specified" if kwargs: raise TypeError, "Too many keywords: %s" % kwargs.keys() self.verify() def __str__(self): return "%s:%d" % (super(IpWithPort, self).__str__(), self.port) def __eq__(self, other): if isinstance(other, IpWithPort): return self.ip == other.ip and self.port == other.port elif isinstance(other, str): try: return (self.ip, self.port) == convert.toIpWithPort(other) except: return False else: return False def verify(self): self.verifyIp() if self.port < 0 or self.port > 0xffff: raise TypeError, "invalid port %d" % self.port def getValue(self): from m5.internal.params import IpWithPort return IpWithPort(self.ip, self.port) 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' @classmethod def cxx_predecls(cls, code): code('#include ') @classmethod def swig_predecls(cls, code): code('%include "python/swig/time.i"') def __init__(self, value): self.value = parse_time(value) def getValue(self): from m5.internal.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) # 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): name = cls.__name__ code('''\ #ifndef __ENUM__${name}__ #define __ENUM__${name}__ namespace Enums { enum $name { ''') code.indent(2) for val in cls.vals: code('$val = ${{cls.map[val]}},') code('Num_$name = ${{len(cls.vals)}},') code.dedent(2) code('''\ }; extern const char *${name}Strings[Num_${name}]; } #endif // __ENUM__${name}__ ''') def cxx_def(cls, code): name = cls.__name__ code('''\ #include "enums/$name.hh" namespace Enums { const char *${name}Strings[Num_${name}] = { ''') code.indent(2) for val in cls.vals: code('"$val",') code.dedent(2) code(''' }; } // namespace Enums ''') def swig_decl(cls, code): name = cls.__name__ code('''\ %module(package="m5.internal") enum_$name %{ #include "enums/$name.hh" %} %include "enums/$name.hh" ''') # 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 @classmethod def cxx_predecls(cls, code): code('#include "enums/$0.hh"', cls.__name__) @classmethod def swig_predecls(cls, code): code('%import "python/m5/internal/enum_$0.i"', cls.__name__) 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' @classmethod def cxx_predecls(cls, code): code('#include "base/types.hh"') @classmethod def swig_predecls(cls, code): code('%import "stdint.i"') code('%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' @classmethod def cxx_predecls(cls, code): code('#include "base/types.hh"') @classmethod def swig_predecls(cls, code): code('%import "stdint.i"') code('%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): # convert to bytes per second 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.internal.pyobject import connectPorts if self.ccConnected: # already done this return peer = self.peer if not self.peer: # nothing to connect to return try: connectPorts(self.simobj.getCCObject(), self.name, self.index, peer.simobj.getCCObject(), peer.name, peer.index) except: print "Error connecting port %s.%s to %s.%s" % \ (self.simobj.path(), self.name, peer.simobj.path(), peer.name) raise 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 baseEnums = allEnums.copy() baseParams = allParams.copy() def clear(): global allEnums, allParams allEnums = baseEnums.copy() allParams = baseParams.copy() __all__ = ['Param', 'VectorParam', 'Enum', 'Bool', 'String', 'Float', 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64', 'Counter', 'Addr', 'Tick', 'Percent', 'TcpPort', 'UdpPort', 'EthernetAddr', 'IpAddress', 'IpNetmask', 'IpWithPort', 'MemorySize', 'MemorySize32', 'Latency', 'Frequency', 'Clock', 'NetworkBandwidth', 'MemoryBandwidth', 'Range', 'AddrRange', 'TickRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'Time', 'NextEthernetAddr', 'NULL', 'Port', 'VectorPort'] import SimObject