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