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