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