params.py revision 3105
12SN/A# Copyright (c) 2004-2006 The Regents of The University of Michigan
21762SN/A# All rights reserved.
37534Ssteve.reinhardt@amd.com#
42SN/A# Redistribution and use in source and binary forms, with or without
52SN/A# modification, are permitted provided that the following conditions are
62SN/A# met: redistributions of source code must retain the above copyright
72SN/A# notice, this list of conditions and the following disclaimer;
82SN/A# redistributions in binary form must reproduce the above copyright
92SN/A# notice, this list of conditions and the following disclaimer in the
102SN/A# documentation and/or other materials provided with the distribution;
112SN/A# neither the name of the copyright holders nor the names of its
122SN/A# contributors may be used to endorse or promote products derived from
132SN/A# this software without specific prior written permission.
142SN/A#
152SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
162SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
172SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
182SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
192SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
202SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
212SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
222SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
232SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
242SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
252SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
262SN/A#
272SN/A# Authors: Steve Reinhardt
282665Ssaidi@eecs.umich.edu#          Nathan Binkert
292665Ssaidi@eecs.umich.edu
302665Ssaidi@eecs.umich.edu#####################################################################
312SN/A#
322SN/A# Parameter description classes
332SN/A#
342SN/A# The _params dictionary in each class maps parameter names to either
352SN/A# a Param or a VectorParam object.  These objects contain the
362SN/A# parameter description string, the parameter type, and the default
372SN/A# value (if any).  The convert() method on these objects is used to
382SN/A# force whatever value is assigned to the parameter to the appropriate
392SN/A# type.
405491Sgblack@eecs.umich.edu#
415491Sgblack@eecs.umich.edu# Note that the default values are loaded into the class's attribute
422SN/A# space when the parameter dictionary is initialized (in
435491Sgblack@eecs.umich.edu# MetaSimObject._new_param()); after that point they aren't used.
442SN/A#
452SN/A#####################################################################
468737Skoansin.tan@gmail.com
474762Snate@binkert.orgimport sys, inspect, copy
489342SAndreas.Sandberg@arm.comimport convert
499356Snilay@cs.wisc.edufrom util import *
5056SN/A
512SN/A# Dummy base class to identify types that are legitimate for SimObject
522797Sktlim@umich.edu# parameters.
532797Sktlim@umich.educlass ParamValue(object):
5410023Smatt.horsnell@ARM.com
559196SAndreas.Sandberg@arm.com    cxx_predecls = []
562SN/A    swig_predecls = []
572SN/A
582SN/A    # default for printing to .ini file is regular string conversion.
599196SAndreas.Sandberg@arm.com    # will be overridden in some cases
609196SAndreas.Sandberg@arm.com    def ini_str(self):
619196SAndreas.Sandberg@arm.com        return str(self)
629196SAndreas.Sandberg@arm.com
639196SAndreas.Sandberg@arm.com    # allows us to blithely call unproxy() on things without checking
649196SAndreas.Sandberg@arm.com    # if they're really proxies or not
659196SAndreas.Sandberg@arm.com    def unproxy(self, base):
669196SAndreas.Sandberg@arm.com        return self
679196SAndreas.Sandberg@arm.com
689196SAndreas.Sandberg@arm.com# Regular parameter description.
699196SAndreas.Sandberg@arm.comclass ParamDesc(object):
709196SAndreas.Sandberg@arm.com    def __init__(self, ptype_str, ptype, *args, **kwargs):
719196SAndreas.Sandberg@arm.com        self.ptype_str = ptype_str
729196SAndreas.Sandberg@arm.com        # remember ptype only if it is provided
739196SAndreas.Sandberg@arm.com        if ptype != None:
749196SAndreas.Sandberg@arm.com            self.ptype = ptype
759196SAndreas.Sandberg@arm.com
769342SAndreas.Sandberg@arm.com        if args:
779196SAndreas.Sandberg@arm.com            if len(args) == 1:
789196SAndreas.Sandberg@arm.com                self.desc = args[0]
799196SAndreas.Sandberg@arm.com            elif len(args) == 2:
809196SAndreas.Sandberg@arm.com                self.default = args[0]
819196SAndreas.Sandberg@arm.com                self.desc = args[1]
829196SAndreas.Sandberg@arm.com            else:
839196SAndreas.Sandberg@arm.com                raise TypeError, 'too many arguments'
842SN/A
859342SAndreas.Sandberg@arm.com        if kwargs.has_key('desc'):
862SN/A            assert(not hasattr(self, 'desc'))
872SN/A            self.desc = kwargs['desc']
882SN/A            del kwargs['desc']
892SN/A
909196SAndreas.Sandberg@arm.com        if kwargs.has_key('default'):
912SN/A            assert(not hasattr(self, 'default'))
922SN/A            self.default = kwargs['default']
9310023Smatt.horsnell@ARM.com            del kwargs['default']
9410023Smatt.horsnell@ARM.com
9510023Smatt.horsnell@ARM.com        if kwargs:
964762Snate@binkert.org            raise TypeError, 'extra unknown kwargs %s' % kwargs
979196SAndreas.Sandberg@arm.com
984762Snate@binkert.org        if not hasattr(self, 'desc'):
994762Snate@binkert.org            raise TypeError, 'desc attribute missing'
1002SN/A
1014762Snate@binkert.org    def __getattr__(self, attr):
1024762Snate@binkert.org        if attr == 'ptype':
1034762Snate@binkert.org            try:
10410422Sandreas.hansson@arm.com                ptype = eval(self.ptype_str, objects.__dict__)
1052SN/A                if not isinstance(ptype, type):
1065034Smilesck@eecs.umich.edu                    raise NameError
1075034Smilesck@eecs.umich.edu                self.ptype = ptype
1081553SN/A                return ptype
109265SN/A            except NameError:
1107532Ssteve.reinhardt@amd.com                raise TypeError, \
1117532Ssteve.reinhardt@amd.com                      "Param qualifier '%s' is not a type" % self.ptype_str
1127532Ssteve.reinhardt@amd.com        raise AttributeError, "'%s' object has no attribute '%s'" % \
1137532Ssteve.reinhardt@amd.com              (type(self).__name__, attr)
1147532Ssteve.reinhardt@amd.com
1157532Ssteve.reinhardt@amd.com    def convert(self, value):
116465SN/A        if isinstance(value, proxy.BaseProxy):
117465SN/A            value.set_param_desc(self)
1187532Ssteve.reinhardt@amd.com            return value
1197532Ssteve.reinhardt@amd.com        if not hasattr(self, 'ptype') and isNullPointer(value):
1207532Ssteve.reinhardt@amd.com            # deferred evaluation of SimObject; continue to defer if
1217532Ssteve.reinhardt@amd.com            # we're just assigning a null pointer
1227532Ssteve.reinhardt@amd.com            return value
1237532Ssteve.reinhardt@amd.com        if isinstance(value, self.ptype):
1247532Ssteve.reinhardt@amd.com            return value
1257532Ssteve.reinhardt@amd.com        if isNullPointer(value) and isSimObjectClass(self.ptype):
1269196SAndreas.Sandberg@arm.com            return value
1279196SAndreas.Sandberg@arm.com        return self.ptype(value)
1287532Ssteve.reinhardt@amd.com
12910905Sandreas.sandberg@arm.com    def cxx_predecls(self):
1307532Ssteve.reinhardt@amd.com        return self.ptype.cxx_predecls
1317532Ssteve.reinhardt@amd.com
1327532Ssteve.reinhardt@amd.com    def swig_predecls(self):
1337532Ssteve.reinhardt@amd.com        return self.ptype.swig_predecls
1347532Ssteve.reinhardt@amd.com
1357532Ssteve.reinhardt@amd.com    def cxx_decl(self):
1367532Ssteve.reinhardt@amd.com        return '%s %s;' % (self.ptype.cxx_type, self.name)
1377532Ssteve.reinhardt@amd.com
1389196SAndreas.Sandberg@arm.com# Vector-valued parameter description.  Just like ParamDesc, except
1399196SAndreas.Sandberg@arm.com# that the value is a vector (list) of the specified type instead of a
1409196SAndreas.Sandberg@arm.com# single value.
1412SN/A
1429196SAndreas.Sandberg@arm.comclass VectorParamValue(list):
1439196SAndreas.Sandberg@arm.com    def ini_str(self):
1449196SAndreas.Sandberg@arm.com        return ' '.join([v.ini_str() for v in self])
1459196SAndreas.Sandberg@arm.com
146330SN/A    def unproxy(self, base):
1472SN/A        return [v.unproxy(base) for v in self]
1487532Ssteve.reinhardt@amd.com
14910023Smatt.horsnell@ARM.comclass SimObjVector(VectorParamValue):
15010023Smatt.horsnell@ARM.com    def print_ini(self):
15110023Smatt.horsnell@ARM.com        for v in self:
15210023Smatt.horsnell@ARM.com            v.print_ini()
15310023Smatt.horsnell@ARM.com
15410023Smatt.horsnell@ARM.comclass VectorParamDesc(ParamDesc):
15510023Smatt.horsnell@ARM.com    # Convert assigned value to appropriate type.  If the RHS is not a
15610023Smatt.horsnell@ARM.com    # list or tuple, it generates a single-element list.
15710023Smatt.horsnell@ARM.com    def convert(self, value):
15810023Smatt.horsnell@ARM.com        if isinstance(value, (list, tuple)):
15910023Smatt.horsnell@ARM.com            # list: coerce each element into new list
16010023Smatt.horsnell@ARM.com            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
16110023Smatt.horsnell@ARM.com            if isSimObjectSequence(tmp_list):
16210023Smatt.horsnell@ARM.com                return SimObjVector(tmp_list)
16310023Smatt.horsnell@ARM.com            else:
1647532Ssteve.reinhardt@amd.com                return VectorParamValue(tmp_list)
1657532Ssteve.reinhardt@amd.com        else:
1667823Ssteve.reinhardt@amd.com            # singleton: leave it be (could coerce to a single-element
1677532Ssteve.reinhardt@amd.com            # list here, but for some historical reason we don't...
1687532Ssteve.reinhardt@amd.com            return ParamDesc.convert(self, value)
1697492Ssteve.reinhardt@amd.com
170330SN/A    def cxx_predecls(self):
1719196SAndreas.Sandberg@arm.com        return ['#include <vector>'] + self.ptype.cxx_predecls
1729342SAndreas.Sandberg@arm.com
1739342SAndreas.Sandberg@arm.com    def swig_predecls(self):
1749342SAndreas.Sandberg@arm.com        return ['%include "std_vector.i"'] + self.ptype.swig_predecls
1759342SAndreas.Sandberg@arm.com
1769342SAndreas.Sandberg@arm.com    def cxx_decl(self):
1779342SAndreas.Sandberg@arm.com        return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
17810905Sandreas.sandberg@arm.com
17910905Sandreas.sandberg@arm.comclass ParamFactory(object):
18010905Sandreas.sandberg@arm.com    def __init__(self, param_desc_class, ptype_str = None):
18110905Sandreas.sandberg@arm.com        self.param_desc_class = param_desc_class
1829342SAndreas.Sandberg@arm.com        self.ptype_str = ptype_str
1839196SAndreas.Sandberg@arm.com
1849196SAndreas.Sandberg@arm.com    def __getattr__(self, attr):
18510905Sandreas.sandberg@arm.com        if self.ptype_str:
186938SN/A            attr = self.ptype_str + '.' + attr
1871031SN/A        return ParamFactory(self.param_desc_class, attr)
1881031SN/A
1891031SN/A    # E.g., Param.Int(5, "number of widgets")
1901031SN/A    def __call__(self, *args, **kwargs):
1911031SN/A        caller_frame = inspect.currentframe().f_back
1921031SN/A        ptype = None
1935314Sstever@gmail.com        try:
1945314Sstever@gmail.com            ptype = eval(self.ptype_str,
1955315Sstever@gmail.com                         caller_frame.f_globals, caller_frame.f_locals)
1965314Sstever@gmail.com            if not isinstance(ptype, type):
1975314Sstever@gmail.com                raise TypeError, \
1985314Sstever@gmail.com                      "Param qualifier is not a type: %s" % ptype
1992SN/A        except NameError:
2002SN/A            # if name isn't defined yet, assume it's a SimObject, and
2019554Sandreas.hansson@arm.com            # try to resolve it later
2029554Sandreas.hansson@arm.com            pass
2039554Sandreas.hansson@arm.com        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
2049554Sandreas.hansson@arm.com
2052SN/AParam = ParamFactory(ParamDesc)
206VectorParam = ParamFactory(VectorParamDesc)
207
208#####################################################################
209#
210# Parameter Types
211#
212# Though native Python types could be used to specify parameter types
213# (the 'ptype' field of the Param and VectorParam classes), it's more
214# flexible to define our own set of types.  This gives us more control
215# over how Python expressions are converted to values (via the
216# __init__() constructor) and how these values are printed out (via
217# the __str__() conversion method).
218#
219#####################################################################
220
221# String-valued parameter.  Just mixin the ParamValue class with the
222# built-in str class.
223class String(ParamValue,str):
224    cxx_type = 'std::string'
225    cxx_predecls = ['#include <string>']
226    swig_predecls = ['%include "std_string.i"\n' +
227                     '%apply const std::string& {std::string *};']
228    pass
229
230# superclass for "numeric" parameter values, to emulate math
231# operations in a type-safe way.  e.g., a Latency times an int returns
232# a new Latency object.
233class NumericParamValue(ParamValue):
234    def __str__(self):
235        return str(self.value)
236
237    def __float__(self):
238        return float(self.value)
239
240    # hook for bounds checking
241    def _check(self):
242        return
243
244    def __mul__(self, other):
245        newobj = self.__class__(self)
246        newobj.value *= other
247        newobj._check()
248        return newobj
249
250    __rmul__ = __mul__
251
252    def __div__(self, other):
253        newobj = self.__class__(self)
254        newobj.value /= other
255        newobj._check()
256        return newobj
257
258    def __sub__(self, other):
259        newobj = self.__class__(self)
260        newobj.value -= other
261        newobj._check()
262        return newobj
263
264# Metaclass for bounds-checked integer parameters.  See CheckedInt.
265class CheckedIntType(type):
266    def __init__(cls, name, bases, dict):
267        super(CheckedIntType, cls).__init__(name, bases, dict)
268
269        # CheckedInt is an abstract base class, so we actually don't
270        # want to do any processing on it... the rest of this code is
271        # just for classes that derive from CheckedInt.
272        if name == 'CheckedInt':
273            return
274
275        if not cls.cxx_predecls:
276            # most derived types require this, so we just do it here once
277            cls.cxx_predecls = ['#include "sim/host.hh"']
278
279        if not cls.swig_predecls:
280            # most derived types require this, so we just do it here once
281            cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
282                                 '%import "sim/host.hh"']
283
284        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
285            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
286                panic("CheckedInt subclass %s must define either\n" \
287                      "    'min' and 'max' or 'size' and 'unsigned'\n" \
288                      % name);
289            if cls.unsigned:
290                cls.min = 0
291                cls.max = 2 ** cls.size - 1
292            else:
293                cls.min = -(2 ** (cls.size - 1))
294                cls.max = (2 ** (cls.size - 1)) - 1
295
296# Abstract superclass for bounds-checked integer parameters.  This
297# class is subclassed to generate parameter classes with specific
298# bounds.  Initialization of the min and max bounds is done in the
299# metaclass CheckedIntType.__init__.
300class CheckedInt(NumericParamValue):
301    __metaclass__ = CheckedIntType
302
303    def _check(self):
304        if not self.min <= self.value <= self.max:
305            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
306                  (self.min, self.value, self.max)
307
308    def __init__(self, value):
309        if isinstance(value, str):
310            self.value = convert.toInteger(value)
311        elif isinstance(value, (int, long, float)):
312            self.value = long(value)
313        self._check()
314
315class Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
316class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
317
318class Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
319class UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
320class Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
321class UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
322class Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
323class UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
324class Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
325class UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
326
327class Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
328class Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
329class TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
330class UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
331
332class Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
333
334class Float(ParamValue, float):
335    pass
336
337class MemorySize(CheckedInt):
338    cxx_type = 'uint64_t'
339    size = 64
340    unsigned = True
341    def __init__(self, value):
342        if isinstance(value, MemorySize):
343            self.value = value.value
344        else:
345            self.value = convert.toMemorySize(value)
346        self._check()
347
348class MemorySize32(CheckedInt):
349    size = 32
350    unsigned = True
351    def __init__(self, value):
352        if isinstance(value, MemorySize):
353            self.value = value.value
354        else:
355            self.value = convert.toMemorySize(value)
356        self._check()
357
358class Addr(CheckedInt):
359    cxx_type = 'Addr'
360    cxx_predecls = ['#include "targetarch/isa_traits.hh"']
361    size = 64
362    unsigned = True
363    def __init__(self, value):
364        if isinstance(value, Addr):
365            self.value = value.value
366        else:
367            try:
368                self.value = convert.toMemorySize(value)
369            except TypeError:
370                self.value = long(value)
371        self._check()
372
373
374class MetaRange(type):
375    def __init__(cls, name, bases, dict):
376        super(MetaRange, cls).__init__(name, bases, dict)
377        if name == 'Range':
378            return
379        cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
380        cls.cxx_predecls = \
381                       ['#include "base/range.hh"'] + cls.type.cxx_predecls
382
383class Range(ParamValue):
384    __metaclass__ = MetaRange
385    type = Int # default; can be overridden in subclasses
386    def __init__(self, *args, **kwargs):
387        def handle_kwargs(self, kwargs):
388            if 'end' in kwargs:
389                self.second = self.type(kwargs.pop('end'))
390            elif 'size' in kwargs:
391                self.second = self.first + self.type(kwargs.pop('size')) - 1
392            else:
393                raise TypeError, "Either end or size must be specified"
394
395        if len(args) == 0:
396            self.first = self.type(kwargs.pop('start'))
397            handle_kwargs(self, kwargs)
398
399        elif len(args) == 1:
400            if kwargs:
401                self.first = self.type(args[0])
402                handle_kwargs(self, kwargs)
403            elif isinstance(args[0], Range):
404                self.first = self.type(args[0].first)
405                self.second = self.type(args[0].second)
406            else:
407                self.first = self.type(0)
408                self.second = self.type(args[0]) - 1
409
410        elif len(args) == 2:
411            self.first = self.type(args[0])
412            self.second = self.type(args[1])
413        else:
414            raise TypeError, "Too many arguments specified"
415
416        if kwargs:
417            raise TypeError, "too many keywords: %s" % kwargs.keys()
418
419    def __str__(self):
420        return '%s:%s' % (self.first, self.second)
421
422class AddrRange(Range):
423    type = Addr
424
425class TickRange(Range):
426    type = Tick
427
428# Boolean parameter type.  Python doesn't let you subclass bool, since
429# it doesn't want to let you create multiple instances of True and
430# False.  Thus this is a little more complicated than String.
431class Bool(ParamValue):
432    cxx_type = 'bool'
433    def __init__(self, value):
434        try:
435            self.value = convert.toBool(value)
436        except TypeError:
437            self.value = bool(value)
438
439    def __str__(self):
440        return str(self.value)
441
442    def ini_str(self):
443        if self.value:
444            return 'true'
445        return 'false'
446
447def IncEthernetAddr(addr, val = 1):
448    bytes = map(lambda x: int(x, 16), addr.split(':'))
449    bytes[5] += val
450    for i in (5, 4, 3, 2, 1):
451        val,rem = divmod(bytes[i], 256)
452        bytes[i] = rem
453        if val == 0:
454            break
455        bytes[i - 1] += val
456    assert(bytes[0] <= 255)
457    return ':'.join(map(lambda x: '%02x' % x, bytes))
458
459class NextEthernetAddr(object):
460    addr = "00:90:00:00:00:01"
461
462    def __init__(self, inc = 1):
463        self.value = NextEthernetAddr.addr
464        NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
465
466class EthernetAddr(ParamValue):
467    cxx_type = 'Net::EthAddr'
468    cxx_predecls = ['#include "base/inet.hh"']
469    swig_predecls = ['class Net::EthAddr;']
470    def __init__(self, value):
471        if value == NextEthernetAddr:
472            self.value = value
473            return
474
475        if not isinstance(value, str):
476            raise TypeError, "expected an ethernet address and didn't get one"
477
478        bytes = value.split(':')
479        if len(bytes) != 6:
480            raise TypeError, 'invalid ethernet address %s' % value
481
482        for byte in bytes:
483            if not 0 <= int(byte) <= 256:
484                raise TypeError, 'invalid ethernet address %s' % value
485
486        self.value = value
487
488    def unproxy(self, base):
489        if self.value == NextEthernetAddr:
490            self.addr = self.value().value
491        return self
492
493    def __str__(self):
494        if self.value == NextEthernetAddr:
495            if hasattr(self, 'addr'):
496                return self.addr
497            else:
498                return "NextEthernetAddr (unresolved)"
499        else:
500            return self.value
501
502# Enumerated types are a little more complex.  The user specifies the
503# type as Enum(foo) where foo is either a list or dictionary of
504# alternatives (typically strings, but not necessarily so).  (In the
505# long run, the integer value of the parameter will be the list index
506# or the corresponding dictionary value.  For now, since we only check
507# that the alternative is valid and then spit it into a .ini file,
508# there's not much point in using the dictionary.)
509
510# What Enum() must do is generate a new type encapsulating the
511# provided list/dictionary so that specific values of the parameter
512# can be instances of that type.  We define two hidden internal
513# classes (_ListEnum and _DictEnum) to serve as base classes, then
514# derive the new type from the appropriate base class on the fly.
515
516
517# Metaclass for Enum types
518class MetaEnum(type):
519    def __init__(cls, name, bases, init_dict):
520        if init_dict.has_key('map'):
521            if not isinstance(cls.map, dict):
522                raise TypeError, "Enum-derived class attribute 'map' " \
523                      "must be of type dict"
524            # build list of value strings from map
525            cls.vals = cls.map.keys()
526            cls.vals.sort()
527        elif init_dict.has_key('vals'):
528            if not isinstance(cls.vals, list):
529                raise TypeError, "Enum-derived class attribute 'vals' " \
530                      "must be of type list"
531            # build string->value map from vals sequence
532            cls.map = {}
533            for idx,val in enumerate(cls.vals):
534                cls.map[val] = idx
535        else:
536            raise TypeError, "Enum-derived class must define "\
537                  "attribute 'map' or 'vals'"
538
539        cls.cxx_type = name + '::Enum'
540
541        super(MetaEnum, cls).__init__(name, bases, init_dict)
542
543    # Generate C++ class declaration for this enum type.
544    # Note that we wrap the enum in a class/struct to act as a namespace,
545    # so that the enum strings can be brief w/o worrying about collisions.
546    def cxx_decl(cls):
547        s = 'struct %s {\n  enum Enum {\n    ' % cls.__name__
548        s += ',\n    '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
549        s += '\n  };\n};\n'
550        return s
551
552# Base class for enum types.
553class Enum(ParamValue):
554    __metaclass__ = MetaEnum
555    vals = []
556
557    def __init__(self, value):
558        if value not in self.map:
559            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
560                  % (value, self.vals)
561        self.value = value
562
563    def __str__(self):
564        return self.value
565
566ticks_per_sec = None
567
568# how big does a rounding error need to be before we warn about it?
569frequency_tolerance = 0.001  # 0.1%
570
571# convert a floting-point # of ticks to integer, and warn if rounding
572# discards too much precision
573def tick_check(float_ticks):
574    if float_ticks == 0:
575        return 0
576    int_ticks = int(round(float_ticks))
577    err = (float_ticks - int_ticks) / float_ticks
578    if err > frequency_tolerance:
579        print >> sys.stderr, "Warning: rounding error > tolerance"
580        print >> sys.stderr, "    %f rounded to %d" % (float_ticks, int_ticks)
581        #raise ValueError
582    return int_ticks
583
584def getLatency(value):
585    if isinstance(value, Latency) or isinstance(value, Clock):
586        return value.value
587    elif isinstance(value, Frequency) or isinstance(value, RootClock):
588        return 1 / value.value
589    elif isinstance(value, str):
590        try:
591            return convert.toLatency(value)
592        except ValueError:
593            try:
594                return 1 / convert.toFrequency(value)
595            except ValueError:
596                pass # fall through
597    raise ValueError, "Invalid Frequency/Latency value '%s'" % value
598
599
600class Latency(NumericParamValue):
601    cxx_type = 'Tick'
602    cxx_predecls = ['#include "sim/host.hh"']
603    swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
604                     '%import "sim/host.hh"']
605    def __init__(self, value):
606        self.value = getLatency(value)
607
608    def __getattr__(self, attr):
609        if attr in ('latency', 'period'):
610            return self
611        if attr == 'frequency':
612            return Frequency(self)
613        raise AttributeError, "Latency object has no attribute '%s'" % attr
614
615    # convert latency to ticks
616    def ini_str(self):
617        return str(tick_check(self.value * ticks_per_sec))
618
619class Frequency(NumericParamValue):
620    cxx_type = 'Tick'
621    cxx_predecls = ['#include "sim/host.hh"']
622    swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
623                     '%import "sim/host.hh"']
624    def __init__(self, value):
625        self.value = 1 / getLatency(value)
626
627    def __getattr__(self, attr):
628        if attr == 'frequency':
629            return self
630        if attr in ('latency', 'period'):
631            return Latency(self)
632        raise AttributeError, "Frequency object has no attribute '%s'" % attr
633
634    # convert frequency to ticks per period
635    def ini_str(self):
636        return self.period.ini_str()
637
638# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
639# We can't inherit from Frequency because we don't want it to be directly
640# assignable to a regular Frequency parameter.
641class RootClock(ParamValue):
642    cxx_type = 'Tick'
643    cxx_predecls = ['#include "sim/host.hh"']
644    swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
645                     '%import "sim/host.hh"']
646    def __init__(self, value):
647        self.value = 1 / getLatency(value)
648
649    def __getattr__(self, attr):
650        if attr == 'frequency':
651            return Frequency(self)
652        if attr in ('latency', 'period'):
653            return Latency(self)
654        raise AttributeError, "Frequency object has no attribute '%s'" % attr
655
656    def ini_str(self):
657        return str(tick_check(self.value))
658
659# A generic frequency and/or Latency value.  Value is stored as a latency,
660# but to avoid ambiguity this object does not support numeric ops (* or /).
661# An explicit conversion to a Latency or Frequency must be made first.
662class Clock(ParamValue):
663    cxx_type = 'Tick'
664    cxx_predecls = ['#include "sim/host.hh"']
665    swig_predecls = ['%import "python/m5/swig/stdint.i"\n' +
666                     '%import "sim/host.hh"']
667    def __init__(self, value):
668        self.value = getLatency(value)
669
670    def __getattr__(self, attr):
671        if attr == 'frequency':
672            return Frequency(self)
673        if attr in ('latency', 'period'):
674            return Latency(self)
675        raise AttributeError, "Frequency object has no attribute '%s'" % attr
676
677    def ini_str(self):
678        return self.period.ini_str()
679
680class NetworkBandwidth(float,ParamValue):
681    cxx_type = 'float'
682    def __new__(cls, value):
683        val = convert.toNetworkBandwidth(value) / 8.0
684        return super(cls, NetworkBandwidth).__new__(cls, val)
685
686    def __str__(self):
687        return str(self.val)
688
689    def ini_str(self):
690        return '%f' % (ticks_per_sec / float(self))
691
692class MemoryBandwidth(float,ParamValue):
693    cxx_type = 'float'
694    def __new__(self, value):
695        val = convert.toMemoryBandwidth(value)
696        return super(cls, MemoryBandwidth).__new__(cls, val)
697
698    def __str__(self):
699        return str(self.val)
700
701    def ini_str(self):
702        return '%f' % (ticks_per_sec / float(self))
703
704#
705# "Constants"... handy aliases for various values.
706#
707
708# Special class for NULL pointers.  Note the special check in
709# make_param_value() above that lets these be assigned where a
710# SimObject is required.
711# only one copy of a particular node
712class NullSimObject(object):
713    __metaclass__ = Singleton
714
715    def __call__(cls):
716        return cls
717
718    def _instantiate(self, parent = None, path = ''):
719        pass
720
721    def ini_str(self):
722        return 'Null'
723
724    def unproxy(self, base):
725        return self
726
727    def set_path(self, parent, name):
728        pass
729    def __str__(self):
730        return 'Null'
731
732# The only instance you'll ever need...
733NULL = NullSimObject()
734
735def isNullPointer(value):
736    return isinstance(value, NullSimObject)
737
738# Some memory range specifications use this as a default upper bound.
739MaxAddr = Addr.max
740MaxTick = Tick.max
741AllMemory = AddrRange(0, MaxAddr)
742
743
744#####################################################################
745#
746# Port objects
747#
748# Ports are used to interconnect objects in the memory system.
749#
750#####################################################################
751
752# Port reference: encapsulates a reference to a particular port on a
753# particular SimObject.
754class PortRef(object):
755    def __init__(self, simobj, name):
756        assert(isSimObject(simobj) or isSimObjectClass(simobj))
757        self.simobj = simobj
758        self.name = name
759        self.peer = None   # not associated with another port yet
760        self.ccConnected = False # C++ port connection done?
761        self.index = -1  # always -1 for non-vector ports
762
763    def __str__(self):
764        return '%s.%s' % (self.simobj, self.name)
765
766    # for config.ini, print peer's name (not ours)
767    def ini_str(self):
768        return str(self.peer)
769
770    def __getattr__(self, attr):
771        if attr == 'peerObj':
772            # shorthand for proxies
773            return self.peer.simobj
774        raise AttributeError, "'%s' object has no attribute '%s'" % \
775              (self.__class__.__name__, attr)
776
777    # Full connection is symmetric (both ways).  Called via
778    # SimObject.__setattr__ as a result of a port assignment, e.g.,
779    # "obj1.portA = obj2.portB", or via VectorPortRef.__setitem__,
780    # e.g., "obj1.portA[3] = obj2.portB".
781    def connect(self, other):
782        if isinstance(other, VectorPortRef):
783            # reference to plain VectorPort is implicit append
784            other = other._get_next()
785        if not (isinstance(other, PortRef) or proxy.isproxy(other)):
786            raise TypeError, \
787                  "assigning non-port reference '%s' to port '%s'" \
788                  % (other, self)
789        if self.peer and not proxy.isproxy(self.peer):
790            print "warning: overwriting port", self, \
791                  "value", self.peer, "with", other
792        self.peer = other
793        assert(not isinstance(self.peer, VectorPortRef))
794        if isinstance(other, PortRef) and other.peer is not self:
795            other.connect(self)
796
797    def clone(self, simobj, memo):
798        if memo.has_key(self):
799            return memo[self]
800        newRef = copy.copy(self)
801        memo[self] = newRef
802        newRef.simobj = simobj
803        assert(isSimObject(newRef.simobj))
804        if self.peer and not proxy.isproxy(self.peer):
805            peerObj = memo[self.peer.simobj]
806            newRef.peer = self.peer.clone(peerObj, memo)
807            assert(not isinstance(newRef.peer, VectorPortRef))
808        return newRef
809
810    def unproxy(self, simobj):
811        assert(simobj is self.simobj)
812        if proxy.isproxy(self.peer):
813            try:
814                realPeer = self.peer.unproxy(self.simobj)
815            except:
816                print "Error in unproxying port '%s' of %s" % \
817                      (self.name, self.simobj.path())
818                raise
819            self.connect(realPeer)
820
821    # Call C++ to create corresponding port connection between C++ objects
822    def ccConnect(self):
823        if self.ccConnected: # already done this
824            return
825        peer = self.peer
826        cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index,
827                             peer.simobj.getCCObject(), peer.name, peer.index)
828        self.ccConnected = True
829        peer.ccConnected = True
830
831# A reference to an individual element of a VectorPort... much like a
832# PortRef, but has an index.
833class VectorPortElementRef(PortRef):
834    def __init__(self, simobj, name, index):
835        PortRef.__init__(self, simobj, name)
836        self.index = index
837
838    def __str__(self):
839        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
840
841# A reference to a complete vector-valued port (not just a single element).
842# Can be indexed to retrieve individual VectorPortElementRef instances.
843class VectorPortRef(object):
844    def __init__(self, simobj, name):
845        assert(isSimObject(simobj) or isSimObjectClass(simobj))
846        self.simobj = simobj
847        self.name = name
848        self.elements = []
849
850    # for config.ini, print peer's name (not ours)
851    def ini_str(self):
852        return ' '.join([el.ini_str() for el in self.elements])
853
854    def __getitem__(self, key):
855        if not isinstance(key, int):
856            raise TypeError, "VectorPort index must be integer"
857        if key >= len(self.elements):
858            # need to extend list
859            ext = [VectorPortElementRef(self.simobj, self.name, i)
860                   for i in range(len(self.elements), key+1)]
861            self.elements.extend(ext)
862        return self.elements[key]
863
864    def _get_next(self):
865        return self[len(self.elements)]
866
867    def __setitem__(self, key, value):
868        if not isinstance(key, int):
869            raise TypeError, "VectorPort index must be integer"
870        self[key].connect(value)
871
872    def connect(self, other):
873        # reference to plain VectorPort is implicit append
874        self._get_next().connect(other)
875
876    def unproxy(self, simobj):
877        [el.unproxy(simobj) for el in self.elements]
878
879    def ccConnect(self):
880        [el.ccConnect() for el in self.elements]
881
882# Port description object.  Like a ParamDesc object, this represents a
883# logical port in the SimObject class, not a particular port on a
884# SimObject instance.  The latter are represented by PortRef objects.
885class Port(object):
886    # Port("description") or Port(default, "description")
887    def __init__(self, *args):
888        if len(args) == 1:
889            self.desc = args[0]
890        elif len(args) == 2:
891            self.default = args[0]
892            self.desc = args[1]
893        else:
894            raise TypeError, 'wrong number of arguments'
895        # self.name is set by SimObject class on assignment
896        # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
897
898    # Generate a PortRef for this port on the given SimObject with the
899    # given name
900    def makeRef(self, simobj):
901        return PortRef(simobj, self.name)
902
903    # Connect an instance of this port (on the given SimObject with
904    # the given name) with the port described by the supplied PortRef
905    def connect(self, simobj, ref):
906        self.makeRef(simobj).connect(ref)
907
908# VectorPort description object.  Like Port, but represents a vector
909# of connections (e.g., as on a Bus).
910class VectorPort(Port):
911    def __init__(self, *args):
912        Port.__init__(self, *args)
913        self.isVec = True
914
915    def makeRef(self, simobj):
916        return VectorPortRef(simobj, self.name)
917
918
919
920__all__ = ['Param', 'VectorParam',
921           'Enum', 'Bool', 'String', 'Float',
922           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
923           'Int32', 'UInt32', 'Int64', 'UInt64',
924           'Counter', 'Addr', 'Tick', 'Percent',
925           'TcpPort', 'UdpPort', 'EthernetAddr',
926           'MemorySize', 'MemorySize32',
927           'Latency', 'Frequency', 'RootClock', 'Clock',
928           'NetworkBandwidth', 'MemoryBandwidth',
929           'Range', 'AddrRange', 'TickRange',
930           'MaxAddr', 'MaxTick', 'AllMemory',
931           'NextEthernetAddr', 'NULL',
932           'Port', 'VectorPort']
933
934# see comment on imports at end of __init__.py.
935from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass
936import proxy
937import objects
938import cc_main
939