params.py revision 5822:05ffa2c3c800
1# Copyright (c) 2004-2006 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Steve Reinhardt
28#          Nathan Binkert
29
30#####################################################################
31#
32# Parameter description classes
33#
34# The _params dictionary in each class maps parameter names to either
35# a Param or a VectorParam object.  These objects contain the
36# parameter description string, the parameter type, and the default
37# value (if any).  The convert() method on these objects is used to
38# force whatever value is assigned to the parameter to the appropriate
39# type.
40#
41# Note that the default values are loaded into the class's attribute
42# space when the parameter dictionary is initialized (in
43# MetaSimObject._new_param()); after that point they aren't used.
44#
45#####################################################################
46
47import copy
48import datetime
49import re
50import sys
51import time
52
53import convert
54import proxy
55import ticks
56from util import *
57
58import SimObject
59
60def isSimObject(*args, **kwargs):
61    return SimObject.isSimObject(*args, **kwargs)
62
63def isSimObjectSequence(*args, **kwargs):
64    return SimObject.isSimObjectSequence(*args, **kwargs)
65
66def isSimObjectClass(*args, **kwargs):
67    return SimObject.isSimObjectClass(*args, **kwargs)
68
69allParams = {}
70
71class MetaParamValue(type):
72    def __new__(mcls, name, bases, dct):
73        cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
74        assert name not in allParams
75        allParams[name] = cls
76        return cls
77
78
79# Dummy base class to identify types that are legitimate for SimObject
80# parameters.
81class ParamValue(object):
82    __metaclass__ = MetaParamValue
83
84    cxx_predecls = []
85    swig_predecls = []
86
87    # default for printing to .ini file is regular string conversion.
88    # will be overridden in some cases
89    def ini_str(self):
90        return str(self)
91
92    # allows us to blithely call unproxy() on things without checking
93    # if they're really proxies or not
94    def unproxy(self, base):
95        return self
96
97# Regular parameter description.
98class ParamDesc(object):
99    def __init__(self, ptype_str, ptype, *args, **kwargs):
100        self.ptype_str = ptype_str
101        # remember ptype only if it is provided
102        if ptype != None:
103            self.ptype = ptype
104
105        if args:
106            if len(args) == 1:
107                self.desc = args[0]
108            elif len(args) == 2:
109                self.default = args[0]
110                self.desc = args[1]
111            else:
112                raise TypeError, 'too many arguments'
113
114        if kwargs.has_key('desc'):
115            assert(not hasattr(self, 'desc'))
116            self.desc = kwargs['desc']
117            del kwargs['desc']
118
119        if kwargs.has_key('default'):
120            assert(not hasattr(self, 'default'))
121            self.default = kwargs['default']
122            del kwargs['default']
123
124        if kwargs:
125            raise TypeError, 'extra unknown kwargs %s' % kwargs
126
127        if not hasattr(self, 'desc'):
128            raise TypeError, 'desc attribute missing'
129
130    def __getattr__(self, attr):
131        if attr == 'ptype':
132            ptype = SimObject.allClasses[self.ptype_str]
133            assert issubclass(ptype, SimObject.SimObject)
134            self.ptype = ptype
135            return ptype
136
137        raise AttributeError, "'%s' object has no attribute '%s'" % \
138              (type(self).__name__, attr)
139
140    def convert(self, value):
141        if isinstance(value, proxy.BaseProxy):
142            value.set_param_desc(self)
143            return value
144        if not hasattr(self, 'ptype') and isNullPointer(value):
145            # deferred evaluation of SimObject; continue to defer if
146            # we're just assigning a null pointer
147            return value
148        if isinstance(value, self.ptype):
149            return value
150        if isNullPointer(value) and isSimObjectClass(self.ptype):
151            return value
152        return self.ptype(value)
153
154    def cxx_predecls(self):
155        return self.ptype.cxx_predecls
156
157    def swig_predecls(self):
158        return self.ptype.swig_predecls
159
160    def cxx_decl(self):
161        return '%s %s;' % (self.ptype.cxx_type, self.name)
162
163# Vector-valued parameter description.  Just like ParamDesc, except
164# that the value is a vector (list) of the specified type instead of a
165# single value.
166
167class VectorParamValue(list):
168    __metaclass__ = MetaParamValue
169    def __setattr__(self, attr, value):
170        raise AttributeError, \
171              "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
172
173    def ini_str(self):
174        return ' '.join([v.ini_str() for v in self])
175
176    def getValue(self):
177        return [ v.getValue() for v in self ]
178
179    def unproxy(self, base):
180        return [v.unproxy(base) for v in self]
181
182class SimObjVector(VectorParamValue):
183    def print_ini(self, ini_file):
184        for v in self:
185            v.print_ini(ini_file)
186
187class VectorParamDesc(ParamDesc):
188    # Convert assigned value to appropriate type.  If the RHS is not a
189    # list or tuple, it generates a single-element list.
190    def convert(self, value):
191        if isinstance(value, (list, tuple)):
192            # list: coerce each element into new list
193            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
194        else:
195            # singleton: coerce to a single-element list
196            tmp_list = [ ParamDesc.convert(self, value) ]
197
198        if isSimObjectSequence(tmp_list):
199            return SimObjVector(tmp_list)
200        else:
201            return VectorParamValue(tmp_list)
202
203    def swig_predecls(self):
204        return ['%%include "%s_vptype.i"' % self.ptype_str]
205
206    def swig_decl(self):
207        cxx_type = re.sub('std::', '', self.ptype.cxx_type)
208        vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
209                (self.ptype_str, cxx_type)
210        return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
211
212    def cxx_predecls(self):
213        return ['#include <vector>'] + self.ptype.cxx_predecls
214
215    def cxx_decl(self):
216        return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
217
218class ParamFactory(object):
219    def __init__(self, param_desc_class, ptype_str = None):
220        self.param_desc_class = param_desc_class
221        self.ptype_str = ptype_str
222
223    def __getattr__(self, attr):
224        if self.ptype_str:
225            attr = self.ptype_str + '.' + attr
226        return ParamFactory(self.param_desc_class, attr)
227
228    # E.g., Param.Int(5, "number of widgets")
229    def __call__(self, *args, **kwargs):
230        ptype = None
231        try:
232            ptype = allParams[self.ptype_str]
233        except KeyError:
234            # if name isn't defined yet, assume it's a SimObject, and
235            # try to resolve it later
236            pass
237        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
238
239Param = ParamFactory(ParamDesc)
240VectorParam = ParamFactory(VectorParamDesc)
241
242#####################################################################
243#
244# Parameter Types
245#
246# Though native Python types could be used to specify parameter types
247# (the 'ptype' field of the Param and VectorParam classes), it's more
248# flexible to define our own set of types.  This gives us more control
249# over how Python expressions are converted to values (via the
250# __init__() constructor) and how these values are printed out (via
251# the __str__() conversion method).
252#
253#####################################################################
254
255# String-valued parameter.  Just mixin the ParamValue class with the
256# built-in str class.
257class String(ParamValue,str):
258    cxx_type = 'std::string'
259    cxx_predecls = ['#include <string>']
260    swig_predecls = ['%include "std_string.i"\n' +
261                     '%apply const std::string& {std::string *};']
262    swig_predecls = ['%include "std_string.i"' ]
263
264    def getValue(self):
265        return self
266
267# superclass for "numeric" parameter values, to emulate math
268# operations in a type-safe way.  e.g., a Latency times an int returns
269# a new Latency object.
270class NumericParamValue(ParamValue):
271    def __str__(self):
272        return str(self.value)
273
274    def __float__(self):
275        return float(self.value)
276
277    def __long__(self):
278        return long(self.value)
279
280    def __int__(self):
281        return int(self.value)
282
283    # hook for bounds checking
284    def _check(self):
285        return
286
287    def __mul__(self, other):
288        newobj = self.__class__(self)
289        newobj.value *= other
290        newobj._check()
291        return newobj
292
293    __rmul__ = __mul__
294
295    def __div__(self, other):
296        newobj = self.__class__(self)
297        newobj.value /= other
298        newobj._check()
299        return newobj
300
301    def __sub__(self, other):
302        newobj = self.__class__(self)
303        newobj.value -= other
304        newobj._check()
305        return newobj
306
307# Metaclass for bounds-checked integer parameters.  See CheckedInt.
308class CheckedIntType(MetaParamValue):
309    def __init__(cls, name, bases, dict):
310        super(CheckedIntType, cls).__init__(name, bases, dict)
311
312        # CheckedInt is an abstract base class, so we actually don't
313        # want to do any processing on it... the rest of this code is
314        # just for classes that derive from CheckedInt.
315        if name == 'CheckedInt':
316            return
317
318        if not cls.cxx_predecls:
319            # most derived types require this, so we just do it here once
320            cls.cxx_predecls = ['#include "sim/host.hh"']
321
322        if not cls.swig_predecls:
323            # most derived types require this, so we just do it here once
324            cls.swig_predecls = ['%import "stdint.i"\n' +
325                                 '%import "sim/host.hh"']
326
327        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
328            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
329                panic("CheckedInt subclass %s must define either\n" \
330                      "    'min' and 'max' or 'size' and 'unsigned'\n",
331                      name);
332            if cls.unsigned:
333                cls.min = 0
334                cls.max = 2 ** cls.size - 1
335            else:
336                cls.min = -(2 ** (cls.size - 1))
337                cls.max = (2 ** (cls.size - 1)) - 1
338
339# Abstract superclass for bounds-checked integer parameters.  This
340# class is subclassed to generate parameter classes with specific
341# bounds.  Initialization of the min and max bounds is done in the
342# metaclass CheckedIntType.__init__.
343class CheckedInt(NumericParamValue):
344    __metaclass__ = CheckedIntType
345
346    def _check(self):
347        if not self.min <= self.value <= self.max:
348            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
349                  (self.min, self.value, self.max)
350
351    def __init__(self, value):
352        if isinstance(value, str):
353            self.value = convert.toInteger(value)
354        elif isinstance(value, (int, long, float, NumericParamValue)):
355            self.value = long(value)
356        else:
357            raise TypeError, "Can't convert object of type %s to CheckedInt" \
358                  % type(value).__name__
359        self._check()
360
361    def getValue(self):
362        return long(self.value)
363
364class Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
365class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
366
367class Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
368class UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
369class Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
370class UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
371class Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
372class UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
373class Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
374class UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
375
376class Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
377class Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
378class TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
379class UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
380
381class Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
382
383class Float(ParamValue, float):
384    cxx_type = 'double'
385
386    def __init__(self, value):
387        if isinstance(value, (int, long, float, NumericParamValue, Float)):
388            self.value = float(value)
389        else:
390            raise TypeError, "Can't convert object of type %s to Float" \
391                  % type(value).__name__
392
393    def getValue(self):
394        return float(self.value)
395
396class MemorySize(CheckedInt):
397    cxx_type = 'uint64_t'
398    size = 64
399    unsigned = True
400    def __init__(self, value):
401        if isinstance(value, MemorySize):
402            self.value = value.value
403        else:
404            self.value = convert.toMemorySize(value)
405        self._check()
406
407class MemorySize32(CheckedInt):
408    cxx_type = 'uint32_t'
409    size = 32
410    unsigned = True
411    def __init__(self, value):
412        if isinstance(value, MemorySize):
413            self.value = value.value
414        else:
415            self.value = convert.toMemorySize(value)
416        self._check()
417
418class Addr(CheckedInt):
419    cxx_type = 'Addr'
420    size = 64
421    unsigned = True
422    def __init__(self, value):
423        if isinstance(value, Addr):
424            self.value = value.value
425        else:
426            try:
427                self.value = convert.toMemorySize(value)
428            except TypeError:
429                self.value = long(value)
430        self._check()
431    def __add__(self, other):
432        if isinstance(other, Addr):
433            return self.value + other.value
434        else:
435            return self.value + other
436
437
438class MetaRange(MetaParamValue):
439    def __init__(cls, name, bases, dict):
440        super(MetaRange, cls).__init__(name, bases, dict)
441        if name == 'Range':
442            return
443        cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
444        cls.cxx_predecls = \
445                       ['#include "base/range.hh"'] + cls.type.cxx_predecls
446
447class Range(ParamValue):
448    __metaclass__ = MetaRange
449    type = Int # default; can be overridden in subclasses
450    def __init__(self, *args, **kwargs):
451        def handle_kwargs(self, kwargs):
452            if 'end' in kwargs:
453                self.second = self.type(kwargs.pop('end'))
454            elif 'size' in kwargs:
455                self.second = self.first + self.type(kwargs.pop('size')) - 1
456            else:
457                raise TypeError, "Either end or size must be specified"
458
459        if len(args) == 0:
460            self.first = self.type(kwargs.pop('start'))
461            handle_kwargs(self, kwargs)
462
463        elif len(args) == 1:
464            if kwargs:
465                self.first = self.type(args[0])
466                handle_kwargs(self, kwargs)
467            elif isinstance(args[0], Range):
468                self.first = self.type(args[0].first)
469                self.second = self.type(args[0].second)
470            elif isinstance(args[0], (list, tuple)):
471                self.first = self.type(args[0][0])
472                self.second = self.type(args[0][1])
473            else:
474                self.first = self.type(0)
475                self.second = self.type(args[0]) - 1
476
477        elif len(args) == 2:
478            self.first = self.type(args[0])
479            self.second = self.type(args[1])
480        else:
481            raise TypeError, "Too many arguments specified"
482
483        if kwargs:
484            raise TypeError, "too many keywords: %s" % kwargs.keys()
485
486    def __str__(self):
487        return '%s:%s' % (self.first, self.second)
488
489class AddrRange(Range):
490    type = Addr
491    swig_predecls = ['%include "python/swig/range.i"']
492
493    def getValue(self):
494        from m5.objects.params import AddrRange
495
496        value = AddrRange()
497        value.start = long(self.first)
498        value.end = long(self.second)
499        return value
500
501class TickRange(Range):
502    type = Tick
503    swig_predecls = ['%include "python/swig/range.i"']
504
505    def getValue(self):
506        from m5.objects.params import TickRange
507
508        value = TickRange()
509        value.start = long(self.first)
510        value.end = long(self.second)
511        return value
512
513# Boolean parameter type.  Python doesn't let you subclass bool, since
514# it doesn't want to let you create multiple instances of True and
515# False.  Thus this is a little more complicated than String.
516class Bool(ParamValue):
517    cxx_type = 'bool'
518    def __init__(self, value):
519        try:
520            self.value = convert.toBool(value)
521        except TypeError:
522            self.value = bool(value)
523
524    def getValue(self):
525        return bool(self.value)
526
527    def __str__(self):
528        return str(self.value)
529
530    def ini_str(self):
531        if self.value:
532            return 'true'
533        return 'false'
534
535def IncEthernetAddr(addr, val = 1):
536    bytes = map(lambda x: int(x, 16), addr.split(':'))
537    bytes[5] += val
538    for i in (5, 4, 3, 2, 1):
539        val,rem = divmod(bytes[i], 256)
540        bytes[i] = rem
541        if val == 0:
542            break
543        bytes[i - 1] += val
544    assert(bytes[0] <= 255)
545    return ':'.join(map(lambda x: '%02x' % x, bytes))
546
547_NextEthernetAddr = "00:90:00:00:00:01"
548def NextEthernetAddr():
549    global _NextEthernetAddr
550
551    value = _NextEthernetAddr
552    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
553    return value
554
555class EthernetAddr(ParamValue):
556    cxx_type = 'Net::EthAddr'
557    cxx_predecls = ['#include "base/inet.hh"']
558    swig_predecls = ['%include "python/swig/inet.i"']
559    def __init__(self, value):
560        if value == NextEthernetAddr:
561            self.value = value
562            return
563
564        if not isinstance(value, str):
565            raise TypeError, "expected an ethernet address and didn't get one"
566
567        bytes = value.split(':')
568        if len(bytes) != 6:
569            raise TypeError, 'invalid ethernet address %s' % value
570
571        for byte in bytes:
572            if not 0 <= int(byte) <= 256:
573                raise TypeError, 'invalid ethernet address %s' % value
574
575        self.value = value
576
577    def unproxy(self, base):
578        if self.value == NextEthernetAddr:
579            return EthernetAddr(self.value())
580        return self
581
582    def getValue(self):
583        from m5.objects.params import EthAddr
584        return EthAddr(self.value)
585
586    def ini_str(self):
587        return self.value
588
589time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
590                 "%a %b %d %H:%M:%S %Z %Y",
591                 "%Y/%m/%d %H:%M:%S",
592                 "%Y/%m/%d %H:%M",
593                 "%Y/%m/%d",
594                 "%m/%d/%Y %H:%M:%S",
595                 "%m/%d/%Y %H:%M",
596                 "%m/%d/%Y",
597                 "%m/%d/%y %H:%M:%S",
598                 "%m/%d/%y %H:%M",
599                 "%m/%d/%y"]
600
601
602def parse_time(value):
603    from time import gmtime, strptime, struct_time, time
604    from datetime import datetime, date
605
606    if isinstance(value, struct_time):
607        return value
608
609    if isinstance(value, (int, long)):
610        return gmtime(value)
611
612    if isinstance(value, (datetime, date)):
613        return value.timetuple()
614
615    if isinstance(value, str):
616        if value in ('Now', 'Today'):
617            return time.gmtime(time.time())
618
619        for format in time_formats:
620            try:
621                return strptime(value, format)
622            except ValueError:
623                pass
624
625    raise ValueError, "Could not parse '%s' as a time" % value
626
627class Time(ParamValue):
628    cxx_type = 'tm'
629    cxx_predecls = [ '#include <time.h>' ]
630    swig_predecls = [ '%include "python/swig/time.i"' ]
631    def __init__(self, value):
632        self.value = parse_time(value)
633
634    def getValue(self):
635        from m5.objects.params import tm
636
637        c_time = tm()
638        py_time = self.value
639
640        # UNIX is years since 1900
641        c_time.tm_year = py_time.tm_year - 1900;
642
643        # Python starts at 1, UNIX starts at 0
644        c_time.tm_mon =  py_time.tm_mon - 1;
645        c_time.tm_mday = py_time.tm_mday;
646        c_time.tm_hour = py_time.tm_hour;
647        c_time.tm_min = py_time.tm_min;
648        c_time.tm_sec = py_time.tm_sec;
649
650        # Python has 0 as Monday, UNIX is 0 as sunday
651        c_time.tm_wday = py_time.tm_wday + 1
652        if c_time.tm_wday > 6:
653            c_time.tm_wday -= 7;
654
655        # Python starts at 1, Unix starts at 0
656        c_time.tm_yday = py_time.tm_yday - 1;
657
658        return c_time
659
660    def __str__(self):
661        return time.asctime(self.value)
662
663    def ini_str(self):
664        return str(self)
665
666# Enumerated types are a little more complex.  The user specifies the
667# type as Enum(foo) where foo is either a list or dictionary of
668# alternatives (typically strings, but not necessarily so).  (In the
669# long run, the integer value of the parameter will be the list index
670# or the corresponding dictionary value.  For now, since we only check
671# that the alternative is valid and then spit it into a .ini file,
672# there's not much point in using the dictionary.)
673
674# What Enum() must do is generate a new type encapsulating the
675# provided list/dictionary so that specific values of the parameter
676# can be instances of that type.  We define two hidden internal
677# classes (_ListEnum and _DictEnum) to serve as base classes, then
678# derive the new type from the appropriate base class on the fly.
679
680allEnums = {}
681# Metaclass for Enum types
682class MetaEnum(MetaParamValue):
683    def __new__(mcls, name, bases, dict):
684        assert name not in allEnums
685
686        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
687        allEnums[name] = cls
688        return cls
689
690    def __init__(cls, name, bases, init_dict):
691        if init_dict.has_key('map'):
692            if not isinstance(cls.map, dict):
693                raise TypeError, "Enum-derived class attribute 'map' " \
694                      "must be of type dict"
695            # build list of value strings from map
696            cls.vals = cls.map.keys()
697            cls.vals.sort()
698        elif init_dict.has_key('vals'):
699            if not isinstance(cls.vals, list):
700                raise TypeError, "Enum-derived class attribute 'vals' " \
701                      "must be of type list"
702            # build string->value map from vals sequence
703            cls.map = {}
704            for idx,val in enumerate(cls.vals):
705                cls.map[val] = idx
706        else:
707            raise TypeError, "Enum-derived class must define "\
708                  "attribute 'map' or 'vals'"
709
710        cls.cxx_type = 'Enums::%s' % name
711
712        super(MetaEnum, cls).__init__(name, bases, init_dict)
713
714    def __str__(cls):
715        return cls.__name__
716
717    # Generate C++ class declaration for this enum type.
718    # Note that we wrap the enum in a class/struct to act as a namespace,
719    # so that the enum strings can be brief w/o worrying about collisions.
720    def cxx_decl(cls):
721        code = "#ifndef __ENUM__%s\n" % cls
722        code += '#define __ENUM__%s\n' % cls
723        code += '\n'
724        code += 'namespace Enums {\n'
725        code += '    enum %s {\n' % cls
726        for val in cls.vals:
727            code += '        %s = %d,\n' % (val, cls.map[val])
728        code += '        Num_%s = %d,\n' % (cls, len(cls.vals))
729        code += '    };\n'
730        code += '    extern const char *%sStrings[Num_%s];\n' % (cls, cls)
731        code += '}\n'
732        code += '\n'
733        code += '#endif\n'
734        return code
735
736    def cxx_def(cls):
737        code = '#include "enums/%s.hh"\n' % cls
738        code += 'namespace Enums {\n'
739        code += '    const char *%sStrings[Num_%s] =\n' % (cls, cls)
740        code += '    {\n'
741        for val in cls.vals:
742            code += '        "%s",\n' % val
743        code += '    };\n'
744        code += '}\n'
745        return code
746
747# Base class for enum types.
748class Enum(ParamValue):
749    __metaclass__ = MetaEnum
750    vals = []
751
752    def __init__(self, value):
753        if value not in self.map:
754            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
755                  % (value, self.vals)
756        self.value = value
757
758    def getValue(self):
759        return int(self.map[self.value])
760
761    def __str__(self):
762        return self.value
763
764# how big does a rounding error need to be before we warn about it?
765frequency_tolerance = 0.001  # 0.1%
766
767class TickParamValue(NumericParamValue):
768    cxx_type = 'Tick'
769    cxx_predecls = ['#include "sim/host.hh"']
770    swig_predecls = ['%import "stdint.i"\n' +
771                     '%import "sim/host.hh"']
772
773    def getValue(self):
774        return long(self.value)
775
776class Latency(TickParamValue):
777    def __init__(self, value):
778        if isinstance(value, (Latency, Clock)):
779            self.ticks = value.ticks
780            self.value = value.value
781        elif isinstance(value, Frequency):
782            self.ticks = value.ticks
783            self.value = 1.0 / value.value
784        elif value.endswith('t'):
785            self.ticks = True
786            self.value = int(value[:-1])
787        else:
788            self.ticks = False
789            self.value = convert.toLatency(value)
790
791    def __getattr__(self, attr):
792        if attr in ('latency', 'period'):
793            return self
794        if attr == 'frequency':
795            return Frequency(self)
796        raise AttributeError, "Latency object has no attribute '%s'" % attr
797
798    def getValue(self):
799        if self.ticks or self.value == 0:
800            value = self.value
801        else:
802            value = ticks.fromSeconds(self.value)
803        return long(value)
804
805    # convert latency to ticks
806    def ini_str(self):
807        return '%d' % self.getValue()
808
809class Frequency(TickParamValue):
810    def __init__(self, value):
811        if isinstance(value, (Latency, Clock)):
812            if value.value == 0:
813                self.value = 0
814            else:
815                self.value = 1.0 / value.value
816            self.ticks = value.ticks
817        elif isinstance(value, Frequency):
818            self.value = value.value
819            self.ticks = value.ticks
820        else:
821            self.ticks = False
822            self.value = convert.toFrequency(value)
823
824    def __getattr__(self, attr):
825        if attr == 'frequency':
826            return self
827        if attr in ('latency', 'period'):
828            return Latency(self)
829        raise AttributeError, "Frequency object has no attribute '%s'" % attr
830
831    # convert latency to ticks
832    def getValue(self):
833        if self.ticks or self.value == 0:
834            value = self.value
835        else:
836            value = ticks.fromSeconds(1.0 / self.value)
837        return long(value)
838
839    def ini_str(self):
840        return '%d' % self.getValue()
841
842# A generic frequency and/or Latency value.  Value is stored as a latency,
843# but to avoid ambiguity this object does not support numeric ops (* or /).
844# An explicit conversion to a Latency or Frequency must be made first.
845class Clock(ParamValue):
846    cxx_type = 'Tick'
847    cxx_predecls = ['#include "sim/host.hh"']
848    swig_predecls = ['%import "stdint.i"\n' +
849                     '%import "sim/host.hh"']
850    def __init__(self, value):
851        if isinstance(value, (Latency, Clock)):
852            self.ticks = value.ticks
853            self.value = value.value
854        elif isinstance(value, Frequency):
855            self.ticks = value.ticks
856            self.value = 1.0 / value.value
857        elif value.endswith('t'):
858            self.ticks = True
859            self.value = int(value[:-1])
860        else:
861            self.ticks = False
862            self.value = convert.anyToLatency(value)
863
864    def __getattr__(self, attr):
865        if attr == 'frequency':
866            return Frequency(self)
867        if attr in ('latency', 'period'):
868            return Latency(self)
869        raise AttributeError, "Frequency object has no attribute '%s'" % attr
870
871    def getValue(self):
872        return self.period.getValue()
873
874    def ini_str(self):
875        return self.period.ini_str()
876
877class NetworkBandwidth(float,ParamValue):
878    cxx_type = 'float'
879    def __new__(cls, value):
880        # convert to bits per second
881        val = convert.toNetworkBandwidth(value)
882        return super(cls, NetworkBandwidth).__new__(cls, val)
883
884    def __str__(self):
885        return str(self.val)
886
887    def getValue(self):
888        # convert to seconds per byte
889        value = 8.0 / float(self)
890        # convert to ticks per byte
891        value = ticks.fromSeconds(value)
892        return float(value)
893
894    def ini_str(self):
895        return '%f' % self.getValue()
896
897class MemoryBandwidth(float,ParamValue):
898    cxx_type = 'float'
899    def __new__(cls, value):
900        # we want the number of ticks per byte of data
901        val = convert.toMemoryBandwidth(value)
902        return super(cls, MemoryBandwidth).__new__(cls, val)
903
904    def __str__(self):
905        return str(self.val)
906
907    def getValue(self):
908        # convert to seconds per byte
909        value = float(self)
910        if value:
911            value = 1.0 / float(self)
912        # convert to ticks per byte
913        value = ticks.fromSeconds(value)
914        return float(value)
915
916    def ini_str(self):
917        return '%f' % self.getValue()
918
919#
920# "Constants"... handy aliases for various values.
921#
922
923# Special class for NULL pointers.  Note the special check in
924# make_param_value() above that lets these be assigned where a
925# SimObject is required.
926# only one copy of a particular node
927class NullSimObject(object):
928    __metaclass__ = Singleton
929
930    def __call__(cls):
931        return cls
932
933    def _instantiate(self, parent = None, path = ''):
934        pass
935
936    def ini_str(self):
937        return 'Null'
938
939    def unproxy(self, base):
940        return self
941
942    def set_path(self, parent, name):
943        pass
944
945    def __str__(self):
946        return 'Null'
947
948    def getValue(self):
949        return None
950
951# The only instance you'll ever need...
952NULL = NullSimObject()
953
954def isNullPointer(value):
955    return isinstance(value, NullSimObject)
956
957# Some memory range specifications use this as a default upper bound.
958MaxAddr = Addr.max
959MaxTick = Tick.max
960AllMemory = AddrRange(0, MaxAddr)
961
962
963#####################################################################
964#
965# Port objects
966#
967# Ports are used to interconnect objects in the memory system.
968#
969#####################################################################
970
971# Port reference: encapsulates a reference to a particular port on a
972# particular SimObject.
973class PortRef(object):
974    def __init__(self, simobj, name):
975        assert(isSimObject(simobj) or isSimObjectClass(simobj))
976        self.simobj = simobj
977        self.name = name
978        self.peer = None   # not associated with another port yet
979        self.ccConnected = False # C++ port connection done?
980        self.index = -1  # always -1 for non-vector ports
981
982    def __str__(self):
983        return '%s.%s' % (self.simobj, self.name)
984
985    # for config.ini, print peer's name (not ours)
986    def ini_str(self):
987        return str(self.peer)
988
989    def __getattr__(self, attr):
990        if attr == 'peerObj':
991            # shorthand for proxies
992            return self.peer.simobj
993        raise AttributeError, "'%s' object has no attribute '%s'" % \
994              (self.__class__.__name__, attr)
995
996    # Full connection is symmetric (both ways).  Called via
997    # SimObject.__setattr__ as a result of a port assignment, e.g.,
998    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
999    # e.g., "obj1.portA[3] = obj2.portB".
1000    def connect(self, other):
1001        if isinstance(other, VectorPortRef):
1002            # reference to plain VectorPort is implicit append
1003            other = other._get_next()
1004        if self.peer and not proxy.isproxy(self.peer):
1005            print "warning: overwriting port", self, \
1006                  "value", self.peer, "with", other
1007            self.peer.peer = None
1008        self.peer = other
1009        if proxy.isproxy(other):
1010            other.set_param_desc(PortParamDesc())
1011        elif isinstance(other, PortRef):
1012            if other.peer is not self:
1013                other.connect(self)
1014        else:
1015            raise TypeError, \
1016                  "assigning non-port reference '%s' to port '%s'" \
1017                  % (other, self)
1018
1019    def clone(self, simobj, memo):
1020        if memo.has_key(self):
1021            return memo[self]
1022        newRef = copy.copy(self)
1023        memo[self] = newRef
1024        newRef.simobj = simobj
1025        assert(isSimObject(newRef.simobj))
1026        if self.peer and not proxy.isproxy(self.peer):
1027            peerObj = self.peer.simobj(_memo=memo)
1028            newRef.peer = self.peer.clone(peerObj, memo)
1029            assert(not isinstance(newRef.peer, VectorPortRef))
1030        return newRef
1031
1032    def unproxy(self, simobj):
1033        assert(simobj is self.simobj)
1034        if proxy.isproxy(self.peer):
1035            try:
1036                realPeer = self.peer.unproxy(self.simobj)
1037            except:
1038                print "Error in unproxying port '%s' of %s" % \
1039                      (self.name, self.simobj.path())
1040                raise
1041            self.connect(realPeer)
1042
1043    # Call C++ to create corresponding port connection between C++ objects
1044    def ccConnect(self):
1045        from m5.objects.params import connectPorts
1046
1047        if self.ccConnected: # already done this
1048            return
1049        peer = self.peer
1050        if not self.peer: # nothing to connect to
1051            return
1052        connectPorts(self.simobj.getCCObject(), self.name, self.index,
1053                     peer.simobj.getCCObject(), peer.name, peer.index)
1054        self.ccConnected = True
1055        peer.ccConnected = True
1056
1057# A reference to an individual element of a VectorPort... much like a
1058# PortRef, but has an index.
1059class VectorPortElementRef(PortRef):
1060    def __init__(self, simobj, name, index):
1061        PortRef.__init__(self, simobj, name)
1062        self.index = index
1063
1064    def __str__(self):
1065        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1066
1067# A reference to a complete vector-valued port (not just a single element).
1068# Can be indexed to retrieve individual VectorPortElementRef instances.
1069class VectorPortRef(object):
1070    def __init__(self, simobj, name):
1071        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1072        self.simobj = simobj
1073        self.name = name
1074        self.elements = []
1075
1076    def __str__(self):
1077        return '%s.%s[:]' % (self.simobj, self.name)
1078
1079    # for config.ini, print peer's name (not ours)
1080    def ini_str(self):
1081        return ' '.join([el.ini_str() for el in self.elements])
1082
1083    def __getitem__(self, key):
1084        if not isinstance(key, int):
1085            raise TypeError, "VectorPort index must be integer"
1086        if key >= len(self.elements):
1087            # need to extend list
1088            ext = [VectorPortElementRef(self.simobj, self.name, i)
1089                   for i in range(len(self.elements), key+1)]
1090            self.elements.extend(ext)
1091        return self.elements[key]
1092
1093    def _get_next(self):
1094        return self[len(self.elements)]
1095
1096    def __setitem__(self, key, value):
1097        if not isinstance(key, int):
1098            raise TypeError, "VectorPort index must be integer"
1099        self[key].connect(value)
1100
1101    def connect(self, other):
1102        if isinstance(other, (list, tuple)):
1103            # Assign list of port refs to vector port.
1104            # For now, append them... not sure if that's the right semantics
1105            # or if it should replace the current vector.
1106            for ref in other:
1107                self._get_next().connect(ref)
1108        else:
1109            # scalar assignment to plain VectorPort is implicit append
1110            self._get_next().connect(other)
1111
1112    def clone(self, simobj, memo):
1113        if memo.has_key(self):
1114            return memo[self]
1115        newRef = copy.copy(self)
1116        memo[self] = newRef
1117        newRef.simobj = simobj
1118        assert(isSimObject(newRef.simobj))
1119        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1120        return newRef
1121
1122    def unproxy(self, simobj):
1123        [el.unproxy(simobj) for el in self.elements]
1124
1125    def ccConnect(self):
1126        [el.ccConnect() for el in self.elements]
1127
1128# Port description object.  Like a ParamDesc object, this represents a
1129# logical port in the SimObject class, not a particular port on a
1130# SimObject instance.  The latter are represented by PortRef objects.
1131class Port(object):
1132    # Port("description") or Port(default, "description")
1133    def __init__(self, *args):
1134        if len(args) == 1:
1135            self.desc = args[0]
1136        elif len(args) == 2:
1137            self.default = args[0]
1138            self.desc = args[1]
1139        else:
1140            raise TypeError, 'wrong number of arguments'
1141        # self.name is set by SimObject class on assignment
1142        # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1143
1144    # Generate a PortRef for this port on the given SimObject with the
1145    # given name
1146    def makeRef(self, simobj):
1147        return PortRef(simobj, self.name)
1148
1149    # Connect an instance of this port (on the given SimObject with
1150    # the given name) with the port described by the supplied PortRef
1151    def connect(self, simobj, ref):
1152        self.makeRef(simobj).connect(ref)
1153
1154# VectorPort description object.  Like Port, but represents a vector
1155# of connections (e.g., as on a Bus).
1156class VectorPort(Port):
1157    def __init__(self, *args):
1158        Port.__init__(self, *args)
1159        self.isVec = True
1160
1161    def makeRef(self, simobj):
1162        return VectorPortRef(simobj, self.name)
1163
1164# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1165# proxy objects (via set_param_desc()) so that proxy error messages
1166# make sense.
1167class PortParamDesc(object):
1168    __metaclass__ = Singleton
1169
1170    ptype_str = 'Port'
1171    ptype = Port
1172
1173__all__ = ['Param', 'VectorParam',
1174           'Enum', 'Bool', 'String', 'Float',
1175           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1176           'Int32', 'UInt32', 'Int64', 'UInt64',
1177           'Counter', 'Addr', 'Tick', 'Percent',
1178           'TcpPort', 'UdpPort', 'EthernetAddr',
1179           'MemorySize', 'MemorySize32',
1180           'Latency', 'Frequency', 'Clock',
1181           'NetworkBandwidth', 'MemoryBandwidth',
1182           'Range', 'AddrRange', 'TickRange',
1183           'MaxAddr', 'MaxTick', 'AllMemory',
1184           'Time',
1185           'NextEthernetAddr', 'NULL',
1186           'Port', 'VectorPort']
1187