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