params.py revision 5219:e93a04703f56
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    cxx_predecls = ['#include "arch/isa_traits.hh"']
410    size = 64
411    unsigned = True
412    def __init__(self, value):
413        if isinstance(value, Addr):
414            self.value = value.value
415        else:
416            try:
417                self.value = convert.toMemorySize(value)
418            except TypeError:
419                self.value = long(value)
420        self._check()
421    def __add__(self, other):
422        if isinstance(other, Addr):
423            return self.value + other.value
424        else:
425            return self.value + other
426
427
428class MetaRange(MetaParamValue):
429    def __init__(cls, name, bases, dict):
430        super(MetaRange, cls).__init__(name, bases, dict)
431        if name == 'Range':
432            return
433        cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
434        cls.cxx_predecls = \
435                       ['#include "base/range.hh"'] + cls.type.cxx_predecls
436
437class Range(ParamValue):
438    __metaclass__ = MetaRange
439    type = Int # default; can be overridden in subclasses
440    def __init__(self, *args, **kwargs):
441        def handle_kwargs(self, kwargs):
442            if 'end' in kwargs:
443                self.second = self.type(kwargs.pop('end'))
444            elif 'size' in kwargs:
445                self.second = self.first + self.type(kwargs.pop('size')) - 1
446            else:
447                raise TypeError, "Either end or size must be specified"
448
449        if len(args) == 0:
450            self.first = self.type(kwargs.pop('start'))
451            handle_kwargs(self, kwargs)
452
453        elif len(args) == 1:
454            if kwargs:
455                self.first = self.type(args[0])
456                handle_kwargs(self, kwargs)
457            elif isinstance(args[0], Range):
458                self.first = self.type(args[0].first)
459                self.second = self.type(args[0].second)
460            elif isinstance(args[0], (list, tuple)):
461                self.first = self.type(args[0][0])
462                self.second = self.type(args[0][1])
463            else:
464                self.first = self.type(0)
465                self.second = self.type(args[0]) - 1
466
467        elif len(args) == 2:
468            self.first = self.type(args[0])
469            self.second = self.type(args[1])
470        else:
471            raise TypeError, "Too many arguments specified"
472
473        if kwargs:
474            raise TypeError, "too many keywords: %s" % kwargs.keys()
475
476    def __str__(self):
477        return '%s:%s' % (self.first, self.second)
478
479class AddrRange(Range):
480    type = Addr
481    swig_predecls = ['%include "python/swig/range.i"']
482
483    def getValue(self):
484        from m5.objects.params import AddrRange
485
486        value = AddrRange()
487        value.start = long(self.first)
488        value.end = long(self.second)
489        return value
490
491class TickRange(Range):
492    type = Tick
493    swig_predecls = ['%include "python/swig/range.i"']
494
495    def getValue(self):
496        from m5.objects.params import TickRange
497
498        value = TickRange()
499        value.start = long(self.first)
500        value.end = long(self.second)
501        return value
502
503# Boolean parameter type.  Python doesn't let you subclass bool, since
504# it doesn't want to let you create multiple instances of True and
505# False.  Thus this is a little more complicated than String.
506class Bool(ParamValue):
507    cxx_type = 'bool'
508    def __init__(self, value):
509        try:
510            self.value = convert.toBool(value)
511        except TypeError:
512            self.value = bool(value)
513
514    def getValue(self):
515        return bool(self.value)
516
517    def __str__(self):
518        return str(self.value)
519
520    def ini_str(self):
521        if self.value:
522            return 'true'
523        return 'false'
524
525def IncEthernetAddr(addr, val = 1):
526    bytes = map(lambda x: int(x, 16), addr.split(':'))
527    bytes[5] += val
528    for i in (5, 4, 3, 2, 1):
529        val,rem = divmod(bytes[i], 256)
530        bytes[i] = rem
531        if val == 0:
532            break
533        bytes[i - 1] += val
534    assert(bytes[0] <= 255)
535    return ':'.join(map(lambda x: '%02x' % x, bytes))
536
537_NextEthernetAddr = "00:90:00:00:00:01"
538def NextEthernetAddr():
539    global _NextEthernetAddr
540
541    value = _NextEthernetAddr
542    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
543    return value
544
545class EthernetAddr(ParamValue):
546    cxx_type = 'Net::EthAddr'
547    cxx_predecls = ['#include "base/inet.hh"']
548    swig_predecls = ['%include "python/swig/inet.i"']
549    def __init__(self, value):
550        if value == NextEthernetAddr:
551            self.value = value
552            return
553
554        if not isinstance(value, str):
555            raise TypeError, "expected an ethernet address and didn't get one"
556
557        bytes = value.split(':')
558        if len(bytes) != 6:
559            raise TypeError, 'invalid ethernet address %s' % value
560
561        for byte in bytes:
562            if not 0 <= int(byte) <= 256:
563                raise TypeError, 'invalid ethernet address %s' % value
564
565        self.value = value
566
567    def unproxy(self, base):
568        if self.value == NextEthernetAddr:
569            return EthernetAddr(self.value())
570        return self
571
572    def getValue(self):
573        from m5.objects.params import EthAddr
574        return EthAddr(self.value)
575
576    def ini_str(self):
577        return self.value
578
579time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
580                 "%a %b %d %H:%M:%S %Z %Y",
581                 "%Y/%m/%d %H:%M:%S",
582                 "%Y/%m/%d %H:%M",
583                 "%Y/%m/%d",
584                 "%m/%d/%Y %H:%M:%S",
585                 "%m/%d/%Y %H:%M",
586                 "%m/%d/%Y",
587                 "%m/%d/%y %H:%M:%S",
588                 "%m/%d/%y %H:%M",
589                 "%m/%d/%y"]
590
591
592def parse_time(value):
593    from time import gmtime, strptime, struct_time, time
594    from datetime import datetime, date
595
596    if isinstance(value, struct_time):
597        return value
598
599    if isinstance(value, (int, long)):
600        return gmtime(value)
601
602    if isinstance(value, (datetime, date)):
603        return value.timetuple()
604
605    if isinstance(value, str):
606        if value in ('Now', 'Today'):
607            return time.gmtime(time.time())
608
609        for format in time_formats:
610            try:
611                return strptime(value, format)
612            except ValueError:
613                pass
614
615    raise ValueError, "Could not parse '%s' as a time" % value
616
617class Time(ParamValue):
618    cxx_type = 'tm'
619    cxx_predecls = [ '#include <time.h>' ]
620    swig_predecls = [ '%include "python/swig/time.i"' ]
621    def __init__(self, value):
622        self.value = parse_time(value)
623
624    def getValue(self):
625        from m5.objects.params import tm
626
627        c_time = tm()
628        py_time = self.value
629
630        # UNIX is years since 1900
631        c_time.tm_year = py_time.tm_year - 1900;
632
633        # Python starts at 1, UNIX starts at 0
634        c_time.tm_mon =  py_time.tm_mon - 1;
635        c_time.tm_mday = py_time.tm_mday;
636        c_time.tm_hour = py_time.tm_hour;
637        c_time.tm_min = py_time.tm_min;
638        c_time.tm_sec = py_time.tm_sec;
639
640        # Python has 0 as Monday, UNIX is 0 as sunday
641        c_time.tm_wday = py_time.tm_wday + 1
642        if c_time.tm_wday > 6:
643            c_time.tm_wday -= 7;
644
645        # Python starts at 1, Unix starts at 0
646        c_time.tm_yday = py_time.tm_yday - 1;
647
648        return c_time
649
650    def __str__(self):
651        return time.asctime(self.value)
652
653    def ini_str(self):
654        return str(self)
655
656# Enumerated types are a little more complex.  The user specifies the
657# type as Enum(foo) where foo is either a list or dictionary of
658# alternatives (typically strings, but not necessarily so).  (In the
659# long run, the integer value of the parameter will be the list index
660# or the corresponding dictionary value.  For now, since we only check
661# that the alternative is valid and then spit it into a .ini file,
662# there's not much point in using the dictionary.)
663
664# What Enum() must do is generate a new type encapsulating the
665# provided list/dictionary so that specific values of the parameter
666# can be instances of that type.  We define two hidden internal
667# classes (_ListEnum and _DictEnum) to serve as base classes, then
668# derive the new type from the appropriate base class on the fly.
669
670allEnums = {}
671# Metaclass for Enum types
672class MetaEnum(MetaParamValue):
673    def __new__(mcls, name, bases, dict):
674        assert name not in allEnums
675
676        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
677        allEnums[name] = cls
678        return cls
679
680    def __init__(cls, name, bases, init_dict):
681        if init_dict.has_key('map'):
682            if not isinstance(cls.map, dict):
683                raise TypeError, "Enum-derived class attribute 'map' " \
684                      "must be of type dict"
685            # build list of value strings from map
686            cls.vals = cls.map.keys()
687            cls.vals.sort()
688        elif init_dict.has_key('vals'):
689            if not isinstance(cls.vals, list):
690                raise TypeError, "Enum-derived class attribute 'vals' " \
691                      "must be of type list"
692            # build string->value map from vals sequence
693            cls.map = {}
694            for idx,val in enumerate(cls.vals):
695                cls.map[val] = idx
696        else:
697            raise TypeError, "Enum-derived class must define "\
698                  "attribute 'map' or 'vals'"
699
700        cls.cxx_type = 'Enums::%s' % name
701
702        super(MetaEnum, cls).__init__(name, bases, init_dict)
703
704    def __str__(cls):
705        return cls.__name__
706
707    # Generate C++ class declaration for this enum type.
708    # Note that we wrap the enum in a class/struct to act as a namespace,
709    # so that the enum strings can be brief w/o worrying about collisions.
710    def cxx_decl(cls):
711        code = "#ifndef __ENUM__%s\n" % cls
712        code += '#define __ENUM__%s\n' % cls
713        code += '\n'
714        code += 'namespace Enums {\n'
715        code += '    enum %s {\n' % cls
716        for val in cls.vals:
717            code += '        %s = %d,\n' % (val, cls.map[val])
718        code += '        Num_%s = %d,\n' % (cls, len(cls.vals))
719        code += '    };\n'
720        code += '    extern const char *%sStrings[Num_%s];\n' % (cls, cls)
721        code += '}\n'
722        code += '\n'
723        code += '#endif\n'
724        return code
725
726    def cxx_def(cls):
727        code = '#include "enums/%s.hh"\n' % cls
728        code += 'namespace Enums {\n'
729        code += '    const char *%sStrings[Num_%s] =\n' % (cls, cls)
730        code += '    {\n'
731        for val in cls.vals:
732            code += '        "%s",\n' % val
733        code += '    };\n'
734        code += '}\n'
735        return code
736
737# Base class for enum types.
738class Enum(ParamValue):
739    __metaclass__ = MetaEnum
740    vals = []
741
742    def __init__(self, value):
743        if value not in self.map:
744            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
745                  % (value, self.vals)
746        self.value = value
747
748    def getValue(self):
749        return int(self.map[self.value])
750
751    def __str__(self):
752        return self.value
753
754# how big does a rounding error need to be before we warn about it?
755frequency_tolerance = 0.001  # 0.1%
756
757class TickParamValue(NumericParamValue):
758    cxx_type = 'Tick'
759    cxx_predecls = ['#include "sim/host.hh"']
760    swig_predecls = ['%import "stdint.i"\n' +
761                     '%import "sim/host.hh"']
762
763    def getValue(self):
764        return long(self.value)
765
766class Latency(TickParamValue):
767    def __init__(self, value):
768        if isinstance(value, (Latency, Clock)):
769            self.ticks = value.ticks
770            self.value = value.value
771        elif isinstance(value, Frequency):
772            self.ticks = value.ticks
773            self.value = 1.0 / value.value
774        elif value.endswith('t'):
775            self.ticks = True
776            self.value = int(value[:-1])
777        else:
778            self.ticks = False
779            self.value = convert.toLatency(value)
780
781    def __getattr__(self, attr):
782        if attr in ('latency', 'period'):
783            return self
784        if attr == 'frequency':
785            return Frequency(self)
786        raise AttributeError, "Latency object has no attribute '%s'" % attr
787
788    def getValue(self):
789        if self.ticks or self.value == 0:
790            value = self.value
791        else:
792            value = ticks.fromSeconds(self.value)
793        return long(value)
794
795    # convert latency to ticks
796    def ini_str(self):
797        return '%d' % self.getValue()
798
799class Frequency(TickParamValue):
800    def __init__(self, value):
801        if isinstance(value, (Latency, Clock)):
802            if value.value == 0:
803                self.value = 0
804            else:
805                self.value = 1.0 / value.value
806            self.ticks = value.ticks
807        elif isinstance(value, Frequency):
808            self.value = value.value
809            self.ticks = value.ticks
810        else:
811            self.ticks = False
812            self.value = convert.toFrequency(value)
813
814    def __getattr__(self, attr):
815        if attr == 'frequency':
816            return self
817        if attr in ('latency', 'period'):
818            return Latency(self)
819        raise AttributeError, "Frequency object has no attribute '%s'" % attr
820
821    # convert latency to ticks
822    def getValue(self):
823        if self.ticks or self.value == 0:
824            value = self.value
825        else:
826            value = ticks.fromSeconds(1.0 / self.value)
827        return long(value)
828
829    def ini_str(self):
830        return '%d' % self.getValue()
831
832# A generic frequency and/or Latency value.  Value is stored as a latency,
833# but to avoid ambiguity this object does not support numeric ops (* or /).
834# An explicit conversion to a Latency or Frequency must be made first.
835class Clock(ParamValue):
836    cxx_type = 'Tick'
837    cxx_predecls = ['#include "sim/host.hh"']
838    swig_predecls = ['%import "stdint.i"\n' +
839                     '%import "sim/host.hh"']
840    def __init__(self, value):
841        if isinstance(value, (Latency, Clock)):
842            self.ticks = value.ticks
843            self.value = value.value
844        elif isinstance(value, Frequency):
845            self.ticks = value.ticks
846            self.value = 1.0 / value.value
847        elif value.endswith('t'):
848            self.ticks = True
849            self.value = int(value[:-1])
850        else:
851            self.ticks = False
852            self.value = convert.anyToLatency(value)
853
854    def __getattr__(self, attr):
855        if attr == 'frequency':
856            return Frequency(self)
857        if attr in ('latency', 'period'):
858            return Latency(self)
859        raise AttributeError, "Frequency object has no attribute '%s'" % attr
860
861    def getValue(self):
862        return self.period.getValue()
863
864    def ini_str(self):
865        return self.period.ini_str()
866
867class NetworkBandwidth(float,ParamValue):
868    cxx_type = 'float'
869    def __new__(cls, value):
870        # convert to bits per second
871        val = convert.toNetworkBandwidth(value)
872        return super(cls, NetworkBandwidth).__new__(cls, val)
873
874    def __str__(self):
875        return str(self.val)
876
877    def getValue(self):
878        # convert to seconds per byte
879        value = 8.0 / float(self)
880        # convert to ticks per byte
881        value = ticks.fromSeconds(value)
882        return float(value)
883
884    def ini_str(self):
885        return '%f' % self.getValue()
886
887class MemoryBandwidth(float,ParamValue):
888    cxx_type = 'float'
889    def __new__(self, value):
890        # we want the number of ticks per byte of data
891        val = convert.toMemoryBandwidth(value)
892        return super(cls, MemoryBandwidth).__new__(cls, val)
893
894    def __str__(self):
895        return str(self.val)
896
897    def getValue(self):
898        # convert to seconds per byte
899        value = 1.0 / float(self)
900        # convert to ticks per byte
901        value = ticks.fromSeconds(value)
902        return float(value)
903
904    def ini_str(self):
905        return '%f' % self.getValue()
906
907#
908# "Constants"... handy aliases for various values.
909#
910
911# Special class for NULL pointers.  Note the special check in
912# make_param_value() above that lets these be assigned where a
913# SimObject is required.
914# only one copy of a particular node
915class NullSimObject(object):
916    __metaclass__ = Singleton
917
918    def __call__(cls):
919        return cls
920
921    def _instantiate(self, parent = None, path = ''):
922        pass
923
924    def ini_str(self):
925        return 'Null'
926
927    def unproxy(self, base):
928        return self
929
930    def set_path(self, parent, name):
931        pass
932
933    def __str__(self):
934        return 'Null'
935
936    def getValue(self):
937        return None
938
939# The only instance you'll ever need...
940NULL = NullSimObject()
941
942def isNullPointer(value):
943    return isinstance(value, NullSimObject)
944
945# Some memory range specifications use this as a default upper bound.
946MaxAddr = Addr.max
947MaxTick = Tick.max
948AllMemory = AddrRange(0, MaxAddr)
949
950
951#####################################################################
952#
953# Port objects
954#
955# Ports are used to interconnect objects in the memory system.
956#
957#####################################################################
958
959# Port reference: encapsulates a reference to a particular port on a
960# particular SimObject.
961class PortRef(object):
962    def __init__(self, simobj, name):
963        assert(isSimObject(simobj) or isSimObjectClass(simobj))
964        self.simobj = simobj
965        self.name = name
966        self.peer = None   # not associated with another port yet
967        self.ccConnected = False # C++ port connection done?
968        self.index = -1  # always -1 for non-vector ports
969
970    def __str__(self):
971        return '%s.%s' % (self.simobj, self.name)
972
973    # for config.ini, print peer's name (not ours)
974    def ini_str(self):
975        return str(self.peer)
976
977    def __getattr__(self, attr):
978        if attr == 'peerObj':
979            # shorthand for proxies
980            return self.peer.simobj
981        raise AttributeError, "'%s' object has no attribute '%s'" % \
982              (self.__class__.__name__, attr)
983
984    # Full connection is symmetric (both ways).  Called via
985    # SimObject.__setattr__ as a result of a port assignment, e.g.,
986    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
987    # e.g., "obj1.portA[3] = obj2.portB".
988    def connect(self, other):
989        if isinstance(other, VectorPortRef):
990            # reference to plain VectorPort is implicit append
991            other = other._get_next()
992        if self.peer and not proxy.isproxy(self.peer):
993            print "warning: overwriting port", self, \
994                  "value", self.peer, "with", other
995        self.peer = other
996        if proxy.isproxy(other):
997            other.set_param_desc(PortParamDesc())
998        elif isinstance(other, PortRef):
999            if other.peer is not self:
1000                other.connect(self)
1001        else:
1002            raise TypeError, \
1003                  "assigning non-port reference '%s' to port '%s'" \
1004                  % (other, self)
1005
1006    def clone(self, simobj, memo):
1007        if memo.has_key(self):
1008            return memo[self]
1009        newRef = copy.copy(self)
1010        memo[self] = newRef
1011        newRef.simobj = simobj
1012        assert(isSimObject(newRef.simobj))
1013        if self.peer and not proxy.isproxy(self.peer):
1014            peerObj = self.peer.simobj(_memo=memo)
1015            newRef.peer = self.peer.clone(peerObj, memo)
1016            assert(not isinstance(newRef.peer, VectorPortRef))
1017        return newRef
1018
1019    def unproxy(self, simobj):
1020        assert(simobj is self.simobj)
1021        if proxy.isproxy(self.peer):
1022            try:
1023                realPeer = self.peer.unproxy(self.simobj)
1024            except:
1025                print "Error in unproxying port '%s' of %s" % \
1026                      (self.name, self.simobj.path())
1027                raise
1028            self.connect(realPeer)
1029
1030    # Call C++ to create corresponding port connection between C++ objects
1031    def ccConnect(self):
1032        from m5.objects.params import connectPorts
1033
1034        if self.ccConnected: # already done this
1035            return
1036        peer = self.peer
1037        connectPorts(self.simobj.getCCObject(), self.name, self.index,
1038                     peer.simobj.getCCObject(), peer.name, peer.index)
1039        self.ccConnected = True
1040        peer.ccConnected = True
1041
1042# A reference to an individual element of a VectorPort... much like a
1043# PortRef, but has an index.
1044class VectorPortElementRef(PortRef):
1045    def __init__(self, simobj, name, index):
1046        PortRef.__init__(self, simobj, name)
1047        self.index = index
1048
1049    def __str__(self):
1050        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1051
1052# A reference to a complete vector-valued port (not just a single element).
1053# Can be indexed to retrieve individual VectorPortElementRef instances.
1054class VectorPortRef(object):
1055    def __init__(self, simobj, name):
1056        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1057        self.simobj = simobj
1058        self.name = name
1059        self.elements = []
1060
1061    def __str__(self):
1062        return '%s.%s[:]' % (self.simobj, self.name)
1063
1064    # for config.ini, print peer's name (not ours)
1065    def ini_str(self):
1066        return ' '.join([el.ini_str() for el in self.elements])
1067
1068    def __getitem__(self, key):
1069        if not isinstance(key, int):
1070            raise TypeError, "VectorPort index must be integer"
1071        if key >= len(self.elements):
1072            # need to extend list
1073            ext = [VectorPortElementRef(self.simobj, self.name, i)
1074                   for i in range(len(self.elements), key+1)]
1075            self.elements.extend(ext)
1076        return self.elements[key]
1077
1078    def _get_next(self):
1079        return self[len(self.elements)]
1080
1081    def __setitem__(self, key, value):
1082        if not isinstance(key, int):
1083            raise TypeError, "VectorPort index must be integer"
1084        self[key].connect(value)
1085
1086    def connect(self, other):
1087        if isinstance(other, (list, tuple)):
1088            # Assign list of port refs to vector port.
1089            # For now, append them... not sure if that's the right semantics
1090            # or if it should replace the current vector.
1091            for ref in other:
1092                self._get_next().connect(ref)
1093        else:
1094            # scalar assignment to plain VectorPort is implicit append
1095            self._get_next().connect(other)
1096
1097    def clone(self, simobj, memo):
1098        if memo.has_key(self):
1099            return memo[self]
1100        newRef = copy.copy(self)
1101        memo[self] = newRef
1102        newRef.simobj = simobj
1103        assert(isSimObject(newRef.simobj))
1104        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1105        return newRef
1106
1107    def unproxy(self, simobj):
1108        [el.unproxy(simobj) for el in self.elements]
1109
1110    def ccConnect(self):
1111        [el.ccConnect() for el in self.elements]
1112
1113# Port description object.  Like a ParamDesc object, this represents a
1114# logical port in the SimObject class, not a particular port on a
1115# SimObject instance.  The latter are represented by PortRef objects.
1116class Port(object):
1117    # Port("description") or Port(default, "description")
1118    def __init__(self, *args):
1119        if len(args) == 1:
1120            self.desc = args[0]
1121        elif len(args) == 2:
1122            self.default = args[0]
1123            self.desc = args[1]
1124        else:
1125            raise TypeError, 'wrong number of arguments'
1126        # self.name is set by SimObject class on assignment
1127        # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1128
1129    # Generate a PortRef for this port on the given SimObject with the
1130    # given name
1131    def makeRef(self, simobj):
1132        return PortRef(simobj, self.name)
1133
1134    # Connect an instance of this port (on the given SimObject with
1135    # the given name) with the port described by the supplied PortRef
1136    def connect(self, simobj, ref):
1137        self.makeRef(simobj).connect(ref)
1138
1139# VectorPort description object.  Like Port, but represents a vector
1140# of connections (e.g., as on a Bus).
1141class VectorPort(Port):
1142    def __init__(self, *args):
1143        Port.__init__(self, *args)
1144        self.isVec = True
1145
1146    def makeRef(self, simobj):
1147        return VectorPortRef(simobj, self.name)
1148
1149# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1150# proxy objects (via set_param_desc()) so that proxy error messages
1151# make sense.
1152class PortParamDesc(object):
1153    __metaclass__ = Singleton
1154
1155    ptype_str = 'Port'
1156    ptype = Port
1157
1158__all__ = ['Param', 'VectorParam',
1159           'Enum', 'Bool', 'String', 'Float',
1160           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1161           'Int32', 'UInt32', 'Int64', 'UInt64',
1162           'Counter', 'Addr', 'Tick', 'Percent',
1163           'TcpPort', 'UdpPort', 'EthernetAddr',
1164           'MemorySize', 'MemorySize32',
1165           'Latency', 'Frequency', 'Clock',
1166           'NetworkBandwidth', 'MemoryBandwidth',
1167           'Range', 'AddrRange', 'TickRange',
1168           'MaxAddr', 'MaxTick', 'AllMemory',
1169           'Time',
1170           'NextEthernetAddr', 'NULL',
1171           'Port', 'VectorPort']
1172