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