params.py revision 6654
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    def __init__(self, ptype_str, ptype, *args, **kwargs):
97        self.ptype_str = ptype_str
98        # remember ptype only if it is provided
99        if ptype != None:
100            self.ptype = ptype
101
102        if args:
103            if len(args) == 1:
104                self.desc = args[0]
105            elif len(args) == 2:
106                self.default = args[0]
107                self.desc = args[1]
108            else:
109                raise TypeError, 'too many arguments'
110
111        if kwargs.has_key('desc'):
112            assert(not hasattr(self, 'desc'))
113            self.desc = kwargs['desc']
114            del kwargs['desc']
115
116        if kwargs.has_key('default'):
117            assert(not hasattr(self, 'default'))
118            self.default = kwargs['default']
119            del kwargs['default']
120
121        if kwargs:
122            raise TypeError, 'extra unknown kwargs %s' % kwargs
123
124        if not hasattr(self, 'desc'):
125            raise TypeError, 'desc attribute missing'
126
127    def __getattr__(self, attr):
128        if attr == 'ptype':
129            ptype = SimObject.allClasses[self.ptype_str]
130            assert issubclass(ptype, SimObject.SimObject)
131            self.ptype = ptype
132            return ptype
133
134        raise AttributeError, "'%s' object has no attribute '%s'" % \
135              (type(self).__name__, attr)
136
137    def convert(self, value):
138        if isinstance(value, proxy.BaseProxy):
139            value.set_param_desc(self)
140            return value
141        if not hasattr(self, 'ptype') and isNullPointer(value):
142            # deferred evaluation of SimObject; continue to defer if
143            # we're just assigning a null pointer
144            return value
145        if isinstance(value, self.ptype):
146            return value
147        if isNullPointer(value) and isSimObjectClass(self.ptype):
148            return value
149        return self.ptype(value)
150
151    def cxx_predecls(self):
152        return self.ptype.cxx_predecls
153
154    def swig_predecls(self):
155        return self.ptype.swig_predecls
156
157    def cxx_decl(self):
158        return '%s %s;' % (self.ptype.cxx_type, self.name)
159
160# Vector-valued parameter description.  Just like ParamDesc, except
161# that the value is a vector (list) of the specified type instead of a
162# single value.
163
164class VectorParamValue(list):
165    __metaclass__ = MetaParamValue
166    def __setattr__(self, attr, value):
167        raise AttributeError, \
168              "Not allowed to set %s on '%s'" % (attr, type(self).__name__)
169
170    def ini_str(self):
171        return ' '.join([v.ini_str() for v in self])
172
173    def getValue(self):
174        return [ v.getValue() for v in self ]
175
176    def unproxy(self, base):
177        return [v.unproxy(base) for v in self]
178
179class SimObjVector(VectorParamValue):
180    def print_ini(self, ini_file):
181        for v in self:
182            v.print_ini(ini_file)
183
184class VectorParamDesc(ParamDesc):
185    # Convert assigned value to appropriate type.  If the RHS is not a
186    # list or tuple, it generates a single-element list.
187    def convert(self, value):
188        if isinstance(value, (list, tuple)):
189            # list: coerce each element into new list
190            tmp_list = [ ParamDesc.convert(self, v) for v in value ]
191        else:
192            # singleton: coerce to a single-element list
193            tmp_list = [ ParamDesc.convert(self, value) ]
194
195        if isSimObjectSequence(tmp_list):
196            return SimObjVector(tmp_list)
197        else:
198            return VectorParamValue(tmp_list)
199
200    def swig_predecls(self):
201        return ['%%include "%s_vptype.i"' % self.ptype_str]
202
203    def swig_decl(self):
204        cxx_type = re.sub('std::', '', self.ptype.cxx_type)
205        vdecl = 'namespace std { %%template(vector_%s) vector< %s >; }' % \
206                (self.ptype_str, cxx_type)
207        return ['%include "std_vector.i"'] + self.ptype.swig_predecls + [vdecl]
208
209    def cxx_predecls(self):
210        return ['#include <vector>'] + self.ptype.cxx_predecls
211
212    def cxx_decl(self):
213        return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name)
214
215class ParamFactory(object):
216    def __init__(self, param_desc_class, ptype_str = None):
217        self.param_desc_class = param_desc_class
218        self.ptype_str = ptype_str
219
220    def __getattr__(self, attr):
221        if self.ptype_str:
222            attr = self.ptype_str + '.' + attr
223        return ParamFactory(self.param_desc_class, attr)
224
225    # E.g., Param.Int(5, "number of widgets")
226    def __call__(self, *args, **kwargs):
227        ptype = None
228        try:
229            ptype = allParams[self.ptype_str]
230        except KeyError:
231            # if name isn't defined yet, assume it's a SimObject, and
232            # try to resolve it later
233            pass
234        return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
235
236Param = ParamFactory(ParamDesc)
237VectorParam = ParamFactory(VectorParamDesc)
238
239#####################################################################
240#
241# Parameter Types
242#
243# Though native Python types could be used to specify parameter types
244# (the 'ptype' field of the Param and VectorParam classes), it's more
245# flexible to define our own set of types.  This gives us more control
246# over how Python expressions are converted to values (via the
247# __init__() constructor) and how these values are printed out (via
248# the __str__() conversion method).
249#
250#####################################################################
251
252# String-valued parameter.  Just mixin the ParamValue class with the
253# built-in str class.
254class String(ParamValue,str):
255    cxx_type = 'std::string'
256    cxx_predecls = ['#include <string>']
257    swig_predecls = ['%include "std_string.i"\n' +
258                     '%apply const std::string& {std::string *};']
259    swig_predecls = ['%include "std_string.i"' ]
260
261    def getValue(self):
262        return self
263
264# superclass for "numeric" parameter values, to emulate math
265# operations in a type-safe way.  e.g., a Latency times an int returns
266# a new Latency object.
267class NumericParamValue(ParamValue):
268    def __str__(self):
269        return str(self.value)
270
271    def __float__(self):
272        return float(self.value)
273
274    def __long__(self):
275        return long(self.value)
276
277    def __int__(self):
278        return int(self.value)
279
280    # hook for bounds checking
281    def _check(self):
282        return
283
284    def __mul__(self, other):
285        newobj = self.__class__(self)
286        newobj.value *= other
287        newobj._check()
288        return newobj
289
290    __rmul__ = __mul__
291
292    def __div__(self, other):
293        newobj = self.__class__(self)
294        newobj.value /= other
295        newobj._check()
296        return newobj
297
298    def __sub__(self, other):
299        newobj = self.__class__(self)
300        newobj.value -= other
301        newobj._check()
302        return newobj
303
304# Metaclass for bounds-checked integer parameters.  See CheckedInt.
305class CheckedIntType(MetaParamValue):
306    def __init__(cls, name, bases, dict):
307        super(CheckedIntType, cls).__init__(name, bases, dict)
308
309        # CheckedInt is an abstract base class, so we actually don't
310        # want to do any processing on it... the rest of this code is
311        # just for classes that derive from CheckedInt.
312        if name == 'CheckedInt':
313            return
314
315        if not cls.cxx_predecls:
316            # most derived types require this, so we just do it here once
317            cls.cxx_predecls = ['#include "base/types.hh"']
318
319        if not cls.swig_predecls:
320            # most derived types require this, so we just do it here once
321            cls.swig_predecls = ['%import "stdint.i"\n' +
322                                 '%import "base/types.hh"']
323
324        if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
325            if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
326                panic("CheckedInt subclass %s must define either\n" \
327                      "    'min' and 'max' or 'size' and 'unsigned'\n",
328                      name);
329            if cls.unsigned:
330                cls.min = 0
331                cls.max = 2 ** cls.size - 1
332            else:
333                cls.min = -(2 ** (cls.size - 1))
334                cls.max = (2 ** (cls.size - 1)) - 1
335
336# Abstract superclass for bounds-checked integer parameters.  This
337# class is subclassed to generate parameter classes with specific
338# bounds.  Initialization of the min and max bounds is done in the
339# metaclass CheckedIntType.__init__.
340class CheckedInt(NumericParamValue):
341    __metaclass__ = CheckedIntType
342
343    def _check(self):
344        if not self.min <= self.value <= self.max:
345            raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
346                  (self.min, self.value, self.max)
347
348    def __init__(self, value):
349        if isinstance(value, str):
350            self.value = convert.toInteger(value)
351        elif isinstance(value, (int, long, float, NumericParamValue)):
352            self.value = long(value)
353        else:
354            raise TypeError, "Can't convert object of type %s to CheckedInt" \
355                  % type(value).__name__
356        self._check()
357
358    def getValue(self):
359        return long(self.value)
360
361class Int(CheckedInt):      cxx_type = 'int';      size = 32; unsigned = False
362class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True
363
364class Int8(CheckedInt):     cxx_type =   'int8_t'; size =  8; unsigned = False
365class UInt8(CheckedInt):    cxx_type =  'uint8_t'; size =  8; unsigned = True
366class Int16(CheckedInt):    cxx_type =  'int16_t'; size = 16; unsigned = False
367class UInt16(CheckedInt):   cxx_type = 'uint16_t'; size = 16; unsigned = True
368class Int32(CheckedInt):    cxx_type =  'int32_t'; size = 32; unsigned = False
369class UInt32(CheckedInt):   cxx_type = 'uint32_t'; size = 32; unsigned = True
370class Int64(CheckedInt):    cxx_type =  'int64_t'; size = 64; unsigned = False
371class UInt64(CheckedInt):   cxx_type = 'uint64_t'; size = 64; unsigned = True
372
373class Counter(CheckedInt):  cxx_type = 'Counter';  size = 64; unsigned = True
374class Tick(CheckedInt):     cxx_type = 'Tick';     size = 64; unsigned = True
375class TcpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
376class UdpPort(CheckedInt):  cxx_type = 'uint16_t'; size = 16; unsigned = True
377
378class Percent(CheckedInt):  cxx_type = 'int'; min = 0; max = 100
379
380class Float(ParamValue, float):
381    cxx_type = 'double'
382
383    def __init__(self, value):
384        if isinstance(value, (int, long, float, NumericParamValue, Float)):
385            self.value = float(value)
386        else:
387            raise TypeError, "Can't convert object of type %s to Float" \
388                  % type(value).__name__
389
390    def getValue(self):
391        return float(self.value)
392
393class MemorySize(CheckedInt):
394    cxx_type = 'uint64_t'
395    size = 64
396    unsigned = True
397    def __init__(self, value):
398        if isinstance(value, MemorySize):
399            self.value = value.value
400        else:
401            self.value = convert.toMemorySize(value)
402        self._check()
403
404class MemorySize32(CheckedInt):
405    cxx_type = 'uint32_t'
406    size = 32
407    unsigned = True
408    def __init__(self, value):
409        if isinstance(value, MemorySize):
410            self.value = value.value
411        else:
412            self.value = convert.toMemorySize(value)
413        self._check()
414
415class Addr(CheckedInt):
416    cxx_type = 'Addr'
417    size = 64
418    unsigned = True
419    def __init__(self, value):
420        if isinstance(value, Addr):
421            self.value = value.value
422        else:
423            try:
424                self.value = convert.toMemorySize(value)
425            except TypeError:
426                self.value = long(value)
427        self._check()
428    def __add__(self, other):
429        if isinstance(other, Addr):
430            return self.value + other.value
431        else:
432            return self.value + other
433
434
435class MetaRange(MetaParamValue):
436    def __init__(cls, name, bases, dict):
437        super(MetaRange, cls).__init__(name, bases, dict)
438        if name == 'Range':
439            return
440        cls.cxx_type = 'Range< %s >' % cls.type.cxx_type
441        cls.cxx_predecls = \
442                       ['#include "base/range.hh"'] + cls.type.cxx_predecls
443
444class Range(ParamValue):
445    __metaclass__ = MetaRange
446    type = Int # default; can be overridden in subclasses
447    def __init__(self, *args, **kwargs):
448        def handle_kwargs(self, kwargs):
449            if 'end' in kwargs:
450                self.second = self.type(kwargs.pop('end'))
451            elif 'size' in kwargs:
452                self.second = self.first + self.type(kwargs.pop('size')) - 1
453            else:
454                raise TypeError, "Either end or size must be specified"
455
456        if len(args) == 0:
457            self.first = self.type(kwargs.pop('start'))
458            handle_kwargs(self, kwargs)
459
460        elif len(args) == 1:
461            if kwargs:
462                self.first = self.type(args[0])
463                handle_kwargs(self, kwargs)
464            elif isinstance(args[0], Range):
465                self.first = self.type(args[0].first)
466                self.second = self.type(args[0].second)
467            elif isinstance(args[0], (list, tuple)):
468                self.first = self.type(args[0][0])
469                self.second = self.type(args[0][1])
470            else:
471                self.first = self.type(0)
472                self.second = self.type(args[0]) - 1
473
474        elif len(args) == 2:
475            self.first = self.type(args[0])
476            self.second = self.type(args[1])
477        else:
478            raise TypeError, "Too many arguments specified"
479
480        if kwargs:
481            raise TypeError, "too many keywords: %s" % kwargs.keys()
482
483    def __str__(self):
484        return '%s:%s' % (self.first, self.second)
485
486class AddrRange(Range):
487    type = Addr
488    swig_predecls = ['%include "python/swig/range.i"']
489
490    def getValue(self):
491        from m5.objects.params import AddrRange
492
493        value = AddrRange()
494        value.start = long(self.first)
495        value.end = long(self.second)
496        return value
497
498class TickRange(Range):
499    type = Tick
500    swig_predecls = ['%include "python/swig/range.i"']
501
502    def getValue(self):
503        from m5.objects.params import TickRange
504
505        value = TickRange()
506        value.start = long(self.first)
507        value.end = long(self.second)
508        return value
509
510# Boolean parameter type.  Python doesn't let you subclass bool, since
511# it doesn't want to let you create multiple instances of True and
512# False.  Thus this is a little more complicated than String.
513class Bool(ParamValue):
514    cxx_type = 'bool'
515    def __init__(self, value):
516        try:
517            self.value = convert.toBool(value)
518        except TypeError:
519            self.value = bool(value)
520
521    def getValue(self):
522        return bool(self.value)
523
524    def __str__(self):
525        return str(self.value)
526
527    def ini_str(self):
528        if self.value:
529            return 'true'
530        return 'false'
531
532def IncEthernetAddr(addr, val = 1):
533    bytes = map(lambda x: int(x, 16), addr.split(':'))
534    bytes[5] += val
535    for i in (5, 4, 3, 2, 1):
536        val,rem = divmod(bytes[i], 256)
537        bytes[i] = rem
538        if val == 0:
539            break
540        bytes[i - 1] += val
541    assert(bytes[0] <= 255)
542    return ':'.join(map(lambda x: '%02x' % x, bytes))
543
544_NextEthernetAddr = "00:90:00:00:00:01"
545def NextEthernetAddr():
546    global _NextEthernetAddr
547
548    value = _NextEthernetAddr
549    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
550    return value
551
552class EthernetAddr(ParamValue):
553    cxx_type = 'Net::EthAddr'
554    cxx_predecls = ['#include "base/inet.hh"']
555    swig_predecls = ['%include "python/swig/inet.i"']
556    def __init__(self, value):
557        if value == NextEthernetAddr:
558            self.value = value
559            return
560
561        if not isinstance(value, str):
562            raise TypeError, "expected an ethernet address and didn't get one"
563
564        bytes = value.split(':')
565        if len(bytes) != 6:
566            raise TypeError, 'invalid ethernet address %s' % value
567
568        for byte in bytes:
569            if not 0 <= int(byte) <= 256:
570                raise TypeError, 'invalid ethernet address %s' % value
571
572        self.value = value
573
574    def unproxy(self, base):
575        if self.value == NextEthernetAddr:
576            return EthernetAddr(self.value())
577        return self
578
579    def getValue(self):
580        from m5.objects.params import EthAddr
581        return EthAddr(self.value)
582
583    def ini_str(self):
584        return self.value
585
586time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
587                 "%a %b %d %H:%M:%S %Z %Y",
588                 "%Y/%m/%d %H:%M:%S",
589                 "%Y/%m/%d %H:%M",
590                 "%Y/%m/%d",
591                 "%m/%d/%Y %H:%M:%S",
592                 "%m/%d/%Y %H:%M",
593                 "%m/%d/%Y",
594                 "%m/%d/%y %H:%M:%S",
595                 "%m/%d/%y %H:%M",
596                 "%m/%d/%y"]
597
598
599def parse_time(value):
600    from time import gmtime, strptime, struct_time, time
601    from datetime import datetime, date
602
603    if isinstance(value, struct_time):
604        return value
605
606    if isinstance(value, (int, long)):
607        return gmtime(value)
608
609    if isinstance(value, (datetime, date)):
610        return value.timetuple()
611
612    if isinstance(value, str):
613        if value in ('Now', 'Today'):
614            return time.gmtime(time.time())
615
616        for format in time_formats:
617            try:
618                return strptime(value, format)
619            except ValueError:
620                pass
621
622    raise ValueError, "Could not parse '%s' as a time" % value
623
624class Time(ParamValue):
625    cxx_type = 'tm'
626    cxx_predecls = [ '#include <time.h>' ]
627    swig_predecls = [ '%include "python/swig/time.i"' ]
628    def __init__(self, value):
629        self.value = parse_time(value)
630
631    def getValue(self):
632        from m5.objects.params import tm
633
634        c_time = tm()
635        py_time = self.value
636
637        # UNIX is years since 1900
638        c_time.tm_year = py_time.tm_year - 1900;
639
640        # Python starts at 1, UNIX starts at 0
641        c_time.tm_mon =  py_time.tm_mon - 1;
642        c_time.tm_mday = py_time.tm_mday;
643        c_time.tm_hour = py_time.tm_hour;
644        c_time.tm_min = py_time.tm_min;
645        c_time.tm_sec = py_time.tm_sec;
646
647        # Python has 0 as Monday, UNIX is 0 as sunday
648        c_time.tm_wday = py_time.tm_wday + 1
649        if c_time.tm_wday > 6:
650            c_time.tm_wday -= 7;
651
652        # Python starts at 1, Unix starts at 0
653        c_time.tm_yday = py_time.tm_yday - 1;
654
655        return c_time
656
657    def __str__(self):
658        return time.asctime(self.value)
659
660    def ini_str(self):
661        return str(self)
662
663# Enumerated types are a little more complex.  The user specifies the
664# type as Enum(foo) where foo is either a list or dictionary of
665# alternatives (typically strings, but not necessarily so).  (In the
666# long run, the integer value of the parameter will be the list index
667# or the corresponding dictionary value.  For now, since we only check
668# that the alternative is valid and then spit it into a .ini file,
669# there's not much point in using the dictionary.)
670
671# What Enum() must do is generate a new type encapsulating the
672# provided list/dictionary so that specific values of the parameter
673# can be instances of that type.  We define two hidden internal
674# classes (_ListEnum and _DictEnum) to serve as base classes, then
675# derive the new type from the appropriate base class on the fly.
676
677allEnums = {}
678# Metaclass for Enum types
679class MetaEnum(MetaParamValue):
680    def __new__(mcls, name, bases, dict):
681        assert name not in allEnums
682
683        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
684        allEnums[name] = cls
685        return cls
686
687    def __init__(cls, name, bases, init_dict):
688        if init_dict.has_key('map'):
689            if not isinstance(cls.map, dict):
690                raise TypeError, "Enum-derived class attribute 'map' " \
691                      "must be of type dict"
692            # build list of value strings from map
693            cls.vals = cls.map.keys()
694            cls.vals.sort()
695        elif init_dict.has_key('vals'):
696            if not isinstance(cls.vals, list):
697                raise TypeError, "Enum-derived class attribute 'vals' " \
698                      "must be of type list"
699            # build string->value map from vals sequence
700            cls.map = {}
701            for idx,val in enumerate(cls.vals):
702                cls.map[val] = idx
703        else:
704            raise TypeError, "Enum-derived class must define "\
705                  "attribute 'map' or 'vals'"
706
707        cls.cxx_type = 'Enums::%s' % name
708
709        super(MetaEnum, cls).__init__(name, bases, init_dict)
710
711    # Generate C++ class declaration for this enum type.
712    # Note that we wrap the enum in a class/struct to act as a namespace,
713    # so that the enum strings can be brief w/o worrying about collisions.
714    def cxx_decl(cls):
715        name = cls.__name__
716        code = "#ifndef __ENUM__%s\n" % name
717        code += '#define __ENUM__%s\n' % name
718        code += '\n'
719        code += 'namespace Enums {\n'
720        code += '    enum %s {\n' % name
721        for val in cls.vals:
722            code += '        %s = %d,\n' % (val, cls.map[val])
723        code += '        Num_%s = %d,\n' % (name, len(cls.vals))
724        code += '    };\n'
725        code += '    extern const char *%sStrings[Num_%s];\n' % (name, name)
726        code += '}\n'
727        code += '\n'
728        code += '#endif\n'
729        return code
730
731    def cxx_def(cls):
732        name = cls.__name__
733        code = '#include "enums/%s.hh"\n' % name
734        code += 'namespace Enums {\n'
735        code += '    const char *%sStrings[Num_%s] =\n' % (name, name)
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 "base/types.hh"']
766    swig_predecls = ['%import "stdint.i"\n' +
767                     '%import "base/types.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 "base/types.hh"']
844    swig_predecls = ['%import "stdint.i"\n' +
845                     '%import "base/types.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.peer = None
1004        self.peer = other
1005        if proxy.isproxy(other):
1006            other.set_param_desc(PortParamDesc())
1007        elif isinstance(other, PortRef):
1008            if other.peer is not self:
1009                other.connect(self)
1010        else:
1011            raise TypeError, \
1012                  "assigning non-port reference '%s' to port '%s'" \
1013                  % (other, self)
1014
1015    def clone(self, simobj, memo):
1016        if memo.has_key(self):
1017            return memo[self]
1018        newRef = copy.copy(self)
1019        memo[self] = newRef
1020        newRef.simobj = simobj
1021        assert(isSimObject(newRef.simobj))
1022        if self.peer and not proxy.isproxy(self.peer):
1023            peerObj = self.peer.simobj(_memo=memo)
1024            newRef.peer = self.peer.clone(peerObj, memo)
1025            assert(not isinstance(newRef.peer, VectorPortRef))
1026        return newRef
1027
1028    def unproxy(self, simobj):
1029        assert(simobj is self.simobj)
1030        if proxy.isproxy(self.peer):
1031            try:
1032                realPeer = self.peer.unproxy(self.simobj)
1033            except:
1034                print "Error in unproxying port '%s' of %s" % \
1035                      (self.name, self.simobj.path())
1036                raise
1037            self.connect(realPeer)
1038
1039    # Call C++ to create corresponding port connection between C++ objects
1040    def ccConnect(self):
1041        from m5.objects.params import connectPorts
1042
1043        if self.ccConnected: # already done this
1044            return
1045        peer = self.peer
1046        if not self.peer: # nothing to connect to
1047            return
1048        connectPorts(self.simobj.getCCObject(), self.name, self.index,
1049                     peer.simobj.getCCObject(), peer.name, peer.index)
1050        self.ccConnected = True
1051        peer.ccConnected = True
1052
1053# A reference to an individual element of a VectorPort... much like a
1054# PortRef, but has an index.
1055class VectorPortElementRef(PortRef):
1056    def __init__(self, simobj, name, index):
1057        PortRef.__init__(self, simobj, name)
1058        self.index = index
1059
1060    def __str__(self):
1061        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1062
1063# A reference to a complete vector-valued port (not just a single element).
1064# Can be indexed to retrieve individual VectorPortElementRef instances.
1065class VectorPortRef(object):
1066    def __init__(self, simobj, name):
1067        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1068        self.simobj = simobj
1069        self.name = name
1070        self.elements = []
1071
1072    def __str__(self):
1073        return '%s.%s[:]' % (self.simobj, self.name)
1074
1075    # for config.ini, print peer's name (not ours)
1076    def ini_str(self):
1077        return ' '.join([el.ini_str() for el in self.elements])
1078
1079    def __getitem__(self, key):
1080        if not isinstance(key, int):
1081            raise TypeError, "VectorPort index must be integer"
1082        if key >= len(self.elements):
1083            # need to extend list
1084            ext = [VectorPortElementRef(self.simobj, self.name, i)
1085                   for i in range(len(self.elements), key+1)]
1086            self.elements.extend(ext)
1087        return self.elements[key]
1088
1089    def _get_next(self):
1090        return self[len(self.elements)]
1091
1092    def __setitem__(self, key, value):
1093        if not isinstance(key, int):
1094            raise TypeError, "VectorPort index must be integer"
1095        self[key].connect(value)
1096
1097    def connect(self, other):
1098        if isinstance(other, (list, tuple)):
1099            # Assign list of port refs to vector port.
1100            # For now, append them... not sure if that's the right semantics
1101            # or if it should replace the current vector.
1102            for ref in other:
1103                self._get_next().connect(ref)
1104        else:
1105            # scalar assignment to plain VectorPort is implicit append
1106            self._get_next().connect(other)
1107
1108    def clone(self, simobj, memo):
1109        if memo.has_key(self):
1110            return memo[self]
1111        newRef = copy.copy(self)
1112        memo[self] = newRef
1113        newRef.simobj = simobj
1114        assert(isSimObject(newRef.simobj))
1115        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1116        return newRef
1117
1118    def unproxy(self, simobj):
1119        [el.unproxy(simobj) for el in self.elements]
1120
1121    def ccConnect(self):
1122        [el.ccConnect() for el in self.elements]
1123
1124# Port description object.  Like a ParamDesc object, this represents a
1125# logical port in the SimObject class, not a particular port on a
1126# SimObject instance.  The latter are represented by PortRef objects.
1127class Port(object):
1128    # Port("description") or Port(default, "description")
1129    def __init__(self, *args):
1130        if len(args) == 1:
1131            self.desc = args[0]
1132        elif len(args) == 2:
1133            self.default = args[0]
1134            self.desc = args[1]
1135        else:
1136            raise TypeError, 'wrong number of arguments'
1137        # self.name is set by SimObject class on assignment
1138        # e.g., pio_port = Port("blah") sets self.name to 'pio_port'
1139
1140    # Generate a PortRef for this port on the given SimObject with the
1141    # given name
1142    def makeRef(self, simobj):
1143        return PortRef(simobj, self.name)
1144
1145    # Connect an instance of this port (on the given SimObject with
1146    # the given name) with the port described by the supplied PortRef
1147    def connect(self, simobj, ref):
1148        self.makeRef(simobj).connect(ref)
1149
1150# VectorPort description object.  Like Port, but represents a vector
1151# of connections (e.g., as on a Bus).
1152class VectorPort(Port):
1153    def __init__(self, *args):
1154        Port.__init__(self, *args)
1155        self.isVec = True
1156
1157    def makeRef(self, simobj):
1158        return VectorPortRef(simobj, self.name)
1159
1160# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
1161# proxy objects (via set_param_desc()) so that proxy error messages
1162# make sense.
1163class PortParamDesc(object):
1164    __metaclass__ = Singleton
1165
1166    ptype_str = 'Port'
1167    ptype = Port
1168
1169baseEnums = allEnums.copy()
1170baseParams = allParams.copy()
1171
1172def clear():
1173    global allEnums, allParams
1174
1175    allEnums = baseEnums.copy()
1176    allParams = baseParams.copy()
1177
1178__all__ = ['Param', 'VectorParam',
1179           'Enum', 'Bool', 'String', 'Float',
1180           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1181           'Int32', 'UInt32', 'Int64', 'UInt64',
1182           'Counter', 'Addr', 'Tick', 'Percent',
1183           'TcpPort', 'UdpPort', 'EthernetAddr',
1184           'MemorySize', 'MemorySize32',
1185           'Latency', 'Frequency', 'Clock',
1186           'NetworkBandwidth', 'MemoryBandwidth',
1187           'Range', 'AddrRange', 'TickRange',
1188           'MaxAddr', 'MaxTick', 'AllMemory',
1189           'Time',
1190           'NextEthernetAddr', 'NULL',
1191           'Port', 'VectorPort']
1192
1193import SimObject
1194