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