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