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
39 class Singleton(type):
40 def __call__(cls
, *args
, **kwargs
):
41 if hasattr(cls
, '_instance'):
44 cls
._instance
= super(Singleton
, cls
).__call
__(*args
, **kwargs
)
47 #####################################################################
49 # M5 Python Configuration Utility
51 # The basic idea is to write simple Python programs that build Python
52 # objects corresponding to M5 SimObjects for the deisred simulation
53 # configuration. For now, the Python emits a .ini file that can be
54 # parsed by M5. In the future, some tighter integration between M5
55 # and the Python interpreter may allow bypassing the .ini file.
57 # Each SimObject class in M5 is represented by a Python class with the
58 # same name. The Python inheritance tree mirrors the M5 C++ tree
59 # (e.g., SimpleCPU derives from BaseCPU in both cases, and all
60 # SimObjects inherit from a single SimObject base class). To specify
61 # an instance of an M5 SimObject in a configuration, the user simply
62 # instantiates the corresponding Python object. The parameters for
63 # that SimObject are given by assigning to attributes of the Python
64 # object, either using keyword assignment in the constructor or in
65 # separate assignment statements. For example:
67 # cache = BaseCache('my_cache', root, size=64*K)
68 # cache.hit_latency = 3
71 # (The first two constructor arguments specify the name of the created
72 # cache and its parent node in the hierarchy.)
74 # The magic lies in the mapping of the Python attributes for SimObject
75 # classes to the actual SimObject parameter specifications. This
76 # allows parameter validity checking in the Python code. Continuing
77 # the example above, the statements "cache.blurfl=3" or
78 # "cache.assoc='hello'" would both result in runtime errors in Python,
79 # since the BaseCache object has no 'blurfl' parameter and the 'assoc'
80 # parameter requires an integer, respectively. This magic is done
81 # primarily by overriding the special __setattr__ method that controls
82 # assignment to object attributes.
84 # The Python module provides another class, ConfigNode, which is a
85 # superclass of SimObject. ConfigNode implements the parent/child
86 # relationship for building the configuration hierarchy tree.
87 # Concrete instances of ConfigNode can be used to group objects in the
88 # hierarchy, but do not correspond to SimObjects themselves (like a
89 # .ini section with "children=" but no "type=".
91 # Once a set of Python objects have been instantiated in a hierarchy,
92 # calling 'instantiate(obj)' (where obj is the root of the hierarchy)
93 # will generate a .ini file. See simple-4cpu.py for an example
94 # (corresponding to m5-test/simple-4cpu.ini).
96 #####################################################################
98 #####################################################################
100 # ConfigNode/SimObject classes
102 # The Python class hierarchy rooted by ConfigNode (which is the base
103 # class of SimObject, which in turn is the base class of all other M5
104 # SimObject classes) has special attribute behavior. In general, an
105 # object in this hierarchy has three categories of attribute-like
108 # 1. Regular Python methods and variables. These must start with an
109 # underscore to be treated normally.
111 # 2. SimObject parameters. These values are stored as normal Python
112 # attributes, but all assignments to these attributes are checked
113 # against the pre-defined set of parameters stored in the class's
114 # _params dictionary. Assignments to attributes that do not
115 # correspond to predefined parameters, or that are not of the correct
116 # type, incur runtime errors.
118 # 3. Hierarchy children. The child nodes of a ConfigNode are stored
119 # in the node's _children dictionary, but can be accessed using the
120 # Python attribute dot-notation (just as they are printed out by the
121 # simulator). Children cannot be created using attribute assigment;
122 # they must be added by specifying the parent node in the child's
123 # constructor or using the '+=' operator.
125 # The SimObject parameters are the most complex, for a few reasons.
126 # First, both parameter descriptions and parameter values are
127 # inherited. Thus parameter description lookup must go up the
128 # inheritance chain like normal attribute lookup, but this behavior
129 # must be explicitly coded since the lookup occurs in each class's
130 # _params attribute. Second, because parameter values can be set
131 # on SimObject classes (to implement default values), the parameter
132 # checking behavior must be enforced on class attribute assignments as
133 # well as instance attribute assignments. Finally, because we allow
134 # class specialization via inheritance (e.g., see the L1Cache class in
135 # the simple-4cpu.py example), we must do parameter checking even on
136 # class instantiation. To provide all these features, we use a
137 # metaclass to define most of the SimObject parameter behavior for
138 # this class hierarchy.
140 #####################################################################
143 def __init__(self
, path
):
148 # path is a list of (attr,index) tuples
149 self
._path
= [(path
,None)]
151 self
._multiplier
= None
153 def __getattr__(self
, attr
):
154 if attr
== '__bases__':
155 return super(Proxy
, self
).__getattr
__(self
, attr
)
156 self
._path
.append((attr
,None))
159 def __setattr__(self
, attr
, value
):
160 if not attr
.startswith('_'):
161 raise AttributeError, 'cannot set attribute %s' % attr
162 super(Proxy
, self
).__setattr
__(attr
, value
)
164 # support indexing on proxies (e.g., parent.cpu[0])
165 def __getitem__(self
, key
):
166 if not isinstance(key
, int):
167 raise TypeError, "Proxy object requires integer index"
168 if self
._path
== None:
169 raise IndexError, "Index applied to 'any' proxy"
170 # replace index portion of last path element with new index
171 self
._path
[-1] = (self
._path
[-1][0], key
)
174 # support multiplying proxies by constants
175 def __mul__(self
, other
):
176 if not isinstance(other
, int):
177 raise TypeError, "Proxy multiplier must be integer"
178 if self
._multiplier
== None:
179 self
._multiplier
= other
181 # support chained multipliers
182 self
._multiplier
*= other
185 def _mulcheck(self
, result
):
186 if self
._multiplier
== None:
188 if not isinstance(result
, int):
189 raise TypeError, "Proxy with multiplier resolves to " \
191 return result
* self
._multiplier
193 def unproxy(self
, base
, ptype
):
198 raise AttributeError, \
199 'Parent of %s type %s not found at path %s' \
200 % (base
.name
, ptype
, self
._path
)
201 result
, done
= obj
.find(ptype
, self
._path
)
204 if isinstance(result
, Proxy
):
205 result
= result
.unproxy(obj
, ptype
)
207 return self
._mulcheck
(result
)
209 def getindex(obj
, index
):
217 # if index is 0 and item is not subscriptable, just
218 # use item itself (so cpu[0] works on uniprocessors)
220 getindex
= staticmethod(getindex
)
222 class ProxyFactory(object):
223 def __getattr__(self
, attr
):
226 # global object for handling parent.foo proxies
227 parent
= ProxyFactory()
229 def isSubClass(value
, cls
):
231 return issubclass(value
, cls
)
235 def isConfigNode(value
):
237 return issubclass(value
, ConfigNode
)
241 def isSimObject(value
):
243 return issubclass(value
, SimObject
)
247 def isSimObjSequence(value
):
248 if not isinstance(value
, (list, tuple)):
252 if not isNullPointer(val
) and not isConfigNode(val
):
257 def isParamContext(value
):
259 return issubclass(value
, ParamContext
)
264 class_decorator
= 'M5M5_SIMOBJECT_'
265 expr_decorator
= 'M5M5_EXPRESSION_'
266 dot_decorator
= '_M5M5_DOT_'
268 # 'Global' map of legitimate types for SimObject parameters.
271 # Dummy base class to identify types that are legitimate for SimObject
273 class ParamType(object):
276 # Add types defined in given context (dict or module) that are derived
277 # from ParamType to param_types map.
278 def add_param_types(ctx
):
279 if isinstance(ctx
, types
.DictType
):
281 elif isinstance(ctx
, types
.ModuleType
):
282 source_dict
= ctx
.__dict
__
285 "m5.config.add_param_types requires dict or module as arg"
286 for key
,val
in source_dict
.iteritems():
287 if isinstance(val
, type) and issubclass(val
, ParamType
):
288 param_types
[key
] = val
290 # The metaclass for ConfigNode (and thus for everything that derives
291 # from ConfigNode, including SimObject). This class controls how new
292 # classes that derive from ConfigNode are instantiated, and provides
293 # inherited class behavior (just like a class controls how instances
294 # of that class are instantiated, and provides inherited instance
296 class MetaConfigNode(type):
297 # Attributes that can be set only at initialization time
299 # Attributes that can be set any time
300 keywords
= { 'check' : types
.FunctionType
,
301 'children' : types
.ListType
}
303 # __new__ is called before __init__, and is where the statements
304 # in the body of the class definition get loaded into the class's
305 # __dict__. We intercept this to filter out parameter assignments
306 # and only allow "private" attributes to be passed to the base
307 # __new__ (starting with underscore).
308 def __new__(mcls
, name
, bases
, dict):
309 # Copy "private" attributes (including special methods such as __new__)
310 # to the official dict. Everything else goes in _init_dict to be
311 # filtered in __init__.
313 for key
,val
in dict.items():
314 if key
.startswith('_'):
317 cls_dict
['_init_dict'] = dict
318 return super(MetaConfigNode
, mcls
).__new
__(mcls
, name
, bases
, cls_dict
)
321 def __init__(cls
, name
, bases
, dict):
322 super(MetaConfigNode
, cls
).__init
__(name
, bases
, dict)
324 # initialize required attributes
327 cls
._param
_types
= {}
328 cls
._bases
= [c
for c
in cls
.__mro
__ if isConfigNode(c
)]
329 cls
._anon
_subclass
_counter
= 0
331 # If your parent has a value in it that's a config node, clone
332 # it. Do this now so if we update any of the values'
333 # attributes we are updating the clone and not the original.
334 for base
in cls
._bases
:
335 for key
,val
in base
._values
.iteritems():
337 # don't clone if (1) we're about to overwrite it with
338 # a local setting or (2) we've already cloned a copy
339 # from an earlier (more derived) base
340 if cls
._init
_dict
.has_key(key
) or cls
._values
.has_key(key
):
343 if isConfigNode(val
):
344 cls
._values
[key
] = val()
345 elif isSimObjSequence(val
):
346 cls
._values
[key
] = [ v() for v
in val
]
347 elif isNullPointer(val
):
348 cls
._values
[key
] = val
350 # process param types from _init_dict, as these may be needed
351 # by param descriptions also in _init_dict
352 for key
,val
in cls
._init
_dict
.items():
353 if isinstance(val
, type) and issubclass(val
, ParamType
):
354 cls
._param
_types
[key
] = val
355 if not issubclass(val
, ConfigNode
):
356 del cls
._init
_dict
[key
]
358 # now process remaining _init_dict items
359 for key
,val
in cls
._init
_dict
.items():
361 if isinstance(val
, _Param
):
362 cls
._params
[key
] = val
363 # try to resolve local param types in local param_types scope
364 val
.maybe_resolve_type(cls
._param
_types
)
366 # init-time-only keywords
367 elif cls
.init_keywords
.has_key(key
):
368 cls
._set
_keyword
(key
, val
, cls
.init_keywords
[key
])
370 # See description of decorators in the importer.py file.
371 # We just strip off the expr_decorator now since we don't
372 # need from this point on.
373 elif key
.startswith(expr_decorator
):
374 key
= key
[len(expr_decorator
):]
375 # because it had dots into a list so that we can find the
376 # proper variable to modify.
377 key
= key
.split(dot_decorator
)
379 for item
in key
[:-1]:
381 setattr(c
, key
[-1], val
)
383 # default: use normal path (ends up in __setattr__)
385 setattr(cls
, key
, val
)
388 def _isvalue(cls
, name
):
390 if c
._params
.has_key(name
):
394 if c
._values
.has_key(name
):
399 # generator that iterates across all parameters for this class and
400 # all classes it inherits from
404 for p
,v
in c
._params
.iteritems():
405 if not params
.has_key(p
):
409 # Lookup a parameter description by name in the given class.
410 def _getparam(cls
, name
, default
= AttributeError):
412 if c
._params
.has_key(name
):
413 return c
._params
[name
]
414 if isSubClass(default
, Exception):
416 "object '%s' has no attribute '%s'" % (cls
.__name
__, name
)
420 def _hasvalue(cls
, name
):
422 if c
._values
.has_key(name
):
429 for i
,c
in enumerate(cls
._bases
):
430 for p
,v
in c
._values
.iteritems():
431 if not values
.has_key(p
):
433 for p
,v
in c
._params
.iteritems():
434 if not values
.has_key(p
) and hasattr(v
, 'default'):
438 panic("Invalid default %s for param %s in node %s"
439 % (v
.default
,p
,cls
.__name
__))
446 def _getvalue(cls
, name
, default
= AttributeError):
449 if c
._values
.has_key(name
):
450 value
= c
._values
[name
]
452 if value
is not None:
455 param
= cls
._getparam
(name
, None)
456 if param
is not None and hasattr(param
, 'default'):
457 param
.valid(param
.default
)
458 value
= param
.default
459 cls
._setvalue
(name
, value
)
462 if isSubClass(default
, Exception):
463 raise default
, 'value for %s not found' % name
467 def _setvalue(cls
, name
, value
):
468 cls
._values
[name
] = value
470 def __getattr__(cls
, attr
):
471 if cls
._isvalue
(attr
):
472 return Value(cls
, attr
)
474 if attr
== '_cpp_param_decl' and hasattr(cls
, 'type'):
475 return cls
.type + '*'
477 raise AttributeError, \
478 "object '%s' has no attribute '%s'" % (cls
.__name
__, attr
)
480 def _set_keyword(cls
, keyword
, val
, kwtype
):
481 if not isinstance(val
, kwtype
):
482 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
483 (keyword
, type(val
), kwtype
)
484 if isinstance(val
, types
.FunctionType
):
485 val
= classmethod(val
)
486 type.__setattr
__(cls
, keyword
, val
)
488 # Set attribute (called on foo.attr = value when foo is an
489 # instance of class cls).
490 def __setattr__(cls
, attr
, value
):
491 # normal processing for private attributes
492 if attr
.startswith('_'):
493 type.__setattr
__(cls
, attr
, value
)
496 if cls
.keywords
.has_key(attr
):
497 cls
._set
_keyword
(attr
, value
, cls
.keywords
[attr
])
500 # must be SimObject param
501 param
= cls
._getparam
(attr
, None)
503 # It's ok: set attribute by delegating to 'object' class.
504 # Note the use of param.make_value() to verify/canonicalize
509 panic("Error setting param %s.%s to %s\n" % \
510 (cls
.__name
__, attr
, value
))
511 cls
._setvalue
(attr
, value
)
512 elif isConfigNode(value
) or isSimObjSequence(value
):
513 cls
._setvalue
(attr
, value
)
515 raise AttributeError, \
516 "Class %s has no parameter %s" % (cls
.__name
__, attr
)
518 def add_child(cls
, instance
, name
, child
):
519 if isNullPointer(child
) or instance
.top_child_names
.has_key(name
):
522 if isinstance(child
, (list, tuple)):
524 for i
,c
in enumerate(child
):
525 n
= '%s%d' % (name
, i
)
526 k
= c
.instantiate(n
, instance
)
528 instance
.children
.append(k
)
529 instance
.child_names
[n
] = k
530 instance
.child_objects
[c
] = k
533 kid
= child
.instantiate(name
, instance
)
534 instance
.children
.append(kid
)
535 instance
.child_names
[name
] = kid
536 instance
.child_objects
[child
] = kid
538 instance
.top_child_names
[name
] = kid
540 # Print instance info to .ini file.
541 def instantiate(cls
, name
, parent
= None):
542 instance
= Node(name
, cls
, parent
, isParamContext(cls
))
544 if hasattr(cls
, 'check'):
547 for key
,value
in cls
._getvalues
().iteritems():
548 if isConfigNode(value
):
549 cls
.add_child(instance
, key
, value
)
550 if isinstance(value
, (list, tuple)):
551 vals
= [ v
for v
in value
if isConfigNode(v
) ]
553 cls
.add_child(instance
, key
, vals
)
555 for pname
,param
in cls
._getparams
().iteritems():
557 value
= cls
._getvalue
(pname
)
559 panic('Error getting %s' % pname
)
562 if isConfigNode(value
):
563 value
= instance
.child_objects
[value
]
564 elif isinstance(value
, (list, tuple)):
567 if isConfigNode(val
):
568 v
.append(instance
.child_objects
[val
])
573 p
= NodeParam(pname
, param
, value
)
574 instance
.params
.append(p
)
575 instance
.param_names
[pname
] = p
577 print 'Exception while evaluating %s.%s' % \
578 (instance
.path
, pname
)
583 def _convert(cls
, value
):
585 if isinstance(value
, Node
):
586 realvalue
= value
.realtype
588 if isinstance(realvalue
, Proxy
):
591 if realvalue
== None or isNullPointer(realvalue
):
594 if isSubClass(realvalue
, cls
):
597 raise TypeError, 'object %s type %s wrong type, should be %s' % \
598 (repr(realvalue
), realvalue
, cls
)
600 def _string(cls
, value
):
601 if isNullPointer(value
):
603 return Node
._string
(value
)
605 # The ConfigNode class is the root of the special hierarchy. Most of
606 # the code in this class deals with the configuration hierarchy itself
607 # (parent/child node relationships).
608 class ConfigNode(object):
609 # Specify metaclass. Any class inheriting from ConfigNode will
610 # get this metaclass.
611 __metaclass__
= MetaConfigNode
613 def __new__(cls
, **kwargs
):
614 name
= cls
.__name
__ + ("_%d" % cls
._anon
_subclass
_counter
)
615 cls
._anon
_subclass
_counter
+= 1
616 return cls
.__metaclass
__(name
, (cls
, ), kwargs
)
618 class ParamContext(ConfigNode
):
621 class MetaSimObject(MetaConfigNode
):
622 # init_keywords and keywords are inherited from MetaConfigNode,
623 # with overrides/additions
624 init_keywords
= MetaConfigNode
.init_keywords
625 init_keywords
.update({ 'abstract' : types
.BooleanType
,
626 'type' : types
.StringType
})
628 keywords
= MetaConfigNode
.keywords
629 # no additional keywords
634 def __init__(cls
, name
, bases
, dict):
635 super(MetaSimObject
, cls
).__init
__(name
, bases
, dict)
637 if hasattr(cls
, 'type'):
638 if name
== 'SimObject':
640 elif hasattr(cls
._bases
[1], 'type'):
641 cls
._cpp
_base
= cls
._bases
[1].type
643 panic("SimObject %s derives from a non-C++ SimObject %s "\
644 "(no 'type')" % (cls
, cls_bases
[1].__name
__))
646 # This class corresponds to a C++ class: put it on the global
647 # list of C++ objects to generate param structs, etc.
648 MetaSimObject
.cpp_classes
.append(cls
)
653 code
+= "\n".join([e
.cpp_declare() for e
in cls
._param
_types
.values()])
655 param_names
= cls
._params
.keys()
657 code
+= "struct Params"
659 code
+= " : public %s::Params" % cls
._cpp
_base
661 code
+= "\n ".join([cls
._params
[pname
].cpp_decl(pname
) \
662 for pname
in param_names
])
666 class NodeParam(object):
667 def __init__(self
, name
, param
, value
):
670 self
.ptype
= param
.ptype
671 self
.convert
= param
.convert
672 self
.string
= param
.string
677 def __init__(self
, name
, realtype
, parent
, paramcontext
):
679 self
.realtype
= realtype
680 if isSimObject(realtype
):
681 self
.type = realtype
.type
686 self
.child_names
= {}
687 self
.child_objects
= {}
688 self
.top_child_names
= {}
690 self
.param_names
= {}
691 self
.paramcontext
= paramcontext
695 while node
is not None:
696 if node
.name
!= 'root':
697 path
.insert(0, node
.name
)
699 assert(node
.parent
is None)
701 self
.path
= '.'.join(path
)
703 def find(self
, realtype
, path
):
705 if issubclass(self
.realtype
, realtype
):
709 for child
in self
.children
:
710 if issubclass(child
.realtype
, realtype
):
712 raise AttributeError, \
713 'parent.any matched more than one: %s %s' % \
714 (obj
.path
, child
.path
)
716 return obj
, obj
is not None
720 for (node
,index
) in path
[:-1]:
721 if obj
.child_names
.has_key(node
):
722 obj
= obj
.child_names
[node
]
724 obj
= obj
.top_child_names
[node
]
725 obj
= Proxy
.getindex(obj
, index
)
727 (last
,index
) = path
[-1]
728 if obj
.child_names
.has_key(last
):
729 value
= obj
.child_names
[last
]
730 return Proxy
.getindex(value
, index
), True
731 elif obj
.top_child_names
.has_key(last
):
732 value
= obj
.top_child_names
[last
]
733 return Proxy
.getindex(value
, index
), True
734 elif obj
.param_names
.has_key(last
):
735 value
= obj
.param_names
[last
]
736 realtype
._convert
(value
.value
)
737 return Proxy
.getindex(value
.value
, index
), True
743 def unproxy(self
, param
, ptype
):
744 if not isinstance(param
, Proxy
):
746 return param
.unproxy(self
, ptype
)
749 self
.all
[self
.path
] = self
751 for param
in self
.params
:
756 if isinstance(pval
, (list, tuple)):
757 param
.value
= [ self
.unproxy(pv
, ptype
) for pv
in pval
]
759 param
.value
= self
.unproxy(pval
, ptype
)
761 print 'Error while fixing up %s:%s' % (self
.path
, param
.name
)
764 for child
in self
.children
:
765 assert(child
!= self
)
768 # print type and parameter values to .ini file
770 print '[' + self
.path
+ ']' # .ini section header
772 if isSimObject(self
.realtype
):
773 print 'type = %s' % self
.type
776 # instantiate children in same order they were added for
777 # backward compatibility (else we can end up with cpu1
778 # before cpu0). Changing ordering can also influence timing
779 # in the current memory system, as caches get added to a bus
780 # in different orders which affects their priority in the
781 # case of simulataneous requests. We should uncomment the
782 # following line once we take care of that issue.
783 # self.children.sort(lambda x,y: cmp(x.name, y.name))
784 children
= [ c
.name
for c
in self
.children
if not c
.paramcontext
]
785 print 'children =', ' '.join(children
)
787 self
.params
.sort(lambda x
,y
: cmp(x
.name
, y
.name
))
788 for param
in self
.params
:
790 if param
.value
is None:
791 raise AttributeError, 'Parameter with no value'
793 value
= param
.convert(param
.value
)
794 string
= param
.string(value
)
796 print 'exception in %s:%s' % (self
.path
, param
.name
)
799 print '%s = %s' % (param
.name
, string
)
803 # recursively dump out children
804 for c
in self
.children
:
807 # print type and parameter values to .ini file
808 def outputDot(self
, dot
):
811 label
= "{%s|" % self
.path
812 if isSimObject(self
.realtype
):
813 label
+= '%s|' % self
.type
816 # instantiate children in same order they were added for
817 # backward compatibility (else we can end up with cpu1
819 for c
in self
.children
:
820 dot
.add_edge(pydot
.Edge(self
.path
,c
.path
, style
="bold"))
823 for param
in self
.params
:
825 if param
.value
is None:
826 raise AttributeError, 'Parameter with no value'
828 value
= param
.convert(param
.value
)
829 string
= param
.string(value
)
831 print 'exception in %s:%s' % (self
.name
, param
.name
)
833 if isConfigNode(param
.ptype
) and string
!= "Null":
834 simobjs
.append(string
)
836 label
+= '%s = %s\\n' % (param
.name
, string
)
839 label
+= "|<%s> %s" % (so
, so
)
840 dot
.add_edge(pydot
.Edge("%s:%s" % (self
.path
, so
), so
, tailport
="w"))
842 dot
.add_node(pydot
.Node(self
.path
,shape
="Mrecord",label
=label
))
844 # recursively dump out children
845 for c
in self
.children
:
848 def _string(cls
, value
):
849 if not isinstance(value
, Node
):
850 raise AttributeError, 'expecting %s got %s' % (Node
, value
)
852 _string
= classmethod(_string
)
854 #####################################################################
856 # Parameter description classes
858 # The _params dictionary in each class maps parameter names to
859 # either a Param or a VectorParam object. These objects contain the
860 # parameter description string, the parameter type, and the default
861 # value (loaded from the PARAM section of the .odesc files). The
862 # _convert() method on these objects is used to force whatever value
863 # is assigned to the parameter to the appropriate type.
865 # Note that the default values are loaded into the class's attribute
866 # space when the parameter dictionary is initialized (in
867 # MetaConfigNode._setparams()); after that point they aren't used.
869 #####################################################################
871 def isNullPointer(value
):
872 return isinstance(value
, NullSimObject
)
875 def __init__(self
, obj
, attr
):
876 super(Value
, self
).__setattr
__('attr', attr
)
877 super(Value
, self
).__setattr
__('obj', obj
)
880 return self
.obj
._getvalue
(self
.attr
)
882 def __setattr__(self
, attr
, value
):
883 setattr(self
._getattr
(), attr
, value
)
885 def __getattr__(self
, attr
):
886 return getattr(self
._getattr
(), attr
)
888 def __getitem__(self
, index
):
889 return self
._getattr
().__getitem
__(index
)
891 def __call__(self
, *args
, **kwargs
):
892 return self
._getattr
().__call
__(*args
, **kwargs
)
894 def __nonzero__(self
):
895 return bool(self
._getattr
())
898 return str(self
._getattr
())
901 return len(self
._getattr
())
904 class _Param(object):
905 def __init__(self
, ptype
, *args
, **kwargs
):
906 if isinstance(ptype
, types
.StringType
):
907 self
.ptype_string
= ptype
908 elif isinstance(ptype
, type):
911 raise TypeError, "Param type is not a type (%s)" % ptype
917 self
.default
= args
[0]
920 raise TypeError, 'too many arguments'
922 if kwargs
.has_key('desc'):
923 assert(not hasattr(self
, 'desc'))
924 self
.desc
= kwargs
['desc']
927 if kwargs
.has_key('default'):
928 assert(not hasattr(self
, 'default'))
929 self
.default
= kwargs
['default']
930 del kwargs
['default']
933 raise TypeError, 'extra unknown kwargs %s' % kwargs
935 if not hasattr(self
, 'desc'):
936 raise TypeError, 'desc attribute missing'
938 def maybe_resolve_type(self
, context
):
939 # check if already resolved... don't use hasattr(),
940 # as that calls __getattr__()
941 if self
.__dict
__.has_key('ptype'):
944 self
.ptype
= context
[self
.ptype_string
]
946 # no harm in trying... we'll try again later using global scope
949 def __getattr__(self
, attr
):
952 self
.ptype
= param_types
[self
.ptype_string
]
955 panic("undefined Param type %s" % self
.ptype_string
)
957 raise AttributeError, "'%s' object has no attribute '%s'" % \
958 (type(self
).__name
__, attr
)
960 def valid(self
, value
):
961 if not isinstance(value
, Proxy
):
962 self
.ptype
._convert
(value
)
964 def convert(self
, value
):
965 return self
.ptype
._convert
(value
)
967 def string(self
, value
):
968 return self
.ptype
._string
(value
)
970 def set(self
, name
, instance
, value
):
971 instance
.__dict
__[name
] = value
973 def cpp_decl(self
, name
):
974 return '%s %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
976 class _ParamProxy(object):
977 def __init__(self
, type):
980 # E.g., Param.Int(5, "number of widgets")
981 def __call__(self
, *args
, **kwargs
):
982 return _Param(self
.ptype
, *args
, **kwargs
)
984 # Strange magic to theoretically allow dotted names as Param classes,
985 # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar
986 def __getattr__(self
, attr
):
987 if attr
== '__bases__':
988 raise AttributeError, ''
992 def __setattr__(self
, attr
, value
):
994 raise AttributeError, \
995 'Attribute %s not available in %s' % (attr
, self
.__class
__)
996 super(_ParamProxy
, self
).__setattr
__(attr
, value
)
999 Param
= _ParamProxy(None)
1001 # Vector-valued parameter description. Just like Param, except that
1002 # the value is a vector (list) of the specified type instead of a
1004 class _VectorParam(_Param
):
1005 def __init__(self
, type, *args
, **kwargs
):
1006 _Param
.__init
__(self
, type, *args
, **kwargs
)
1008 def valid(self
, value
):
1012 if isinstance(value
, (list, tuple)):
1014 if not isinstance(val
, Proxy
):
1015 self
.ptype
._convert
(val
)
1016 elif not isinstance(value
, Proxy
):
1017 self
.ptype
._convert
(value
)
1019 # Convert assigned value to appropriate type. If the RHS is not a
1020 # list or tuple, it generates a single-element list.
1021 def convert(self
, value
):
1025 if isinstance(value
, (list, tuple)):
1026 # list: coerce each element into new list
1027 return [ self
.ptype
._convert
(v
) for v
in value
]
1029 # singleton: coerce & wrap in a list
1030 return self
.ptype
._convert
(value
)
1032 def string(self
, value
):
1033 if isinstance(value
, (list, tuple)):
1034 return ' '.join([ self
.ptype
._string
(v
) for v
in value
])
1036 return self
.ptype
._string
(value
)
1038 def cpp_decl(self
, name
):
1039 return 'std::vector<%s> %s;' % (self
.ptype
._cpp
_param
_decl
, name
)
1041 class _VectorParamProxy(_ParamProxy
):
1042 # E.g., VectorParam.Int(5, "number of widgets")
1043 def __call__(self
, *args
, **kwargs
):
1044 return _VectorParam(self
.ptype
, *args
, **kwargs
)
1046 VectorParam
= _VectorParamProxy(None)
1048 #####################################################################
1052 # Though native Python types could be used to specify parameter types
1053 # (the 'ptype' field of the Param and VectorParam classes), it's more
1054 # flexible to define our own set of types. This gives us more control
1055 # over how Python expressions are converted to values (via the
1056 # __init__() constructor) and how these values are printed out (via
1057 # the __str__() conversion method). Eventually we'll need these types
1058 # to correspond to distinct C++ types as well.
1060 #####################################################################
1063 # Metaclass for bounds-checked integer parameters. See CheckedInt.
1064 class CheckedIntType(type):
1065 def __init__(cls
, name
, bases
, dict):
1066 super(CheckedIntType
, cls
).__init
__(name
, bases
, dict)
1068 # CheckedInt is an abstract base class, so we actually don't
1069 # want to do any processing on it... the rest of this code is
1070 # just for classes that derive from CheckedInt.
1071 if name
== 'CheckedInt':
1074 if not (hasattr(cls
, 'min') and hasattr(cls
, 'max')):
1075 if not (hasattr(cls
, 'size') and hasattr(cls
, 'unsigned')):
1076 panic("CheckedInt subclass %s must define either\n" \
1077 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1081 cls
.max = 2 ** cls
.size
- 1
1083 cls
.min = -(2 ** (cls
.size
- 1))
1084 cls
.max = (2 ** (cls
.size
- 1)) - 1
1086 cls
._cpp
_param
_decl
= cls
.cppname
1088 def _convert(cls
, value
):
1089 if isinstance(value
, bool):
1092 if not isinstance(value
, (int, long, float, str)):
1093 raise TypeError, 'Integer param of invalid type %s' % type(value
)
1095 if isinstance(value
, (str, float)):
1096 value
= long(float(value
))
1098 if not cls
.min <= value
<= cls
.max:
1099 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1100 (cls
.min, value
, cls
.max)
1104 def _string(cls
, value
):
1107 # Abstract superclass for bounds-checked integer parameters. This
1108 # class is subclassed to generate parameter classes with specific
1109 # bounds. Initialization of the min and max bounds is done in the
1110 # metaclass CheckedIntType.__init__.
1111 class CheckedInt(ParamType
):
1112 __metaclass__
= CheckedIntType
1114 class Int(CheckedInt
): cppname
= 'int'; size
= 32; unsigned
= False
1115 class Unsigned(CheckedInt
): cppname
= 'unsigned'; size
= 32; unsigned
= True
1117 class Int8(CheckedInt
): cppname
= 'int8_t'; size
= 8; unsigned
= False
1118 class UInt8(CheckedInt
): cppname
= 'uint8_t'; size
= 8; unsigned
= True
1119 class Int16(CheckedInt
): cppname
= 'int16_t'; size
= 16; unsigned
= False
1120 class UInt16(CheckedInt
): cppname
= 'uint16_t'; size
= 16; unsigned
= True
1121 class Int32(CheckedInt
): cppname
= 'int32_t'; size
= 32; unsigned
= False
1122 class UInt32(CheckedInt
): cppname
= 'uint32_t'; size
= 32; unsigned
= True
1123 class Int64(CheckedInt
): cppname
= 'int64_t'; size
= 64; unsigned
= False
1124 class UInt64(CheckedInt
): cppname
= 'uint64_t'; size
= 64; unsigned
= True
1126 class Counter(CheckedInt
): cppname
= 'Counter'; size
= 64; unsigned
= True
1127 class Addr(CheckedInt
): cppname
= 'Addr'; size
= 64; unsigned
= True
1128 class Tick(CheckedInt
): cppname
= 'Tick'; size
= 64; unsigned
= True
1130 class Percent(CheckedInt
): cppname
= 'int'; min = 0; max = 100
1133 def __init__(self
, first
, second
):
1135 self
.second
= second
1137 class MetaRange(type):
1138 def __init__(cls
, name
, bases
, dict):
1139 super(MetaRange
, cls
).__init
__(name
, bases
, dict)
1142 cls
._cpp
_param
_decl
= 'Range<%s>' % cls
.type._cpp
_param
_decl
1144 def _convert(cls
, value
):
1145 if not isinstance(value
, Pair
):
1146 raise TypeError, 'value %s is not a Pair' % value
1147 return Pair(cls
.type._convert
(value
.first
),
1148 cls
.type._convert
(value
.second
))
1150 def _string(cls
, value
):
1151 return '%s:%s' % (cls
.type._string
(value
.first
),
1152 cls
.type._string
(value
.second
))
1154 class Range(ParamType
):
1155 __metaclass__
= MetaRange
1157 def RangeSize(start
, size
):
1158 return Pair(start
, start
+ size
- 1)
1160 class AddrRange(Range
): type = Addr
1162 # Boolean parameter type.
1163 class Bool(ParamType
):
1164 _cpp_param_decl
= 'bool'
1165 def _convert(value
):
1170 if t
== int or t
== long:
1175 if v
== "true" or v
== "t" or v
== "yes" or v
== "y":
1177 elif v
== "false" or v
== "f" or v
== "no" or v
== "n":
1180 raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v
, t
)
1181 _convert
= staticmethod(_convert
)
1188 _string
= staticmethod(_string
)
1190 # String-valued parameter.
1191 class String(ParamType
):
1192 _cpp_param_decl
= 'string'
1194 # Constructor. Value must be Python string.
1195 def _convert(cls
,value
):
1198 if isinstance(value
, str):
1202 "String param got value %s %s" % (repr(value
), type(value
))
1203 _convert
= classmethod(_convert
)
1205 # Generate printable string version. Not too tricky.
1206 def _string(cls
, value
):
1208 _string
= classmethod(_string
)
1211 def IncEthernetAddr(addr
, val
= 1):
1212 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
1214 for i
in (5, 4, 3, 2, 1):
1215 val
,rem
= divmod(bytes
[i
], 256)
1220 assert(bytes
[0] <= 255)
1221 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
1223 class NextEthernetAddr(object):
1224 __metaclass__
= Singleton
1225 addr
= "00:90:00:00:00:01"
1227 def __init__(self
, inc
= 1):
1228 self
.value
= self
.addr
1229 self
.addr
= IncEthernetAddr(self
.addr
, inc
)
1231 class EthernetAddr(ParamType
):
1232 _cpp_param_decl
= 'EthAddr'
1234 def _convert(cls
, value
):
1235 if value
== NextEthernetAddr
:
1238 if not isinstance(value
, str):
1239 raise TypeError, "expected an ethernet address and didn't get one"
1241 bytes
= value
.split(':')
1243 raise TypeError, 'invalid ethernet address %s' % value
1246 if not 0 <= int(byte
) <= 256:
1247 raise TypeError, 'invalid ethernet address %s' % value
1250 _convert
= classmethod(_convert
)
1252 def _string(cls
, value
):
1253 if value
== NextEthernetAddr
:
1254 value
= value().value
1256 _string
= classmethod(_string
)
1258 # Special class for NULL pointers. Note the special check in
1259 # make_param_value() above that lets these be assigned where a
1260 # SimObject is required.
1261 # only one copy of a particular node
1262 class NullSimObject(object):
1263 __metaclass__
= Singleton
1268 def _instantiate(self
, parent
= None, path
= ''):
1271 def _convert(cls
, value
):
1275 if isinstance(value
, cls
):
1278 raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1279 (repr(value
), type(value
), cls
)
1280 _convert
= classmethod(_convert
)
1284 _string
= staticmethod(_string
)
1286 # The only instance you'll ever need...
1287 Null
= NULL
= NullSimObject()
1289 # Enumerated types are a little more complex. The user specifies the
1290 # type as Enum(foo) where foo is either a list or dictionary of
1291 # alternatives (typically strings, but not necessarily so). (In the
1292 # long run, the integer value of the parameter will be the list index
1293 # or the corresponding dictionary value. For now, since we only check
1294 # that the alternative is valid and then spit it into a .ini file,
1295 # there's not much point in using the dictionary.)
1297 # What Enum() must do is generate a new type encapsulating the
1298 # provided list/dictionary so that specific values of the parameter
1299 # can be instances of that type. We define two hidden internal
1300 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1301 # derive the new type from the appropriate base class on the fly.
1304 # Metaclass for Enum types
1305 class MetaEnum(type):
1307 def __init__(cls
, name
, bases
, init_dict
):
1308 if init_dict
.has_key('map'):
1309 if not isinstance(cls
.map, dict):
1310 raise TypeError, "Enum-derived class attribute 'map' " \
1311 "must be of type dict"
1312 # build list of value strings from map
1313 cls
.vals
= cls
.map.keys()
1315 elif init_dict
.has_key('vals'):
1316 if not isinstance(cls
.vals
, list):
1317 raise TypeError, "Enum-derived class attribute 'vals' " \
1318 "must be of type list"
1319 # build string->value map from vals sequence
1321 for idx
,val
in enumerate(cls
.vals
):
1324 raise TypeError, "Enum-derived class must define "\
1325 "attribute 'map' or 'vals'"
1327 cls
._cpp
_param
_decl
= name
1329 super(MetaEnum
, cls
).__init
__(name
, bases
, init_dict
)
1331 def cpp_declare(cls
):
1332 s
= 'enum %s {\n ' % cls
.__name
__
1333 s
+= ',\n '.join(['%s = %d' % (v
,cls
.map[v
]) for v
in cls
.vals
])
1337 # Base class for enum types.
1338 class Enum(ParamType
):
1339 __metaclass__
= MetaEnum
1342 def _convert(self
, value
):
1343 if value
not in self
.map:
1344 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1345 % (value
, self
.vals
)
1347 _convert
= classmethod(_convert
)
1349 # Generate printable string version of value.
1350 def _string(self
, value
):
1352 _string
= classmethod(_string
)
1354 # "Constants"... handy aliases for various values.
1357 # Some memory range specifications use this as a default upper bound.
1361 # For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1366 #####################################################################
1368 # The final hook to generate .ini files. Called from configuration
1369 # script once config is built.
1370 def instantiate(root
):
1371 instance
= root
.instantiate('root')
1376 instance
.outputDot(dot
)
1377 dot
.orientation
= "portrait"
1379 dot
.ranksep
="equally"
1381 dot
.write("config.dot")
1382 dot
.write_ps("config.ps")
1384 # SimObject is a minimal extension of ConfigNode, implementing a
1385 # hierarchy node that corresponds to an M5 SimObject. It prints out a
1386 # "type=" line to indicate its SimObject class, prints out the
1387 # assigned parameters corresponding to its class, and allows
1388 # parameters to be set by keyword in the constructor. Note that most
1389 # of the heavy lifting for the SimObject param handling is done in the
1390 # MetaConfigNode metaclass.
1391 class SimObject(ConfigNode
, ParamType
):
1392 __metaclass__
= MetaSimObject
1396 # __all__ defines the list of symbols that get exported when
1397 # 'from config import *' is invoked. Try to keep this reasonably
1398 # short to avoid polluting other namespaces.
1399 __all__
= ['ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam',
1401 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1402 'Int32', 'UInt32', 'Int64', 'UInt64',
1403 'Counter', 'Addr', 'Tick', 'Percent',
1404 'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M',