SimObject.py revision 1342
1# Copyright (c) 2004 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27from __future__ import generators
28import os, re, sys, types
29noDot = False
30try:
31   import pydot
32except:
33   noDot = True
34
35env = {}
36env.update(os.environ)
37def defined(key):
38    return env.has_key(key)
39
40def define(key, value = True):
41    env[key] = value
42
43def panic(*args, **kwargs):
44    sys.exit(*args, **kwargs)
45
46def AddToPath(path):
47    path = os.path.realpath(path)
48    if os.path.isdir(path):
49        sys.path.append(path)
50
51def Import(path):
52    AddToPath(os.path.dirname(path))
53    exec('from m5config import *')
54    mpy_exec(file(path, 'r'))
55
56def issequence(value):
57    return isinstance(value, tuple) or isinstance(value, list)
58
59class Singleton(type):
60    def __call__(cls, *args, **kwargs):
61        if hasattr(cls, '_instance'):
62            return cls._instance
63
64        cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
65        return cls._instance
66
67if os.environ.has_key('FULL_SYSTEM'):
68    FULL_SYSTEM = True
69
70#####################################################################
71#
72# M5 Python Configuration Utility
73#
74# The basic idea is to write simple Python programs that build Python
75# objects corresponding to M5 SimObjects for the deisred simulation
76# configuration.  For now, the Python emits a .ini file that can be
77# parsed by M5.  In the future, some tighter integration between M5
78# and the Python interpreter may allow bypassing the .ini file.
79#
80# Each SimObject class in M5 is represented by a Python class with the
81# same name.  The Python inheritance tree mirrors the M5 C++ tree
82# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
83# SimObjects inherit from a single SimObject base class).  To specify
84# an instance of an M5 SimObject in a configuration, the user simply
85# instantiates the corresponding Python object.  The parameters for
86# that SimObject are given by assigning to attributes of the Python
87# object, either using keyword assignment in the constructor or in
88# separate assignment statements.  For example:
89#
90# cache = BaseCache('my_cache', root, size=64*K)
91# cache.hit_latency = 3
92# cache.assoc = 8
93#
94# (The first two constructor arguments specify the name of the created
95# cache and its parent node in the hierarchy.)
96#
97# The magic lies in the mapping of the Python attributes for SimObject
98# classes to the actual SimObject parameter specifications.  This
99# allows parameter validity checking in the Python code.  Continuing
100# the example above, the statements "cache.blurfl=3" or
101# "cache.assoc='hello'" would both result in runtime errors in Python,
102# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
103# parameter requires an integer, respectively.  This magic is done
104# primarily by overriding the special __setattr__ method that controls
105# assignment to object attributes.
106#
107# The Python module provides another class, ConfigNode, which is a
108# superclass of SimObject.  ConfigNode implements the parent/child
109# relationship for building the configuration hierarchy tree.
110# Concrete instances of ConfigNode can be used to group objects in the
111# hierarchy, but do not correspond to SimObjects themselves (like a
112# .ini section with "children=" but no "type=".
113#
114# Once a set of Python objects have been instantiated in a hierarchy,
115# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
116# will generate a .ini file.  See simple-4cpu.py for an example
117# (corresponding to m5-test/simple-4cpu.ini).
118#
119#####################################################################
120
121#####################################################################
122#
123# ConfigNode/SimObject classes
124#
125# The Python class hierarchy rooted by ConfigNode (which is the base
126# class of SimObject, which in turn is the base class of all other M5
127# SimObject classes) has special attribute behavior.  In general, an
128# object in this hierarchy has three categories of attribute-like
129# things:
130#
131# 1. Regular Python methods and variables.  These must start with an
132# underscore to be treated normally.
133#
134# 2. SimObject parameters.  These values are stored as normal Python
135# attributes, but all assignments to these attributes are checked
136# against the pre-defined set of parameters stored in the class's
137# _params dictionary.  Assignments to attributes that do not
138# correspond to predefined parameters, or that are not of the correct
139# type, incur runtime errors.
140#
141# 3. Hierarchy children.  The child nodes of a ConfigNode are stored
142# in the node's _children dictionary, but can be accessed using the
143# Python attribute dot-notation (just as they are printed out by the
144# simulator).  Children cannot be created using attribute assigment;
145# they must be added by specifying the parent node in the child's
146# constructor or using the '+=' operator.
147
148# The SimObject parameters are the most complex, for a few reasons.
149# First, both parameter descriptions and parameter values are
150# inherited.  Thus parameter description lookup must go up the
151# inheritance chain like normal attribute lookup, but this behavior
152# must be explicitly coded since the lookup occurs in each class's
153# _params attribute.  Second, because parameter values can be set
154# on SimObject classes (to implement default values), the parameter
155# checking behavior must be enforced on class attribute assignments as
156# well as instance attribute assignments.  Finally, because we allow
157# class specialization via inheritance (e.g., see the L1Cache class in
158# the simple-4cpu.py example), we must do parameter checking even on
159# class instantiation.  To provide all these features, we use a
160# metaclass to define most of the SimObject parameter behavior for
161# this class hierarchy.
162#
163#####################################################################
164
165class Proxy(object):
166    def __init__(self, path = ()):
167        self._object = None
168        self._path = path
169
170    def __getattr__(self, attr):
171        return Proxy(self._path + (attr, ))
172
173    def __setattr__(self, attr, value):
174        if not attr.startswith('_'):
175            raise AttributeError, 'cannot set attribute %s' % attr
176        super(Proxy, self).__setattr__(attr, value)
177
178    def _convert(self):
179        obj = self._object
180        for attr in self._path:
181            obj = obj.__getattribute__(attr)
182        return obj
183
184Super = Proxy()
185
186def isSubClass(value, cls):
187    try:
188        return issubclass(value, cls)
189    except:
190        return False
191
192def isParam(self):
193    return isinstance(self, _Param)
194
195def isConfigNode(value):
196    try:
197        return issubclass(value, ConfigNode)
198    except:
199        return False
200
201def isSimObject(value):
202    try:
203        return issubclass(value, SimObject)
204    except:
205        return False
206
207def isSimObjSequence(value):
208    if not issequence(value):
209        return False
210
211    for val in value:
212        if not isNullPointer(val) and not isConfigNode(val):
213            return False
214
215    return True
216
217# The metaclass for ConfigNode (and thus for everything that derives
218# from ConfigNode, including SimObject).  This class controls how new
219# classes that derive from ConfigNode are instantiated, and provides
220# inherited class behavior (just like a class controls how instances
221# of that class are instantiated, and provides inherited instance
222# behavior).
223class MetaConfigNode(type):
224    keywords = { 'abstract' : types.BooleanType,
225                 'check' : types.FunctionType,
226                 '_init' : types.FunctionType,
227                 'type' : (types.NoneType, types.StringType) }
228
229    # __new__ is called before __init__, and is where the statements
230    # in the body of the class definition get loaded into the class's
231    # __dict__.  We intercept this to filter out parameter assignments
232    # and only allow "private" attributes to be passed to the base
233    # __new__ (starting with underscore).
234    def __new__(mcls, name, bases, dict):
235        priv = { 'abstract' : False,
236                 # initialize _params and _values dicts to empty
237                 '_params' : {},
238                 '_values' : {},
239                 '_disable' : {} }
240
241        for key,val in dict.items():
242            if mcls.keywords.has_key(key):
243                if not isinstance(val, mcls.keywords[key]):
244                    raise TypeError, \
245                          'keyword %s has the wrong type %s should be %s' % \
246                          (key, type(val), mcls.keywords[key])
247
248                if isinstance(val, types.FunctionType):
249                    val = classmethod(val)
250                priv[key] = val
251                del dict[key]
252
253            elif key.startswith('_'):
254                priv[key] = val
255                del dict[key]
256
257            elif not isNullPointer(val) and isConfigNode(val):
258                dict[key] = val()
259
260            elif isSimObjSequence(val):
261                dict[key] = [ v() for v in val ]
262
263        # If your parent has a value in it that's a config node, clone it.
264        for base in bases:
265            if not isConfigNode(base):
266                continue
267
268            for name,value in base._values.iteritems():
269                if dict.has_key(name):
270                    continue
271
272                if isConfigNode(value):
273                    priv['_values'][name] = value()
274                elif isSimObjSequence(value):
275                    priv['_values'][name] = [ val() for val in value ]
276
277        # entries left in dict will get passed to __init__, where we'll
278        # deal with them as params.
279        return super(MetaConfigNode, mcls).__new__(mcls, name, bases, priv)
280
281    # initialization: start out with an empty _params dict (makes life
282    # simpler if we can assume _params is always valid).
283    def __init__(cls, name, bases, dict):
284        super(MetaConfigNode, cls).__init__(cls, name, bases, {})
285
286        cls._bases = [c for c in cls.__mro__ if isConfigNode(c)]
287
288        # initialize attributes with values from class definition
289        for pname,value in dict.iteritems():
290            setattr(cls, pname, value)
291
292        if hasattr(cls, '_init'):
293            cls._init()
294            del cls._init
295
296    def _isvalue(cls, name):
297        for c in cls._bases:
298            if c._params.has_key(name):
299                return True
300
301        for c in cls._bases:
302            if c._values.has_key(name):
303                return True
304
305        return False
306
307    # generator that iterates across all parameters for this class and
308    # all classes it inherits from
309    def _getparams(cls):
310        params = {}
311        for c in cls._bases:
312            for p,v in c._params.iteritems():
313                if not params.has_key(p):
314                    params[p] = v
315        return params
316
317    # Lookup a parameter description by name in the given class.
318    def _getparam(cls, name, default = AttributeError):
319        for c in cls._bases:
320            if c._params.has_key(name):
321                return c._params[name]
322        if isSubClass(default, Exception):
323            raise default, \
324                  "object '%s' has no attribute '%s'" % (cls.__name__, name)
325        else:
326            return default
327
328    def _setparam(cls, name, value):
329        cls._params[name] = value
330
331    def _hasvalue(cls, name):
332        for c in cls._bases:
333            if c._values.has_key(name):
334                return True
335
336        return False
337
338    def _getvalues(cls):
339        values = {}
340        for i,c in enumerate(cls._bases):
341            for p,v in c._values.iteritems():
342                if not values.has_key(p):
343                    values[p] = v
344        return values
345
346    def _getvalue(cls, name, default = AttributeError):
347        value = None
348        for c in cls._bases:
349            if c._values.has_key(name):
350                value = c._values[name]
351                break
352        if value is not None:
353            return value
354
355        param = cls._getparam(name, None)
356        if param is not None and hasattr(param, 'default'):
357            param.valid(param.default)
358            value = param.default
359            cls._setvalue(name, value)
360            return value
361
362        if isSubClass(default, Exception):
363            raise default, 'value for %s not found' % name
364        else:
365            return default
366
367    def _setvalue(cls, name, value):
368        cls._values[name] = value
369
370    def _getdisable(cls, name):
371        for c in cls._bases:
372            if c._disable.has_key(name):
373                return c._disable[name]
374        return False
375
376    def _setdisable(cls, name, value):
377        cls._disable[name] = value
378
379    def __getattr__(cls, attr):
380        if cls._isvalue(attr):
381            return Value(cls, attr)
382
383        raise AttributeError, \
384              "object '%s' has no attribute '%s'" % (cls.__name__, cls)
385
386
387    # Set attribute (called on foo.attr = value when foo is an
388    # instance of class cls).
389    def __setattr__(cls, attr, value):
390        # normal processing for private attributes
391        if attr.startswith('_'):
392            type.__setattr__(cls, attr, value)
393            return
394
395        if cls.keywords.has_key(attr):
396            raise TypeError, \
397                  "keyword '%s' can only be set in a simobj definition" % attr
398
399        if isParam(value):
400            cls._setparam(attr, value)
401            return
402
403        # must be SimObject param
404        param = cls._getparam(attr, None)
405        if param:
406            # It's ok: set attribute by delegating to 'object' class.
407            # Note the use of param.make_value() to verify/canonicalize
408            # the assigned value
409            param.valid(value)
410            cls._setvalue(attr, value)
411        elif isConfigNode(value) or isSimObjSequence(value):
412            cls._setvalue(attr, value)
413        else:
414            for p,v in cls._getparams().iteritems():
415                print p,v
416            raise AttributeError, \
417                  "Class %s has no parameter %s" % (cls.__name__, attr)
418
419    def add_child(cls, instance, name, child):
420        if isNullPointer(child) or instance.top_child_names.has_key(name):
421            return
422
423        if issequence(child):
424            kid = []
425            for i,c in enumerate(child):
426                n = '%s%d' % (name, i)
427                k = c.instantiate(n, instance)
428
429                instance.children.append(k)
430                instance.child_names[n] = k
431                instance.child_objects[c] = k
432                kid.append(k)
433        else:
434            kid = child.instantiate(name, instance)
435            instance.children.append(kid)
436            instance.child_names[name] = kid
437            instance.child_objects[child] = kid
438
439        instance.top_child_names[name] = kid
440
441    # Print instance info to .ini file.
442    def instantiate(cls, name, parent = None):
443        instance = Node(name, cls, cls.type, parent)
444
445        if hasattr(cls, 'check'):
446            cls.check()
447
448        for key,value in cls._getvalues().iteritems():
449            if cls._getdisable(key):
450                continue
451
452            if isConfigNode(value):
453                cls.add_child(instance, key, value)
454            if issequence(value):
455                list = [ v for v in value if isConfigNode(v) ]
456                if len(list):
457                    cls.add_child(instance, key, list)
458
459        for pname,param in cls._getparams().iteritems():
460            try:
461                if cls._getdisable(pname):
462                    continue
463
464                value = cls._getvalue(pname)
465
466                if isConfigNode(value):
467                    value = instance.child_objects[value]
468                elif issequence(value):
469                    v = []
470                    for val in value:
471                        if isConfigNode(val):
472                            v.append(instance.child_objects[val])
473                        else:
474                            v.append(val)
475                    value = v
476
477                p = NodeParam(pname, param, value)
478                instance.params.append(p)
479                instance.param_names[pname] = p
480            except:
481                print 'Exception while evaluating %s.%s' % \
482                      (instance.path, pname)
483                raise
484
485        return instance
486
487    def _convert(cls, value):
488        realvalue = value
489        if isinstance(value, Node):
490            realvalue = value.realtype
491
492        if isinstance(realvalue, Proxy):
493            return value
494
495        if realvalue == None or isNullPointer(realvalue):
496            return value
497
498        if isSubClass(realvalue, cls):
499            return value
500
501        raise TypeError, 'object %s type %s wrong type, should be %s' % \
502              (repr(realvalue), realvalue, cls)
503
504    def _string(cls, value):
505        if isNullPointer(value):
506            return 'Null'
507        return Node._string(value)
508
509# The ConfigNode class is the root of the special hierarchy.  Most of
510# the code in this class deals with the configuration hierarchy itself
511# (parent/child node relationships).
512class ConfigNode(object):
513    # Specify metaclass.  Any class inheriting from ConfigNode will
514    # get this metaclass.
515    __metaclass__ = MetaConfigNode
516    type = None
517
518    def __new__(cls, **kwargs):
519        return MetaConfigNode(cls.__name__, (cls, ), kwargs)
520
521    # Set attribute.  All attribute assignments go through here.  Must
522    # be private attribute (starts with '_') or valid parameter entry.
523    # Basically identical to MetaConfigClass.__setattr__(), except
524    # this sets attributes on specific instances rather than on classes.
525    #def __setattr__(self, attr, value):
526    #    if attr.startswith('_'):
527    #        object.__setattr__(self, attr, value)
528    #        return
529        # not private; look up as param
530    #    param = self.__class__.lookup_param(attr)
531    #    if not param:
532    #        raise AttributeError, \
533    #              "Class %s has no parameter %s" \
534    #              % (self.__class__.__name__, attr)
535        # It's ok: set attribute by delegating to 'object' class.
536        # Note the use of param.make_value() to verify/canonicalize
537        # the assigned value.
538    #    v = param.convert(value)
539    #    object.__setattr__(self, attr, v)
540
541# SimObject is a minimal extension of ConfigNode, implementing a
542# hierarchy node that corresponds to an M5 SimObject.  It prints out a
543# "type=" line to indicate its SimObject class, prints out the
544# assigned parameters corresponding to its class, and allows
545# parameters to be set by keyword in the constructor.  Note that most
546# of the heavy lifting for the SimObject param handling is done in the
547# MetaConfigNode metaclass.
548class SimObject(ConfigNode):
549    def _sim_code(cls):
550        name = cls.__name__
551        param_names = cls._params.keys()
552        param_names.sort()
553        code = "BEGIN_DECLARE_SIM_OBJECT_PARAMS(%s)\n" % name
554        decls = ["  " + cls._params[pname].sim_decl(pname) \
555                 for pname in param_names]
556        code += "\n".join(decls) + "\n"
557        code += "END_DECLARE_SIM_OBJECT_PARAMS(%s)\n\n" % name
558        code += "BEGIN_INIT_SIM_OBJECT_PARAMS(%s)\n" % name
559        inits = ["  " + cls._params[pname].sim_init(pname) \
560                 for pname in param_names]
561        code += ",\n".join(inits) + "\n"
562        code += "END_INIT_SIM_OBJECT_PARAMS(%s)\n\n" % name
563        return code
564    _sim_code = classmethod(_sim_code)
565
566class NodeParam(object):
567    def __init__(self, name, param, value):
568        self.name = name
569        self.param = param
570        self.ptype = param.ptype
571        self.convert = param.convert
572        self.string = param.string
573        self.value = value
574
575class Node(object):
576    all = {}
577    def __init__(self, name, realtype, type, parent):
578        self.name = name
579        self.realtype = realtype
580        self.type = type
581        self.parent = parent
582        self.children = []
583        self.child_names = {}
584        self.child_objects = {}
585        self.top_child_names = {}
586        self.params = []
587        self.param_names = {}
588
589        path = [ self.name ]
590        node = self.parent
591        while node is not None:
592            if node.name != 'root':
593                path.insert(0, node.name)
594            else:
595                assert(node.parent is None)
596            node = node.parent
597        self.path = '.'.join(path)
598
599    def find(self, realtype, path):
600        rtype = eval(realtype)
601        if not path:
602            if issubclass(self.realtype, rtype):
603                return self, True
604
605            obj = None
606            for child in self.children:
607                if issubclass(child.realtype, rtype):
608                    if obj is not None:
609                        raise AttributeError, \
610                              'Super matched more than one: %s %s' % \
611                              (obj.path, child.path)
612                    obj = child
613            return obj, obj is not None
614
615        try:
616            obj = self
617            for node in path[:-1]:
618                obj = obj.child_names[node]
619
620            last = path[-1]
621            if obj.child_names.has_key(last):
622                value = obj.child_names[last]
623                if issubclass(value.realtype, rtype):
624                    return value, True
625            elif obj.param_names.has_key(last):
626                value = obj.param_names[last]
627                rtype._convert(value.value)
628                return value.value, True
629        except KeyError:
630            pass
631
632        return None, False
633
634    def unproxy(self, ptype, value):
635        if not isinstance(value, Proxy):
636            return value
637
638        if value is None:
639            raise AttributeError, 'Error while fixing up %s' % self.path
640
641        obj = self
642        done = False
643        while not done:
644            if obj is None:
645                raise AttributeError, \
646                      'Parent of %s type %s not found at path %s' \
647                      % (self.name, ptype, value._path)
648            found, done = obj.find(ptype, value._path)
649            if isinstance(found, Proxy):
650                done = false
651            obj = obj.parent
652
653        return found
654
655    def fixup(self):
656        self.all[self.path] = self
657
658        for param in self.params:
659            ptype = param.ptype
660            pval = param.value
661
662            try:
663                if issequence(pval):
664                    param.value = [ self.unproxy(ptype, pv) for pv in pval ]
665                else:
666                    param.value = self.unproxy(ptype, pval)
667            except:
668                print 'Error while fixing up %s:%s' % (self.path, param.name)
669                raise
670
671        for child in self.children:
672            assert(child != self)
673            child.fixup()
674
675    # print type and parameter values to .ini file
676    def display(self):
677        print '[' + self.path + ']'	# .ini section header
678
679        if isSimObject(self.realtype):
680            print 'type = %s' % self.type
681
682        if self.children:
683            # instantiate children in same order they were added for
684            # backward compatibility (else we can end up with cpu1
685            # before cpu0).
686            print 'children =', ' '.join([ c.name for c in self.children])
687
688        for param in self.params:
689            try:
690                if param.value is None:
691                    raise AttributeError, 'Parameter with no value'
692
693                value = param.convert(param.value)
694                string = param.string(value)
695            except:
696                print 'exception in %s:%s' % (self.path, param.name)
697                raise
698
699            print '%s = %s' % (param.name, string)
700
701        print
702
703        # recursively dump out children
704        for c in self.children:
705            c.display()
706
707    # print type and parameter values to .ini file
708    def outputDot(self, dot):
709
710
711        label = "{%s|" % self.path
712        if isSimObject(self.realtype):
713            label +=  '%s|' % self.type
714
715        if self.children:
716            # instantiate children in same order they were added for
717            # backward compatibility (else we can end up with cpu1
718            # before cpu0).
719            for c in self.children:
720                dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
721
722        simobjs = []
723        for param in self.params:
724            try:
725                if param.value is None:
726                    raise AttributeError, 'Parameter with no value'
727
728                value = param.convert(param.value)
729                string = param.string(value)
730            except:
731                print 'exception in %s:%s' % (self.name, param.name)
732                raise
733            ptype = eval(param.ptype)
734            if isConfigNode(ptype) and string != "Null":
735                simobjs.append(string)
736            else:
737                label += '%s = %s\\n' % (param.name, string)
738
739        for so in simobjs:
740            label += "|<%s> %s" % (so, so)
741            dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, tailport="w"))
742        label += '}'
743        dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
744
745        # recursively dump out children
746        for c in self.children:
747            c.outputDot(dot)
748
749    def _string(cls, value):
750        if not isinstance(value, Node):
751            raise AttributeError, 'expecting %s got %s' % (Node, value)
752        return value.path
753    _string = classmethod(_string)
754
755#####################################################################
756#
757# Parameter description classes
758#
759# The _params dictionary in each class maps parameter names to
760# either a Param or a VectorParam object.  These objects contain the
761# parameter description string, the parameter type, and the default
762# value (loaded from the PARAM section of the .odesc files).  The
763# _convert() method on these objects is used to force whatever value
764# is assigned to the parameter to the appropriate type.
765#
766# Note that the default values are loaded into the class's attribute
767# space when the parameter dictionary is initialized (in
768# MetaConfigNode._setparams()); after that point they aren't used.
769#
770#####################################################################
771
772def isNullPointer(value):
773    return isinstance(value, NullSimObject)
774
775class Value(object):
776    def __init__(self, obj, attr):
777        super(Value, self).__setattr__('attr', attr)
778        super(Value, self).__setattr__('obj', obj)
779
780    def _getattr(self):
781        return self.obj._getvalue(self.attr)
782
783    def __setattr__(self, attr, value):
784        if attr == 'disable':
785            self.obj._setdisable(self.attr, value)
786        else:
787            setattr(self._getattr(), attr, value)
788
789    def __getattr__(self, attr):
790        if attr == 'disable':
791            return self.obj._getdisable(self.attr)
792        else:
793            return getattr(self._getattr(), attr)
794
795    def __getitem__(self, index):
796        return self._getattr().__getitem__(index)
797
798    def __call__(self, *args, **kwargs):
799        return self._getattr().__call__(*args, **kwargs)
800
801    def __nonzero__(self):
802        return bool(self._getattr())
803
804    def __str__(self):
805        return str(self._getattr())
806
807# Regular parameter.
808class _Param(object):
809    def __init__(self, ptype, *args, **kwargs):
810        self.ptype = ptype
811
812        if args:
813            if len(args) == 1:
814                self.desc = args[0]
815            elif len(args) == 2:
816                self.default = args[0]
817                self.desc = args[1]
818            else:
819                raise TypeError, 'too many arguments'
820
821        if kwargs.has_key('desc'):
822            assert(not hasattr(self, 'desc'))
823            self.desc = kwargs['desc']
824            del kwargs['desc']
825
826        if kwargs.has_key('default'):
827            assert(not hasattr(self, 'default'))
828            self.default = kwargs['default']
829            del kwargs['default']
830
831        if kwargs:
832            raise TypeError, 'extra unknown kwargs %s' % kwargs
833
834        if not hasattr(self, 'desc'):
835            raise TypeError, 'desc attribute missing'
836
837    def valid(self, value):
838        if not isinstance(value, Proxy):
839            ptype = eval(self.ptype)
840            ptype._convert(value)
841
842    def convert(self, value):
843        ptype = eval(self.ptype)
844        return ptype._convert(value)
845
846    def string(self, value):
847        ptype = eval(self.ptype)
848        return ptype._string(value)
849
850    def get(self, name, instance, owner):
851        # nothing to do if None or already correct type.  Also allow NULL
852        # pointer to be assigned where a SimObject is expected.
853        try:
854            if value == None or isinstance(value, self.ptype) or \
855                   isConfigNode(self.ptype) and \
856                   (isNullPointer(value) or issubclass(value, self.ptype)):
857                return value
858
859        except TypeError:
860            # this type conversion will raise an exception if it's illegal
861            return self.ptype(value)
862
863    def set(self, name, instance, value):
864        instance.__dict__[name] = value
865
866    def sim_decl(self, name):
867        return 'Param<%s> %s;' % (self.ptype.__name__, name)
868
869    def sim_init(self, name):
870        if self.default == None:
871            return 'INIT_PARAM(%s, "%s")' % (name, self.desc)
872        else:
873            return 'INIT_PARAM_DFLT(%s, "%s", %s)' % \
874                   (name, self.desc, str(self.default))
875
876class _ParamProxy(object):
877    def __init__(self, type):
878        self.ptype = type
879
880    # E.g., Param.Int(5, "number of widgets")
881    def __call__(self, *args, **kwargs):
882        return _Param(self.ptype, *args, **kwargs)
883
884    def __getattr__(self, attr):
885        if attr == '__bases__':
886            raise AttributeError, ''
887        cls = type(self)
888        return cls(attr)
889
890    def __setattr__(self, attr, value):
891        if attr != 'ptype':
892            raise AttributeError, \
893                  'Attribute %s not available in %s' % (attr, self.__class__)
894        super(_ParamProxy, self).__setattr__(attr, value)
895
896
897Param = _ParamProxy(None)
898
899# Vector-valued parameter description.  Just like Param, except that
900# the value is a vector (list) of the specified type instead of a
901# single value.
902class _VectorParam(_Param):
903    def __init__(self, type, *args, **kwargs):
904        _Param.__init__(self, type, *args, **kwargs)
905
906    def valid(self, value):
907        if value == None:
908            return True
909
910        ptype = eval(self.ptype)
911        if issequence(value):
912            for val in value:
913                if not isinstance(val, Proxy):
914                    ptype._convert(val)
915        elif not isinstance(value, Proxy):
916            ptype._convert(value)
917
918    # Convert assigned value to appropriate type.  If the RHS is not a
919    # list or tuple, it generates a single-element list.
920    def convert(self, value):
921        if value == None:
922            return []
923
924        ptype = eval(self.ptype)
925        if issequence(value):
926            # list: coerce each element into new list
927            return [ ptype._convert(v) for v in value ]
928        else:
929            # singleton: coerce & wrap in a list
930            return ptype._convert(value)
931
932    def string(self, value):
933        ptype = eval(self.ptype)
934        if issequence(value):
935            return ' '.join([ ptype._string(v) for v in value])
936        else:
937            return ptype._string(value)
938
939    def sim_decl(self, name):
940        return 'VectorParam<%s> %s;' % (self.ptype.__name__, name)
941
942class _VectorParamProxy(_ParamProxy):
943    # E.g., VectorParam.Int(5, "number of widgets")
944    def __call__(self, *args, **kwargs):
945        return _VectorParam(self.ptype, *args, **kwargs)
946
947VectorParam = _VectorParamProxy(None)
948
949#####################################################################
950#
951# Parameter Types
952#
953# Though native Python types could be used to specify parameter types
954# (the 'ptype' field of the Param and VectorParam classes), it's more
955# flexible to define our own set of types.  This gives us more control
956# over how Python expressions are converted to values (via the
957# __init__() constructor) and how these values are printed out (via
958# the __str__() conversion method).  Eventually we'll need these types
959# to correspond to distinct C++ types as well.
960#
961#####################################################################
962# Integer parameter type.
963class _CheckedInt(object):
964    def _convert(cls, value):
965        t = type(value)
966        if t == bool:
967            return int(value)
968
969        if t != int and t != long and t != float and t != str:
970            raise TypeError, 'Integer parameter of invalid type %s' % t
971
972        if t == str or t == float:
973            value = long(value)
974
975        if not cls._min <= value <= cls._max:
976            raise TypeError, 'Integer parameter out of bounds %d < %d < %d' % \
977                  (cls._min, value, cls._max)
978
979        return value
980    _convert = classmethod(_convert)
981
982    def _string(cls, value):
983        return str(value)
984    _string = classmethod(_string)
985
986class CheckedInt(type):
987    def __new__(cls, name, min, max):
988        # New class derives from _CheckedInt base with proper bounding
989        # parameters
990        dict = { '_name' : name, '_min' : min, '_max' : max }
991        return type.__new__(cls, name, (_CheckedInt, ), dict)
992
993class CheckedIntType(CheckedInt):
994    def __new__(cls, name, size, unsigned):
995        dict = {}
996        if unsigned:
997            min = 0
998            max = 2 ** size - 1
999        else:
1000            min = -(2 ** (size - 1))
1001            max = (2 ** (size - 1)) - 1
1002
1003        return super(cls, CheckedIntType).__new__(cls, name, min, max)
1004
1005Int      = CheckedIntType('int',      32, False)
1006Unsigned = CheckedIntType('unsigned', 32, True)
1007
1008Int8     = CheckedIntType('int8_t',    8, False)
1009UInt8    = CheckedIntType('uint8_t',   8, True)
1010Int16    = CheckedIntType('int16_t',  16, False)
1011UInt16   = CheckedIntType('uint16_t', 16, True)
1012Int32    = CheckedIntType('int32_t',  32, False)
1013UInt32   = CheckedIntType('uint32_t', 32, True)
1014Int64    = CheckedIntType('int64_t',  64, False)
1015UInt64   = CheckedIntType('uint64_t', 64, True)
1016
1017Counter  = CheckedIntType('Counter', 64, True)
1018Addr     = CheckedIntType('Addr',    64, True)
1019Tick     = CheckedIntType('Tick',    64, True)
1020
1021Percent  = CheckedInt('int', 0, 100)
1022
1023class Pair(object):
1024    def __init__(self, first, second):
1025        self.first = first
1026        self.second = second
1027
1028class _Range(object):
1029    def _convert(cls, value):
1030        if not isinstance(value, Pair):
1031            raise TypeError, 'value %s is not a Pair' % value
1032        return Pair(cls._type._convert(value.first),
1033                    cls._type._convert(value.second))
1034    _convert = classmethod(_convert)
1035
1036    def _string(cls, value):
1037        return '%s:%s' % (cls._type._string(value.first),
1038                          cls._type._string(value.second))
1039    _string = classmethod(_string)
1040
1041def RangeSize(start, size):
1042    return Pair(start, start + size - 1)
1043
1044class Range(type):
1045    def __new__(cls, type):
1046        dict = { '_name' : 'Range<%s>' + type._name, '_type' : type }
1047        cname = 'Range_' + type.__name__
1048        return super(cls, Range).__new__(cls, cname, (_Range, ), dict)
1049
1050AddrRange = Range(Addr)
1051
1052# Boolean parameter type.
1053class Bool(object):
1054    _name = 'bool'
1055    def _convert(value):
1056        t = type(value)
1057        if t == bool:
1058            return value
1059
1060        if t == int or t == long:
1061            return bool(value)
1062
1063        if t == str:
1064            v = value.lower()
1065            if v == "true" or v == "t" or v == "yes" or v == "y":
1066                return True
1067            elif v == "false" or v == "f" or v == "no" or v == "n":
1068                return False
1069
1070        raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v, t)
1071    _convert = staticmethod(_convert)
1072
1073    def _string(value):
1074        if value:
1075            return "true"
1076        else:
1077            return "false"
1078    _string = staticmethod(_string)
1079
1080# String-valued parameter.
1081class String(object):
1082    _name = 'string'
1083
1084    # Constructor.  Value must be Python string.
1085    def _convert(cls,value):
1086        if value is None:
1087            return ''
1088        if isinstance(value, str):
1089            return value
1090
1091        raise TypeError, \
1092              "String param got value %s %s" % (repr(value), type(value))
1093    _convert = classmethod(_convert)
1094
1095    # Generate printable string version.  Not too tricky.
1096    def _string(cls, value):
1097        return value
1098    _string = classmethod(_string)
1099
1100
1101def IncEthernetAddr(addr, val = 1):
1102    bytes = map(lambda x: int(x, 16), addr.split(':'))
1103    bytes[5] += val
1104    for i in (5, 4, 3, 2, 1):
1105        val,rem = divmod(bytes[i], 256)
1106        bytes[i] = rem
1107        if val == 0:
1108            break
1109        bytes[i - 1] += val
1110    assert(bytes[0] <= 255)
1111    return ':'.join(map(lambda x: '%02x' % x, bytes))
1112
1113class NextEthernetAddr(object):
1114    __metaclass__ = Singleton
1115    addr = "00:90:00:00:00:01"
1116
1117    def __init__(self, inc = 1):
1118        self.value = self.addr
1119        self.addr = IncEthernetAddr(self.addr, inc)
1120
1121class EthernetAddr(object):
1122    _name = 'EthAddr'
1123
1124    def _convert(cls, value):
1125        if value == NextEthernetAddr:
1126            return value
1127
1128        if not isinstance(value, str):
1129            raise TypeError, "expected an ethernet address and didn't get one"
1130
1131        bytes = value.split(':')
1132        if len(bytes) != 6:
1133            raise TypeError, 'invalid ethernet address %s' % value
1134
1135        for byte in bytes:
1136            if not 0 <= int(byte) <= 256:
1137                raise TypeError, 'invalid ethernet address %s' % value
1138
1139        return value
1140    _convert = classmethod(_convert)
1141
1142    def _string(cls, value):
1143        if value == NextEthernetAddr:
1144            value = value().value
1145        return value
1146    _string = classmethod(_string)
1147
1148# Special class for NULL pointers.  Note the special check in
1149# make_param_value() above that lets these be assigned where a
1150# SimObject is required.
1151# only one copy of a particular node
1152class NullSimObject(object):
1153    __metaclass__ = Singleton
1154    _name = 'NULL'
1155
1156    def __call__(cls):
1157        return cls
1158
1159    def _sim_code(cls):
1160        pass
1161    _sim_code = classmethod(_sim_code)
1162
1163    def _instantiate(self, parent = None, path = ''):
1164        pass
1165
1166    def _convert(cls, value):
1167        if value == Nxone:
1168            return
1169
1170        if isinstance(value, cls):
1171            return value
1172
1173        raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1174              (repr(value), type(value), cls)
1175    _convert = classmethod(_convert)
1176
1177    def _string():
1178        return 'NULL'
1179    _string = staticmethod(_string)
1180
1181# The only instance you'll ever need...
1182Null = NULL = NullSimObject()
1183
1184# Enumerated types are a little more complex.  The user specifies the
1185# type as Enum(foo) where foo is either a list or dictionary of
1186# alternatives (typically strings, but not necessarily so).  (In the
1187# long run, the integer value of the parameter will be the list index
1188# or the corresponding dictionary value.  For now, since we only check
1189# that the alternative is valid and then spit it into a .ini file,
1190# there's not much point in using the dictionary.)
1191
1192# What Enum() must do is generate a new type encapsulating the
1193# provided list/dictionary so that specific values of the parameter
1194# can be instances of that type.  We define two hidden internal
1195# classes (_ListEnum and _DictEnum) to serve as base classes, then
1196# derive the new type from the appropriate base class on the fly.
1197
1198
1199# Base class for Enum types.
1200class _Enum(object):
1201    def _convert(self, value):
1202        if value not in self.map:
1203            raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1204                  % (value, self.map)
1205        return value
1206    _convert = classmethod(_convert)
1207
1208    # Generate printable string version of value.
1209    def _string(self, value):
1210        return str(value)
1211    _string = classmethod(_string)
1212
1213# Enum metaclass... calling Enum(foo) generates a new type (class)
1214# that derives from _ListEnum or _DictEnum as appropriate.
1215class Enum(type):
1216    # counter to generate unique names for generated classes
1217    counter = 1
1218
1219    def __new__(cls, *args):
1220        if len(args) > 1:
1221            enum_map = args
1222        else:
1223            enum_map = args[0]
1224
1225        if isinstance(enum_map, dict):
1226            map = enum_map
1227        elif issequence(enum_map):
1228            map = {}
1229            for idx,val in enumerate(enum_map):
1230                map[val] = idx
1231        else:
1232            raise TypeError, "Enum map must be list or dict (got %s)" % map
1233
1234        classname = "Enum%04d" % Enum.counter
1235        Enum.counter += 1
1236
1237        # New class derives from _Enum base, and gets a 'map'
1238        # attribute containing the specified list or dict.
1239        return type.__new__(cls, classname, (_Enum, ), { 'map': map })
1240
1241
1242#
1243# "Constants"... handy aliases for various values.
1244#
1245
1246# For compatibility with C++ bool constants.
1247false = False
1248true = True
1249
1250# Some memory range specifications use this as a default upper bound.
1251MAX_ADDR = Addr._max
1252
1253# For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1254K = 1024
1255M = K*K
1256G = K*M
1257
1258#####################################################################
1259
1260# Munge an arbitrary Python code string to get it to execute (mostly
1261# dealing with indentation).  Stolen from isa_parser.py... see
1262# comments there for a more detailed description.
1263#def fixPythonIndentation(s):
1264#    # get rid of blank lines first
1265#    s = re.sub(r'(?m)^\s*\n', '', s);
1266#    if (s != '' and re.match(r'[ \t]', s[0])):
1267#        s = 'if 1:\n' + s
1268#    return s
1269
1270# Hook to generate C++ parameter code.
1271def gen_sim_code(file):
1272    for objname in sim_object_list:
1273        print >> file, eval("%s._sim_code()" % objname)
1274
1275# The final hook to generate .ini files.  Called from configuration
1276# script once config is built.
1277def instantiate(root):
1278    if not issubclass(root, Root):
1279        raise AttributeError, 'Can only instantiate the Root of the tree'
1280
1281    instance = root.instantiate('root')
1282    instance.fixup()
1283    instance.display()
1284    if not noDot:
1285       dot = pydot.Dot()
1286       instance.outputDot(dot)
1287       dot.orientation = "portrait"
1288       dot.size = "8.5,11"
1289       dot.ranksep="equally"
1290       dot.rank="samerank"
1291       dot.write("config.dot")
1292       dot.write_ps("config.ps")
1293
1294from objects import *
1295
1296