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