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