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
30 from smartdict
import SmartDict
40 env
.update(os
.environ
)
43 print >>sys
.stderr
, 'panic:', string
46 def issequence(value
):
47 return isinstance(value
, tuple) or isinstance(value
, list)
49 class Singleton(type):
50 def __call__(cls
, *args
, **kwargs
):
51 if hasattr(cls
, '_instance'):
54 cls
._instance
= super(Singleton
, cls
).__call
__(*args
, **kwargs
)
57 #####################################################################
59 # M5 Python Configuration Utility
61 # The basic idea is to write simple Python programs that build Python
62 # objects corresponding to M5 SimObjects for the deisred simulation
63 # configuration. For now, the Python emits a .ini file that can be
64 # parsed by M5. In the future, some tighter integration between M5
65 # and the Python interpreter may allow bypassing the .ini file.
67 # Each SimObject class in M5 is represented by a Python class with the
68 # same name. The Python inheritance tree mirrors the M5 C++ tree
69 # (e.g., SimpleCPU derives from BaseCPU in both cases, and all
70 # SimObjects inherit from a single SimObject base class). To specify
71 # an instance of an M5 SimObject in a configuration, the user simply
72 # instantiates the corresponding Python object. The parameters for
73 # that SimObject are given by assigning to attributes of the Python
74 # object, either using keyword assignment in the constructor or in
75 # separate assignment statements. For example:
77 # cache = BaseCache('my_cache', root, size=64*K)
78 # cache.hit_latency = 3
81 # (The first two constructor arguments specify the name of the created
82 # cache and its parent node in the hierarchy.)
84 # The magic lies in the mapping of the Python attributes for SimObject
85 # classes to the actual SimObject parameter specifications. This
86 # allows parameter validity checking in the Python code. Continuing
87 # the example above, the statements "cache.blurfl=3" or
88 # "cache.assoc='hello'" would both result in runtime errors in Python,
89 # since the BaseCache object has no 'blurfl' parameter and the 'assoc'
90 # parameter requires an integer, respectively. This magic is done
91 # primarily by overriding the special __setattr__ method that controls
92 # assignment to object attributes.
94 # The Python module provides another class, ConfigNode, which is a
95 # superclass of SimObject. ConfigNode implements the parent/child
96 # relationship for building the configuration hierarchy tree.
97 # Concrete instances of ConfigNode can be used to group objects in the
98 # hierarchy, but do not correspond to SimObjects themselves (like a
99 # .ini section with "children=" but no "type=".
101 # Once a set of Python objects have been instantiated in a hierarchy,
102 # calling 'instantiate(obj)' (where obj is the root of the hierarchy)
103 # will generate a .ini file. See simple-4cpu.py for an example
104 # (corresponding to m5-test/simple-4cpu.ini).
106 #####################################################################
108 #####################################################################
110 # ConfigNode/SimObject classes
112 # The Python class hierarchy rooted by ConfigNode (which is the base
113 # class of SimObject, which in turn is the base class of all other M5
114 # SimObject classes) has special attribute behavior. In general, an
115 # object in this hierarchy has three categories of attribute-like
118 # 1. Regular Python methods and variables. These must start with an
119 # underscore to be treated normally.
121 # 2. SimObject parameters. These values are stored as normal Python
122 # attributes, but all assignments to these attributes are checked
123 # against the pre-defined set of parameters stored in the class's
124 # _params dictionary. Assignments to attributes that do not
125 # correspond to predefined parameters, or that are not of the correct
126 # type, incur runtime errors.
128 # 3. Hierarchy children. The child nodes of a ConfigNode are stored
129 # in the node's _children dictionary, but can be accessed using the
130 # Python attribute dot-notation (just as they are printed out by the
131 # simulator). Children cannot be created using attribute assigment;
132 # they must be added by specifying the parent node in the child's
133 # constructor or using the '+=' operator.
135 # The SimObject parameters are the most complex, for a few reasons.
136 # First, both parameter descriptions and parameter values are
137 # inherited. Thus parameter description lookup must go up the
138 # inheritance chain like normal attribute lookup, but this behavior
139 # must be explicitly coded since the lookup occurs in each class's
140 # _params attribute. Second, because parameter values can be set
141 # on SimObject classes (to implement default values), the parameter
142 # checking behavior must be enforced on class attribute assignments as
143 # well as instance attribute assignments. Finally, because we allow
144 # class specialization via inheritance (e.g., see the L1Cache class in
145 # the simple-4cpu.py example), we must do parameter checking even on
146 # class instantiation. To provide all these features, we use a
147 # metaclass to define most of the SimObject parameter behavior for
148 # this class hierarchy.
150 #####################################################################
153 def __init__(self
, path
= ()):
157 def __getattr__(self
, attr
):
158 return Proxy(self
._path
+ (attr
, ))
160 def __setattr__(self
, attr
, value
):
161 if not attr
.startswith('_'):
162 raise AttributeError, 'cannot set attribute %s' % attr
163 super(Proxy
, self
).__setattr
__(attr
, value
)
167 for attr
in self
._path
:
168 obj
= obj
.__getattribute
__(attr
)
173 def isSubClass(value
, cls
):
175 return issubclass(value
, cls
)
179 def isConfigNode(value
):
181 return issubclass(value
, ConfigNode
)
185 def isSimObject(value
):
187 return issubclass(value
, SimObject
)
191 def isSimObjSequence(value
):
192 if not issequence(value
):
196 if not isNullPointer(val
) and not isConfigNode(val
):
201 def isParamContext(value
):
203 return issubclass(value
, ParamContext
)
208 class_decorator
= 'M5M5_SIMOBJECT_'
209 expr_decorator
= 'M5M5_EXPRESSION_'
210 dot_decorator
= '_M5M5_DOT_'
212 # 'Global' map of legitimate types for SimObject parameters.
215 # Dummy base class to identify types that are legitimate for SimObject
217 class ParamType(object):
220 # Add types defined in given context (dict or module) that are derived
221 # from ParamType to param_types map.
222 def add_param_types(ctx
):
223 if isinstance(ctx
, types
.DictType
):
225 elif isinstance(ctx
, types
.ModuleType
):
226 source_dict
= ctx
.__dict
__
229 "m5.config.add_param_types requires dict or module as arg"
230 for key
,val
in source_dict
.iteritems():
231 if isinstance(val
, type) and issubclass(val
, ParamType
):
232 param_types
[key
] = val
234 # The metaclass for ConfigNode (and thus for everything that derives
235 # from ConfigNode, including SimObject). This class controls how new
236 # classes that derive from ConfigNode are instantiated, and provides
237 # inherited class behavior (just like a class controls how instances
238 # of that class are instantiated, and provides inherited instance
240 class MetaConfigNode(type):
241 # Attributes that can be set only at initialization time
243 # Attributes that can be set any time
244 keywords
= { 'check' : types
.FunctionType
,
245 'children' : types
.ListType
}
247 # __new__ is called before __init__, and is where the statements
248 # in the body of the class definition get loaded into the class's
249 # __dict__. We intercept this to filter out parameter assignments
250 # and only allow "private" attributes to be passed to the base
251 # __new__ (starting with underscore).
252 def __new__(mcls
, name
, bases
, dict):
253 # Copy "private" attributes (including special methods such as __new__)
254 # to the official dict. Everything else goes in _init_dict to be
255 # filtered in __init__.
257 for key
,val
in dict.items():
258 if key
.startswith('_'):
261 cls_dict
['_init_dict'] = dict
262 return super(MetaConfigNode
, mcls
).__new
__(mcls
, name
, bases
, cls_dict
)
265 def __init__(cls
, name
, bases
, dict):
266 super(MetaConfigNode
, cls
).__init
__(name
, bases
, dict)
268 # initialize required attributes
271 cls
._param
_types
= {}
272 cls
._bases
= [c
for c
in cls
.__mro
__ if isConfigNode(c
)]
273 cls
._anon
_subclass
_counter
= 0
275 # If your parent has a value in it that's a config node, clone
276 # it. Do this now so if we update any of the values'
277 # attributes we are updating the clone and not the original.
278 for base
in cls
._bases
:
279 for key
,val
in base
._values
.iteritems():
281 # don't clone if (1) we're about to overwrite it with
282 # a local setting or (2) we've already cloned a copy
283 # from an earlier (more derived) base
284 if cls
._init
_dict
.has_key(key
) or cls
._values
.has_key(key
):
287 if isConfigNode(val
):
288 cls
._values
[key
] = val()
289 elif isSimObjSequence(val
):
290 cls
._values
[key
] = [ v() for v
in val
]
291 elif isNullPointer(val
):
292 cls
._values
[key
] = val
294 # process param types from _init_dict, as these may be needed
295 # by param descriptions also in _init_dict
296 for key
,val
in cls
._init
_dict
.items():
297 if isinstance(val
, type) and issubclass(val
, ParamType
):
298 cls
._param
_types
[key
] = val
299 if not issubclass(val
, ConfigNode
):
300 del cls
._init
_dict
[key
]
302 # now process remaining _init_dict items
303 for key
,val
in cls
._init
_dict
.items():
305 if isinstance(val
, _Param
):
306 cls
._params
[key
] = val
307 # try to resolve local param types in local param_types scope
308 val
.maybe_resolve_type(cls
._param
_types
)
310 # init-time-only keywords
311 elif cls
.init_keywords
.has_key(key
):
312 cls
._set
_keyword
(key
, val
, cls
.init_keywords
[key
])
314 # See description of decorators in the importer.py file.
315 # We just strip off the expr_decorator now since we don't
316 # need from this point on.
317 elif key
.startswith(expr_decorator
):
318 key
= key
[len(expr_decorator
):]
319 # because it had dots into a list so that we can find the
320 # proper variable to modify.
321 key
= key
.split(dot_decorator
)
323 for item
in key
[:-1]:
325 setattr(c
, key
[-1], val
)
327 # default: use normal path (ends up in __setattr__)
329 setattr(cls
, key
, val
)
332 def _isvalue(cls
, name
):
334 if c
._params
.has_key(name
):
338 if c
._values
.has_key(name
):
343 # generator that iterates across all parameters for this class and
344 # all classes it inherits from
348 for p
,v
in c
._params
.iteritems():
349 if not params
.has_key(p
):
353 # Lookup a parameter description by name in the given class.
354 def _getparam(cls
, name
, default
= AttributeError):
356 if c
._params
.has_key(name
):
357 return c
._params
[name
]
358 if isSubClass(default
, Exception):
360 "object '%s' has no attribute '%s'" % (cls
.__name
__, name
)
364 def _hasvalue(cls
, name
):
366 if c
._values
.has_key(name
):
373 for i
,c
in enumerate(cls
._bases
):
374 for p
,v
in c
._values
.iteritems():
375 if not values
.has_key(p
):
377 for p
,v
in c
._params
.iteritems():
378 if not values
.has_key(p
) and hasattr(v
, 'default'):
382 panic("Invalid default %s for param %s in node %s"
383 % (v
.default
,p
,cls
.__name
__))
390 def _getvalue(cls
, name
, default
= AttributeError):
393 if c
._values
.has_key(name
):
394 value
= c
._values
[name
]
396 if value
is not None:
399 param
= cls
._getparam
(name
, None)
400 if param
is not None and hasattr(param
, 'default'):
401 param
.valid(param
.default
)
402 value
= param
.default
403 cls
._setvalue
(name
, value
)
406 if isSubClass(default
, Exception):
407 raise default
, 'value for %s not found' % name
411 def _setvalue(cls
, name
, value
):
412 cls
._values
[name
] = value
414 def __getattr__(cls
, attr
):
415 if cls
._isvalue
(attr
):
416 return Value(cls
, attr
)
418 if attr
== '_cpp_param_decl' and hasattr(cls
, 'type'):
419 return cls
.type + '*'
421 raise AttributeError, \
422 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
)
424 def _set_keyword(cls
, keyword
, val
, kwtype
):
425 if not isinstance(val
, kwtype
):
426 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
427 (keyword
, type(val
), kwtype
)
428 if isinstance(val
, types
.FunctionType
):
429 val
= classmethod(val
)
430 type.__setattr
__(cls
, keyword
, val
)
432 # Set attribute (called on foo.attr = value when foo is an
433 # instance of class cls).
434 def __setattr__(cls
, attr
, value
):
435 # normal processing for private attributes
436 if attr
.startswith('_'):
437 type.__setattr
__(cls
, attr
, value
)
440 if cls
.keywords
.has_key(attr
):
441 cls
._set
_keyword
(attr
, value
, cls
.keywords
[attr
])
444 # must be SimObject param
445 param
= cls
._getparam
(attr
, None)
447 # It's ok: set attribute by delegating to 'object' class.
448 # Note the use of param.make_value() to verify/canonicalize
453 panic("Error setting param %s.%s to %s\n" % \
454 (cls
.__name
__, attr
, value
))
455 cls
._setvalue
(attr
, value
)
456 elif isConfigNode(value
) or isSimObjSequence(value
):
457 cls
._setvalue
(attr
, value
)
459 raise AttributeError, \
460 "Class %s has no parameter %s" % (cls
.__name
__, attr
)
462 def add_child(cls
, instance
, name
, child
):
463 if isNullPointer(child
) or instance
.top_child_names
.has_key(name
):
466 if issequence(child
):
468 for i
,c
in enumerate(child
):
469 n
= '%s%d' % (name
, i
)
470 k
= c
.instantiate(n
, instance
)
472 instance
.children
.append(k
)
473 instance
.child_names
[n
] = k
474 instance
.child_objects
[c
] = k
477 kid
= child
.instantiate(name
, instance
)
478 instance
.children
.append(kid
)
479 instance
.child_names
[name
] = kid
480 instance
.child_objects
[child
] = kid
482 instance
.top_child_names
[name
] = kid
484 # Print instance info to .ini file.
485 def instantiate(cls
, name
, parent
= None):
486 instance
= Node(name
, cls
, parent
, isParamContext(cls
))
488 if hasattr(cls
, 'check'):
491 for key
,value
in cls
._getvalues
().iteritems():
492 if isConfigNode(value
):
493 cls
.add_child(instance
, key
, value
)
494 if issequence(value
):
495 list = [ v
for v
in value
if isConfigNode(v
) ]
497 cls
.add_child(instance
, key
, list)
499 for pname
,param
in cls
._getparams
().iteritems():
501 value
= cls
._getvalue
(pname
)
503 panic('Error getting %s' % pname
)
506 if isConfigNode(value
):
507 value
= instance
.child_objects
[value
]
508 elif issequence(value
):
511 if isConfigNode(val
):
512 v
.append(instance
.child_objects
[val
])
517 p
= NodeParam(pname
, param
, value
)
518 instance
.params
.append(p
)
519 instance
.param_names
[pname
] = p
521 print 'Exception while evaluating %s.%s' % \
522 (instance
.path
, pname
)
527 def _convert(cls
, value
):
529 if isinstance(value
, Node
):
530 realvalue
= value
.realtype
532 if isinstance(realvalue
, Proxy
):
535 if realvalue
== None or isNullPointer(realvalue
):
538 if isSubClass(realvalue
, cls
):
541 raise TypeError, 'object %s type %s wrong type, should be %s' % \
542 (repr(realvalue
), realvalue
, cls
)
544 def _string(cls
, value
):
545 if isNullPointer(value
):
547 return Node
._string
(value
)
549 # The ConfigNode class is the root of the special hierarchy. Most of
550 # the code in this class deals with the configuration hierarchy itself
551 # (parent/child node relationships).
552 class ConfigNode(object):
553 # Specify metaclass. Any class inheriting from ConfigNode will
554 # get this metaclass.
555 __metaclass__
= MetaConfigNode
557 def __new__(cls
, **kwargs
):
558 name
= cls
.__name
__ + ("_%d" % cls
._anon
_subclass
_counter
)
559 cls
._anon
_subclass
_counter
+= 1
560 return cls
.__metaclass
__(name
, (cls
, ), kwargs
)
562 class ParamContext(ConfigNode
):
565 class MetaSimObject(MetaConfigNode
):
566 # init_keywords and keywords are inherited from MetaConfigNode,
567 # with overrides/additions
568 init_keywords
= MetaConfigNode
.init_keywords
569 init_keywords
.update({ 'abstract' : types
.BooleanType
,
570 'type' : types
.StringType
})
572 keywords
= MetaConfigNode
.keywords
573 # no additional keywords
578 def __init__(cls
, name
, bases
, dict):
579 super(MetaSimObject
, cls
).__init
__(name
, bases
, dict)
581 if hasattr(cls
, 'type'):
582 if name
== 'SimObject':
584 elif hasattr(cls
._bases
[1], 'type'):
585 cls
._cpp
_base
= cls
._bases
[1].type
587 panic("SimObject %s derives from a non-C++ SimObject %s "\
588 "(no 'type')" % (cls
, cls_bases
[1].__name
__))
590 # This class corresponds to a C++ class: put it on the global
591 # list of C++ objects to generate param structs, etc.
592 MetaSimObject
.cpp_classes
.append(cls
)
597 code
+= "\n".join([e
.cpp_declare() for e
in cls
._param
_types
.values()])
599 param_names
= cls
._params
.keys()
601 code
+= "struct Params"
603 code
+= " : public %s::Params" % cls
._cpp
_base
605 code
+= "\n ".join([cls
._params
[pname
].cpp_decl(pname
) \
606 for pname
in param_names
])
610 class NodeParam(object):
611 def __init__(self
, name
, param
, value
):
614 self
.ptype
= param
.ptype
615 self
.convert
= param
.convert
616 self
.string
= param
.string
621 def __init__(self
, name
, realtype
, parent
, paramcontext
):
623 self
.realtype
= realtype
624 if isSimObject(realtype
):
625 self
.type = realtype
.type
630 self
.child_names
= {}
631 self
.child_objects
= {}
632 self
.top_child_names
= {}
634 self
.param_names
= {}
635 self
.paramcontext
= paramcontext
639 while node
is not None:
640 if node
.name
!= 'root':
641 path
.insert(0, node
.name
)
643 assert(node
.parent
is None)
645 self
.path
= '.'.join(path
)
647 def find(self
, realtype
, path
):
649 if issubclass(self
.realtype
, realtype
):
653 for child
in self
.children
:
654 if issubclass(child
.realtype
, realtype
):
656 raise AttributeError, \
657 'Super matched more than one: %s %s' % \
658 (obj
.path
, child
.path
)
660 return obj
, obj
is not None
664 for node
in path
[:-1]:
665 obj
= obj
.child_names
[node
]
668 if obj
.child_names
.has_key(last
):
669 value
= obj
.child_names
[last
]
670 if issubclass(value
.realtype
, realtype
):
672 elif obj
.param_names
.has_key(last
):
673 value
= obj
.param_names
[last
]
674 realtype
._convert
(value
.value
)
675 return value
.value
, True
681 def unproxy(self
, ptype
, value
):
682 if not isinstance(value
, Proxy
):
686 raise AttributeError, 'Error while fixing up %s' % self
.path
692 raise AttributeError, \
693 'Parent of %s type %s not found at path %s' \
694 % (self
.name
, ptype
, value
._path
)
695 found
, done
= obj
.find(ptype
, value
._path
)
696 if isinstance(found
, Proxy
):
703 self
.all
[self
.path
] = self
705 for param
in self
.params
:
711 param
.value
= [ self
.unproxy(ptype
, pv
) for pv
in pval
]
713 param
.value
= self
.unproxy(ptype
, pval
)
715 print 'Error while fixing up %s:%s' % (self
.path
, param
.name
)
718 for child
in self
.children
:
719 assert(child
!= self
)
722 # print type and parameter values to .ini file
724 print '[' + self
.path
+ ']' # .ini section header
726 if isSimObject(self
.realtype
):
727 print 'type = %s' % self
.type
730 # instantiate children in same order they were added for
731 # backward compatibility (else we can end up with cpu1
732 # before cpu0). Changing ordering can also influence timing
733 # in the current memory system, as caches get added to a bus
734 # in different orders which affects their priority in the
735 # case of simulataneous requests. We should uncomment the
736 # following line once we take care of that issue.
737 # self.children.sort(lambda x,y: cmp(x.name, y.name))
738 children
= [ c
.name
for c
in self
.children
if not c
.paramcontext
]
739 print 'children =', ' '.join(children
)
741 self
.params
.sort(lambda x
,y
: cmp(x
.name
, y
.name
))
742 for param
in self
.params
:
744 if param
.value
is None:
745 raise AttributeError, 'Parameter with no value'
747 value
= param
.convert(param
.value
)
748 string
= param
.string(value
)
750 print 'exception in %s:%s' % (self
.path
, param
.name
)
753 print '%s = %s' % (param
.name
, string
)
757 # recursively dump out children
758 for c
in self
.children
:
761 # print type and parameter values to .ini file
762 def outputDot(self
, dot
):
765 label
= "{%s|" % self
.path
766 if isSimObject(self
.realtype
):
767 label
+= '%s|' % self
.type
770 # instantiate children in same order they were added for
771 # backward compatibility (else we can end up with cpu1
773 for c
in self
.children
:
774 dot
.add_edge(pydot
.Edge(self
.path
,c
.path
, style
="bold"))
777 for param
in self
.params
:
779 if param
.value
is None:
780 raise AttributeError, 'Parameter with no value'
782 value
= param
.convert(param
.value
)
783 string
= param
.string(value
)
785 print 'exception in %s:%s' % (self
.name
, param
.name
)
787 if isConfigNode(param
.ptype
) and string
!= "Null":
788 simobjs
.append(string
)
790 label
+= '%s = %s\\n' % (param
.name
, string
)
793 label
+= "|<%s> %s" % (so
, so
)
794 dot
.add_edge(pydot
.Edge("%s:%s" % (self
.path
, so
), so
, tailport
="w"))
796 dot
.add_node(pydot
.Node(self
.path
,shape
="Mrecord",label
=label
))
798 # recursively dump out children
799 for c
in self
.children
:
802 def _string(cls
, value
):
803 if not isinstance(value
, Node
):
804 raise AttributeError, 'expecting %s got %s' % (Node
, value
)
806 _string
= classmethod(_string
)
808 #####################################################################
810 # Parameter description classes
812 # The _params dictionary in each class maps parameter names to
813 # either a Param or a VectorParam object. These objects contain the
814 # parameter description string, the parameter type, and the default
815 # value (loaded from the PARAM section of the .odesc files). The
816 # _convert() method on these objects is used to force whatever value
817 # is assigned to the parameter to the appropriate type.
819 # Note that the default values are loaded into the class's attribute
820 # space when the parameter dictionary is initialized (in
821 # MetaConfigNode._setparams()); after that point they aren't used.
823 #####################################################################
825 def isNullPointer(value
):
826 return isinstance(value
, NullSimObject
)
829 def __init__(self
, obj
, attr
):
830 super(Value
, self
).__setattr
__('attr', attr
)
831 super(Value
, self
).__setattr
__('obj', obj
)
834 return self
.obj
._getvalue
(self
.attr
)
836 def __setattr__(self
, attr
, value
):
837 setattr(self
._getattr
(), attr
, value
)
839 def __getattr__(self
, attr
):
840 return getattr(self
._getattr
(), attr
)
842 def __getitem__(self
, index
):
843 return self
._getattr
().__getitem
__(index
)
845 def __call__(self
, *args
, **kwargs
):
846 return self
._getattr
().__call
__(*args
, **kwargs
)
848 def __nonzero__(self
):
849 return bool(self
._getattr
())
852 return str(self
._getattr
())
855 class _Param(object):
856 def __init__(self
, ptype
, *args
, **kwargs
):
857 if isinstance(ptype
, types
.StringType
):
858 self
.ptype_string
= ptype
859 elif isinstance(ptype
, type):
862 raise TypeError, "Param type is not a type (%s)" % ptype
868 self
.default
= args
[0]
871 raise TypeError, 'too many arguments'
873 if kwargs
.has_key('desc'):
874 assert(not hasattr(self
, 'desc'))
875 self
.desc
= kwargs
['desc']
878 if kwargs
.has_key('default'):
879 assert(not hasattr(self
, 'default'))
880 self
.default
= kwargs
['default']
881 del kwargs
['default']
884 raise TypeError, 'extra unknown kwargs %s' % kwargs
886 if not hasattr(self
, 'desc'):
887 raise TypeError, 'desc attribute missing'
889 def maybe_resolve_type(self
, context
):
890 # check if already resolved... don't use hasattr(),
891 # as that calls __getattr__()
892 if self
.__dict
__.has_key('ptype'):
895 self
.ptype
= context
[self
.ptype_string
]
897 # no harm in trying... we'll try again later using global scope
900 def __getattr__(self
, attr
):
903 self
.ptype
= param_types
[self
.ptype_string
]
906 panic("undefined Param type %s" % self
.ptype_string
)
908 raise AttributeError, "'%s' object has no attribute '%s'" % \
909 (type(self
).__name
__, attr
)
911 def valid(self
, value
):
912 if not isinstance(value
, Proxy
):
913 self
.ptype
._convert
(value
)
915 def convert(self
, value
):
916 return self
.ptype
._convert
(value
)
918 def string(self
, value
):
919 return self
.ptype
._string
(value
)
921 def set(self
, name
, instance
, value
):
922 instance
.__dict
__[name
] = value
924 def cpp_decl(self
, name
):
925 return '%s %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
927 class _ParamProxy(object):
928 def __init__(self
, type):
931 # E.g., Param.Int(5, "number of widgets")
932 def __call__(self
, *args
, **kwargs
):
933 return _Param(self
.ptype
, *args
, **kwargs
)
935 # Strange magic to theoretically allow dotted names as Param classes,
936 # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
937 def __getattr__(self
, attr
):
938 if attr
== '__bases__':
939 raise AttributeError, ''
943 def __setattr__(self
, attr
, value
):
945 raise AttributeError, \
946 'Attribute %s not available in %s' % (attr
, self
.__class
__)
947 super(_ParamProxy
, self
).__setattr
__(attr
, value
)
950 Param
= _ParamProxy(None)
952 # Vector-valued parameter description. Just like Param, except that
953 # the value is a vector (list) of the specified type instead of a
955 class _VectorParam(_Param
):
956 def __init__(self
, type, *args
, **kwargs
):
957 _Param
.__init
__(self
, type, *args
, **kwargs
)
959 def valid(self
, value
):
963 if issequence(value
):
965 if not isinstance(val
, Proxy
):
966 self
.ptype
._convert
(val
)
967 elif not isinstance(value
, Proxy
):
968 self
.ptype
._convert
(value
)
970 # Convert assigned value to appropriate type. If the RHS is not a
971 # list or tuple, it generates a single-element list.
972 def convert(self
, value
):
976 if issequence(value
):
977 # list: coerce each element into new list
978 return [ self
.ptype
._convert
(v
) for v
in value
]
980 # singleton: coerce & wrap in a list
981 return self
.ptype
._convert
(value
)
983 def string(self
, value
):
984 if issequence(value
):
985 return ' '.join([ self
.ptype
._string
(v
) for v
in value
])
987 return self
.ptype
._string
(value
)
989 def cpp_decl(self
, name
):
990 return 'std::vector<%s> %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
992 class _VectorParamProxy(_ParamProxy
):
993 # E.g., VectorParam.Int(5, "number of widgets")
994 def __call__(self
, *args
, **kwargs
):
995 return _VectorParam(self
.ptype
, *args
, **kwargs
)
997 VectorParam
= _VectorParamProxy(None)
999 #####################################################################
1003 # Though native Python types could be used to specify parameter types
1004 # (the 'ptype' field of the Param and VectorParam classes), it's more
1005 # flexible to define our own set of types. This gives us more control
1006 # over how Python expressions are converted to values (via the
1007 # __init__() constructor) and how these values are printed out (via
1008 # the __str__() conversion method). Eventually we'll need these types
1009 # to correspond to distinct C++ types as well.
1011 #####################################################################
1014 # Metaclass for bounds-checked integer parameters. See CheckedInt.
1015 class CheckedIntType(type):
1016 def __init__(cls
, name
, bases
, dict):
1017 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
1019 # CheckedInt is an abstract base class, so we actually don't
1020 # want to do any processing on it... the rest of this code is
1021 # just for classes that derive from CheckedInt.
1022 if name
== 'CheckedInt':
1025 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
1026 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
1027 panic("CheckedInt subclass %s must define either\n" \
1028 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1032 cls
.max = 2 ** cls
.size
- 1
1034 cls
.min = -(2 ** (cls
.size
- 1))
1035 cls
.max = (2 ** (cls
.size
- 1)) - 1
1037 cls
._cpp
_param
_decl
= cls
.cppname
1039 def _convert(cls
, value
):
1040 if isinstance(value
, bool):
1043 if not isinstance(value
, (int, long, float, str)):
1044 raise TypeError, 'Integer param of invalid type %s' % type(value
)
1046 if isinstance(value
, (str, float)):
1047 value
= long(float(value
))
1049 if not cls
.min <= value
<= cls
.max:
1050 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1051 (cls
.min, value
, cls
.max)
1055 def _string(cls
, value
):
1058 # Abstract superclass for bounds-checked integer parameters. This
1059 # class is subclassed to generate parameter classes with specific
1060 # bounds. Initialization of the min and max bounds is done in the
1061 # metaclass CheckedIntType.__init__.
1062 class CheckedInt(ParamType
):
1063 __metaclass__
= CheckedIntType
1065 class Int(CheckedInt
): cppname
= 'int'; size
= 32; unsigned
= False
1066 class Unsigned(CheckedInt
): cppname
= 'unsigned'; size
= 32; unsigned
= True
1068 class Int8(CheckedInt
): cppname
= 'int8_t'; size
= 8; unsigned
= False
1069 class UInt8(CheckedInt
): cppname
= 'uint8_t'; size
= 8; unsigned
= True
1070 class Int16(CheckedInt
): cppname
= 'int16_t'; size
= 16; unsigned
= False
1071 class UInt16(CheckedInt
): cppname
= 'uint16_t'; size
= 16; unsigned
= True
1072 class Int32(CheckedInt
): cppname
= 'int32_t'; size
= 32; unsigned
= False
1073 class UInt32(CheckedInt
): cppname
= 'uint32_t'; size
= 32; unsigned
= True
1074 class Int64(CheckedInt
): cppname
= 'int64_t'; size
= 64; unsigned
= False
1075 class UInt64(CheckedInt
): cppname
= 'uint64_t'; size
= 64; unsigned
= True
1077 class Counter(CheckedInt
): cppname
= 'Counter'; size
= 64; unsigned
= True
1078 class Addr(CheckedInt
): cppname
= 'Addr'; size
= 64; unsigned
= True
1079 class Tick(CheckedInt
): cppname
= 'Tick'; size
= 64; unsigned
= True
1081 class Percent(CheckedInt
): cppname
= 'int'; min = 0; max = 100
1084 def __init__(self
, first
, second
):
1086 self
.second
= second
1088 class MetaRange(type):
1089 def __init__(cls
, name
, bases
, dict):
1090 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
1093 cls
._cpp
_param
_decl
= 'Range<%s>' % cls
.type._cpp
_param
_decl
1095 def _convert(cls
, value
):
1096 if not isinstance(value
, Pair
):
1097 raise TypeError, 'value %s is not a Pair' % value
1098 return Pair(cls
.type._convert
(value
.first
),
1099 cls
.type._convert
(value
.second
))
1101 def _string(cls
, value
):
1102 return '%s:%s' % (cls
.type._string
(value
.first
),
1103 cls
.type._string
(value
.second
))
1105 class Range(ParamType
):
1106 __metaclass__
= MetaRange
1108 def RangeSize(start
, size
):
1109 return Pair(start
, start
+ size
- 1)
1111 class AddrRange(Range
): type = Addr
1113 # Boolean parameter type.
1114 class Bool(ParamType
):
1115 _cpp_param_decl
= 'bool'
1116 def _convert(value
):
1121 if t
== int or t
== long:
1126 if v
== "true" or v
== "t" or v
== "yes" or v
== "y":
1128 elif v
== "false" or v
== "f" or v
== "no" or v
== "n":
1131 raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v
, t
)
1132 _convert
= staticmethod(_convert
)
1139 _string
= staticmethod(_string
)
1141 # String-valued parameter.
1142 class String(ParamType
):
1143 _cpp_param_decl
= 'string'
1145 # Constructor. Value must be Python string.
1146 def _convert(cls
,value
):
1149 if isinstance(value
, str):
1153 "String param got value %s %s" % (repr(value
), type(value
))
1154 _convert
= classmethod(_convert
)
1156 # Generate printable string version. Not too tricky.
1157 def _string(cls
, value
):
1159 _string
= classmethod(_string
)
1162 def IncEthernetAddr(addr
, val
= 1):
1163 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
1165 for i
in (5, 4, 3, 2, 1):
1166 val
,rem
= divmod(bytes
[i
], 256)
1171 assert(bytes
[0] <= 255)
1172 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
1174 class NextEthernetAddr(object):
1175 __metaclass__
= Singleton
1176 addr
= "00:90:00:00:00:01"
1178 def __init__(self
, inc
= 1):
1179 self
.value
= self
.addr
1180 self
.addr
= IncEthernetAddr(self
.addr
, inc
)
1182 class EthernetAddr(ParamType
):
1183 _cpp_param_decl
= 'EthAddr'
1185 def _convert(cls
, value
):
1186 if value
== NextEthernetAddr
:
1189 if not isinstance(value
, str):
1190 raise TypeError, "expected an ethernet address and didn't get one"
1192 bytes
= value
.split(':')
1194 raise TypeError, 'invalid ethernet address %s' % value
1197 if not 0 <= int(byte
) <= 256:
1198 raise TypeError, 'invalid ethernet address %s' % value
1201 _convert
= classmethod(_convert
)
1203 def _string(cls
, value
):
1204 if value
== NextEthernetAddr
:
1205 value
= value().value
1207 _string
= classmethod(_string
)
1209 # Special class for NULL pointers. Note the special check in
1210 # make_param_value() above that lets these be assigned where a
1211 # SimObject is required.
1212 # only one copy of a particular node
1213 class NullSimObject(object):
1214 __metaclass__
= Singleton
1219 def _instantiate(self
, parent
= None, path
= ''):
1222 def _convert(cls
, value
):
1226 if isinstance(value
, cls
):
1229 raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1230 (repr(value
), type(value
), cls
)
1231 _convert
= classmethod(_convert
)
1235 _string
= staticmethod(_string
)
1237 # The only instance you'll ever need...
1238 Null
= NULL
= NullSimObject()
1240 # Enumerated types are a little more complex. The user specifies the
1241 # type as Enum(foo) where foo is either a list or dictionary of
1242 # alternatives (typically strings, but not necessarily so). (In the
1243 # long run, the integer value of the parameter will be the list index
1244 # or the corresponding dictionary value. For now, since we only check
1245 # that the alternative is valid and then spit it into a .ini file,
1246 # there's not much point in using the dictionary.)
1248 # What Enum() must do is generate a new type encapsulating the
1249 # provided list/dictionary so that specific values of the parameter
1250 # can be instances of that type. We define two hidden internal
1251 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1252 # derive the new type from the appropriate base class on the fly.
1255 # Metaclass for Enum types
1256 class MetaEnum(type):
1258 def __init__(cls
, name
, bases
, init_dict
):
1259 if init_dict
.has_key('map'):
1260 if not isinstance(cls
.map, dict):
1261 raise TypeError, "Enum-derived class attribute 'map' " \
1262 "must be of type dict"
1263 # build list of value strings from map
1264 cls
.vals
= cls
.map.keys()
1266 elif init_dict
.has_key('vals'):
1267 if not isinstance(cls
.vals
, list):
1268 raise TypeError, "Enum-derived class attribute 'vals' " \
1269 "must be of type list"
1270 # build string->value map from vals sequence
1272 for idx
,val
in enumerate(cls
.vals
):
1275 raise TypeError, "Enum-derived class must define "\
1276 "attribute 'map' or 'vals'"
1278 cls
._cpp
_param
_decl
= name
1280 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
1282 def cpp_declare(cls
):
1283 s
= 'enum %s {\n ' % cls
.__name
__
1284 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
1288 # Base class for enum types.
1289 class Enum(ParamType
):
1290 __metaclass__
= MetaEnum
1293 def _convert(self
, value
):
1294 if value
not in self
.map:
1295 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1296 % (value
, self
.vals
)
1298 _convert
= classmethod(_convert
)
1300 # Generate printable string version of value.
1301 def _string(self
, value
):
1303 _string
= classmethod(_string
)
1305 # "Constants"... handy aliases for various values.
1308 # Some memory range specifications use this as a default upper bound.
1312 # For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1317 #####################################################################
1319 # The final hook to generate .ini files. Called from configuration
1320 # script once config is built.
1321 def instantiate(root
):
1322 instance
= root
.instantiate('root')
1327 instance
.outputDot(dot
)
1328 dot
.orientation
= "portrait"
1330 dot
.ranksep
="equally"
1332 dot
.write("config.dot")
1333 dot
.write_ps("config.ps")
1335 # SimObject is a minimal extension of ConfigNode, implementing a
1336 # hierarchy node that corresponds to an M5 SimObject. It prints out a
1337 # "type=" line to indicate its SimObject class, prints out the
1338 # assigned parameters corresponding to its class, and allows
1339 # parameters to be set by keyword in the constructor. Note that most
1340 # of the heavy lifting for the SimObject param handling is done in the
1341 # MetaConfigNode metaclass.
1342 class SimObject(ConfigNode
, ParamType
):
1343 __metaclass__
= MetaSimObject
1347 # __all__ defines the list of symbols that get exported when
1348 # 'from config import *' is invoked. Try to keep this reasonably
1349 # short to avoid polluting other namespaces.
1350 __all__
= ['env', 'issequence', 'panic',
1351 'ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam',
1353 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1354 'Int32', 'UInt32', 'Int64', 'UInt64',
1355 'Counter', 'Addr', 'Tick', 'Percent',
1356 'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M',