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