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