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