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