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