1 # Copyright (c) 2004 The Regents of The University of Michigan
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.
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.
27 from __future__
import generators
28 import os
, re
, sys
, types
, inspect
38 def issequence(value
):
39 return isinstance(value
, tuple) or isinstance(value
, list)
41 class Singleton(type):
42 def __call__(cls
, *args
, **kwargs
):
43 if hasattr(cls
, '_instance'):
46 cls
._instance
= super(Singleton
, cls
).__call
__(*args
, **kwargs
)
49 #####################################################################
51 # M5 Python Configuration Utility
53 # The basic idea is to write simple Python programs that build Python
54 # objects corresponding to M5 SimObjects for the deisred simulation
55 # configuration. For now, the Python emits a .ini file that can be
56 # parsed by M5. In the future, some tighter integration between M5
57 # and the Python interpreter may allow bypassing the .ini file.
59 # Each SimObject class in M5 is represented by a Python class with the
60 # same name. The Python inheritance tree mirrors the M5 C++ tree
61 # (e.g., SimpleCPU derives from BaseCPU in both cases, and all
62 # SimObjects inherit from a single SimObject base class). To specify
63 # an instance of an M5 SimObject in a configuration, the user simply
64 # instantiates the corresponding Python object. The parameters for
65 # that SimObject are given by assigning to attributes of the Python
66 # object, either using keyword assignment in the constructor or in
67 # separate assignment statements. For example:
69 # cache = BaseCache('my_cache', root, size=64*K)
70 # cache.hit_latency = 3
73 # (The first two constructor arguments specify the name of the created
74 # cache and its parent node in the hierarchy.)
76 # The magic lies in the mapping of the Python attributes for SimObject
77 # classes to the actual SimObject parameter specifications. This
78 # allows parameter validity checking in the Python code. Continuing
79 # the example above, the statements "cache.blurfl=3" or
80 # "cache.assoc='hello'" would both result in runtime errors in Python,
81 # since the BaseCache object has no 'blurfl' parameter and the 'assoc'
82 # parameter requires an integer, respectively. This magic is done
83 # primarily by overriding the special __setattr__ method that controls
84 # assignment to object attributes.
86 # The Python module provides another class, ConfigNode, which is a
87 # superclass of SimObject. ConfigNode implements the parent/child
88 # relationship for building the configuration hierarchy tree.
89 # Concrete instances of ConfigNode can be used to group objects in the
90 # hierarchy, but do not correspond to SimObjects themselves (like a
91 # .ini section with "children=" but no "type=".
93 # Once a set of Python objects have been instantiated in a hierarchy,
94 # calling 'instantiate(obj)' (where obj is the root of the hierarchy)
95 # will generate a .ini file. See simple-4cpu.py for an example
96 # (corresponding to m5-test/simple-4cpu.ini).
98 #####################################################################
100 #####################################################################
102 # ConfigNode/SimObject classes
104 # The Python class hierarchy rooted by ConfigNode (which is the base
105 # class of SimObject, which in turn is the base class of all other M5
106 # SimObject classes) has special attribute behavior. In general, an
107 # object in this hierarchy has three categories of attribute-like
110 # 1. Regular Python methods and variables. These must start with an
111 # underscore to be treated normally.
113 # 2. SimObject parameters. These values are stored as normal Python
114 # attributes, but all assignments to these attributes are checked
115 # against the pre-defined set of parameters stored in the class's
116 # _params dictionary. Assignments to attributes that do not
117 # correspond to predefined parameters, or that are not of the correct
118 # type, incur runtime errors.
120 # 3. Hierarchy children. The child nodes of a ConfigNode are stored
121 # in the node's _children dictionary, but can be accessed using the
122 # Python attribute dot-notation (just as they are printed out by the
123 # simulator). Children cannot be created using attribute assigment;
124 # they must be added by specifying the parent node in the child's
125 # constructor or using the '+=' operator.
127 # The SimObject parameters are the most complex, for a few reasons.
128 # First, both parameter descriptions and parameter values are
129 # inherited. Thus parameter description lookup must go up the
130 # inheritance chain like normal attribute lookup, but this behavior
131 # must be explicitly coded since the lookup occurs in each class's
132 # _params attribute. Second, because parameter values can be set
133 # on SimObject classes (to implement default values), the parameter
134 # checking behavior must be enforced on class attribute assignments as
135 # well as instance attribute assignments. Finally, because we allow
136 # class specialization via inheritance (e.g., see the L1Cache class in
137 # the simple-4cpu.py example), we must do parameter checking even on
138 # class instantiation. To provide all these features, we use a
139 # metaclass to define most of the SimObject parameter behavior for
140 # this class hierarchy.
142 #####################################################################
145 def __init__(self
, path
= ()):
149 def __getattr__(self
, attr
):
150 return Proxy(self
._path
+ (attr
, ))
152 def __setattr__(self
, attr
, value
):
153 if not attr
.startswith('_'):
154 raise AttributeError, 'cannot set attribute %s' % attr
155 super(Proxy
, self
).__setattr
__(attr
, value
)
159 for attr
in self
._path
:
160 obj
= obj
.__getattribute
__(attr
)
165 def isSubClass(value
, cls
):
167 return issubclass(value
, cls
)
171 def isConfigNode(value
):
173 return issubclass(value
, ConfigNode
)
177 def isSimObject(value
):
179 return issubclass(value
, SimObject
)
183 def isSimObjSequence(value
):
184 if not issequence(value
):
188 if not isNullPointer(val
) and not isConfigNode(val
):
193 def isParamContext(value
):
195 return issubclass(value
, ParamContext
)
200 class_decorator
= 'M5M5_SIMOBJECT_'
201 expr_decorator
= 'M5M5_EXPRESSION_'
202 dot_decorator
= '_M5M5_DOT_'
204 # 'Global' map of legitimate types for SimObject parameters.
207 # Dummy base class to identify types that are legitimate for SimObject
209 class ParamType(object):
212 # Add types defined in given context (dict or module) that are derived
213 # from ParamType to param_types map.
214 def add_param_types(ctx
):
215 if isinstance(ctx
, types
.DictType
):
217 elif isinstance(ctx
, types
.ModuleType
):
218 source_dict
= ctx
.__dict
__
221 "m5.config.add_param_types requires dict or module as arg"
222 for key
,val
in source_dict
.iteritems():
223 if isinstance(val
, type) and issubclass(val
, ParamType
):
224 param_types
[key
] = val
226 # The metaclass for ConfigNode (and thus for everything that derives
227 # from ConfigNode, including SimObject). This class controls how new
228 # classes that derive from ConfigNode are instantiated, and provides
229 # inherited class behavior (just like a class controls how instances
230 # of that class are instantiated, and provides inherited instance
232 class MetaConfigNode(type):
233 # Attributes that can be set only at initialization time
235 # Attributes that can be set any time
236 keywords
= { 'check' : types
.FunctionType
,
237 'children' : types
.ListType
}
239 # __new__ is called before __init__, and is where the statements
240 # in the body of the class definition get loaded into the class's
241 # __dict__. We intercept this to filter out parameter assignments
242 # and only allow "private" attributes to be passed to the base
243 # __new__ (starting with underscore).
244 def __new__(mcls
, name
, bases
, dict):
245 # Copy "private" attributes (including special methods such as __new__)
246 # to the official dict. Everything else goes in _init_dict to be
247 # filtered in __init__.
249 for key
,val
in dict.items():
250 if key
.startswith('_'):
253 cls_dict
['_init_dict'] = dict
254 return super(MetaConfigNode
, mcls
).__new
__(mcls
, name
, bases
, cls_dict
)
257 def __init__(cls
, name
, bases
, dict):
258 super(MetaConfigNode
, cls
).__init
__(name
, bases
, dict)
260 # initialize required attributes
263 cls
._param
_types
= {}
264 cls
._bases
= [c
for c
in cls
.__mro
__ if isConfigNode(c
)]
265 cls
._anon
_subclass
_counter
= 0
267 # If your parent has a value in it that's a config node, clone
268 # it. Do this now so if we update any of the values'
269 # attributes we are updating the clone and not the original.
270 for base
in cls
._bases
:
271 for key
,val
in base
._values
.iteritems():
273 # don't clone if (1) we're about to overwrite it with
274 # a local setting or (2) we've already cloned a copy
275 # from an earlier (more derived) base
276 if cls
._init
_dict
.has_key(key
) or cls
._values
.has_key(key
):
279 if isConfigNode(val
):
280 cls
._values
[key
] = val()
281 elif isSimObjSequence(val
):
282 cls
._values
[key
] = [ v() for v
in val
]
283 elif isNullPointer(val
):
284 cls
._values
[key
] = val
286 # process param types from _init_dict, as these may be needed
287 # by param descriptions also in _init_dict
288 for key
,val
in cls
._init
_dict
.items():
289 if isinstance(val
, type) and issubclass(val
, ParamType
):
290 cls
._param
_types
[key
] = val
291 if not issubclass(val
, ConfigNode
):
292 del cls
._init
_dict
[key
]
294 # now process remaining _init_dict items
295 for key
,val
in cls
._init
_dict
.items():
297 if isinstance(val
, _Param
):
298 cls
._params
[key
] = val
299 # try to resolve local param types in local param_types scope
300 val
.maybe_resolve_type(cls
._param
_types
)
302 # init-time-only keywords
303 elif cls
.init_keywords
.has_key(key
):
304 cls
._set
_keyword
(key
, val
, cls
.init_keywords
[key
])
306 # See description of decorators in the importer.py file.
307 # We just strip off the expr_decorator now since we don't
308 # need from this point on.
309 elif key
.startswith(expr_decorator
):
310 key
= key
[len(expr_decorator
):]
311 # because it had dots into a list so that we can find the
312 # proper variable to modify.
313 key
= key
.split(dot_decorator
)
315 for item
in key
[:-1]:
317 setattr(c
, key
[-1], val
)
319 # default: use normal path (ends up in __setattr__)
321 setattr(cls
, key
, val
)
324 def _isvalue(cls
, name
):
326 if c
._params
.has_key(name
):
330 if c
._values
.has_key(name
):
335 # generator that iterates across all parameters for this class and
336 # all classes it inherits from
340 for p
,v
in c
._params
.iteritems():
341 if not params
.has_key(p
):
345 # Lookup a parameter description by name in the given class.
346 def _getparam(cls
, name
, default
= AttributeError):
348 if c
._params
.has_key(name
):
349 return c
._params
[name
]
350 if isSubClass(default
, Exception):
352 "object '%s' has no attribute '%s'" % (cls
.__name
__, name
)
356 def _hasvalue(cls
, name
):
358 if c
._values
.has_key(name
):
365 for i
,c
in enumerate(cls
._bases
):
366 for p
,v
in c
._values
.iteritems():
367 if not values
.has_key(p
):
369 for p
,v
in c
._params
.iteritems():
370 if not values
.has_key(p
) and hasattr(v
, 'default'):
374 panic("Invalid default %s for param %s in node %s"
375 % (v
.default
,p
,cls
.__name
__))
382 def _getvalue(cls
, name
, default
= AttributeError):
385 if c
._values
.has_key(name
):
386 value
= c
._values
[name
]
388 if value
is not None:
391 param
= cls
._getparam
(name
, None)
392 if param
is not None and hasattr(param
, 'default'):
393 param
.valid(param
.default
)
394 value
= param
.default
395 cls
._setvalue
(name
, value
)
398 if isSubClass(default
, Exception):
399 raise default
, 'value for %s not found' % name
403 def _setvalue(cls
, name
, value
):
404 cls
._values
[name
] = value
406 def __getattr__(cls
, attr
):
407 if cls
._isvalue
(attr
):
408 return Value(cls
, attr
)
410 if attr
== '_cpp_param_decl' and hasattr(cls
, 'type'):
411 return cls
.type + '*'
413 raise AttributeError, \
414 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
)
416 def _set_keyword(cls
, keyword
, val
, kwtype
):
417 if not isinstance(val
, kwtype
):
418 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
419 (keyword
, type(val
), kwtype
)
420 if isinstance(val
, types
.FunctionType
):
421 val
= classmethod(val
)
422 type.__setattr
__(cls
, keyword
, val
)
424 # Set attribute (called on foo.attr = value when foo is an
425 # instance of class cls).
426 def __setattr__(cls
, attr
, value
):
427 # normal processing for private attributes
428 if attr
.startswith('_'):
429 type.__setattr
__(cls
, attr
, value
)
432 if cls
.keywords
.has_key(attr
):
433 cls
._set
_keyword
(attr
, value
, cls
.keywords
[attr
])
436 # must be SimObject param
437 param
= cls
._getparam
(attr
, None)
439 # It's ok: set attribute by delegating to 'object' class.
440 # Note the use of param.make_value() to verify/canonicalize
445 panic("Error setting param %s.%s to %s\n" % \
446 (cls
.__name
__, attr
, value
))
447 cls
._setvalue
(attr
, value
)
448 elif isConfigNode(value
) or isSimObjSequence(value
):
449 cls
._setvalue
(attr
, value
)
451 raise AttributeError, \
452 "Class %s has no parameter %s" % (cls
.__name
__, attr
)
454 def add_child(cls
, instance
, name
, child
):
455 if isNullPointer(child
) or instance
.top_child_names
.has_key(name
):
458 if issequence(child
):
460 for i
,c
in enumerate(child
):
461 n
= '%s%d' % (name
, i
)
462 k
= c
.instantiate(n
, instance
)
464 instance
.children
.append(k
)
465 instance
.child_names
[n
] = k
466 instance
.child_objects
[c
] = k
469 kid
= child
.instantiate(name
, instance
)
470 instance
.children
.append(kid
)
471 instance
.child_names
[name
] = kid
472 instance
.child_objects
[child
] = kid
474 instance
.top_child_names
[name
] = kid
476 # Print instance info to .ini file.
477 def instantiate(cls
, name
, parent
= None):
478 instance
= Node(name
, cls
, parent
, isParamContext(cls
))
480 if hasattr(cls
, 'check'):
483 for key
,value
in cls
._getvalues
().iteritems():
484 if isConfigNode(value
):
485 cls
.add_child(instance
, key
, value
)
486 if issequence(value
):
487 list = [ v
for v
in value
if isConfigNode(v
) ]
489 cls
.add_child(instance
, key
, list)
491 for pname
,param
in cls
._getparams
().iteritems():
493 value
= cls
._getvalue
(pname
)
495 panic('Error getting %s' % pname
)
498 if isConfigNode(value
):
499 value
= instance
.child_objects
[value
]
500 elif issequence(value
):
503 if isConfigNode(val
):
504 v
.append(instance
.child_objects
[val
])
509 p
= NodeParam(pname
, param
, value
)
510 instance
.params
.append(p
)
511 instance
.param_names
[pname
] = p
513 print 'Exception while evaluating %s.%s' % \
514 (instance
.path
, pname
)
519 def _convert(cls
, value
):
521 if isinstance(value
, Node
):
522 realvalue
= value
.realtype
524 if isinstance(realvalue
, Proxy
):
527 if realvalue
== None or isNullPointer(realvalue
):
530 if isSubClass(realvalue
, cls
):
533 raise TypeError, 'object %s type %s wrong type, should be %s' % \
534 (repr(realvalue
), realvalue
, cls
)
536 def _string(cls
, value
):
537 if isNullPointer(value
):
539 return Node
._string
(value
)
541 # The ConfigNode class is the root of the special hierarchy. Most of
542 # the code in this class deals with the configuration hierarchy itself
543 # (parent/child node relationships).
544 class ConfigNode(object):
545 # Specify metaclass. Any class inheriting from ConfigNode will
546 # get this metaclass.
547 __metaclass__
= MetaConfigNode
549 def __new__(cls
, **kwargs
):
550 name
= cls
.__name
__ + ("_%d" % cls
._anon
_subclass
_counter
)
551 cls
._anon
_subclass
_counter
+= 1
552 return cls
.__metaclass
__(name
, (cls
, ), kwargs
)
554 class ParamContext(ConfigNode
):
557 class MetaSimObject(MetaConfigNode
):
558 # init_keywords and keywords are inherited from MetaConfigNode,
559 # with overrides/additions
560 init_keywords
= MetaConfigNode
.init_keywords
561 init_keywords
.update({ 'abstract' : types
.BooleanType
,
562 'type' : types
.StringType
})
564 keywords
= MetaConfigNode
.keywords
565 # no additional keywords
570 def __init__(cls
, name
, bases
, dict):
571 super(MetaSimObject
, cls
).__init
__(name
, bases
, dict)
573 if hasattr(cls
, 'type'):
574 if name
== 'SimObject':
576 elif hasattr(cls
._bases
[1], 'type'):
577 cls
._cpp
_base
= cls
._bases
[1].type
579 panic("SimObject %s derives from a non-C++ SimObject %s "\
580 "(no 'type')" % (cls
, cls_bases
[1].__name
__))
582 # This class corresponds to a C++ class: put it on the global
583 # list of C++ objects to generate param structs, etc.
584 MetaSimObject
.cpp_classes
.append(cls
)
589 code
+= "\n".join([e
.cpp_declare() for e
in cls
._param
_types
.values()])
591 param_names
= cls
._params
.keys()
593 code
+= "struct Params"
595 code
+= " : public %s::Params" % cls
._cpp
_base
597 code
+= "\n ".join([cls
._params
[pname
].cpp_decl(pname
) \
598 for pname
in param_names
])
602 class NodeParam(object):
603 def __init__(self
, name
, param
, value
):
606 self
.ptype
= param
.ptype
607 self
.convert
= param
.convert
608 self
.string
= param
.string
613 def __init__(self
, name
, realtype
, parent
, paramcontext
):
615 self
.realtype
= realtype
616 if isSimObject(realtype
):
617 self
.type = realtype
.type
622 self
.child_names
= {}
623 self
.child_objects
= {}
624 self
.top_child_names
= {}
626 self
.param_names
= {}
627 self
.paramcontext
= paramcontext
631 while node
is not None:
632 if node
.name
!= 'root':
633 path
.insert(0, node
.name
)
635 assert(node
.parent
is None)
637 self
.path
= '.'.join(path
)
639 def find(self
, realtype
, path
):
641 if issubclass(self
.realtype
, realtype
):
645 for child
in self
.children
:
646 if issubclass(child
.realtype
, realtype
):
648 raise AttributeError, \
649 'Super matched more than one: %s %s' % \
650 (obj
.path
, child
.path
)
652 return obj
, obj
is not None
656 for node
in path
[:-1]:
657 obj
= obj
.child_names
[node
]
660 if obj
.child_names
.has_key(last
):
661 value
= obj
.child_names
[last
]
662 if issubclass(value
.realtype
, realtype
):
664 elif obj
.param_names
.has_key(last
):
665 value
= obj
.param_names
[last
]
666 realtype
._convert
(value
.value
)
667 return value
.value
, True
673 def unproxy(self
, ptype
, value
):
674 if not isinstance(value
, Proxy
):
678 raise AttributeError, 'Error while fixing up %s' % self
.path
684 raise AttributeError, \
685 'Parent of %s type %s not found at path %s' \
686 % (self
.name
, ptype
, value
._path
)
687 found
, done
= obj
.find(ptype
, value
._path
)
688 if isinstance(found
, Proxy
):
695 self
.all
[self
.path
] = self
697 for param
in self
.params
:
703 param
.value
= [ self
.unproxy(ptype
, pv
) for pv
in pval
]
705 param
.value
= self
.unproxy(ptype
, pval
)
707 print 'Error while fixing up %s:%s' % (self
.path
, param
.name
)
710 for child
in self
.children
:
711 assert(child
!= self
)
714 # print type and parameter values to .ini file
716 print '[' + self
.path
+ ']' # .ini section header
718 if isSimObject(self
.realtype
):
719 print 'type = %s' % self
.type
722 # instantiate children in same order they were added for
723 # backward compatibility (else we can end up with cpu1
724 # before cpu0). Changing ordering can also influence timing
725 # in the current memory system, as caches get added to a bus
726 # in different orders which affects their priority in the
727 # case of simulataneous requests. We should uncomment the
728 # following line once we take care of that issue.
729 # self.children.sort(lambda x,y: cmp(x.name, y.name))
730 children
= [ c
.name
for c
in self
.children
if not c
.paramcontext
]
731 print 'children =', ' '.join(children
)
733 self
.params
.sort(lambda x
,y
: cmp(x
.name
, y
.name
))
734 for param
in self
.params
:
736 if param
.value
is None:
737 raise AttributeError, 'Parameter with no value'
739 value
= param
.convert(param
.value
)
740 string
= param
.string(value
)
742 print 'exception in %s:%s' % (self
.path
, param
.name
)
745 print '%s = %s' % (param
.name
, string
)
749 # recursively dump out children
750 for c
in self
.children
:
753 # print type and parameter values to .ini file
754 def outputDot(self
, dot
):
757 label
= "{%s|" % self
.path
758 if isSimObject(self
.realtype
):
759 label
+= '%s|' % self
.type
762 # instantiate children in same order they were added for
763 # backward compatibility (else we can end up with cpu1
765 for c
in self
.children
:
766 dot
.add_edge(pydot
.Edge(self
.path
,c
.path
, style
="bold"))
769 for param
in self
.params
:
771 if param
.value
is None:
772 raise AttributeError, 'Parameter with no value'
774 value
= param
.convert(param
.value
)
775 string
= param
.string(value
)
777 print 'exception in %s:%s' % (self
.name
, param
.name
)
779 if isConfigNode(param
.ptype
) and string
!= "Null":
780 simobjs
.append(string
)
782 label
+= '%s = %s\\n' % (param
.name
, string
)
785 label
+= "|<%s> %s" % (so
, so
)
786 dot
.add_edge(pydot
.Edge("%s:%s" % (self
.path
, so
), so
, tailport
="w"))
788 dot
.add_node(pydot
.Node(self
.path
,shape
="Mrecord",label
=label
))
790 # recursively dump out children
791 for c
in self
.children
:
794 def _string(cls
, value
):
795 if not isinstance(value
, Node
):
796 raise AttributeError, 'expecting %s got %s' % (Node
, value
)
798 _string
= classmethod(_string
)
800 #####################################################################
802 # Parameter description classes
804 # The _params dictionary in each class maps parameter names to
805 # either a Param or a VectorParam object. These objects contain the
806 # parameter description string, the parameter type, and the default
807 # value (loaded from the PARAM section of the .odesc files). The
808 # _convert() method on these objects is used to force whatever value
809 # is assigned to the parameter to the appropriate type.
811 # Note that the default values are loaded into the class's attribute
812 # space when the parameter dictionary is initialized (in
813 # MetaConfigNode._setparams()); after that point they aren't used.
815 #####################################################################
817 def isNullPointer(value
):
818 return isinstance(value
, NullSimObject
)
821 def __init__(self
, obj
, attr
):
822 super(Value
, self
).__setattr
__('attr', attr
)
823 super(Value
, self
).__setattr
__('obj', obj
)
826 return self
.obj
._getvalue
(self
.attr
)
828 def __setattr__(self
, attr
, value
):
829 setattr(self
._getattr
(), attr
, value
)
831 def __getattr__(self
, attr
):
832 return getattr(self
._getattr
(), attr
)
834 def __getitem__(self
, index
):
835 return self
._getattr
().__getitem
__(index
)
837 def __call__(self
, *args
, **kwargs
):
838 return self
._getattr
().__call
__(*args
, **kwargs
)
840 def __nonzero__(self
):
841 return bool(self
._getattr
())
844 return str(self
._getattr
())
847 class _Param(object):
848 def __init__(self
, ptype
, *args
, **kwargs
):
849 if isinstance(ptype
, types
.StringType
):
850 self
.ptype_string
= ptype
851 elif isinstance(ptype
, type):
854 raise TypeError, "Param type is not a type (%s)" % ptype
860 self
.default
= args
[0]
863 raise TypeError, 'too many arguments'
865 if kwargs
.has_key('desc'):
866 assert(not hasattr(self
, 'desc'))
867 self
.desc
= kwargs
['desc']
870 if kwargs
.has_key('default'):
871 assert(not hasattr(self
, 'default'))
872 self
.default
= kwargs
['default']
873 del kwargs
['default']
876 raise TypeError, 'extra unknown kwargs %s' % kwargs
878 if not hasattr(self
, 'desc'):
879 raise TypeError, 'desc attribute missing'
881 def maybe_resolve_type(self
, context
):
882 # check if already resolved... don't use hasattr(),
883 # as that calls __getattr__()
884 if self
.__dict
__.has_key('ptype'):
887 self
.ptype
= context
[self
.ptype_string
]
889 # no harm in trying... we'll try again later using global scope
892 def __getattr__(self
, attr
):
895 self
.ptype
= param_types
[self
.ptype_string
]
898 panic("undefined Param type %s" % self
.ptype_string
)
900 raise AttributeError, "'%s' object has no attribute '%s'" % \
901 (type(self
).__name
__, attr
)
903 def valid(self
, value
):
904 if not isinstance(value
, Proxy
):
905 self
.ptype
._convert
(value
)
907 def convert(self
, value
):
908 return self
.ptype
._convert
(value
)
910 def string(self
, value
):
911 return self
.ptype
._string
(value
)
913 def set(self
, name
, instance
, value
):
914 instance
.__dict
__[name
] = value
916 def cpp_decl(self
, name
):
917 return '%s %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
919 class _ParamProxy(object):
920 def __init__(self
, type):
923 # E.g., Param.Int(5, "number of widgets")
924 def __call__(self
, *args
, **kwargs
):
925 return _Param(self
.ptype
, *args
, **kwargs
)
927 # Strange magic to theoretically allow dotted names as Param classes,
928 # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
929 def __getattr__(self
, attr
):
930 if attr
== '__bases__':
931 raise AttributeError, ''
935 def __setattr__(self
, attr
, value
):
937 raise AttributeError, \
938 'Attribute %s not available in %s' % (attr
, self
.__class
__)
939 super(_ParamProxy
, self
).__setattr
__(attr
, value
)
942 Param
= _ParamProxy(None)
944 # Vector-valued parameter description. Just like Param, except that
945 # the value is a vector (list) of the specified type instead of a
947 class _VectorParam(_Param
):
948 def __init__(self
, type, *args
, **kwargs
):
949 _Param
.__init
__(self
, type, *args
, **kwargs
)
951 def valid(self
, value
):
955 if issequence(value
):
957 if not isinstance(val
, Proxy
):
958 self
.ptype
._convert
(val
)
959 elif not isinstance(value
, Proxy
):
960 self
.ptype
._convert
(value
)
962 # Convert assigned value to appropriate type. If the RHS is not a
963 # list or tuple, it generates a single-element list.
964 def convert(self
, value
):
968 if issequence(value
):
969 # list: coerce each element into new list
970 return [ self
.ptype
._convert
(v
) for v
in value
]
972 # singleton: coerce & wrap in a list
973 return self
.ptype
._convert
(value
)
975 def string(self
, value
):
976 if issequence(value
):
977 return ' '.join([ self
.ptype
._string
(v
) for v
in value
])
979 return self
.ptype
._string
(value
)
981 def cpp_decl(self
, name
):
982 return 'std::vector<%s> %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
984 class _VectorParamProxy(_ParamProxy
):
985 # E.g., VectorParam.Int(5, "number of widgets")
986 def __call__(self
, *args
, **kwargs
):
987 return _VectorParam(self
.ptype
, *args
, **kwargs
)
989 VectorParam
= _VectorParamProxy(None)
991 #####################################################################
995 # Though native Python types could be used to specify parameter types
996 # (the 'ptype' field of the Param and VectorParam classes), it's more
997 # flexible to define our own set of types. This gives us more control
998 # over how Python expressions are converted to values (via the
999 # __init__() constructor) and how these values are printed out (via
1000 # the __str__() conversion method). Eventually we'll need these types
1001 # to correspond to distinct C++ types as well.
1003 #####################################################################
1006 # Metaclass for bounds-checked integer parameters. See CheckedInt.
1007 class CheckedIntType(type):
1008 def __init__(cls
, name
, bases
, dict):
1009 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
1011 # CheckedInt is an abstract base class, so we actually don't
1012 # want to do any processing on it... the rest of this code is
1013 # just for classes that derive from CheckedInt.
1014 if name
== 'CheckedInt':
1017 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
1018 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
1019 panic("CheckedInt subclass %s must define either\n" \
1020 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1024 cls
.max = 2 ** cls
.size
- 1
1026 cls
.min = -(2 ** (cls
.size
- 1))
1027 cls
.max = (2 ** (cls
.size
- 1)) - 1
1029 cls
._cpp
_param
_decl
= cls
.cppname
1031 def _convert(cls
, value
):
1032 if isinstance(value
, bool):
1035 if not isinstance(value
, (int, long, float, str)):
1036 raise TypeError, 'Integer param of invalid type %s' % type(value
)
1038 if isinstance(value
, (str, float)):
1039 value
= long(float(value
))
1041 if not cls
.min <= value
<= cls
.max:
1042 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1043 (cls
.min, value
, cls
.max)
1047 def _string(cls
, value
):
1050 # Abstract superclass for bounds-checked integer parameters. This
1051 # class is subclassed to generate parameter classes with specific
1052 # bounds. Initialization of the min and max bounds is done in the
1053 # metaclass CheckedIntType.__init__.
1054 class CheckedInt(ParamType
):
1055 __metaclass__
= CheckedIntType
1057 class Int(CheckedInt
): cppname
= 'int'; size
= 32; unsigned
= False
1058 class Unsigned(CheckedInt
): cppname
= 'unsigned'; size
= 32; unsigned
= True
1060 class Int8(CheckedInt
): cppname
= 'int8_t'; size
= 8; unsigned
= False
1061 class UInt8(CheckedInt
): cppname
= 'uint8_t'; size
= 8; unsigned
= True
1062 class Int16(CheckedInt
): cppname
= 'int16_t'; size
= 16; unsigned
= False
1063 class UInt16(CheckedInt
): cppname
= 'uint16_t'; size
= 16; unsigned
= True
1064 class Int32(CheckedInt
): cppname
= 'int32_t'; size
= 32; unsigned
= False
1065 class UInt32(CheckedInt
): cppname
= 'uint32_t'; size
= 32; unsigned
= True
1066 class Int64(CheckedInt
): cppname
= 'int64_t'; size
= 64; unsigned
= False
1067 class UInt64(CheckedInt
): cppname
= 'uint64_t'; size
= 64; unsigned
= True
1069 class Counter(CheckedInt
): cppname
= 'Counter'; size
= 64; unsigned
= True
1070 class Addr(CheckedInt
): cppname
= 'Addr'; size
= 64; unsigned
= True
1071 class Tick(CheckedInt
): cppname
= 'Tick'; size
= 64; unsigned
= True
1073 class Percent(CheckedInt
): cppname
= 'int'; min = 0; max = 100
1076 def __init__(self
, first
, second
):
1078 self
.second
= second
1080 class MetaRange(type):
1081 def __init__(cls
, name
, bases
, dict):
1082 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
1085 cls
._cpp
_param
_decl
= 'Range<%s>' % cls
.type._cpp
_param
_decl
1087 def _convert(cls
, value
):
1088 if not isinstance(value
, Pair
):
1089 raise TypeError, 'value %s is not a Pair' % value
1090 return Pair(cls
.type._convert
(value
.first
),
1091 cls
.type._convert
(value
.second
))
1093 def _string(cls
, value
):
1094 return '%s:%s' % (cls
.type._string
(value
.first
),
1095 cls
.type._string
(value
.second
))
1097 class Range(ParamType
):
1098 __metaclass__
= MetaRange
1100 def RangeSize(start
, size
):
1101 return Pair(start
, start
+ size
- 1)
1103 class AddrRange(Range
): type = Addr
1105 # Boolean parameter type.
1106 class Bool(ParamType
):
1107 _cpp_param_decl
= 'bool'
1108 def _convert(value
):
1113 if t
== int or t
== long:
1118 if v
== "true" or v
== "t" or v
== "yes" or v
== "y":
1120 elif v
== "false" or v
== "f" or v
== "no" or v
== "n":
1123 raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v
, t
)
1124 _convert
= staticmethod(_convert
)
1131 _string
= staticmethod(_string
)
1133 # String-valued parameter.
1134 class String(ParamType
):
1135 _cpp_param_decl
= 'string'
1137 # Constructor. Value must be Python string.
1138 def _convert(cls
,value
):
1141 if isinstance(value
, str):
1145 "String param got value %s %s" % (repr(value
), type(value
))
1146 _convert
= classmethod(_convert
)
1148 # Generate printable string version. Not too tricky.
1149 def _string(cls
, value
):
1151 _string
= classmethod(_string
)
1154 def IncEthernetAddr(addr
, val
= 1):
1155 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
1157 for i
in (5, 4, 3, 2, 1):
1158 val
,rem
= divmod(bytes
[i
], 256)
1163 assert(bytes
[0] <= 255)
1164 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
1166 class NextEthernetAddr(object):
1167 __metaclass__
= Singleton
1168 addr
= "00:90:00:00:00:01"
1170 def __init__(self
, inc
= 1):
1171 self
.value
= self
.addr
1172 self
.addr
= IncEthernetAddr(self
.addr
, inc
)
1174 class EthernetAddr(ParamType
):
1175 _cpp_param_decl
= 'EthAddr'
1177 def _convert(cls
, value
):
1178 if value
== NextEthernetAddr
:
1181 if not isinstance(value
, str):
1182 raise TypeError, "expected an ethernet address and didn't get one"
1184 bytes
= value
.split(':')
1186 raise TypeError, 'invalid ethernet address %s' % value
1189 if not 0 <= int(byte
) <= 256:
1190 raise TypeError, 'invalid ethernet address %s' % value
1193 _convert
= classmethod(_convert
)
1195 def _string(cls
, value
):
1196 if value
== NextEthernetAddr
:
1197 value
= value().value
1199 _string
= classmethod(_string
)
1201 # Special class for NULL pointers. Note the special check in
1202 # make_param_value() above that lets these be assigned where a
1203 # SimObject is required.
1204 # only one copy of a particular node
1205 class NullSimObject(object):
1206 __metaclass__
= Singleton
1211 def _instantiate(self
, parent
= None, path
= ''):
1214 def _convert(cls
, value
):
1218 if isinstance(value
, cls
):
1221 raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1222 (repr(value
), type(value
), cls
)
1223 _convert
= classmethod(_convert
)
1227 _string
= staticmethod(_string
)
1229 # The only instance you'll ever need...
1230 Null
= NULL
= NullSimObject()
1232 # Enumerated types are a little more complex. The user specifies the
1233 # type as Enum(foo) where foo is either a list or dictionary of
1234 # alternatives (typically strings, but not necessarily so). (In the
1235 # long run, the integer value of the parameter will be the list index
1236 # or the corresponding dictionary value. For now, since we only check
1237 # that the alternative is valid and then spit it into a .ini file,
1238 # there's not much point in using the dictionary.)
1240 # What Enum() must do is generate a new type encapsulating the
1241 # provided list/dictionary so that specific values of the parameter
1242 # can be instances of that type. We define two hidden internal
1243 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1244 # derive the new type from the appropriate base class on the fly.
1247 # Metaclass for Enum types
1248 class MetaEnum(type):
1250 def __init__(cls
, name
, bases
, init_dict
):
1251 if init_dict
.has_key('map'):
1252 if not isinstance(cls
.map, dict):
1253 raise TypeError, "Enum-derived class attribute 'map' " \
1254 "must be of type dict"
1255 # build list of value strings from map
1256 cls
.vals
= cls
.map.keys()
1258 elif init_dict
.has_key('vals'):
1259 if not isinstance(cls
.vals
, list):
1260 raise TypeError, "Enum-derived class attribute 'vals' " \
1261 "must be of type list"
1262 # build string->value map from vals sequence
1264 for idx
,val
in enumerate(cls
.vals
):
1267 raise TypeError, "Enum-derived class must define "\
1268 "attribute 'map' or 'vals'"
1270 cls
._cpp
_param
_decl
= name
1272 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
1274 def cpp_declare(cls
):
1275 s
= 'enum %s {\n ' % cls
.__name
__
1276 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
1280 # Base class for enum types.
1281 class Enum(ParamType
):
1282 __metaclass__
= MetaEnum
1285 def _convert(self
, value
):
1286 if value
not in self
.map:
1287 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1288 % (value
, self
.vals
)
1290 _convert
= classmethod(_convert
)
1292 # Generate printable string version of value.
1293 def _string(self
, value
):
1295 _string
= classmethod(_string
)
1297 # "Constants"... handy aliases for various values.
1300 # Some memory range specifications use this as a default upper bound.
1304 # For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1309 #####################################################################
1311 # The final hook to generate .ini files. Called from configuration
1312 # script once config is built.
1313 def instantiate(root
):
1314 instance
= root
.instantiate('root')
1319 instance
.outputDot(dot
)
1320 dot
.orientation
= "portrait"
1322 dot
.ranksep
="equally"
1324 dot
.write("config.dot")
1325 dot
.write_ps("config.ps")
1327 # SimObject is a minimal extension of ConfigNode, implementing a
1328 # hierarchy node that corresponds to an M5 SimObject. It prints out a
1329 # "type=" line to indicate its SimObject class, prints out the
1330 # assigned parameters corresponding to its class, and allows
1331 # parameters to be set by keyword in the constructor. Note that most
1332 # of the heavy lifting for the SimObject param handling is done in the
1333 # MetaConfigNode metaclass.
1334 class SimObject(ConfigNode
, ParamType
):
1335 __metaclass__
= MetaSimObject
1339 # __all__ defines the list of symbols that get exported when
1340 # 'from config import *' is invoked. Try to keep this reasonably
1341 # short to avoid polluting other namespaces.
1342 __all__
= ['issequence',
1343 'ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam',
1345 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1346 'Int32', 'UInt32', 'Int64', 'UInt64',
1347 'Counter', 'Addr', 'Tick', 'Percent',
1348 'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M',