params.py revision 12563
1# Copyright (c) 2012-2014, 2017 ARM Limited
2# All rights reserved.
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Copyright (c) 2004-2006 The Regents of The University of Michigan
14# Copyright (c) 2010-2011 Advanced Micro Devices, Inc.
15# All rights reserved.
16#
17# Redistribution and use in source and binary forms, with or without
18# modification, are permitted provided that the following conditions are
19# met: redistributions of source code must retain the above copyright
20# notice, this list of conditions and the following disclaimer;
21# redistributions in binary form must reproduce the above copyright
22# notice, this list of conditions and the following disclaimer in the
23# documentation and/or other materials provided with the distribution;
24# neither the name of the copyright holders nor the names of its
25# contributors may be used to endorse or promote products derived from
26# this software without specific prior written permission.
27#
28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39#
40# Authors: Steve Reinhardt
41#          Nathan Binkert
42#          Gabe Black
43#          Andreas Hansson
44
45#####################################################################
46#
47# Parameter description classes
48#
49# The _params dictionary in each class maps parameter names to either
50# a Param or a VectorParam object.  These objects contain the
51# parameter description string, the parameter type, and the default
52# value (if any).  The convert() method on these objects is used to
53# force whatever value is assigned to the parameter to the appropriate
54# type.
55#
56# Note that the default values are loaded into the class's attribute
57# space when the parameter dictionary is initialized (in
58# MetaSimObject._new_param()); after that point they aren't used.
59#
60#####################################################################
61
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, \
238              "Not allowed to set %s on '%s'" % (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" % 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" % 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" % 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        cls.cxx_type = 'Enums::%s' % name
1247
1248        super(MetaEnum, cls).__init__(name, bases, init_dict)
1249
1250    # Generate C++ class declaration for this enum type.
1251    # Note that we wrap the enum in a class/struct to act as a namespace,
1252    # so that the enum strings can be brief w/o worrying about collisions.
1253    def cxx_decl(cls, code):
1254        wrapper_name = cls.wrapper_name
1255        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
1256        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1257        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
1258
1259        code('''\
1260#ifndef $idem_macro
1261#define $idem_macro
1262
1263$wrapper $wrapper_name {
1264    enum $name {
1265''')
1266        code.indent(2)
1267        for val in cls.vals:
1268            code('$val = ${{cls.map[val]}},')
1269        code('Num_$name = ${{len(cls.vals)}}')
1270        code.dedent(2)
1271        code('    };')
1272
1273        if cls.wrapper_is_struct:
1274            code('    static const char *${name}Strings[Num_${name}];')
1275            code('};')
1276        else:
1277            code('extern const char *${name}Strings[Num_${name}];')
1278            code('}')
1279
1280        code()
1281        code('#endif // $idem_macro')
1282
1283    def cxx_def(cls, code):
1284        wrapper_name = cls.wrapper_name
1285        file_name = cls.__name__
1286        name = cls.__name__ if cls.enum_name is None else cls.enum_name
1287
1288        code('#include "enums/$file_name.hh"')
1289        if cls.wrapper_is_struct:
1290            code('const char *${wrapper_name}::${name}Strings'
1291                '[Num_${name}] =')
1292        else:
1293            code('namespace Enums {')
1294            code.indent(1)
1295            code(' const char *${name}Strings[Num_${name}] =')
1296
1297        code('{')
1298        code.indent(1)
1299        for val in cls.vals:
1300            code('"$val",')
1301        code.dedent(1)
1302        code('};')
1303
1304        if not cls.wrapper_is_struct:
1305            code('} // namespace $wrapper_name')
1306            code.dedent(1)
1307
1308    def pybind_def(cls, code):
1309        name = cls.__name__
1310        wrapper_name = cls.wrapper_name
1311        enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
1312
1313        code('''#include "pybind11/pybind11.h"
1314#include "pybind11/stl.h"
1315
1316#include <sim/init.hh>
1317
1318namespace py = pybind11;
1319
1320static void
1321module_init(py::module &m_internal)
1322{
1323    py::module m = m_internal.def_submodule("enum_${name}");
1324
1325    py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")
1326''')
1327
1328        code.indent()
1329        code.indent()
1330        for val in cls.vals:
1331            code('.value("${val}", ${wrapper_name}::${val})')
1332        code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
1333        code('.export_values()')
1334        code(';')
1335        code.dedent()
1336
1337        code('}')
1338        code.dedent()
1339        code()
1340        code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
1341
1342
1343# Base class for enum types.
1344class Enum(ParamValue):
1345    __metaclass__ = MetaEnum
1346    vals = []
1347    cmd_line_settable = True
1348
1349    # The name of the wrapping namespace or struct
1350    wrapper_name = 'Enums'
1351
1352    # If true, the enum is wrapped in a struct rather than a namespace
1353    wrapper_is_struct = False
1354
1355    # If not None, use this as the enum name rather than this class name
1356    enum_name = None
1357
1358    def __init__(self, value):
1359        if value not in self.map:
1360            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1361                  % (value, self.vals)
1362        self.value = value
1363
1364    def __call__(self, value):
1365        self.__init__(value)
1366        return value
1367
1368    @classmethod
1369    def cxx_predecls(cls, code):
1370        code('#include "enums/$0.hh"', cls.__name__)
1371
1372    @classmethod
1373    def cxx_ini_parse(cls, code, src, dest, ret):
1374        code('if (false) {')
1375        for elem_name in cls.map.iterkeys():
1376            code('} else if (%s == "%s") {' % (src, elem_name))
1377            code.indent()
1378            code('%s = Enums::%s;' % (dest, elem_name))
1379            code('%s true;' % ret)
1380            code.dedent()
1381        code('} else {')
1382        code('    %s false;' % ret)
1383        code('}')
1384
1385    def getValue(self):
1386        import m5.internal.params
1387        e = getattr(m5.internal.params, "enum_%s" % self.__class__.__name__)
1388        return e(self.map[self.value])
1389
1390    def __str__(self):
1391        return self.value
1392
1393# how big does a rounding error need to be before we warn about it?
1394frequency_tolerance = 0.001  # 0.1%
1395
1396class TickParamValue(NumericParamValue):
1397    cxx_type = 'Tick'
1398    ex_str = "1MHz"
1399    cmd_line_settable = True
1400
1401    @classmethod
1402    def cxx_predecls(cls, code):
1403        code('#include "base/types.hh"')
1404
1405    def __call__(self, value):
1406        self.__init__(value)
1407        return value
1408
1409    def getValue(self):
1410        return long(self.value)
1411
1412    @classmethod
1413    def cxx_ini_predecls(cls, code):
1414        code('#include <sstream>')
1415
1416    # Ticks are expressed in seconds in JSON files and in plain
1417    # Ticks in .ini files.  Switch based on a config flag
1418    @classmethod
1419    def cxx_ini_parse(self, code, src, dest, ret):
1420        code('${ret} to_number(${src}, ${dest});')
1421
1422class Latency(TickParamValue):
1423    ex_str = "100ns"
1424
1425    def __init__(self, value):
1426        if isinstance(value, (Latency, Clock)):
1427            self.ticks = value.ticks
1428            self.value = value.value
1429        elif isinstance(value, Frequency):
1430            self.ticks = value.ticks
1431            self.value = 1.0 / value.value
1432        elif value.endswith('t'):
1433            self.ticks = True
1434            self.value = int(value[:-1])
1435        else:
1436            self.ticks = False
1437            self.value = convert.toLatency(value)
1438
1439    def __call__(self, value):
1440        self.__init__(value)
1441        return value
1442
1443    def __getattr__(self, attr):
1444        if attr in ('latency', 'period'):
1445            return self
1446        if attr == 'frequency':
1447            return Frequency(self)
1448        raise AttributeError, "Latency object has no attribute '%s'" % attr
1449
1450    def getValue(self):
1451        if self.ticks or self.value == 0:
1452            value = self.value
1453        else:
1454            value = ticks.fromSeconds(self.value)
1455        return long(value)
1456
1457    def config_value(self):
1458        return self.getValue()
1459
1460    # convert latency to ticks
1461    def ini_str(self):
1462        return '%d' % self.getValue()
1463
1464class Frequency(TickParamValue):
1465    ex_str = "1GHz"
1466
1467    def __init__(self, value):
1468        if isinstance(value, (Latency, Clock)):
1469            if value.value == 0:
1470                self.value = 0
1471            else:
1472                self.value = 1.0 / value.value
1473            self.ticks = value.ticks
1474        elif isinstance(value, Frequency):
1475            self.value = value.value
1476            self.ticks = value.ticks
1477        else:
1478            self.ticks = False
1479            self.value = convert.toFrequency(value)
1480
1481    def __call__(self, value):
1482        self.__init__(value)
1483        return value
1484
1485    def __getattr__(self, attr):
1486        if attr == 'frequency':
1487            return self
1488        if attr in ('latency', 'period'):
1489            return Latency(self)
1490        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1491
1492    # convert latency to ticks
1493    def getValue(self):
1494        if self.ticks or self.value == 0:
1495            value = self.value
1496        else:
1497            value = ticks.fromSeconds(1.0 / self.value)
1498        return long(value)
1499
1500    def config_value(self):
1501        return self.getValue()
1502
1503    def ini_str(self):
1504        return '%d' % self.getValue()
1505
1506# A generic Frequency and/or Latency value. Value is stored as a
1507# latency, just like Latency and Frequency.
1508class Clock(TickParamValue):
1509    def __init__(self, value):
1510        if isinstance(value, (Latency, Clock)):
1511            self.ticks = value.ticks
1512            self.value = value.value
1513        elif isinstance(value, Frequency):
1514            self.ticks = value.ticks
1515            self.value = 1.0 / value.value
1516        elif value.endswith('t'):
1517            self.ticks = True
1518            self.value = int(value[:-1])
1519        else:
1520            self.ticks = False
1521            self.value = convert.anyToLatency(value)
1522
1523    def __call__(self, value):
1524        self.__init__(value)
1525        return value
1526
1527    def __str__(self):
1528        return "%s" % Latency(self)
1529
1530    def __getattr__(self, attr):
1531        if attr == 'frequency':
1532            return Frequency(self)
1533        if attr in ('latency', 'period'):
1534            return Latency(self)
1535        raise AttributeError, "Frequency object has no attribute '%s'" % attr
1536
1537    def getValue(self):
1538        return self.period.getValue()
1539
1540    def config_value(self):
1541        return self.period.config_value()
1542
1543    def ini_str(self):
1544        return self.period.ini_str()
1545
1546class Voltage(Float):
1547    ex_str = "1V"
1548
1549    def __new__(cls, value):
1550        value = convert.toVoltage(value)
1551        return super(cls, Voltage).__new__(cls, value)
1552
1553    def __init__(self, value):
1554        value = convert.toVoltage(value)
1555        super(Voltage, self).__init__(value)
1556
1557class Current(Float):
1558    ex_str = "1mA"
1559
1560    def __new__(cls, value):
1561        value = convert.toCurrent(value)
1562        return super(cls, Current).__new__(cls, value)
1563
1564    def __init__(self, value):
1565        value = convert.toCurrent(value)
1566        super(Current, self).__init__(value)
1567
1568class Energy(Float):
1569    ex_str = "1pJ"
1570
1571    def __new__(cls, value):
1572        value = convert.toEnergy(value)
1573        return super(cls, Energy).__new__(cls, value)
1574
1575    def __init__(self, value):
1576        value = convert.toEnergy(value)
1577        super(Energy, self).__init__(value)
1578
1579class NetworkBandwidth(float,ParamValue):
1580    cxx_type = 'float'
1581    ex_str = "1Gbps"
1582    cmd_line_settable = True
1583
1584    def __new__(cls, value):
1585        # convert to bits per second
1586        val = convert.toNetworkBandwidth(value)
1587        return super(cls, NetworkBandwidth).__new__(cls, val)
1588
1589    def __str__(self):
1590        return str(self.val)
1591
1592    def __call__(self, value):
1593        val = convert.toNetworkBandwidth(value)
1594        self.__init__(val)
1595        return value
1596
1597    def getValue(self):
1598        # convert to seconds per byte
1599        value = 8.0 / float(self)
1600        # convert to ticks per byte
1601        value = ticks.fromSeconds(value)
1602        return float(value)
1603
1604    def ini_str(self):
1605        return '%f' % self.getValue()
1606
1607    def config_value(self):
1608        return '%f' % self.getValue()
1609
1610    @classmethod
1611    def cxx_ini_predecls(cls, code):
1612        code('#include <sstream>')
1613
1614    @classmethod
1615    def cxx_ini_parse(self, code, src, dest, ret):
1616        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1617
1618class MemoryBandwidth(float,ParamValue):
1619    cxx_type = 'float'
1620    ex_str = "1GB/s"
1621    cmd_line_settable = True
1622
1623    def __new__(cls, value):
1624        # convert to bytes per second
1625        val = convert.toMemoryBandwidth(value)
1626        return super(cls, MemoryBandwidth).__new__(cls, val)
1627
1628    def __call__(self, value):
1629        val = convert.toMemoryBandwidth(value)
1630        self.__init__(val)
1631        return value
1632
1633    def getValue(self):
1634        # convert to seconds per byte
1635        value = float(self)
1636        if value:
1637            value = 1.0 / float(self)
1638        # convert to ticks per byte
1639        value = ticks.fromSeconds(value)
1640        return float(value)
1641
1642    def ini_str(self):
1643        return '%f' % self.getValue()
1644
1645    def config_value(self):
1646        return '%f' % self.getValue()
1647
1648    @classmethod
1649    def cxx_ini_predecls(cls, code):
1650        code('#include <sstream>')
1651
1652    @classmethod
1653    def cxx_ini_parse(self, code, src, dest, ret):
1654        code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest))
1655
1656#
1657# "Constants"... handy aliases for various values.
1658#
1659
1660# Special class for NULL pointers.  Note the special check in
1661# make_param_value() above that lets these be assigned where a
1662# SimObject is required.
1663# only one copy of a particular node
1664class NullSimObject(object):
1665    __metaclass__ = Singleton
1666    _name = 'Null'
1667
1668    def __call__(cls):
1669        return cls
1670
1671    def _instantiate(self, parent = None, path = ''):
1672        pass
1673
1674    def ini_str(self):
1675        return 'Null'
1676
1677    def unproxy(self, base):
1678        return self
1679
1680    def set_path(self, parent, name):
1681        pass
1682
1683    def set_parent(self, parent, name):
1684        pass
1685
1686    def clear_parent(self, old_parent):
1687        pass
1688
1689    def descendants(self):
1690        return
1691        yield None
1692
1693    def get_config_as_dict(self):
1694        return {}
1695
1696    def __str__(self):
1697        return self._name
1698
1699    def config_value(self):
1700        return None
1701
1702    def getValue(self):
1703        return None
1704
1705# The only instance you'll ever need...
1706NULL = NullSimObject()
1707
1708def isNullPointer(value):
1709    return isinstance(value, NullSimObject)
1710
1711# Some memory range specifications use this as a default upper bound.
1712MaxAddr = Addr.max
1713MaxTick = Tick.max
1714AllMemory = AddrRange(0, MaxAddr)
1715
1716
1717#####################################################################
1718#
1719# Port objects
1720#
1721# Ports are used to interconnect objects in the memory system.
1722#
1723#####################################################################
1724
1725# Port reference: encapsulates a reference to a particular port on a
1726# particular SimObject.
1727class PortRef(object):
1728    def __init__(self, simobj, name, role):
1729        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1730        self.simobj = simobj
1731        self.name = name
1732        self.role = role
1733        self.peer = None   # not associated with another port yet
1734        self.ccConnected = False # C++ port connection done?
1735        self.index = -1  # always -1 for non-vector ports
1736
1737    def __str__(self):
1738        return '%s.%s' % (self.simobj, self.name)
1739
1740    def __len__(self):
1741        # Return the number of connected ports, i.e. 0 is we have no
1742        # peer and 1 if we do.
1743        return int(self.peer != None)
1744
1745    # for config.ini, print peer's name (not ours)
1746    def ini_str(self):
1747        return str(self.peer)
1748
1749    # for config.json
1750    def get_config_as_dict(self):
1751        return {'role' : self.role, 'peer' : str(self.peer)}
1752
1753    def __getattr__(self, attr):
1754        if attr == 'peerObj':
1755            # shorthand for proxies
1756            return self.peer.simobj
1757        raise AttributeError, "'%s' object has no attribute '%s'" % \
1758              (self.__class__.__name__, attr)
1759
1760    # Full connection is symmetric (both ways).  Called via
1761    # SimObject.__setattr__ as a result of a port assignment, e.g.,
1762    # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__,
1763    # e.g., "obj1.portA[3] = obj2.portB".
1764    def connect(self, other):
1765        if isinstance(other, VectorPortRef):
1766            # reference to plain VectorPort is implicit append
1767            other = other._get_next()
1768        if self.peer and not proxy.isproxy(self.peer):
1769            fatal("Port %s is already connected to %s, cannot connect %s\n",
1770                  self, self.peer, other);
1771        self.peer = other
1772        if proxy.isproxy(other):
1773            other.set_param_desc(PortParamDesc())
1774        elif isinstance(other, PortRef):
1775            if other.peer is not self:
1776                other.connect(self)
1777        else:
1778            raise TypeError, \
1779                  "assigning non-port reference '%s' to port '%s'" \
1780                  % (other, self)
1781
1782    # Allow a master/slave port pair to be spliced between
1783    # a port and its connected peer. Useful operation for connecting
1784    # instrumentation structures into a system when it is necessary
1785    # to connect the instrumentation after the full system has been
1786    # constructed.
1787    def splice(self, new_master_peer, new_slave_peer):
1788        if self.peer and not proxy.isproxy(self.peer):
1789            if isinstance(new_master_peer, PortRef) and \
1790               isinstance(new_slave_peer, PortRef):
1791                 old_peer = self.peer
1792                 if self.role == 'SLAVE':
1793                     self.peer = new_master_peer
1794                     old_peer.peer = new_slave_peer
1795                     new_master_peer.connect(self)
1796                     new_slave_peer.connect(old_peer)
1797                 elif self.role == 'MASTER':
1798                     self.peer = new_slave_peer
1799                     old_peer.peer = new_master_peer
1800                     new_slave_peer.connect(self)
1801                     new_master_peer.connect(old_peer)
1802                 else:
1803                     panic("Port %s has unknown role, "+\
1804                           "cannot splice in new peers\n", self)
1805            else:
1806                raise TypeError, \
1807                      "Splicing non-port references '%s','%s' to port '%s'"\
1808                      % (new_peer, peers_new_peer, self)
1809        else:
1810            fatal("Port %s not connected, cannot splice in new peers\n", self)
1811
1812    def clone(self, simobj, memo):
1813        if memo.has_key(self):
1814            return memo[self]
1815        newRef = copy.copy(self)
1816        memo[self] = newRef
1817        newRef.simobj = simobj
1818        assert(isSimObject(newRef.simobj))
1819        if self.peer and not proxy.isproxy(self.peer):
1820            peerObj = self.peer.simobj(_memo=memo)
1821            newRef.peer = self.peer.clone(peerObj, memo)
1822            assert(not isinstance(newRef.peer, VectorPortRef))
1823        return newRef
1824
1825    def unproxy(self, simobj):
1826        assert(simobj is self.simobj)
1827        if proxy.isproxy(self.peer):
1828            try:
1829                realPeer = self.peer.unproxy(self.simobj)
1830            except:
1831                print("Error in unproxying port '%s' of %s" %
1832                      (self.name, self.simobj.path()))
1833                raise
1834            self.connect(realPeer)
1835
1836    # Call C++ to create corresponding port connection between C++ objects
1837    def ccConnect(self):
1838        from _m5.pyobject import connectPorts
1839
1840        if self.role == 'SLAVE':
1841            # do nothing and let the master take care of it
1842            return
1843
1844        if self.ccConnected: # already done this
1845            return
1846        peer = self.peer
1847        if not self.peer: # nothing to connect to
1848            return
1849
1850        # check that we connect a master to a slave
1851        if self.role == peer.role:
1852            raise TypeError, \
1853                "cannot connect '%s' and '%s' due to identical role '%s'" \
1854                % (peer, self, self.role)
1855
1856        try:
1857            # self is always the master and peer the slave
1858            connectPorts(self.simobj.getCCObject(), self.name, self.index,
1859                         peer.simobj.getCCObject(), peer.name, peer.index)
1860        except:
1861            print("Error connecting port %s.%s to %s.%s" %
1862                  (self.simobj.path(), self.name,
1863                   peer.simobj.path(), peer.name))
1864            raise
1865        self.ccConnected = True
1866        peer.ccConnected = True
1867
1868# A reference to an individual element of a VectorPort... much like a
1869# PortRef, but has an index.
1870class VectorPortElementRef(PortRef):
1871    def __init__(self, simobj, name, role, index):
1872        PortRef.__init__(self, simobj, name, role)
1873        self.index = index
1874
1875    def __str__(self):
1876        return '%s.%s[%d]' % (self.simobj, self.name, self.index)
1877
1878# A reference to a complete vector-valued port (not just a single element).
1879# Can be indexed to retrieve individual VectorPortElementRef instances.
1880class VectorPortRef(object):
1881    def __init__(self, simobj, name, role):
1882        assert(isSimObject(simobj) or isSimObjectClass(simobj))
1883        self.simobj = simobj
1884        self.name = name
1885        self.role = role
1886        self.elements = []
1887
1888    def __str__(self):
1889        return '%s.%s[:]' % (self.simobj, self.name)
1890
1891    def __len__(self):
1892        # Return the number of connected peers, corresponding the the
1893        # length of the elements.
1894        return len(self.elements)
1895
1896    # for config.ini, print peer's name (not ours)
1897    def ini_str(self):
1898        return ' '.join([el.ini_str() for el in self.elements])
1899
1900    # for config.json
1901    def get_config_as_dict(self):
1902        return {'role' : self.role,
1903                'peer' : [el.ini_str() for el in self.elements]}
1904
1905    def __getitem__(self, key):
1906        if not isinstance(key, int):
1907            raise TypeError, "VectorPort index must be integer"
1908        if key >= len(self.elements):
1909            # need to extend list
1910            ext = [VectorPortElementRef(self.simobj, self.name, self.role, i)
1911                   for i in range(len(self.elements), key+1)]
1912            self.elements.extend(ext)
1913        return self.elements[key]
1914
1915    def _get_next(self):
1916        return self[len(self.elements)]
1917
1918    def __setitem__(self, key, value):
1919        if not isinstance(key, int):
1920            raise TypeError, "VectorPort index must be integer"
1921        self[key].connect(value)
1922
1923    def connect(self, other):
1924        if isinstance(other, (list, tuple)):
1925            # Assign list of port refs to vector port.
1926            # For now, append them... not sure if that's the right semantics
1927            # or if it should replace the current vector.
1928            for ref in other:
1929                self._get_next().connect(ref)
1930        else:
1931            # scalar assignment to plain VectorPort is implicit append
1932            self._get_next().connect(other)
1933
1934    def clone(self, simobj, memo):
1935        if memo.has_key(self):
1936            return memo[self]
1937        newRef = copy.copy(self)
1938        memo[self] = newRef
1939        newRef.simobj = simobj
1940        assert(isSimObject(newRef.simobj))
1941        newRef.elements = [el.clone(simobj, memo) for el in self.elements]
1942        return newRef
1943
1944    def unproxy(self, simobj):
1945        [el.unproxy(simobj) for el in self.elements]
1946
1947    def ccConnect(self):
1948        [el.ccConnect() for el in self.elements]
1949
1950# Port description object.  Like a ParamDesc object, this represents a
1951# logical port in the SimObject class, not a particular port on a
1952# SimObject instance.  The latter are represented by PortRef objects.
1953class Port(object):
1954    # Generate a PortRef for this port on the given SimObject with the
1955    # given name
1956    def makeRef(self, simobj):
1957        return PortRef(simobj, self.name, self.role)
1958
1959    # Connect an instance of this port (on the given SimObject with
1960    # the given name) with the port described by the supplied PortRef
1961    def connect(self, simobj, ref):
1962        self.makeRef(simobj).connect(ref)
1963
1964    # No need for any pre-declarations at the moment as we merely rely
1965    # on an unsigned int.
1966    def cxx_predecls(self, code):
1967        pass
1968
1969    def pybind_predecls(self, code):
1970        cls.cxx_predecls(self, code)
1971
1972    # Declare an unsigned int with the same name as the port, that
1973    # will eventually hold the number of connected ports (and thus the
1974    # number of elements for a VectorPort).
1975    def cxx_decl(self, code):
1976        code('unsigned int port_${{self.name}}_connection_count;')
1977
1978class MasterPort(Port):
1979    # MasterPort("description")
1980    def __init__(self, *args):
1981        if len(args) == 1:
1982            self.desc = args[0]
1983            self.role = 'MASTER'
1984        else:
1985            raise TypeError, 'wrong number of arguments'
1986
1987class SlavePort(Port):
1988    # SlavePort("description")
1989    def __init__(self, *args):
1990        if len(args) == 1:
1991            self.desc = args[0]
1992            self.role = 'SLAVE'
1993        else:
1994            raise TypeError, 'wrong number of arguments'
1995
1996# VectorPort description object.  Like Port, but represents a vector
1997# of connections (e.g., as on a XBar).
1998class VectorPort(Port):
1999    def __init__(self, *args):
2000        self.isVec = True
2001
2002    def makeRef(self, simobj):
2003        return VectorPortRef(simobj, self.name, self.role)
2004
2005class VectorMasterPort(VectorPort):
2006    # VectorMasterPort("description")
2007    def __init__(self, *args):
2008        if len(args) == 1:
2009            self.desc = args[0]
2010            self.role = 'MASTER'
2011            VectorPort.__init__(self, *args)
2012        else:
2013            raise TypeError, 'wrong number of arguments'
2014
2015class VectorSlavePort(VectorPort):
2016    # VectorSlavePort("description")
2017    def __init__(self, *args):
2018        if len(args) == 1:
2019            self.desc = args[0]
2020            self.role = 'SLAVE'
2021            VectorPort.__init__(self, *args)
2022        else:
2023            raise TypeError, 'wrong number of arguments'
2024
2025# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
2026# proxy objects (via set_param_desc()) so that proxy error messages
2027# make sense.
2028class PortParamDesc(object):
2029    __metaclass__ = Singleton
2030
2031    ptype_str = 'Port'
2032    ptype = Port
2033
2034baseEnums = allEnums.copy()
2035baseParams = allParams.copy()
2036
2037def clear():
2038    global allEnums, allParams
2039
2040    allEnums = baseEnums.copy()
2041    allParams = baseParams.copy()
2042
2043__all__ = ['Param', 'VectorParam',
2044           'Enum', 'Bool', 'String', 'Float',
2045           'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
2046           'Int32', 'UInt32', 'Int64', 'UInt64',
2047           'Counter', 'Addr', 'Tick', 'Percent',
2048           'TcpPort', 'UdpPort', 'EthernetAddr',
2049           'IpAddress', 'IpNetmask', 'IpWithPort',
2050           'MemorySize', 'MemorySize32',
2051           'Latency', 'Frequency', 'Clock', 'Voltage', 'Current', 'Energy',
2052           'NetworkBandwidth', 'MemoryBandwidth',
2053           'AddrRange',
2054           'MaxAddr', 'MaxTick', 'AllMemory',
2055           'Time',
2056           'NextEthernetAddr', 'NULL',
2057           'MasterPort', 'SlavePort',
2058           'VectorMasterPort', 'VectorSlavePort']
2059
2060import SimObject
2061