params.py revision 11228:021524c21cbc
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.internal") ${{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.internal.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' % (self.start, self.end)
782
783    def size(self):
784        # Divide the size by the size of the interleaving slice
785        return (long(self.end) - long(self.start) + 1) >> self.intlvBits
786
787    @classmethod
788    def cxx_predecls(cls, code):
789        Addr.cxx_predecls(code)
790        code('#include "base/addr_range.hh"')
791
792    @classmethod
793    def swig_predecls(cls, code):
794        Addr.swig_predecls(code)
795
796    @classmethod
797    def cxx_ini_predecls(cls, code):
798        code('#include <sstream>')
799
800    @classmethod
801    def cxx_ini_parse(cls, code, src, dest, ret):
802        code('uint64_t _start, _end;')
803        code('char _sep;')
804        code('std::istringstream _stream(${src});')
805        code('_stream >> _start;')
806        code('_stream.get(_sep);')
807        code('_stream >> _end;')
808        code('bool _ret = !_stream.fail() &&'
809            '_stream.eof() && _sep == \':\';')
810        code('if (_ret)')
811        code('   ${dest} = AddrRange(_start, _end);')
812        code('${ret} _ret;')
813
814    def getValue(self):
815        # Go from the Python class to the wrapped C++ class generated
816        # by swig
817        from m5.internal.range import AddrRange
818
819        return AddrRange(long(self.start), long(self.end),
820                         int(self.intlvHighBit), int(self.xorHighBit),
821                         int(self.intlvBits), int(self.intlvMatch))
822
823# Boolean parameter type.  Python doesn't let you subclass bool, since
824# it doesn't want to let you create multiple instances of True and
825# False.  Thus this is a little more complicated than String.
826class Bool(ParamValue):
827    cxx_type = 'bool'
828    cmd_line_settable = True
829
830    def __init__(self, value):
831        try:
832            self.value = convert.toBool(value)
833        except TypeError:
834            self.value = bool(value)
835
836    def __call__(self, value):
837        self.__init__(value)
838        return value
839
840    def getValue(self):
841        return bool(self.value)
842
843    def __str__(self):
844        return str(self.value)
845
846    # implement truth value testing for Bool parameters so that these params
847    # evaluate correctly during the python configuration phase
848    def __nonzero__(self):
849        return bool(self.value)
850
851    def ini_str(self):
852        if self.value:
853            return 'true'
854        return 'false'
855
856    def config_value(self):
857        return self.value
858
859    @classmethod
860    def cxx_ini_predecls(cls, code):
861        # Assume that base/str.hh will be included anyway
862        # code('#include "base/str.hh"')
863        pass
864
865    @classmethod
866    def cxx_ini_parse(cls, code, src, dest, ret):
867        code('%s to_bool(%s, %s);' % (ret, src, dest))
868
869def IncEthernetAddr(addr, val = 1):
870    bytes = map(lambda x: int(x, 16), addr.split(':'))
871    bytes[5] += val
872    for i in (5, 4, 3, 2, 1):
873        val,rem = divmod(bytes[i], 256)
874        bytes[i] = rem
875        if val == 0:
876            break
877        bytes[i - 1] += val
878    assert(bytes[0] <= 255)
879    return ':'.join(map(lambda x: '%02x' % x, bytes))
880
881_NextEthernetAddr = "00:90:00:00:00:01"
882def NextEthernetAddr():
883    global _NextEthernetAddr
884
885    value = _NextEthernetAddr
886    _NextEthernetAddr = IncEthernetAddr(_NextEthernetAddr, 1)
887    return value
888
889class EthernetAddr(ParamValue):
890    cxx_type = 'Net::EthAddr'
891    ex_str = "00:90:00:00:00:01"
892    cmd_line_settable = True
893
894    @classmethod
895    def cxx_predecls(cls, code):
896        code('#include "base/inet.hh"')
897
898    @classmethod
899    def swig_predecls(cls, code):
900        code('%include "python/swig/inet.i"')
901
902    def __init__(self, value):
903        if value == NextEthernetAddr:
904            self.value = value
905            return
906
907        if not isinstance(value, str):
908            raise TypeError, "expected an ethernet address and didn't get one"
909
910        bytes = value.split(':')
911        if len(bytes) != 6:
912            raise TypeError, 'invalid ethernet address %s' % value
913
914        for byte in bytes:
915            if not 0 <= int(byte, base=16) <= 0xff:
916                raise TypeError, 'invalid ethernet address %s' % value
917
918        self.value = value
919
920    def __call__(self, value):
921        self.__init__(value)
922        return value
923
924    def unproxy(self, base):
925        if self.value == NextEthernetAddr:
926            return EthernetAddr(self.value())
927        return self
928
929    def getValue(self):
930        from m5.internal.params import EthAddr
931        return EthAddr(self.value)
932
933    def __str__(self):
934        return self.value
935
936    def ini_str(self):
937        return self.value
938
939    @classmethod
940    def cxx_ini_parse(self, code, src, dest, ret):
941        code('%s = Net::EthAddr(%s);' % (dest, src))
942        code('%s true;' % ret)
943
944# When initializing an IpAddress, pass in an existing IpAddress, a string of
945# the form "a.b.c.d", or an integer representing an IP.
946class IpAddress(ParamValue):
947    cxx_type = 'Net::IpAddress'
948    ex_str = "127.0.0.1"
949    cmd_line_settable = True
950
951    @classmethod
952    def cxx_predecls(cls, code):
953        code('#include "base/inet.hh"')
954
955    @classmethod
956    def swig_predecls(cls, code):
957        code('%include "python/swig/inet.i"')
958
959    def __init__(self, value):
960        if isinstance(value, IpAddress):
961            self.ip = value.ip
962        else:
963            try:
964                self.ip = convert.toIpAddress(value)
965            except TypeError:
966                self.ip = long(value)
967        self.verifyIp()
968
969    def __call__(self, value):
970        self.__init__(value)
971        return value
972
973    def __str__(self):
974        tup = [(self.ip >> i)  & 0xff for i in (24, 16, 8, 0)]
975        return '%d.%d.%d.%d' % tuple(tup)
976
977    def __eq__(self, other):
978        if isinstance(other, IpAddress):
979            return self.ip == other.ip
980        elif isinstance(other, str):
981            try:
982                return self.ip == convert.toIpAddress(other)
983            except:
984                return False
985        else:
986            return self.ip == other
987
988    def __ne__(self, other):
989        return not (self == other)
990
991    def verifyIp(self):
992        if self.ip < 0 or self.ip >= (1 << 32):
993            raise TypeError, "invalid ip address %#08x" % self.ip
994
995    def getValue(self):
996        from m5.internal.params import IpAddress
997        return IpAddress(self.ip)
998
999# When initializing an IpNetmask, pass in an existing IpNetmask, a string of
1000# the form "a.b.c.d/n" or "a.b.c.d/e.f.g.h", or an ip and netmask as
1001# positional or keyword arguments.
1002class IpNetmask(IpAddress):
1003    cxx_type = 'Net::IpNetmask'
1004    ex_str = "127.0.0.0/24"
1005    cmd_line_settable = True
1006
1007    @classmethod
1008    def cxx_predecls(cls, code):
1009        code('#include "base/inet.hh"')
1010
1011    @classmethod
1012    def swig_predecls(cls, code):
1013        code('%include "python/swig/inet.i"')
1014
1015    def __init__(self, *args, **kwargs):
1016        def handle_kwarg(self, kwargs, key, elseVal = None):
1017            if key in kwargs:
1018                setattr(self, key, kwargs.pop(key))
1019            elif elseVal:
1020                setattr(self, key, elseVal)
1021            else:
1022                raise TypeError, "No value set for %s" % key
1023
1024        if len(args) == 0:
1025            handle_kwarg(self, kwargs, 'ip')
1026            handle_kwarg(self, kwargs, 'netmask')
1027
1028        elif len(args) == 1:
1029            if kwargs:
1030                if not 'ip' in kwargs and not 'netmask' in kwargs:
1031                    raise TypeError, "Invalid arguments"
1032                handle_kwarg(self, kwargs, 'ip', args[0])
1033                handle_kwarg(self, kwargs, 'netmask', args[0])
1034            elif isinstance(args[0], IpNetmask):
1035                self.ip = args[0].ip
1036                self.netmask = args[0].netmask
1037            else:
1038                (self.ip, self.netmask) = convert.toIpNetmask(args[0])
1039
1040        elif len(args) == 2:
1041            self.ip = args[0]
1042            self.netmask = args[1]
1043        else:
1044            raise TypeError, "Too many arguments specified"
1045
1046        if kwargs:
1047            raise TypeError, "Too many keywords: %s" % kwargs.keys()
1048
1049        self.verify()
1050
1051    def __call__(self, value):
1052        self.__init__(value)
1053        return value
1054
1055    def __str__(self):
1056        return "%s/%d" % (super(IpNetmask, self).__str__(), self.netmask)
1057
1058    def __eq__(self, other):
1059        if isinstance(other, IpNetmask):
1060            return self.ip == other.ip and self.netmask == other.netmask
1061        elif isinstance(other, str):
1062            try:
1063                return (self.ip, self.netmask) == convert.toIpNetmask(other)
1064            except:
1065                return False
1066        else:
1067            return False
1068
1069    def verify(self):
1070        self.verifyIp()
1071        if self.netmask < 0 or self.netmask > 32:
1072            raise TypeError, "invalid netmask %d" % netmask
1073
1074    def getValue(self):
1075        from m5.internal.params import IpNetmask
1076        return IpNetmask(self.ip, self.netmask)
1077
1078# When initializing an IpWithPort, pass in an existing IpWithPort, a string of
1079# the form "a.b.c.d:p", or an ip and port as positional or keyword arguments.
1080class IpWithPort(IpAddress):
1081    cxx_type = 'Net::IpWithPort'
1082    ex_str = "127.0.0.1:80"
1083    cmd_line_settable = True
1084
1085    @classmethod
1086    def cxx_predecls(cls, code):
1087        code('#include "base/inet.hh"')
1088
1089    @classmethod
1090    def swig_predecls(cls, code):
1091        code('%include "python/swig/inet.i"')
1092
1093    def __init__(self, *args, **kwargs):
1094        def handle_kwarg(self, kwargs, key, elseVal = None):
1095            if key in kwargs:
1096                setattr(self, key, kwargs.pop(key))
1097            elif elseVal:
1098                setattr(self, key, elseVal)
1099            else:
1100                raise TypeError, "No value set for %s" % key
1101
1102        if len(args) == 0:
1103            handle_kwarg(self, kwargs, 'ip')
1104            handle_kwarg(self, kwargs, 'port')
1105
1106        elif len(args) == 1:
1107            if kwargs:
1108                if not 'ip' in kwargs and not 'port' in kwargs:
1109                    raise TypeError, "Invalid arguments"
1110                handle_kwarg(self, kwargs, 'ip', args[0])
1111                handle_kwarg(self, kwargs, 'port', args[0])
1112            elif isinstance(args[0], IpWithPort):
1113                self.ip = args[0].ip
1114                self.port = args[0].port
1115            else:
1116                (self.ip, self.port) = convert.toIpWithPort(args[0])
1117
1118        elif len(args) == 2:
1119            self.ip = args[0]
1120            self.port = args[1]
1121        else:
1122            raise TypeError, "Too many arguments specified"
1123
1124        if kwargs:
1125            raise TypeError, "Too many keywords: %s" % kwargs.keys()
1126
1127        self.verify()
1128
1129    def __call__(self, value):
1130        self.__init__(value)
1131        return value
1132
1133    def __str__(self):
1134        return "%s:%d" % (super(IpWithPort, self).__str__(), self.port)
1135
1136    def __eq__(self, other):
1137        if isinstance(other, IpWithPort):
1138            return self.ip == other.ip and self.port == other.port
1139        elif isinstance(other, str):
1140            try:
1141                return (self.ip, self.port) == convert.toIpWithPort(other)
1142            except:
1143                return False
1144        else:
1145            return False
1146
1147    def verify(self):
1148        self.verifyIp()
1149        if self.port < 0 or self.port > 0xffff:
1150            raise TypeError, "invalid port %d" % self.port
1151
1152    def getValue(self):
1153        from m5.internal.params import IpWithPort
1154        return IpWithPort(self.ip, self.port)
1155
1156time_formats = [ "%a %b %d %H:%M:%S %Z %Y",
1157                 "%a %b %d %H:%M:%S %Y",
1158                 "%Y/%m/%d %H:%M:%S",
1159                 "%Y/%m/%d %H:%M",
1160                 "%Y/%m/%d",
1161                 "%m/%d/%Y %H:%M:%S",
1162                 "%m/%d/%Y %H:%M",
1163                 "%m/%d/%Y",
1164                 "%m/%d/%y %H:%M:%S",
1165                 "%m/%d/%y %H:%M",
1166                 "%m/%d/%y"]
1167
1168
1169def parse_time(value):
1170    from time import gmtime, strptime, struct_time, time
1171    from datetime import datetime, date
1172
1173    if isinstance(value, struct_time):
1174        return value
1175
1176    if isinstance(value, (int, long)):
1177        return gmtime(value)
1178
1179    if isinstance(value, (datetime, date)):
1180        return value.timetuple()
1181
1182    if isinstance(value, str):
1183        if value in ('Now', 'Today'):
1184            return time.gmtime(time.time())
1185
1186        for format in time_formats:
1187            try:
1188                return strptime(value, format)
1189            except ValueError:
1190                pass
1191
1192    raise ValueError, "Could not parse '%s' as a time" % value
1193
1194class Time(ParamValue):
1195    cxx_type = 'tm'
1196
1197    @classmethod
1198    def cxx_predecls(cls, code):
1199        code('#include <time.h>')
1200
1201    @classmethod
1202    def swig_predecls(cls, code):
1203        code('%include "python/swig/time.i"')
1204
1205    def __init__(self, value):
1206        self.value = parse_time(value)
1207
1208    def __call__(self, value):
1209        self.__init__(value)
1210        return value
1211
1212    def getValue(self):
1213        from m5.internal.params import tm
1214
1215        c_time = tm()
1216        py_time = self.value
1217
1218        # UNIX is years since 1900
1219        c_time.tm_year = py_time.tm_year - 1900;
1220
1221        # Python starts at 1, UNIX starts at 0
1222        c_time.tm_mon =  py_time.tm_mon - 1;
1223        c_time.tm_mday = py_time.tm_mday;
1224        c_time.tm_hour = py_time.tm_hour;
1225        c_time.tm_min = py_time.tm_min;
1226        c_time.tm_sec = py_time.tm_sec;
1227
1228        # Python has 0 as Monday, UNIX is 0 as sunday
1229        c_time.tm_wday = py_time.tm_wday + 1
1230        if c_time.tm_wday > 6:
1231            c_time.tm_wday -= 7;
1232
1233        # Python starts at 1, Unix starts at 0
1234        c_time.tm_yday = py_time.tm_yday - 1;
1235
1236        return c_time
1237
1238    def __str__(self):
1239        return time.asctime(self.value)
1240
1241    def ini_str(self):
1242        return str(self)
1243
1244    def get_config_as_dict(self):
1245        assert false
1246        return str(self)
1247
1248    @classmethod
1249    def cxx_ini_predecls(cls, code):
1250        code('#include <time.h>')
1251
1252    @classmethod
1253    def cxx_ini_parse(cls, code, src, dest, ret):
1254        code('char *_parse_ret = strptime((${src}).c_str(),')
1255        code('    "%a %b %d %H:%M:%S %Y", &(${dest}));')
1256        code('${ret} _parse_ret && *_parse_ret == \'\\0\';');
1257
1258# Enumerated types are a little more complex.  The user specifies the
1259# type as Enum(foo) where foo is either a list or dictionary of
1260# alternatives (typically strings, but not necessarily so).  (In the
1261# long run, the integer value of the parameter will be the list index
1262# or the corresponding dictionary value.  For now, since we only check
1263# that the alternative is valid and then spit it into a .ini file,
1264# there's not much point in using the dictionary.)
1265
1266# What Enum() must do is generate a new type encapsulating the
1267# provided list/dictionary so that specific values of the parameter
1268# can be instances of that type.  We define two hidden internal
1269# classes (_ListEnum and _DictEnum) to serve as base classes, then
1270# derive the new type from the appropriate base class on the fly.
1271
1272allEnums = {}
1273# Metaclass for Enum types
1274class MetaEnum(MetaParamValue):
1275    def __new__(mcls, name, bases, dict):
1276        assert name not in allEnums
1277
1278        cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
1279        allEnums[name] = cls
1280        return cls
1281
1282    def __init__(cls, name, bases, init_dict):
1283        if init_dict.has_key('map'):
1284            if not isinstance(cls.map, dict):
1285                raise TypeError, "Enum-derived class attribute 'map' " \
1286                      "must be of type dict"
1287            # build list of value strings from map
1288            cls.vals = cls.map.keys()
1289            cls.vals.sort()
1290        elif init_dict.has_key('vals'):
1291            if not isinstance(cls.vals, list):
1292                raise TypeError, "Enum-derived class attribute 'vals' " \
1293                      "must be of type list"
1294            # build string->value map from vals sequence
1295            cls.map = {}
1296            for idx,val in enumerate(cls.vals):
1297                cls.map[val] = idx
1298        else:
1299            raise TypeError, "Enum-derived class must define "\
1300                  "attribute 'map' or 'vals'"
1301
1302        cls.cxx_type = 'Enums::%s' % name
1303
1304        super(MetaEnum, cls).__init__(name, bases, init_dict)
1305
1306    # Generate C++ class declaration for this enum type.
1307    # Note that we wrap the enum in a class/struct to act as a namespace,
1308    # so that the enum strings can be brief w/o worrying about collisions.
1309    def cxx_decl(cls, code):
1310        wrapper_name = cls.wrapper_name
1311        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1312        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1313        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1314
1315        code('''\
1316#ifndef $idem_macro
1317#define $idem_macro
1318
1319$wrapper $wrapper_name {
1320    enum $name {
1321''')
1322        code.indent(2)
1323        for val in cls.vals:
1324            code('$val = ${{cls.map[val]}},')
1325        code('Num_$name = ${{len(cls.vals)}}')
1326        code.dedent(2)
1327        code('    };')
1328
1329        if cls.wrapper_is_struct:
1330            code('    static const char *${name}Strings[Num_${name}];')
1331            code('};')
1332        else:
1333            code('extern const char *${name}Strings[Num_${name}];')
1334            code('}')
1335
1336        code()
1337        code('#endif // $idem_macro')
1338
1339    def cxx_def(cls, code):
1340        wrapper_name = cls.wrapper_name
1341        file_name = cls.__name__
1342        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1343
1344        code('#include "enums/$file_name.hh"')
1345        if cls.wrapper_is_struct:
1346            code('const char *${wrapper_name}::${name}Strings'
1347                '[Num_${name}] =')
1348        else:
1349            code('namespace Enums {')
1350            code.indent(1)
1351            code(' const char *${name}Strings[Num_${name}] =')
1352
1353        code('{')
1354        code.indent(1)
1355        for val in cls.vals:
1356            code('"$val",')
1357        code.dedent(1)
1358        code('};')
1359
1360        if not cls.wrapper_is_struct:
1361            code('} // namespace $wrapper_name')
1362            code.dedent(1)
1363
1364    def swig_decl(cls, code):
1365        name = cls.__name__
1366        code('''\
1367%module(package="m5.internal") enum_$name
1368
1369%{
1370#include "enums/$name.hh"
1371%}
1372
1373%include "enums/$name.hh"
1374''')
1375
1376
1377# Base class for enum types.
1378class Enum(ParamValue):
1379    __metaclass__ = MetaEnum
1380    vals = []
1381    cmd_line_settable = True
1382
1383    # The name of the wrapping namespace or struct
1384    wrapper_name = 'Enums'
1385
1386    # If true, the enum is wrapped in a struct rather than a namespace
1387    wrapper_is_struct = False
1388
1389    # If not None, use this as the enum name rather than this class name
1390    enum_name = None
1391
1392    def __init__(self, value):
1393        if value not in self.map:
1394            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1395                  % (value, self.vals)
1396        self.value = value
1397
1398    def __call__(self, value):
1399        self.__init__(value)
1400        return value
1401
1402    @classmethod
1403    def cxx_predecls(cls, code):
1404        code('#include "enums/$0.hh"', cls.__name__)
1405
1406    @classmethod
1407    def swig_predecls(cls, code):
1408        code('%import "python/m5/internal/enum_$0.i"', cls.__name__)
1409
1410    @classmethod
1411    def cxx_ini_parse(cls, code, src, dest, ret):
1412        code('if (false) {')
1413        for elem_name in cls.map.iterkeys():
1414            code('} else if (%s == "%s") {' % (src, elem_name))
1415            code.indent()
1416            code('%s = Enums::%s;' % (dest, elem_name))
1417            code('%s true;' % ret)
1418            code.dedent()
1419        code('} else {')
1420        code('    %s false;' % ret)
1421        code('}')
1422
1423    def getValue(self):
1424        return int(self.map[self.value])
1425
1426    def __str__(self):
1427        return self.value
1428
1429# how big does a rounding error need to be before we warn about it?
1430frequency_tolerance = 0.001  # 0.1%
1431
1432class TickParamValue(NumericParamValue):
1433    cxx_type = 'Tick'
1434    ex_str = "1MHz"
1435    cmd_line_settable = True
1436
1437    @classmethod
1438    def cxx_predecls(cls, code):
1439        code('#include "base/types.hh"')
1440
1441    @classmethod
1442    def swig_predecls(cls, code):
1443        code('%import "stdint.i"')
1444        code('%import "base/types.hh"')
1445
1446    def __call__(self, value):
1447        self.__init__(value)
1448        return value
1449
1450    def getValue(self):
1451        return long(self.value)
1452
1453    @classmethod
1454    def cxx_ini_predecls(cls, code):
1455        code('#include <sstream>')
1456
1457    # Ticks are expressed in seconds in JSON files and in plain
1458    # Ticks in .ini files.  Switch based on a config flag
1459    @classmethod
1460    def cxx_ini_parse(self, code, src, dest, ret):
1461        code('${ret} to_number(${src}, ${dest});')
1462
1463class Latency(TickParamValue):
1464    ex_str = "100ns"
1465
1466    def __init__(self, value):
1467        if isinstance(value, (Latency, Clock)):
1468            self.ticks = value.ticks
1469            self.value = value.value
1470        elif isinstance(value, Frequency):
1471            self.ticks = value.ticks
1472            self.value = 1.0 / value.value
1473        elif value.endswith('t'):
1474            self.ticks = True
1475            self.value = int(value[:-1])
1476        else:
1477            self.ticks = False
1478            self.value = convert.toLatency(value)
1479
1480    def __call__(self, value):
1481        self.__init__(value)
1482        return value
1483
1484    def __getattr__(self, attr):
1485        if attr in ('latency', 'period'):
1486            return self
1487        if attr == 'frequency':
1488            return Frequency(self)
1489        raise AttributeError, "Latency object has no attribute '%s'" % attr
1490
1491    def getValue(self):
1492        if self.ticks or self.value == 0:
1493            value = self.value
1494        else:
1495            value = ticks.fromSeconds(self.value)
1496        return long(value)
1497
1498    def config_value(self):
1499        return self.getValue()
1500
1501    # convert latency to ticks
1502    def ini_str(self):
1503        return '%d' % self.getValue()
1504
1505class Frequency(TickParamValue):
1506    ex_str = "1GHz"
1507
1508    def __init__(self, value):
1509        if isinstance(value, (Latency, Clock)):
1510            if value.value == 0:
1511                self.value = 0
1512            else:
1513                self.value = 1.0 / value.value
1514            self.ticks = value.ticks
1515        elif isinstance(value, Frequency):
1516            self.value = value.value
1517            self.ticks = value.ticks
1518        else:
1519            self.ticks = False
1520            self.value = convert.toFrequency(value)
1521
1522    def __call__(self, value):
1523        self.__init__(value)
1524        return value
1525
1526    def __getattr__(self, attr):
1527        if attr == 'frequency':
1528            return self
1529        if attr in ('latency', 'period'):
1530            return Latency(self)
1531        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1532
1533    # convert latency to ticks
1534    def getValue(self):
1535        if self.ticks or self.value == 0:
1536            value = self.value
1537        else:
1538            value = ticks.fromSeconds(1.0 / self.value)
1539        return long(value)
1540
1541    def config_value(self):
1542        return self.getValue()
1543
1544    def ini_str(self):
1545        return '%d' % self.getValue()
1546
1547# A generic Frequency and/or Latency value. Value is stored as a
1548# latency, just like Latency and Frequency.
1549class Clock(TickParamValue):
1550    def __init__(self, value):
1551        if isinstance(value, (Latency, Clock)):
1552            self.ticks = value.ticks
1553            self.value = value.value
1554        elif isinstance(value, Frequency):
1555            self.ticks = value.ticks
1556            self.value = 1.0 / value.value
1557        elif value.endswith('t'):
1558            self.ticks = True
1559            self.value = int(value[:-1])
1560        else:
1561            self.ticks = False
1562            self.value = convert.anyToLatency(value)
1563
1564    def __call__(self, value):
1565        self.__init__(value)
1566        return value
1567
1568    def __str__(self):
1569        return "%s" % Latency(self)
1570
1571    def __getattr__(self, attr):
1572        if attr == 'frequency':
1573            return Frequency(self)
1574        if attr in ('latency', 'period'):
1575            return Latency(self)
1576        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1577
1578    def getValue(self):
1579        return self.period.getValue()
1580
1581    def config_value(self):
1582        return self.period.config_value()
1583
1584    def ini_str(self):
1585        return self.period.ini_str()
1586
1587class Voltage(float,ParamValue):
1588    cxx_type = 'double'
1589    ex_str = "1V"
1590    cmd_line_settable = False
1591
1592    def __new__(cls, value):
1593        # convert to voltage
1594        val = convert.toVoltage(value)
1595        return super(cls, Voltage).__new__(cls, val)
1596
1597    def __call__(self, value):
1598        val = convert.toVoltage(value)
1599        self.__init__(val)
1600        return value
1601
1602    def __str__(self):
1603        return str(self.getValue())
1604
1605    def getValue(self):
1606        value = float(self)
1607        return value
1608
1609    def ini_str(self):
1610        return '%f' % self.getValue()
1611
1612    @classmethod
1613    def cxx_ini_predecls(cls, code):
1614        code('#include <sstream>')
1615
1616    @classmethod
1617    def cxx_ini_parse(self, code, src, dest, ret):
1618        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1619
1620class Current(float, ParamValue):
1621    cxx_type = 'double'
1622    ex_str = "1mA"
1623    cmd_line_settable = False
1624
1625    def __new__(cls, value):
1626        # convert to current
1627        val = convert.toCurrent(value)
1628        return super(cls, Current).__new__(cls, val)
1629
1630    def __call__(self, value):
1631        val = convert.toCurrent(value)
1632        self.__init__(val)
1633        return value
1634
1635    def __str__(self):
1636        return str(self.getValue())
1637
1638    def getValue(self):
1639        value = float(self)
1640        return value
1641
1642    def ini_str(self):
1643        return '%f' % self.getValue()
1644
1645    @classmethod
1646    def cxx_ini_predecls(cls, code):
1647        code('#include <sstream>')
1648
1649    @classmethod
1650    def cxx_ini_parse(self, code, src, dest, ret):
1651        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1652
1653class NetworkBandwidth(float,ParamValue):
1654    cxx_type = 'float'
1655    ex_str = "1Gbps"
1656    cmd_line_settable = True
1657
1658    def __new__(cls, value):
1659        # convert to bits per second
1660        val = convert.toNetworkBandwidth(value)
1661        return super(cls, NetworkBandwidth).__new__(cls, val)
1662
1663    def __str__(self):
1664        return str(self.val)
1665
1666    def __call__(self, value):
1667        val = convert.toNetworkBandwidth(value)
1668        self.__init__(val)
1669        return value
1670
1671    def getValue(self):
1672        # convert to seconds per byte
1673        value = 8.0 / float(self)
1674        # convert to ticks per byte
1675        value = ticks.fromSeconds(value)
1676        return float(value)
1677
1678    def ini_str(self):
1679        return '%f' % self.getValue()
1680
1681    def config_value(self):
1682        return '%f' % self.getValue()
1683
1684    @classmethod
1685    def cxx_ini_predecls(cls, code):
1686        code('#include <sstream>')
1687
1688    @classmethod
1689    def cxx_ini_parse(self, code, src, dest, ret):
1690        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1691
1692class MemoryBandwidth(float,ParamValue):
1693    cxx_type = 'float'
1694    ex_str = "1GB/s"
1695    cmd_line_settable = True
1696
1697    def __new__(cls, value):
1698        # convert to bytes per second
1699        val = convert.toMemoryBandwidth(value)
1700        return super(cls, MemoryBandwidth).__new__(cls, val)
1701
1702    def __call__(self, value):
1703        val = convert.toMemoryBandwidth(value)
1704        self.__init__(val)
1705        return value
1706
1707    def getValue(self):
1708        # convert to seconds per byte
1709        value = float(self)
1710        if value:
1711            value = 1.0 / float(self)
1712        # convert to ticks per byte
1713        value = ticks.fromSeconds(value)
1714        return float(value)
1715
1716    def ini_str(self):
1717        return '%f' % self.getValue()
1718
1719    def config_value(self):
1720        return '%f' % self.getValue()
1721
1722    @classmethod
1723    def cxx_ini_predecls(cls, code):
1724        code('#include <sstream>')
1725
1726    @classmethod
1727    def cxx_ini_parse(self, code, src, dest, ret):
1728        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1729
1730#
1731# "Constants"... handy aliases for various values.
1732#
1733
1734# Special class for NULL pointers.  Note the special check in
1735# make_param_value() above that lets these be assigned where a
1736# SimObject is required.
1737# only one copy of a particular node
1738class NullSimObject(object):
1739    __metaclass__ = Singleton
1740
1741    def __call__(cls):
1742        return cls
1743
1744    def _instantiate(self, parent = None, path = ''):
1745        pass
1746
1747    def ini_str(self):
1748        return 'Null'
1749
1750    def unproxy(self, base):
1751        return self
1752
1753    def set_path(self, parent, name):
1754        pass
1755
1756    def __str__(self):
1757        return 'Null'
1758
1759    def config_value(self):
1760        return None
1761
1762    def getValue(self):
1763        return None
1764
1765# The only instance you'll ever need...
1766NULL = NullSimObject()
1767
1768def isNullPointer(value):
1769    return isinstance(value, NullSimObject)
1770
1771# Some memory range specifications use this as a default upper bound.
1772MaxAddr = Addr.max
1773MaxTick = Tick.max
1774AllMemory = AddrRange(0, MaxAddr)
1775
1776
1777#####################################################################
1778#
1779# Port objects
1780#
1781# Ports are used to interconnect objects in the memory system.
1782#
1783#####################################################################
1784
1785# Port reference: encapsulates a reference to a particular port on a
1786# particular SimObject.
1787class PortRef(object):
1788    def __init__(self, simobj, name, role):
1789        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1790        self.simobj = simobj
1791        self.name = name
1792        self.role = role
1793        self.peer = None   # not associated with another port yet
1794        self.ccConnected = False # C++ port connection done?
1795        self.index = -1  # always -1 for non-vector ports
1796
1797    def __str__(self):
1798        return '%s.%s' % (self.simobj, self.name)
1799
1800    def __len__(self):
1801        # Return the number of connected ports, i.e. 0 is we have no
1802        # peer and 1 if we do.
1803        return int(self.peer != None)
1804
1805    # for config.ini, print peer's name (not ours)
1806    def ini_str(self):
1807        return str(self.peer)
1808
1809    # for config.json
1810    def get_config_as_dict(self):
1811        return {'role' : self.role, 'peer' : str(self.peer)}
1812
1813    def __getattr__(self, attr):
1814        if attr == 'peerObj':
1815            # shorthand for proxies
1816            return self.peer.simobj
1817        raise AttributeError, "'%s' object has no attribute '%s'" % \
1818              (self.__class__.__name__, attr)
1819
1820    # Full connection is symmetric (both ways).  Called via
1821    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1822    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1823    # e.g., "obj1.portA[3] = obj2.portB".
1824    def connect(self, other):
1825        if isinstance(other, VectorPortRef):
1826            # reference to plain VectorPort is implicit append
1827            other = other._get_next()
1828        if self.peer and not proxy.isproxy(self.peer):
1829            fatal("Port %s is already connected to %s, cannot connect %s\n",
1830                  self, self.peer, other);
1831        self.peer = other
1832        if proxy.isproxy(other):
1833            other.set_param_desc(PortParamDesc())
1834        elif isinstance(other, PortRef):
1835            if other.peer is not self:
1836                other.connect(self)
1837        else:
1838            raise TypeError, \
1839                  "assigning non-port reference '%s' to port '%s'" \
1840                  % (other, self)
1841
1842    # Allow a master/slave port pair to be spliced between
1843    # a port and its connected peer. Useful operation for connecting
1844    # instrumentation structures into a system when it is necessary
1845    # to connect the instrumentation after the full system has been
1846    # constructed.
1847    def splice(self, new_master_peer, new_slave_peer):
1848        if self.peer and not proxy.isproxy(self.peer):
1849            if isinstance(new_master_peer, PortRef) and \
1850               isinstance(new_slave_peer, PortRef):
1851                 old_peer = self.peer
1852                 if self.role == 'SLAVE':
1853                     self.peer = new_master_peer
1854                     old_peer.peer = new_slave_peer
1855                     new_master_peer.connect(self)
1856                     new_slave_peer.connect(old_peer)
1857                 elif self.role == 'MASTER':
1858                     self.peer = new_slave_peer
1859                     old_peer.peer = new_master_peer
1860                     new_slave_peer.connect(self)
1861                     new_master_peer.connect(old_peer)
1862                 else:
1863                     panic("Port %s has unknown role, "+\
1864                           "cannot splice in new peers\n", self)
1865            else:
1866                raise TypeError, \
1867                      "Splicing non-port references '%s','%s' to port '%s'"\
1868                      % (new_peer, peers_new_peer, self)
1869        else:
1870            fatal("Port %s not connected, cannot splice in new peers\n", self)
1871
1872    def clone(self, simobj, memo):
1873        if memo.has_key(self):
1874            return memo[self]
1875        newRef = copy.copy(self)
1876        memo[self] = newRef
1877        newRef.simobj = simobj
1878        assert(isSimObject(newRef.simobj))
1879        if self.peer and not proxy.isproxy(self.peer):
1880            peerObj = self.peer.simobj(_memo=memo)
1881            newRef.peer = self.peer.clone(peerObj, memo)
1882            assert(not isinstance(newRef.peer, VectorPortRef))
1883        return newRef
1884
1885    def unproxy(self, simobj):
1886        assert(simobj is self.simobj)
1887        if proxy.isproxy(self.peer):
1888            try:
1889                realPeer = self.peer.unproxy(self.simobj)
1890            except:
1891                print "Error in unproxying port '%s' of %s" % \
1892                      (self.name, self.simobj.path())
1893                raise
1894            self.connect(realPeer)
1895
1896    # Call C++ to create corresponding port connection between C++ objects
1897    def ccConnect(self):
1898        from m5.internal.pyobject import connectPorts
1899
1900        if self.role == 'SLAVE':
1901            # do nothing and let the master take care of it
1902            return
1903
1904        if self.ccConnected: # already done this
1905            return
1906        peer = self.peer
1907        if not self.peer: # nothing to connect to
1908            return
1909
1910        # check that we connect a master to a slave
1911        if self.role == peer.role:
1912            raise TypeError, \
1913                "cannot connect '%s' and '%s' due to identical role '%s'" \
1914                % (peer, self, self.role)
1915
1916        try:
1917            # self is always the master and peer the slave
1918            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1919                         peer.simobj.getCCObject(), peer.name, peer.index)
1920        except:
1921            print "Error connecting port %s.%s to %s.%s" % \
1922                  (self.simobj.path(), self.name,
1923                   peer.simobj.path(), peer.name)
1924            raise
1925        self.ccConnected = True
1926        peer.ccConnected = True
1927
1928# A reference to an individual element of a VectorPort... much like a
1929# PortRef, but has an index.
1930class VectorPortElementRef(PortRef):
1931    def __init__(self, simobj, name, role, index):
1932        PortRef.__init__(self, simobj, name, role)
1933        self.index = index
1934
1935    def __str__(self):
1936        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1937
1938# A reference to a complete vector-valued port (not just a single element).
1939# Can be indexed to retrieve individual VectorPortElementRef instances.
1940class VectorPortRef(object):
1941    def __init__(self, simobj, name, role):
1942        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1943        self.simobj = simobj
1944        self.name = name
1945        self.role = role
1946        self.elements = []
1947
1948    def __str__(self):
1949        return '%s.%s[:]' % (self.simobj, self.name)
1950
1951    def __len__(self):
1952        # Return the number of connected peers, corresponding the the
1953        # length of the elements.
1954        return len(self.elements)
1955
1956    # for config.ini, print peer's name (not ours)
1957    def ini_str(self):
1958        return ' '.join([el.ini_str() for el in self.elements])
1959
1960    # for config.json
1961    def get_config_as_dict(self):
1962        return {'role' : self.role,
1963                'peer' : [el.ini_str() for el in self.elements]}
1964
1965    def __getitem__(self, key):
1966        if not isinstance(key, int):
1967            raise TypeError, "VectorPort index must be integer"
1968        if key >= len(self.elements):
1969            # need to extend list
1970            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1971                   for i in range(len(self.elements), key+1)]
1972            self.elements.extend(ext)
1973        return self.elements[key]
1974
1975    def _get_next(self):
1976        return self[len(self.elements)]
1977
1978    def __setitem__(self, key, value):
1979        if not isinstance(key, int):
1980            raise TypeError, "VectorPort index must be integer"
1981        self[key].connect(value)
1982
1983    def connect(self, other):
1984        if isinstance(other, (list, tuple)):
1985            # Assign list of port refs to vector port.
1986            # For now, append them... not sure if that's the right semantics
1987            # or if it should replace the current vector.
1988            for ref in other:
1989                self._get_next().connect(ref)
1990        else:
1991            # scalar assignment to plain VectorPort is implicit append
1992            self._get_next().connect(other)
1993
1994    def clone(self, simobj, memo):
1995        if memo.has_key(self):
1996            return memo[self]
1997        newRef = copy.copy(self)
1998        memo[self] = newRef
1999        newRef.simobj = simobj
2000        assert(isSimObject(newRef.simobj))
2001        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
2002        return newRef
2003
2004    def unproxy(self, simobj):
2005        [el.unproxy(simobj) for el in self.elements]
2006
2007    def ccConnect(self):
2008        [el.ccConnect() for el in self.elements]
2009
2010# Port description object.  Like a ParamDesc object, this represents a
2011# logical port in the SimObject class, not a particular port on a
2012# SimObject instance.  The latter are represented by PortRef objects.
2013class Port(object):
2014    # Generate a PortRef for this port on the given SimObject with the
2015    # given name
2016    def makeRef(self, simobj):
2017        return PortRef(simobj, self.name, self.role)
2018
2019    # Connect an instance of this port (on the given SimObject with
2020    # the given name) with the port described by the supplied PortRef
2021    def connect(self, simobj, ref):
2022        self.makeRef(simobj).connect(ref)
2023
2024    # No need for any pre-declarations at the moment as we merely rely
2025    # on an unsigned int.
2026    def cxx_predecls(self, code):
2027        pass
2028
2029    # Declare an unsigned int with the same name as the port, that
2030    # will eventually hold the number of connected ports (and thus the
2031    # number of elements for a VectorPort).
2032    def cxx_decl(self, code):
2033        code('unsigned int port_${{self.name}}_connection_count;')
2034
2035class MasterPort(Port):
2036    # MasterPort("description")
2037    def __init__(self, *args):
2038        if len(args) == 1:
2039            self.desc = args[0]
2040            self.role = 'MASTER'
2041        else:
2042            raise TypeError, 'wrong number of arguments'
2043
2044class SlavePort(Port):
2045    # SlavePort("description")
2046    def __init__(self, *args):
2047        if len(args) == 1:
2048            self.desc = args[0]
2049            self.role = 'SLAVE'
2050        else:
2051            raise TypeError, 'wrong number of arguments'
2052
2053# VectorPort description object.  Like Port, but represents a vector
2054# of connections (e.g., as on a XBar).
2055class VectorPort(Port):
2056    def __init__(self, *args):
2057        self.isVec = True
2058
2059    def makeRef(self, simobj):
2060        return VectorPortRef(simobj, self.name, self.role)
2061
2062class VectorMasterPort(VectorPort):
2063    # VectorMasterPort("description")
2064    def __init__(self, *args):
2065        if len(args) == 1:
2066            self.desc = args[0]
2067            self.role = 'MASTER'
2068            VectorPort.__init__(self, *args)
2069        else:
2070            raise TypeError, 'wrong number of arguments'
2071
2072class VectorSlavePort(VectorPort):
2073    # VectorSlavePort("description")
2074    def __init__(self, *args):
2075        if len(args) == 1:
2076            self.desc = args[0]
2077            self.role = 'SLAVE'
2078            VectorPort.__init__(self, *args)
2079        else:
2080            raise TypeError, 'wrong number of arguments'
2081
2082# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2083# proxy objects (via set_param_desc()) so that proxy error messages
2084# make sense.
2085class PortParamDesc(object):
2086    __metaclass__ = Singleton
2087
2088    ptype_str = 'Port'
2089    ptype = Port
2090
2091baseEnums = allEnums.copy()
2092baseParams = allParams.copy()
2093
2094def clear():
2095    global allEnums, allParams
2096
2097    allEnums = baseEnums.copy()
2098    allParams = baseParams.copy()
2099
2100__all__ = ['Param', 'VectorParam',
2101           'Enum', 'Bool', 'String', 'Float',
2102           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2103           'Int32', 'UInt32', 'Int64', 'UInt64',
2104           'Counter', 'Addr', 'Tick', 'Percent',
2105           'TcpPort', 'UdpPort', 'EthernetAddr',
2106           'IpAddress', 'IpNetmask', 'IpWithPort',
2107           'MemorySize', 'MemorySize32',
2108           'Latency', 'Frequency', 'Clock', 'Voltage',
2109           'NetworkBandwidth', 'MemoryBandwidth',
2110           'AddrRange',
2111           'MaxAddr', 'MaxTick', 'AllMemory',
2112           'Time',
2113           'NextEthernetAddr', 'NULL',
2114           'MasterPort', 'SlavePort',
2115           'VectorMasterPort', 'VectorSlavePort']
2116
2117import SimObject
2118