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_'
214 class ParamType(object):
217 def add_param_types(ctx
):
218 if isinstance(ctx
, types
.DictType
):
220 elif isinstance(ctx
, types
.ModuleType
):
221 source_dict
= ctx
.__dict
__
224 "m5.config.add_param_types requires dict or module as arg"
225 for key
,val
in source_dict
.iteritems():
226 if isinstance(val
, type) and issubclass(val
, ParamType
):
227 param_types
[key
] = val
229 # The metaclass for ConfigNode (and thus for everything that derives
230 # from ConfigNode, including SimObject). This class controls how new
231 # classes that derive from ConfigNode are instantiated, and provides
232 # inherited class behavior (just like a class controls how instances
233 # of that class are instantiated, and provides inherited instance
235 class MetaConfigNode(type):
236 # Attributes that can be set only at initialization time
238 # Attributes that can be set any time
239 keywords
= { 'check' : types
.FunctionType
,
240 'children' : types
.ListType
}
242 # __new__ is called before __init__, and is where the statements
243 # in the body of the class definition get loaded into the class's
244 # __dict__. We intercept this to filter out parameter assignments
245 # and only allow "private" attributes to be passed to the base
246 # __new__ (starting with underscore).
247 def __new__(mcls
, name
, bases
, dict):
248 # Copy "private" attributes (including special methods such as __new__)
249 # to the official dict. Everything else goes in _init_dict to be
250 # filtered in __init__.
252 for key
,val
in dict.items():
253 if key
.startswith('_'):
256 cls_dict
['_init_dict'] = dict
257 return super(MetaConfigNode
, mcls
).__new
__(mcls
, name
, bases
, cls_dict
)
260 def __init__(cls
, name
, bases
, dict):
261 super(MetaConfigNode
, cls
).__init
__(name
, bases
, dict)
263 # initialize required attributes
266 cls
._param
_types
= {}
267 cls
._bases
= [c
for c
in cls
.__mro
__ if isConfigNode(c
)]
268 cls
._anon
_subclass
_counter
= 0
270 # If your parent has a value in it that's a config node, clone
271 # it. Do this now so if we update any of the values'
272 # attributes we are updating the clone and not the original.
273 for base
in cls
._bases
:
274 for key
,val
in base
._values
.iteritems():
276 # don't clone if (1) we're about to overwrite it with
277 # a local setting or (2) we've already cloned a copy
278 # from an earlier (more derived) base
279 if cls
._init
_dict
.has_key(key
) or cls
._values
.has_key(key
):
282 if isConfigNode(val
):
283 cls
._values
[key
] = val()
284 elif isSimObjSequence(val
):
285 cls
._values
[key
] = [ v() for v
in val
]
286 elif isNullPointer(val
):
287 cls
._values
[key
] = val
289 # process param types from _init_dict, as these may be needed
290 # by param descriptions also in _init_dict
291 for key
,val
in cls
._init
_dict
.items():
292 if isinstance(val
, type) and issubclass(val
, ParamType
):
293 cls
._param
_types
[key
] = val
294 if not issubclass(val
, ConfigNode
):
295 del cls
._init
_dict
[key
]
297 # now process remaining _init_dict items
298 for key
,val
in cls
._init
_dict
.items():
300 if isinstance(val
, _Param
):
301 cls
._params
[key
] = val
302 # try to resolve local param types in local param_types scope
303 val
.maybe_resolve_type(cls
._param
_types
)
305 # init-time-only keywords
306 elif cls
.init_keywords
.has_key(key
):
307 cls
._set
_keyword
(key
, val
, cls
.init_keywords
[key
])
309 # See description of decorators in the importer.py file.
310 # We just strip off the expr_decorator now since we don't
311 # need from this point on.
312 elif key
.startswith(expr_decorator
):
313 key
= key
[len(expr_decorator
):]
314 # because it had dots into a list so that we can find the
315 # proper variable to modify.
316 key
= key
.split(dot_decorator
)
318 for item
in key
[:-1]:
320 setattr(c
, key
[-1], val
)
322 # default: use normal path (ends up in __setattr__)
324 setattr(cls
, key
, val
)
327 def _isvalue(cls
, name
):
329 if c
._params
.has_key(name
):
333 if c
._values
.has_key(name
):
338 # generator that iterates across all parameters for this class and
339 # all classes it inherits from
343 for p
,v
in c
._params
.iteritems():
344 if not params
.has_key(p
):
348 # Lookup a parameter description by name in the given class.
349 def _getparam(cls
, name
, default
= AttributeError):
351 if c
._params
.has_key(name
):
352 return c
._params
[name
]
353 if isSubClass(default
, Exception):
355 "object '%s' has no attribute '%s'" % (cls
.__name
__, name
)
359 def _hasvalue(cls
, name
):
361 if c
._values
.has_key(name
):
368 for i
,c
in enumerate(cls
._bases
):
369 for p
,v
in c
._values
.iteritems():
370 if not values
.has_key(p
):
372 for p
,v
in c
._params
.iteritems():
373 if not values
.has_key(p
) and hasattr(v
, 'default'):
377 panic("Invalid default %s for param %s in node %s"
378 % (v
.default
,p
,cls
.__name
__))
385 def _getvalue(cls
, name
, default
= AttributeError):
388 if c
._values
.has_key(name
):
389 value
= c
._values
[name
]
391 if value
is not None:
394 param
= cls
._getparam
(name
, None)
395 if param
is not None and hasattr(param
, 'default'):
396 param
.valid(param
.default
)
397 value
= param
.default
398 cls
._setvalue
(name
, value
)
401 if isSubClass(default
, Exception):
402 raise default
, 'value for %s not found' % name
406 def _setvalue(cls
, name
, value
):
407 cls
._values
[name
] = value
409 def __getattr__(cls
, attr
):
410 if cls
._isvalue
(attr
):
411 return Value(cls
, attr
)
413 if attr
== '_cpp_param_decl' and hasattr(cls
, 'type'):
414 return cls
.type + '*'
416 raise AttributeError, \
417 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
)
419 def _set_keyword(cls
, keyword
, val
, kwtype
):
420 if not isinstance(val
, kwtype
):
421 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
422 (keyword
, type(val
), kwtype
)
423 if isinstance(val
, types
.FunctionType
):
424 val
= classmethod(val
)
425 type.__setattr
__(cls
, keyword
, val
)
427 # Set attribute (called on foo.attr = value when foo is an
428 # instance of class cls).
429 def __setattr__(cls
, attr
, value
):
430 # normal processing for private attributes
431 if attr
.startswith('_'):
432 type.__setattr
__(cls
, attr
, value
)
435 if cls
.keywords
.has_key(attr
):
436 cls
._set
_keyword
(attr
, value
, cls
.keywords
[attr
])
439 # must be SimObject param
440 param
= cls
._getparam
(attr
, None)
442 # It's ok: set attribute by delegating to 'object' class.
443 # Note the use of param.make_value() to verify/canonicalize
448 panic("Error setting param %s.%s to %s\n" % \
449 (cls
.__name
__, attr
, value
))
450 cls
._setvalue
(attr
, value
)
451 elif isConfigNode(value
) or isSimObjSequence(value
):
452 cls
._setvalue
(attr
, value
)
454 raise AttributeError, \
455 "Class %s has no parameter %s" % (cls
.__name
__, attr
)
457 def add_child(cls
, instance
, name
, child
):
458 if isNullPointer(child
) or instance
.top_child_names
.has_key(name
):
461 if issequence(child
):
463 for i
,c
in enumerate(child
):
464 n
= '%s%d' % (name
, i
)
465 k
= c
.instantiate(n
, instance
)
467 instance
.children
.append(k
)
468 instance
.child_names
[n
] = k
469 instance
.child_objects
[c
] = k
472 kid
= child
.instantiate(name
, instance
)
473 instance
.children
.append(kid
)
474 instance
.child_names
[name
] = kid
475 instance
.child_objects
[child
] = kid
477 instance
.top_child_names
[name
] = kid
479 # Print instance info to .ini file.
480 def instantiate(cls
, name
, parent
= None):
481 instance
= Node(name
, cls
, parent
, isParamContext(cls
))
483 if hasattr(cls
, 'check'):
486 for key
,value
in cls
._getvalues
().iteritems():
487 if isConfigNode(value
):
488 cls
.add_child(instance
, key
, value
)
489 if issequence(value
):
490 list = [ v
for v
in value
if isConfigNode(v
) ]
492 cls
.add_child(instance
, key
, list)
494 for pname
,param
in cls
._getparams
().iteritems():
496 value
= cls
._getvalue
(pname
)
498 panic('Error getting %s' % pname
)
501 if isConfigNode(value
):
502 value
= instance
.child_objects
[value
]
503 elif issequence(value
):
506 if isConfigNode(val
):
507 v
.append(instance
.child_objects
[val
])
512 p
= NodeParam(pname
, param
, value
)
513 instance
.params
.append(p
)
514 instance
.param_names
[pname
] = p
516 print 'Exception while evaluating %s.%s' % \
517 (instance
.path
, pname
)
522 def _convert(cls
, value
):
524 if isinstance(value
, Node
):
525 realvalue
= value
.realtype
527 if isinstance(realvalue
, Proxy
):
530 if realvalue
== None or isNullPointer(realvalue
):
533 if isSubClass(realvalue
, cls
):
536 raise TypeError, 'object %s type %s wrong type, should be %s' % \
537 (repr(realvalue
), realvalue
, cls
)
539 def _string(cls
, value
):
540 if isNullPointer(value
):
542 return Node
._string
(value
)
544 # The ConfigNode class is the root of the special hierarchy. Most of
545 # the code in this class deals with the configuration hierarchy itself
546 # (parent/child node relationships).
547 class ConfigNode(object):
548 # Specify metaclass. Any class inheriting from ConfigNode will
549 # get this metaclass.
550 __metaclass__
= MetaConfigNode
552 def __new__(cls
, **kwargs
):
553 name
= cls
.__name
__ + ("_%d" % cls
._anon
_subclass
_counter
)
554 cls
._anon
_subclass
_counter
+= 1
555 return cls
.__metaclass
__(name
, (cls
, ), kwargs
)
557 class ParamContext(ConfigNode
):
560 class MetaSimObject(MetaConfigNode
):
561 # init_keywords and keywords are inherited from MetaConfigNode,
562 # with overrides/additions
563 init_keywords
= MetaConfigNode
.init_keywords
564 init_keywords
.update({ 'abstract' : types
.BooleanType
,
565 'type' : types
.StringType
})
567 keywords
= MetaConfigNode
.keywords
568 # no additional keywords
573 def __init__(cls
, name
, bases
, dict):
574 super(MetaSimObject
, cls
).__init
__(name
, bases
, dict)
576 if hasattr(cls
, 'type'):
577 if name
== 'SimObject':
579 elif hasattr(cls
._bases
[1], 'type'):
580 cls
._cpp
_base
= cls
._bases
[1].type
582 panic("SimObject %s derives from a non-C++ SimObject %s "\
583 "(no 'type')" % (cls
, cls_bases
[1].__name
__))
585 # This class corresponds to a C++ class: put it on the global
586 # list of C++ objects to generate param structs, etc.
587 MetaSimObject
.cpp_classes
.append(cls
)
592 code
+= "\n".join([e
.cpp_declare() for e
in cls
._param
_types
.values()])
594 param_names
= cls
._params
.keys()
596 code
+= "struct Params"
598 code
+= " : public %s::Params" % cls
._cpp
_base
600 code
+= "\n ".join([cls
._params
[pname
].cpp_decl(pname
) \
601 for pname
in param_names
])
605 class NodeParam(object):
606 def __init__(self
, name
, param
, value
):
609 self
.ptype
= param
.ptype
610 self
.convert
= param
.convert
611 self
.string
= param
.string
616 def __init__(self
, name
, realtype
, parent
, paramcontext
):
618 self
.realtype
= realtype
619 if isSimObject(realtype
):
620 self
.type = realtype
.type
625 self
.child_names
= {}
626 self
.child_objects
= {}
627 self
.top_child_names
= {}
629 self
.param_names
= {}
630 self
.paramcontext
= paramcontext
634 while node
is not None:
635 if node
.name
!= 'root':
636 path
.insert(0, node
.name
)
638 assert(node
.parent
is None)
640 self
.path
= '.'.join(path
)
642 def find(self
, realtype
, path
):
644 if issubclass(self
.realtype
, realtype
):
648 for child
in self
.children
:
649 if issubclass(child
.realtype
, realtype
):
651 raise AttributeError, \
652 'Super matched more than one: %s %s' % \
653 (obj
.path
, child
.path
)
655 return obj
, obj
is not None
659 for node
in path
[:-1]:
660 obj
= obj
.child_names
[node
]
663 if obj
.child_names
.has_key(last
):
664 value
= obj
.child_names
[last
]
665 if issubclass(value
.realtype
, realtype
):
667 elif obj
.param_names
.has_key(last
):
668 value
= obj
.param_names
[last
]
669 realtype
._convert
(value
.value
)
670 return value
.value
, True
676 def unproxy(self
, ptype
, value
):
677 if not isinstance(value
, Proxy
):
681 raise AttributeError, 'Error while fixing up %s' % self
.path
687 raise AttributeError, \
688 'Parent of %s type %s not found at path %s' \
689 % (self
.name
, ptype
, value
._path
)
690 found
, done
= obj
.find(ptype
, value
._path
)
691 if isinstance(found
, Proxy
):
698 self
.all
[self
.path
] = self
700 for param
in self
.params
:
706 param
.value
= [ self
.unproxy(ptype
, pv
) for pv
in pval
]
708 param
.value
= self
.unproxy(ptype
, pval
)
710 print 'Error while fixing up %s:%s' % (self
.path
, param
.name
)
713 for child
in self
.children
:
714 assert(child
!= self
)
717 # print type and parameter values to .ini file
719 print '[' + self
.path
+ ']' # .ini section header
721 if isSimObject(self
.realtype
):
722 print 'type = %s' % self
.type
725 # instantiate children in same order they were added for
726 # backward compatibility (else we can end up with cpu1
727 # before cpu0). Changing ordering can also influence timing
728 # in the current memory system, as caches get added to a bus
729 # in different orders which affects their priority in the
730 # case of simulataneous requests. We should uncomment the
731 # following line once we take care of that issue.
732 # self.children.sort(lambda x,y: cmp(x.name, y.name))
733 children
= [ c
.name
for c
in self
.children
if not c
.paramcontext
]
734 print 'children =', ' '.join(children
)
736 self
.params
.sort(lambda x
,y
: cmp(x
.name
, y
.name
))
737 for param
in self
.params
:
739 if param
.value
is None:
740 raise AttributeError, 'Parameter with no value'
742 value
= param
.convert(param
.value
)
743 string
= param
.string(value
)
745 print 'exception in %s:%s' % (self
.path
, param
.name
)
748 print '%s = %s' % (param
.name
, string
)
752 # recursively dump out children
753 for c
in self
.children
:
756 # print type and parameter values to .ini file
757 def outputDot(self
, dot
):
760 label
= "{%s|" % self
.path
761 if isSimObject(self
.realtype
):
762 label
+= '%s|' % self
.type
765 # instantiate children in same order they were added for
766 # backward compatibility (else we can end up with cpu1
768 for c
in self
.children
:
769 dot
.add_edge(pydot
.Edge(self
.path
,c
.path
, style
="bold"))
772 for param
in self
.params
:
774 if param
.value
is None:
775 raise AttributeError, 'Parameter with no value'
777 value
= param
.convert(param
.value
)
778 string
= param
.string(value
)
780 print 'exception in %s:%s' % (self
.name
, param
.name
)
782 if isConfigNode(param
.ptype
) and string
!= "Null":
783 simobjs
.append(string
)
785 label
+= '%s = %s\\n' % (param
.name
, string
)
788 label
+= "|<%s> %s" % (so
, so
)
789 dot
.add_edge(pydot
.Edge("%s:%s" % (self
.path
, so
), so
, tailport
="w"))
791 dot
.add_node(pydot
.Node(self
.path
,shape
="Mrecord",label
=label
))
793 # recursively dump out children
794 for c
in self
.children
:
797 def _string(cls
, value
):
798 if not isinstance(value
, Node
):
799 raise AttributeError, 'expecting %s got %s' % (Node
, value
)
801 _string
= classmethod(_string
)
803 #####################################################################
805 # Parameter description classes
807 # The _params dictionary in each class maps parameter names to
808 # either a Param or a VectorParam object. These objects contain the
809 # parameter description string, the parameter type, and the default
810 # value (loaded from the PARAM section of the .odesc files). The
811 # _convert() method on these objects is used to force whatever value
812 # is assigned to the parameter to the appropriate type.
814 # Note that the default values are loaded into the class's attribute
815 # space when the parameter dictionary is initialized (in
816 # MetaConfigNode._setparams()); after that point they aren't used.
818 #####################################################################
820 def isNullPointer(value
):
821 return isinstance(value
, NullSimObject
)
824 def __init__(self
, obj
, attr
):
825 super(Value
, self
).__setattr
__('attr', attr
)
826 super(Value
, self
).__setattr
__('obj', obj
)
829 return self
.obj
._getvalue
(self
.attr
)
831 def __setattr__(self
, attr
, value
):
832 setattr(self
._getattr
(), attr
, value
)
834 def __getattr__(self
, attr
):
835 return getattr(self
._getattr
(), attr
)
837 def __getitem__(self
, index
):
838 return self
._getattr
().__getitem
__(index
)
840 def __call__(self
, *args
, **kwargs
):
841 return self
._getattr
().__call
__(*args
, **kwargs
)
843 def __nonzero__(self
):
844 return bool(self
._getattr
())
847 return str(self
._getattr
())
850 class _Param(object):
851 def __init__(self
, ptype
, *args
, **kwargs
):
852 if isinstance(ptype
, types
.StringType
):
853 self
.ptype_string
= ptype
854 elif isinstance(ptype
, type):
857 raise TypeError, "Param type is not a type (%s)" % ptype
863 self
.default
= args
[0]
866 raise TypeError, 'too many arguments'
868 if kwargs
.has_key('desc'):
869 assert(not hasattr(self
, 'desc'))
870 self
.desc
= kwargs
['desc']
873 if kwargs
.has_key('default'):
874 assert(not hasattr(self
, 'default'))
875 self
.default
= kwargs
['default']
876 del kwargs
['default']
879 raise TypeError, 'extra unknown kwargs %s' % kwargs
881 if not hasattr(self
, 'desc'):
882 raise TypeError, 'desc attribute missing'
884 def maybe_resolve_type(self
, context
):
885 # check if already resolved... don't use hasattr(),
886 # as that calls __getattr__()
887 if self
.__dict
__.has_key('ptype'):
890 self
.ptype
= context
[self
.ptype_string
]
892 # no harm in trying... we'll try again later using global scope
895 def __getattr__(self
, attr
):
898 self
.ptype
= param_types
[self
.ptype_string
]
901 panic("undefined Param type %s" % self
.ptype_string
)
903 raise AttributeError, "'%s' object has no attribute '%s'" % \
904 (type(self
).__name
__, attr
)
906 def valid(self
, value
):
907 if not isinstance(value
, Proxy
):
908 self
.ptype
._convert
(value
)
910 def convert(self
, value
):
911 return self
.ptype
._convert
(value
)
913 def string(self
, value
):
914 return self
.ptype
._string
(value
)
916 def set(self
, name
, instance
, value
):
917 instance
.__dict
__[name
] = value
919 def cpp_decl(self
, name
):
920 return '%s %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
922 class _ParamProxy(object):
923 def __init__(self
, type):
926 # E.g., Param.Int(5, "number of widgets")
927 def __call__(self
, *args
, **kwargs
):
928 return _Param(self
.ptype
, *args
, **kwargs
)
930 # Strange magic to theoretically allow dotted names as Param classes,
931 # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
932 def __getattr__(self
, attr
):
933 if attr
== '__bases__':
934 raise AttributeError, ''
938 def __setattr__(self
, attr
, value
):
940 raise AttributeError, \
941 'Attribute %s not available in %s' % (attr
, self
.__class
__)
942 super(_ParamProxy
, self
).__setattr
__(attr
, value
)
945 Param
= _ParamProxy(None)
947 # Vector-valued parameter description. Just like Param, except that
948 # the value is a vector (list) of the specified type instead of a
950 class _VectorParam(_Param
):
951 def __init__(self
, type, *args
, **kwargs
):
952 _Param
.__init
__(self
, type, *args
, **kwargs
)
954 def valid(self
, value
):
958 if issequence(value
):
960 if not isinstance(val
, Proxy
):
961 self
.ptype
._convert
(val
)
962 elif not isinstance(value
, Proxy
):
963 self
.ptype
._convert
(value
)
965 # Convert assigned value to appropriate type. If the RHS is not a
966 # list or tuple, it generates a single-element list.
967 def convert(self
, value
):
971 if issequence(value
):
972 # list: coerce each element into new list
973 return [ self
.ptype
._convert
(v
) for v
in value
]
975 # singleton: coerce & wrap in a list
976 return self
.ptype
._convert
(value
)
978 def string(self
, value
):
979 if issequence(value
):
980 return ' '.join([ self
.ptype
._string
(v
) for v
in value
])
982 return self
.ptype
._string
(value
)
984 def cpp_decl(self
, name
):
985 return 'std::vector<%s> %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
987 class _VectorParamProxy(_ParamProxy
):
988 # E.g., VectorParam.Int(5, "number of widgets")
989 def __call__(self
, *args
, **kwargs
):
990 return _VectorParam(self
.ptype
, *args
, **kwargs
)
992 VectorParam
= _VectorParamProxy(None)
994 #####################################################################
998 # Though native Python types could be used to specify parameter types
999 # (the 'ptype' field of the Param and VectorParam classes), it's more
1000 # flexible to define our own set of types. This gives us more control
1001 # over how Python expressions are converted to values (via the
1002 # __init__() constructor) and how these values are printed out (via
1003 # the __str__() conversion method). Eventually we'll need these types
1004 # to correspond to distinct C++ types as well.
1006 #####################################################################
1009 # Metaclass for bounds-checked integer parameters. See CheckedInt.
1010 class CheckedIntType(type):
1011 def __init__(cls
, name
, bases
, dict):
1012 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
1014 # CheckedInt is an abstract base class, so we actually don't
1015 # want to do any processing on it... the rest of this code is
1016 # just for classes that derive from CheckedInt.
1017 if name
== 'CheckedInt':
1020 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
1021 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
1022 panic("CheckedInt subclass %s must define either\n" \
1023 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1027 cls
.max = 2 ** cls
.size
- 1
1029 cls
.min = -(2 ** (cls
.size
- 1))
1030 cls
.max = (2 ** (cls
.size
- 1)) - 1
1032 cls
._cpp
_param
_decl
= cls
.cppname
1034 def _convert(cls
, value
):
1035 if isinstance(value
, bool):
1038 if not isinstance(value
, (int, long, float, str)):
1039 raise TypeError, 'Integer param of invalid type %s' % type(value
)
1041 if isinstance(value
, (str, float)):
1042 value
= long(float(value
))
1044 if not cls
.min <= value
<= cls
.max:
1045 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1046 (cls
.min, value
, cls
.max)
1050 def _string(cls
, value
):
1053 # Abstract superclass for bounds-checked integer parameters. This
1054 # class is subclassed to generate parameter classes with specific
1055 # bounds. Initialization of the min and max bounds is done in the
1056 # metaclass CheckedIntType.__init__.
1057 class CheckedInt(ParamType
):
1058 __metaclass__
= CheckedIntType
1060 class Int(CheckedInt
): cppname
= 'int'; size
= 32; unsigned
= False
1061 class Unsigned(CheckedInt
): cppname
= 'unsigned'; size
= 32; unsigned
= True
1063 class Int8(CheckedInt
): cppname
= 'int8_t'; size
= 8; unsigned
= False
1064 class UInt8(CheckedInt
): cppname
= 'uint8_t'; size
= 8; unsigned
= True
1065 class Int16(CheckedInt
): cppname
= 'int16_t'; size
= 16; unsigned
= False
1066 class UInt16(CheckedInt
): cppname
= 'uint16_t'; size
= 16; unsigned
= True
1067 class Int32(CheckedInt
): cppname
= 'int32_t'; size
= 32; unsigned
= False
1068 class UInt32(CheckedInt
): cppname
= 'uint32_t'; size
= 32; unsigned
= True
1069 class Int64(CheckedInt
): cppname
= 'int64_t'; size
= 64; unsigned
= False
1070 class UInt64(CheckedInt
): cppname
= 'uint64_t'; size
= 64; unsigned
= True
1072 class Counter(CheckedInt
): cppname
= 'Counter'; size
= 64; unsigned
= True
1073 class Addr(CheckedInt
): cppname
= 'Addr'; size
= 64; unsigned
= True
1074 class Tick(CheckedInt
): cppname
= 'Tick'; size
= 64; unsigned
= True
1076 class Percent(CheckedInt
): cppname
= 'int'; min = 0; max = 100
1079 def __init__(self
, first
, second
):
1081 self
.second
= second
1083 class MetaRange(type):
1084 def __init__(cls
, name
, bases
, dict):
1085 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
1088 cls
._cpp
_param
_decl
= 'Range<%s>' % cls
.type._cpp
_param
_decl
1090 def _convert(cls
, value
):
1091 if not isinstance(value
, Pair
):
1092 raise TypeError, 'value %s is not a Pair' % value
1093 return Pair(cls
.type._convert
(value
.first
),
1094 cls
.type._convert
(value
.second
))
1096 def _string(cls
, value
):
1097 return '%s:%s' % (cls
.type._string
(value
.first
),
1098 cls
.type._string
(value
.second
))
1100 class Range(ParamType
):
1101 __metaclass__
= MetaRange
1103 def RangeSize(start
, size
):
1104 return Pair(start
, start
+ size
- 1)
1106 class AddrRange(Range
): type = Addr
1108 # Boolean parameter type.
1109 class Bool(ParamType
):
1110 _cpp_param_decl
= 'bool'
1111 def _convert(value
):
1116 if t
== int or t
== long:
1121 if v
== "true" or v
== "t" or v
== "yes" or v
== "y":
1123 elif v
== "false" or v
== "f" or v
== "no" or v
== "n":
1126 raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v
, t
)
1127 _convert
= staticmethod(_convert
)
1134 _string
= staticmethod(_string
)
1136 # String-valued parameter.
1137 class String(ParamType
):
1138 _cpp_param_decl
= 'string'
1140 # Constructor. Value must be Python string.
1141 def _convert(cls
,value
):
1144 if isinstance(value
, str):
1148 "String param got value %s %s" % (repr(value
), type(value
))
1149 _convert
= classmethod(_convert
)
1151 # Generate printable string version. Not too tricky.
1152 def _string(cls
, value
):
1154 _string
= classmethod(_string
)
1157 def IncEthernetAddr(addr
, val
= 1):
1158 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
1160 for i
in (5, 4, 3, 2, 1):
1161 val
,rem
= divmod(bytes
[i
], 256)
1166 assert(bytes
[0] <= 255)
1167 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
1169 class NextEthernetAddr(object):
1170 __metaclass__
= Singleton
1171 addr
= "00:90:00:00:00:01"
1173 def __init__(self
, inc
= 1):
1174 self
.value
= self
.addr
1175 self
.addr
= IncEthernetAddr(self
.addr
, inc
)
1177 class EthernetAddr(ParamType
):
1178 _cpp_param_decl
= 'EthAddr'
1180 def _convert(cls
, value
):
1181 if value
== NextEthernetAddr
:
1184 if not isinstance(value
, str):
1185 raise TypeError, "expected an ethernet address and didn't get one"
1187 bytes
= value
.split(':')
1189 raise TypeError, 'invalid ethernet address %s' % value
1192 if not 0 <= int(byte
) <= 256:
1193 raise TypeError, 'invalid ethernet address %s' % value
1196 _convert
= classmethod(_convert
)
1198 def _string(cls
, value
):
1199 if value
== NextEthernetAddr
:
1200 value
= value().value
1202 _string
= classmethod(_string
)
1204 # Special class for NULL pointers. Note the special check in
1205 # make_param_value() above that lets these be assigned where a
1206 # SimObject is required.
1207 # only one copy of a particular node
1208 class NullSimObject(object):
1209 __metaclass__
= Singleton
1214 def _instantiate(self
, parent
= None, path
= ''):
1217 def _convert(cls
, value
):
1221 if isinstance(value
, cls
):
1224 raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1225 (repr(value
), type(value
), cls
)
1226 _convert
= classmethod(_convert
)
1230 _string
= staticmethod(_string
)
1232 # The only instance you'll ever need...
1233 Null
= NULL
= NullSimObject()
1235 # Enumerated types are a little more complex. The user specifies the
1236 # type as Enum(foo) where foo is either a list or dictionary of
1237 # alternatives (typically strings, but not necessarily so). (In the
1238 # long run, the integer value of the parameter will be the list index
1239 # or the corresponding dictionary value. For now, since we only check
1240 # that the alternative is valid and then spit it into a .ini file,
1241 # there's not much point in using the dictionary.)
1243 # What Enum() must do is generate a new type encapsulating the
1244 # provided list/dictionary so that specific values of the parameter
1245 # can be instances of that type. We define two hidden internal
1246 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1247 # derive the new type from the appropriate base class on the fly.
1250 # Metaclass for Enum types
1251 class MetaEnum(type):
1253 def __init__(cls
, name
, bases
, init_dict
):
1254 if init_dict
.has_key('map'):
1255 if not isinstance(cls
.map, dict):
1256 raise TypeError, "Enum-derived class attribute 'map' " \
1257 "must be of type dict"
1258 # build list of value strings from map
1259 cls
.vals
= cls
.map.keys()
1261 elif init_dict
.has_key('vals'):
1262 if not isinstance(cls
.vals
, list):
1263 raise TypeError, "Enum-derived class attribute 'vals' " \
1264 "must be of type list"
1265 # build string->value map from vals sequence
1267 for idx
,val
in enumerate(cls
.vals
):
1270 raise TypeError, "Enum-derived class must define "\
1271 "attribute 'map' or 'vals'"
1273 cls
._cpp
_param
_decl
= name
1275 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
1277 def cpp_declare(cls
):
1278 s
= 'enum %s {\n ' % cls
.__name
__
1279 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
1283 # Base class for enum types.
1284 class Enum(ParamType
):
1285 __metaclass__
= MetaEnum
1288 def _convert(self
, value
):
1289 if value
not in self
.map:
1290 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1291 % (value
, self
.vals
)
1293 _convert
= classmethod(_convert
)
1295 # Generate printable string version of value.
1296 def _string(self
, value
):
1298 _string
= classmethod(_string
)
1300 # "Constants"... handy aliases for various values.
1303 # Some memory range specifications use this as a default upper bound.
1307 # For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1312 #####################################################################
1314 # The final hook to generate .ini files. Called from configuration
1315 # script once config is built.
1316 def instantiate(root
):
1317 instance
= root
.instantiate('root')
1322 instance
.outputDot(dot
)
1323 dot
.orientation
= "portrait"
1325 dot
.ranksep
="equally"
1327 dot
.write("config.dot")
1328 dot
.write_ps("config.ps")
1330 # SimObject is a minimal extension of ConfigNode, implementing a
1331 # hierarchy node that corresponds to an M5 SimObject. It prints out a
1332 # "type=" line to indicate its SimObject class, prints out the
1333 # assigned parameters corresponding to its class, and allows
1334 # parameters to be set by keyword in the constructor. Note that most
1335 # of the heavy lifting for the SimObject param handling is done in the
1336 # MetaConfigNode metaclass.
1337 class SimObject(ConfigNode
, ParamType
):
1338 __metaclass__
= MetaSimObject
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',