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