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