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