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