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