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