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