4e2a377b0e195e2b203f0dad55bd455c710348d6
1 # Copyright (c) 2004 The Regents of The University of Michigan
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met: redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer;
8 # redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution;
11 # neither the name of the copyright holders nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 from __future__
import generators
28 import os
, re
, sys
, types
31 env
.update(os
.environ
)
33 return env
.has_key(key
)
35 def define(key
, value
= True):
38 def panic(*args
, **kwargs
):
39 sys
.exit(*args
, **kwargs
)
42 path
= os
.path
.realpath(path
)
43 if os
.path
.isdir(path
):
47 AddToPath(os
.path
.dirname(path
))
48 exec('from m5config import *')
49 mpy_exec(file(path
, 'r'))
51 def issequence(value
):
52 return isinstance(value
, tuple) or isinstance(value
, list)
54 class Singleton(type):
55 def __call__(cls
, *args
, **kwargs
):
56 if hasattr(cls
, '_instance'):
59 cls
._instance
= super(Singleton
, cls
).__call
__(*args
, **kwargs
)
62 if os
.environ
.has_key('FULL_SYSTEM'):
65 #####################################################################
67 # M5 Python Configuration Utility
69 # The basic idea is to write simple Python programs that build Python
70 # objects corresponding to M5 SimObjects for the deisred simulation
71 # configuration. For now, the Python emits a .ini file that can be
72 # parsed by M5. In the future, some tighter integration between M5
73 # and the Python interpreter may allow bypassing the .ini file.
75 # Each SimObject class in M5 is represented by a Python class with the
76 # same name. The Python inheritance tree mirrors the M5 C++ tree
77 # (e.g., SimpleCPU derives from BaseCPU in both cases, and all
78 # SimObjects inherit from a single SimObject base class). To specify
79 # an instance of an M5 SimObject in a configuration, the user simply
80 # instantiates the corresponding Python object. The parameters for
81 # that SimObject are given by assigning to attributes of the Python
82 # object, either using keyword assignment in the constructor or in
83 # separate assignment statements. For example:
85 # cache = BaseCache('my_cache', root, size=64*K)
86 # cache.hit_latency = 3
89 # (The first two constructor arguments specify the name of the created
90 # cache and its parent node in the hierarchy.)
92 # The magic lies in the mapping of the Python attributes for SimObject
93 # classes to the actual SimObject parameter specifications. This
94 # allows parameter validity checking in the Python code. Continuing
95 # the example above, the statements "cache.blurfl=3" or
96 # "cache.assoc='hello'" would both result in runtime errors in Python,
97 # since the BaseCache object has no 'blurfl' parameter and the 'assoc'
98 # parameter requires an integer, respectively. This magic is done
99 # primarily by overriding the special __setattr__ method that controls
100 # assignment to object attributes.
102 # The Python module provides another class, ConfigNode, which is a
103 # superclass of SimObject. ConfigNode implements the parent/child
104 # relationship for building the configuration hierarchy tree.
105 # Concrete instances of ConfigNode can be used to group objects in the
106 # hierarchy, but do not correspond to SimObjects themselves (like a
107 # .ini section with "children=" but no "type=".
109 # Once a set of Python objects have been instantiated in a hierarchy,
110 # calling 'instantiate(obj)' (where obj is the root of the hierarchy)
111 # will generate a .ini file. See simple-4cpu.py for an example
112 # (corresponding to m5-test/simple-4cpu.ini).
114 #####################################################################
116 #####################################################################
118 # ConfigNode/SimObject classes
120 # The Python class hierarchy rooted by ConfigNode (which is the base
121 # class of SimObject, which in turn is the base class of all other M5
122 # SimObject classes) has special attribute behavior. In general, an
123 # object in this hierarchy has three categories of attribute-like
126 # 1. Regular Python methods and variables. These must start with an
127 # underscore to be treated normally.
129 # 2. SimObject parameters. These values are stored as normal Python
130 # attributes, but all assignments to these attributes are checked
131 # against the pre-defined set of parameters stored in the class's
132 # _params dictionary. Assignments to attributes that do not
133 # correspond to predefined parameters, or that are not of the correct
134 # type, incur runtime errors.
136 # 3. Hierarchy children. The child nodes of a ConfigNode are stored
137 # in the node's _children dictionary, but can be accessed using the
138 # Python attribute dot-notation (just as they are printed out by the
139 # simulator). Children cannot be created using attribute assigment;
140 # they must be added by specifying the parent node in the child's
141 # constructor or using the '+=' operator.
143 # The SimObject parameters are the most complex, for a few reasons.
144 # First, both parameter descriptions and parameter values are
145 # inherited. Thus parameter description lookup must go up the
146 # inheritance chain like normal attribute lookup, but this behavior
147 # must be explicitly coded since the lookup occurs in each class's
148 # _params attribute. Second, because parameter values can be set
149 # on SimObject classes (to implement default values), the parameter
150 # checking behavior must be enforced on class attribute assignments as
151 # well as instance attribute assignments. Finally, because we allow
152 # class specialization via inheritance (e.g., see the L1Cache class in
153 # the simple-4cpu.py example), we must do parameter checking even on
154 # class instantiation. To provide all these features, we use a
155 # metaclass to define most of the SimObject parameter behavior for
156 # this class hierarchy.
158 #####################################################################
161 def __init__(self
, path
= ()):
165 def __getattr__(self
, attr
):
166 return Proxy(self
._path
+ (attr
, ))
168 def __setattr__(self
, attr
, value
):
169 if not attr
.startswith('_'):
170 raise AttributeError, 'cannot set attribute %s' % attr
171 super(Proxy
, self
).__setattr
__(attr
, value
)
175 for attr
in self
._path
:
176 obj
= obj
.__getattribute
__(attr
)
181 def isSubClass(value
, cls
):
183 return issubclass(value
, cls
)
188 return isinstance(self
, _Param
)
190 def isConfigNode(value
):
192 return issubclass(value
, ConfigNode
)
196 def isSimObject(value
):
198 return issubclass(value
, SimObject
)
202 def isSimObjSequence(value
):
203 if not issequence(value
):
207 if not isNullPointer(val
) and not isConfigNode(val
):
212 # The metaclass for ConfigNode (and thus for everything that derives
213 # from ConfigNode, including SimObject). This class controls how new
214 # classes that derive from ConfigNode are instantiated, and provides
215 # inherited class behavior (just like a class controls how instances
216 # of that class are instantiated, and provides inherited instance
218 class MetaConfigNode(type):
219 keywords
= { 'abstract' : types
.BooleanType
,
220 'check' : types
.FunctionType
,
221 '_init' : types
.FunctionType
,
222 'type' : (types
.NoneType
, types
.StringType
) }
224 # __new__ is called before __init__, and is where the statements
225 # in the body of the class definition get loaded into the class's
226 # __dict__. We intercept this to filter out parameter assignments
227 # and only allow "private" attributes to be passed to the base
228 # __new__ (starting with underscore).
229 def __new__(mcls
, name
, bases
, dict):
230 priv
= { 'abstract' : False,
231 # initialize _params and _values dicts to empty
236 for key
,val
in dict.items():
237 if mcls
.keywords
.has_key(key
):
238 if not isinstance(val
, mcls
.keywords
[key
]):
240 'keyword %s has the wrong type %s should be %s' % \
241 (key
, type(val
), mcls
.keywords
[key
])
243 if isinstance(val
, types
.FunctionType
):
244 val
= classmethod(val
)
248 elif key
.startswith('_'):
252 elif not isNullPointer(val
) and isConfigNode(val
):
255 elif isSimObjSequence(val
):
256 dict[key
] = [ v() for v
in val
]
258 # If your parent has a value in it that's a config node, clone it.
260 if not isConfigNode(base
):
263 for name
,value
in base
._values
.iteritems():
264 if dict.has_key(name
):
267 if isConfigNode(value
):
268 priv
['_values'][name
] = value()
269 elif isSimObjSequence(value
):
270 priv
['_values'][name
] = [ val() for val
in value
]
272 # entries left in dict will get passed to __init__, where we'll
273 # deal with them as params.
274 return super(MetaConfigNode
, mcls
).__new
__(mcls
, name
, bases
, priv
)
276 # initialization: start out with an empty _params dict (makes life
277 # simpler if we can assume _params is always valid).
278 def __init__(cls
, name
, bases
, dict):
279 super(MetaConfigNode
, cls
).__init
__(cls
, name
, bases
, {})
281 cls
._bases
= [c
for c
in cls
.__mro
__ if isConfigNode(c
)]
283 # initialize attributes with values from class definition
284 for pname
,value
in dict.iteritems():
285 setattr(cls
, pname
, value
)
287 if hasattr(cls
, '_init'):
291 def _isvalue(cls
, name
):
293 if c
._params
.has_key(name
):
297 if c
._values
.has_key(name
):
302 # generator that iterates across all parameters for this class and
303 # all classes it inherits from
307 for p
,v
in c
._params
.iteritems():
308 if not params
.has_key(p
):
312 # Lookup a parameter description by name in the given class.
313 def _getparam(cls
, name
, default
= AttributeError):
315 if c
._params
.has_key(name
):
316 return c
._params
[name
]
317 if isSubClass(default
, Exception):
319 "object '%s' has no attribute '%s'" % (cls
.__name
__, name
)
323 def _setparam(cls
, name
, value
):
324 cls
._params
[name
] = value
326 def _hasvalue(cls
, name
):
328 if c
._values
.has_key(name
):
335 for i
,c
in enumerate(cls
._bases
):
336 for p
,v
in c
._values
.iteritems():
337 if not values
.has_key(p
):
341 def _getvalue(cls
, name
, default
= AttributeError):
344 if c
._values
.has_key(name
):
345 value
= c
._values
[name
]
347 if value
is not None:
350 param
= cls
._getparam
(name
, None)
351 if param
is not None and hasattr(param
, 'default'):
352 param
.valid(param
.default
)
353 value
= param
.default
354 cls
._setvalue
(name
, value
)
357 if isSubClass(default
, Exception):
358 raise default
, 'value for %s not found' % name
362 def _setvalue(cls
, name
, value
):
363 cls
._values
[name
] = value
365 def _getdisable(cls
, name
):
367 if c
._disable
.has_key(name
):
368 return c
._disable
[name
]
371 def _setdisable(cls
, name
, value
):
372 cls
._disable
[name
] = value
374 def __getattr__(cls
, attr
):
375 if cls
._isvalue
(attr
):
376 return Value(cls
, attr
)
378 raise AttributeError, \
379 "object '%s' has no attribute '%s'" % (cls
.__name
__, cls
)
382 # Set attribute (called on foo.attr = value when foo is an
383 # instance of class cls).
384 def __setattr__(cls
, attr
, value
):
385 # normal processing for private attributes
386 if attr
.startswith('_'):
387 type.__setattr
__(cls
, attr
, value
)
390 if cls
.keywords
.has_key(attr
):
392 "keyword '%s' can only be set in a simobj definition" % attr
395 cls
._setparam
(attr
, value
)
398 # must be SimObject param
399 param
= cls
._getparam
(attr
, None)
401 # It's ok: set attribute by delegating to 'object' class.
402 # Note the use of param.make_value() to verify/canonicalize
405 cls
._setvalue
(attr
, value
)
406 elif isConfigNode(value
) or isSimObjSequence(value
):
407 cls
._setvalue
(attr
, value
)
409 for p
,v
in cls
._getparams
().iteritems():
411 raise AttributeError, \
412 "Class %s has no parameter %s" % (cls
.__name
__, attr
)
414 def add_child(cls
, instance
, name
, child
):
415 if isNullPointer(child
) or instance
.top_child_names
.has_key(name
):
418 if issequence(child
):
420 for i
,c
in enumerate(child
):
421 n
= '%s%d' % (name
, i
)
422 k
= c
.instantiate(n
, instance
)
424 instance
.children
.append(k
)
425 instance
.child_names
[n
] = k
426 instance
.child_objects
[c
] = k
429 kid
= child
.instantiate(name
, instance
)
430 instance
.children
.append(kid
)
431 instance
.child_names
[name
] = kid
432 instance
.child_objects
[child
] = kid
434 instance
.top_child_names
[name
] = kid
436 # Print instance info to .ini file.
437 def instantiate(cls
, name
, parent
= None):
438 instance
= Node(name
, cls
, cls
.type, parent
)
440 if hasattr(cls
, 'check'):
443 for key
,value
in cls
._getvalues
().iteritems():
444 if cls
._getdisable
(key
):
447 if isConfigNode(value
):
448 cls
.add_child(instance
, key
, value
)
449 if issequence(value
):
450 list = [ v
for v
in value
if isConfigNode(v
) ]
452 cls
.add_child(instance
, key
, list)
454 for pname
,param
in cls
._getparams
().iteritems():
456 if cls
._getdisable
(pname
):
459 value
= cls
._getvalue
(pname
)
461 if isConfigNode(value
):
462 value
= instance
.child_objects
[value
]
463 elif issequence(value
):
466 if isConfigNode(val
):
467 v
.append(instance
.child_objects
[val
])
472 p
= NodeParam(pname
, param
, value
)
473 instance
.params
.append(p
)
474 instance
.param_names
[pname
] = p
476 print 'Exception while evaluating %s.%s' % \
477 (instance
.path
, pname
)
482 def _convert(cls
, value
):
484 if isinstance(value
, Node
):
485 realvalue
= value
.realtype
487 if isinstance(realvalue
, Proxy
):
490 if realvalue
== None or isNullPointer(realvalue
):
493 if isSubClass(realvalue
, cls
):
496 raise TypeError, 'object %s type %s wrong type, should be %s' % \
497 (repr(realvalue
), realvalue
, cls
)
499 def _string(cls
, value
):
500 if isNullPointer(value
):
502 return Node
._string
(value
)
504 # The ConfigNode class is the root of the special hierarchy. Most of
505 # the code in this class deals with the configuration hierarchy itself
506 # (parent/child node relationships).
507 class ConfigNode(object):
508 # Specify metaclass. Any class inheriting from ConfigNode will
509 # get this metaclass.
510 __metaclass__
= MetaConfigNode
513 def __new__(cls
, **kwargs
):
514 return MetaConfigNode(cls
.__name
__, (cls
, ), kwargs
)
516 # Set attribute. All attribute assignments go through here. Must
517 # be private attribute (starts with '_') or valid parameter entry.
518 # Basically identical to MetaConfigClass.__setattr__(), except
519 # this sets attributes on specific instances rather than on classes.
520 #def __setattr__(self, attr, value):
521 # if attr.startswith('_'):
522 # object.__setattr__(self, attr, value)
524 # not private; look up as param
525 # param = self.__class__.lookup_param(attr)
527 # raise AttributeError, \
528 # "Class %s has no parameter %s" \
529 # % (self.__class__.__name__, attr)
530 # It's ok: set attribute by delegating to 'object' class.
531 # Note the use of param.make_value() to verify/canonicalize
532 # the assigned value.
533 # v = param.convert(value)
534 # object.__setattr__(self, attr, v)
536 # SimObject is a minimal extension of ConfigNode, implementing a
537 # hierarchy node that corresponds to an M5 SimObject. It prints out a
538 # "type=" line to indicate its SimObject class, prints out the
539 # assigned parameters corresponding to its class, and allows
540 # parameters to be set by keyword in the constructor. Note that most
541 # of the heavy lifting for the SimObject param handling is done in the
542 # MetaConfigNode metaclass.
543 class SimObject(ConfigNode
):
546 param_names
= cls
._params
.keys()
548 code
= "BEGIN_DECLARE_SIM_OBJECT_PARAMS(%s)\n" % name
549 decls
= [" " + cls
._params
[pname
].sim_decl(pname
) \
550 for pname
in param_names
]
551 code
+= "\n".join(decls
) + "\n"
552 code
+= "END_DECLARE_SIM_OBJECT_PARAMS(%s)\n\n" % name
553 code
+= "BEGIN_INIT_SIM_OBJECT_PARAMS(%s)\n" % name
554 inits
= [" " + cls
._params
[pname
].sim_init(pname
) \
555 for pname
in param_names
]
556 code
+= ",\n".join(inits
) + "\n"
557 code
+= "END_INIT_SIM_OBJECT_PARAMS(%s)\n\n" % name
559 _sim_code
= classmethod(_sim_code
)
561 class NodeParam(object):
562 def __init__(self
, name
, param
, value
):
565 self
.ptype
= param
.ptype
566 self
.convert
= param
.convert
567 self
.string
= param
.string
572 def __init__(self
, name
, realtype
, type, parent
):
574 self
.realtype
= realtype
578 self
.child_names
= {}
579 self
.child_objects
= {}
580 self
.top_child_names
= {}
582 self
.param_names
= {}
586 while node
is not None:
587 if node
.name
!= 'root':
588 path
.insert(0, node
.name
)
590 assert(node
.parent
is None)
592 self
.path
= '.'.join(path
)
594 def find(self
, realtype
, path
):
595 rtype
= eval(realtype
)
597 if issubclass(self
.realtype
, rtype
):
601 for child
in self
.children
:
602 if issubclass(child
.realtype
, rtype
):
604 raise AttributeError, \
605 'Super matched more than one: %s %s' % \
606 (obj
.path
, child
.path
)
608 return obj
, obj
is not None
612 for node
in path
[:-1]:
613 obj
= obj
.child_names
[node
]
616 if obj
.child_names
.has_key(last
):
617 value
= obj
.child_names
[last
]
618 if issubclass(value
.realtype
, rtype
):
620 elif obj
.param_names
.has_key(last
):
621 value
= obj
.param_names
[last
]
622 rtype
._convert
(value
.value
)
623 return value
.value
, True
629 def unproxy(self
, ptype
, value
):
630 if not isinstance(value
, Proxy
):
634 raise AttributeError, 'Error while fixing up %s' % self
.path
640 raise AttributeError, \
641 'Parent of %s type %s not found at path %s' \
642 % (self
.name
, ptype
, value
._path
)
643 found
, done
= obj
.find(ptype
, value
._path
)
644 if isinstance(found
, Proxy
):
651 self
.all
[self
.path
] = self
653 for param
in self
.params
:
659 param
.value
= [ self
.unproxy(ptype
, pv
) for pv
in pval
]
661 param
.value
= self
.unproxy(ptype
, pval
)
663 print 'Error while fixing up %s:%s' % (self
.path
, param
.name
)
666 for child
in self
.children
:
667 assert(child
!= self
)
670 # print type and parameter values to .ini file
672 print '[' + self
.path
+ ']' # .ini section header
674 if isSimObject(self
.realtype
):
675 print 'type = %s' % self
.type
678 # instantiate children in same order they were added for
679 # backward compatibility (else we can end up with cpu1
681 print 'children =', ' '.join([ c
.name
for c
in self
.children
])
683 for param
in self
.params
:
685 if param
.value
is None:
686 raise AttributeError, 'Parameter with no value'
688 value
= param
.convert(param
.value
)
689 string
= param
.string(value
)
691 print 'exception in %s:%s' % (self
.path
, param
.name
)
694 print '%s = %s' % (param
.name
, string
)
698 # recursively dump out children
699 for c
in self
.children
:
702 def _string(cls
, value
):
703 if not isinstance(value
, Node
):
704 raise AttributeError, 'expecting %s got %s' % (Node
, value
)
706 _string
= classmethod(_string
)
708 #####################################################################
710 # Parameter description classes
712 # The _params dictionary in each class maps parameter names to
713 # either a Param or a VectorParam object. These objects contain the
714 # parameter description string, the parameter type, and the default
715 # value (loaded from the PARAM section of the .odesc files). The
716 # _convert() method on these objects is used to force whatever value
717 # is assigned to the parameter to the appropriate type.
719 # Note that the default values are loaded into the class's attribute
720 # space when the parameter dictionary is initialized (in
721 # MetaConfigNode._setparams()); after that point they aren't used.
723 #####################################################################
725 def isNullPointer(value
):
726 return isinstance(value
, NullSimObject
)
729 def __init__(self
, obj
, attr
):
730 super(Value
, self
).__setattr
__('attr', attr
)
731 super(Value
, self
).__setattr
__('obj', obj
)
734 return self
.obj
._getvalue
(self
.attr
)
736 def __setattr__(self
, attr
, value
):
737 if attr
== 'disable':
738 self
.obj
._setdisable
(self
.attr
, value
)
740 setattr(self
._getattr
(), attr
, value
)
742 def __getattr__(self
, attr
):
743 if attr
== 'disable':
744 return self
.obj
._getdisable
(self
.attr
)
746 return getattr(self
._getattr
(), attr
)
748 def __getitem__(self
, index
):
749 return self
._getattr
().__getitem
__(index
)
751 def __call__(self
, *args
, **kwargs
):
752 return self
._getattr
().__call
__(*args
, **kwargs
)
754 def __nonzero__(self
):
755 return bool(self
._getattr
())
758 return str(self
._getattr
())
761 class _Param(object):
762 def __init__(self
, ptype
, *args
, **kwargs
):
769 self
.default
= args
[0]
772 raise TypeError, 'too many arguments'
774 if kwargs
.has_key('desc'):
775 assert(not hasattr(self
, 'desc'))
776 self
.desc
= kwargs
['desc']
779 if kwargs
.has_key('default'):
780 assert(not hasattr(self
, 'default'))
781 self
.default
= kwargs
['default']
782 del kwargs
['default']
785 raise TypeError, 'extra unknown kwargs %s' % kwargs
787 if not hasattr(self
, 'desc'):
788 raise TypeError, 'desc attribute missing'
790 def valid(self
, value
):
791 if not isinstance(value
, Proxy
):
792 ptype
= eval(self
.ptype
)
793 ptype
._convert
(value
)
795 def convert(self
, value
):
796 ptype
= eval(self
.ptype
)
797 return ptype
._convert
(value
)
799 def string(self
, value
):
800 ptype
= eval(self
.ptype
)
801 return ptype
._string
(value
)
803 def get(self
, name
, instance
, owner
):
804 # nothing to do if None or already correct type. Also allow NULL
805 # pointer to be assigned where a SimObject is expected.
807 if value
== None or isinstance(value
, self
.ptype
) or \
808 isConfigNode(self
.ptype
) and \
809 (isNullPointer(value
) or issubclass(value
, self
.ptype
)):
813 # this type conversion will raise an exception if it's illegal
814 return self
.ptype(value
)
816 def set(self
, name
, instance
, value
):
817 instance
.__dict
__[name
] = value
819 def sim_decl(self
, name
):
820 return 'Param<%s> %s;' % (self
.ptype
.__name
__, name
)
822 def sim_init(self
, name
):
823 if self
.default
== None:
824 return 'INIT_PARAM(%s, "%s")' % (name
, self
.desc
)
826 return 'INIT_PARAM_DFLT(%s, "%s", %s)' % \
827 (name
, self
.desc
, str(self
.default
))
829 class _ParamProxy(object):
830 def __init__(self
, type):
833 # E.g., Param.Int(5, "number of widgets")
834 def __call__(self
, *args
, **kwargs
):
835 return _Param(self
.ptype
, *args
, **kwargs
)
837 def __getattr__(self
, attr
):
838 if attr
== '__bases__':
839 raise AttributeError, ''
843 def __setattr__(self
, attr
, value
):
845 raise AttributeError, \
846 'Attribute %s not available in %s' % (attr
, self
.__class
__)
847 super(_ParamProxy
, self
).__setattr
__(attr
, value
)
850 Param
= _ParamProxy(None)
852 # Vector-valued parameter description. Just like Param, except that
853 # the value is a vector (list) of the specified type instead of a
855 class _VectorParam(_Param
):
856 def __init__(self
, type, *args
, **kwargs
):
857 _Param
.__init
__(self
, type, *args
, **kwargs
)
859 def valid(self
, value
):
863 ptype
= eval(self
.ptype
)
864 if issequence(value
):
866 if not isinstance(val
, Proxy
):
868 elif not isinstance(value
, Proxy
):
869 ptype
._convert
(value
)
871 # Convert assigned value to appropriate type. If the RHS is not a
872 # list or tuple, it generates a single-element list.
873 def convert(self
, value
):
877 ptype
= eval(self
.ptype
)
878 if issequence(value
):
879 # list: coerce each element into new list
880 return [ ptype
._convert
(v
) for v
in value
]
882 # singleton: coerce & wrap in a list
883 return ptype
._convert
(value
)
885 def string(self
, value
):
886 ptype
= eval(self
.ptype
)
887 if issequence(value
):
888 return ' '.join([ ptype
._string
(v
) for v
in value
])
890 return ptype
._string
(value
)
892 def sim_decl(self
, name
):
893 return 'VectorParam<%s> %s;' % (self
.ptype
.__name
__, name
)
895 class _VectorParamProxy(_ParamProxy
):
896 # E.g., VectorParam.Int(5, "number of widgets")
897 def __call__(self
, *args
, **kwargs
):
898 return _VectorParam(self
.ptype
, *args
, **kwargs
)
900 VectorParam
= _VectorParamProxy(None)
902 #####################################################################
906 # Though native Python types could be used to specify parameter types
907 # (the 'ptype' field of the Param and VectorParam classes), it's more
908 # flexible to define our own set of types. This gives us more control
909 # over how Python expressions are converted to values (via the
910 # __init__() constructor) and how these values are printed out (via
911 # the __str__() conversion method). Eventually we'll need these types
912 # to correspond to distinct C++ types as well.
914 #####################################################################
915 # Integer parameter type.
916 class _CheckedInt(object):
917 def _convert(cls
, value
):
922 if t
!= int and t
!= long and t
!= float and t
!= str:
923 raise TypeError, 'Integer parameter of invalid type %s' % t
925 if t
== str or t
== float:
928 if not cls
._min
<= value
<= cls
._max
:
929 raise TypeError, 'Integer parameter out of bounds %d < %d < %d' % \
930 (cls
._min
, value
, cls
._max
)
933 _convert
= classmethod(_convert
)
935 def _string(cls
, value
):
937 _string
= classmethod(_string
)
939 class CheckedInt(type):
940 def __new__(cls
, name
, min, max):
941 # New class derives from _CheckedInt base with proper bounding
943 dict = { '_name' : name
, '_min' : min, '_max' : max }
944 return type.__new
__(cls
, name
, (_CheckedInt
, ), dict)
946 class CheckedIntType(CheckedInt
):
947 def __new__(cls
, name
, size
, unsigned
):
953 min = -(2 ** (size
- 1))
954 max = (2 ** (size
- 1)) - 1
956 return super(cls
, CheckedIntType
).__new
__(cls
, name
, min, max)
958 Int
= CheckedIntType('int', 32, False)
959 Unsigned
= CheckedIntType('unsigned', 32, True)
961 Int8
= CheckedIntType('int8_t', 8, False)
962 UInt8
= CheckedIntType('uint8_t', 8, True)
963 Int16
= CheckedIntType('int16_t', 16, False)
964 UInt16
= CheckedIntType('uint16_t', 16, True)
965 Int32
= CheckedIntType('int32_t', 32, False)
966 UInt32
= CheckedIntType('uint32_t', 32, True)
967 Int64
= CheckedIntType('int64_t', 64, False)
968 UInt64
= CheckedIntType('uint64_t', 64, True)
970 Counter
= CheckedIntType('Counter', 64, True)
971 Addr
= CheckedIntType('Addr', 64, True)
972 Tick
= CheckedIntType('Tick', 64, True)
974 Percent
= CheckedInt('int', 0, 100)
977 def __init__(self
, first
, second
):
981 class _Range(object):
982 def _convert(cls
, value
):
983 if not isinstance(value
, Pair
):
984 raise TypeError, 'value %s is not a Pair' % value
985 return Pair(cls
._type
._convert
(value
.first
),
986 cls
._type
._convert
(value
.second
))
987 _convert
= classmethod(_convert
)
989 def _string(cls
, value
):
990 return '%s:%s' % (cls
._type
._string
(value
.first
),
991 cls
._type
._string
(value
.second
))
992 _string
= classmethod(_string
)
994 def RangeSize(start
, size
):
995 return Pair(start
, start
+ size
- 1)
998 def __new__(cls
, type):
999 dict = { '_name' : 'Range<%s>' + type._name
, '_type' : type }
1000 cname
= 'Range_' + type.__name
__
1001 return super(cls
, Range
).__new
__(cls
, cname
, (_Range
, ), dict)
1003 AddrRange
= Range(Addr
)
1005 # Boolean parameter type.
1008 def _convert(value
):
1013 if t
== int or t
== long:
1018 if v
== "true" or v
== "t" or v
== "yes" or v
== "y":
1020 elif v
== "false" or v
== "f" or v
== "no" or v
== "n":
1023 raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v
, t
)
1024 _convert
= staticmethod(_convert
)
1031 _string
= staticmethod(_string
)
1033 # String-valued parameter.
1034 class String(object):
1037 # Constructor. Value must be Python string.
1038 def _convert(cls
,value
):
1041 if isinstance(value
, str):
1045 "String param got value %s %s" % (repr(value
), type(value
))
1046 _convert
= classmethod(_convert
)
1048 # Generate printable string version. Not too tricky.
1049 def _string(cls
, value
):
1051 _string
= classmethod(_string
)
1054 def IncEthernetAddr(addr
, val
= 1):
1055 bytes
= map(lambda x
: int(x
, 16), addr
.split(':'))
1057 for i
in (5, 4, 3, 2, 1):
1058 val
,rem
= divmod(bytes
[i
], 256)
1063 assert(bytes
[0] <= 255)
1064 return ':'.join(map(lambda x
: '%02x' % x
, bytes
))
1066 class NextEthernetAddr(object):
1067 __metaclass__
= Singleton
1068 addr
= "00:90:00:00:00:01"
1070 def __init__(self
, inc
= 1):
1071 self
.value
= self
.addr
1072 self
.addr
= IncEthernetAddr(self
.addr
, inc
)
1074 class EthernetAddr(object):
1077 def _convert(cls
, value
):
1078 if value
== NextEthernetAddr
:
1081 if not isinstance(value
, str):
1082 raise TypeError, "expected an ethernet address and didn't get one"
1084 bytes
= value
.split(':')
1086 raise TypeError, 'invalid ethernet address %s' % value
1089 if not 0 <= int(byte
) <= 256:
1090 raise TypeError, 'invalid ethernet address %s' % value
1093 _convert
= classmethod(_convert
)
1095 def _string(cls
, value
):
1096 if value
== NextEthernetAddr
:
1097 value
= value().value
1099 _string
= classmethod(_string
)
1101 # Special class for NULL pointers. Note the special check in
1102 # make_param_value() above that lets these be assigned where a
1103 # SimObject is required.
1104 # only one copy of a particular node
1105 class NullSimObject(object):
1106 __metaclass__
= Singleton
1114 _sim_code
= classmethod(_sim_code
)
1116 def _instantiate(self
, parent
= None, path
= ''):
1119 def _convert(cls
, value
):
1123 if isinstance(value
, cls
):
1126 raise TypeError, 'object %s %s of the wrong type, should be %s' % \
1127 (repr(value
), type(value
), cls
)
1128 _convert
= classmethod(_convert
)
1132 _string
= staticmethod(_string
)
1134 # The only instance you'll ever need...
1135 Null
= NULL
= NullSimObject()
1137 # Enumerated types are a little more complex. The user specifies the
1138 # type as Enum(foo) where foo is either a list or dictionary of
1139 # alternatives (typically strings, but not necessarily so). (In the
1140 # long run, the integer value of the parameter will be the list index
1141 # or the corresponding dictionary value. For now, since we only check
1142 # that the alternative is valid and then spit it into a .ini file,
1143 # there's not much point in using the dictionary.)
1145 # What Enum() must do is generate a new type encapsulating the
1146 # provided list/dictionary so that specific values of the parameter
1147 # can be instances of that type. We define two hidden internal
1148 # classes (_ListEnum and _DictEnum) to serve as base classes, then
1149 # derive the new type from the appropriate base class on the fly.
1152 # Base class for Enum types.
1153 class _Enum(object):
1154 def _convert(self
, value
):
1155 if value
not in self
.map:
1156 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1159 _convert
= classmethod(_convert
)
1161 # Generate printable string version of value.
1162 def _string(self
, value
):
1164 _string
= classmethod(_string
)
1166 # Enum metaclass... calling Enum(foo) generates a new type (class)
1167 # that derives from _ListEnum or _DictEnum as appropriate.
1169 # counter to generate unique names for generated classes
1172 def __new__(cls
, *args
):
1178 if isinstance(enum_map
, dict):
1180 elif issequence(enum_map
):
1182 for idx
,val
in enumerate(enum_map
):
1185 raise TypeError, "Enum map must be list or dict (got %s)" % map
1187 classname
= "Enum%04d" % Enum
.counter
1190 # New class derives from _Enum base, and gets a 'map'
1191 # attribute containing the specified list or dict.
1192 return type.__new
__(cls
, classname
, (_Enum
, ), { 'map': map })
1196 # "Constants"... handy aliases for various values.
1199 # For compatibility with C++ bool constants.
1203 # Some memory range specifications use this as a default upper bound.
1204 MAX_ADDR
= Addr
._max
1206 # For power-of-two sizing, e.g. 64*K gives an integer value 65536.
1211 #####################################################################
1213 # Munge an arbitrary Python code string to get it to execute (mostly
1214 # dealing with indentation). Stolen from isa_parser.py... see
1215 # comments there for a more detailed description.
1216 #def fixPythonIndentation(s):
1217 # # get rid of blank lines first
1218 # s = re.sub(r'(?m)^\s*\n', '', s);
1219 # if (s != '' and re.match(r'[ \t]', s[0])):
1223 # Hook to generate C++ parameter code.
1224 def gen_sim_code(file):
1225 for objname
in sim_object_list
:
1226 print >> file, eval("%s._sim_code()" % objname
)
1228 # The final hook to generate .ini files. Called from configuration
1229 # script once config is built.
1230 def instantiate(root
):
1231 if not issubclass(root
, Root
):
1232 raise AttributeError, 'Can only instantiate the Root of the tree'
1234 instance
= root
.instantiate('root')
1238 from objects
import *