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