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