config.py (2714:baa3445b7b9e) config.py (2738:5d7a31c7fa29)
1# Copyright (c) 2004-2005 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# Authors: Steve Reinhardt
28# Nathan Binkert
29
30import os, re, sys, types, inspect
31
32import m5
33from m5 import panic
34from convert import *
35from multidict import multidict
36
37noDot = False
38try:
39 import pydot
40except:
41 noDot = True
42
43class Singleton(type):
44 def __call__(cls, *args, **kwargs):
45 if hasattr(cls, '_instance'):
46 return cls._instance
47
48 cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
49 return cls._instance
50
51#####################################################################
52#
53# M5 Python Configuration Utility
54#
55# The basic idea is to write simple Python programs that build Python
56# objects corresponding to M5 SimObjects for the desired simulation
57# configuration. For now, the Python emits a .ini file that can be
58# parsed by M5. In the future, some tighter integration between M5
59# and the Python interpreter may allow bypassing the .ini file.
60#
61# Each SimObject class in M5 is represented by a Python class with the
62# same name. The Python inheritance tree mirrors the M5 C++ tree
63# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
64# SimObjects inherit from a single SimObject base class). To specify
65# an instance of an M5 SimObject in a configuration, the user simply
66# instantiates the corresponding Python object. The parameters for
67# that SimObject are given by assigning to attributes of the Python
68# object, either using keyword assignment in the constructor or in
69# separate assignment statements. For example:
70#
71# cache = BaseCache(size='64KB')
72# cache.hit_latency = 3
73# cache.assoc = 8
74#
75# The magic lies in the mapping of the Python attributes for SimObject
76# classes to the actual SimObject parameter specifications. This
77# allows parameter validity checking in the Python code. Continuing
78# the example above, the statements "cache.blurfl=3" or
79# "cache.assoc='hello'" would both result in runtime errors in Python,
80# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
81# parameter requires an integer, respectively. This magic is done
82# primarily by overriding the special __setattr__ method that controls
83# assignment to object attributes.
84#
85# Once a set of Python objects have been instantiated in a hierarchy,
86# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
87# will generate a .ini file. See simple-4cpu.py for an example
88# (corresponding to m5-test/simple-4cpu.ini).
89#
90#####################################################################
91
92#####################################################################
93#
94# ConfigNode/SimObject classes
95#
96# The Python class hierarchy rooted by ConfigNode (which is the base
97# class of SimObject, which in turn is the base class of all other M5
98# SimObject classes) has special attribute behavior. In general, an
99# object in this hierarchy has three categories of attribute-like
100# things:
101#
102# 1. Regular Python methods and variables. These must start with an
103# underscore to be treated normally.
104#
105# 2. SimObject parameters. These values are stored as normal Python
106# attributes, but all assignments to these attributes are checked
107# against the pre-defined set of parameters stored in the class's
108# _params dictionary. Assignments to attributes that do not
109# correspond to predefined parameters, or that are not of the correct
110# type, incur runtime errors.
111#
112# 3. Hierarchy children. The child nodes of a ConfigNode are stored
113# in the node's _children dictionary, but can be accessed using the
114# Python attribute dot-notation (just as they are printed out by the
115# simulator). Children cannot be created using attribute assigment;
116# they must be added by specifying the parent node in the child's
117# constructor or using the '+=' operator.
118
119# The SimObject parameters are the most complex, for a few reasons.
120# First, both parameter descriptions and parameter values are
121# inherited. Thus parameter description lookup must go up the
122# inheritance chain like normal attribute lookup, but this behavior
123# must be explicitly coded since the lookup occurs in each class's
124# _params attribute. Second, because parameter values can be set
125# on SimObject classes (to implement default values), the parameter
126# checking behavior must be enforced on class attribute assignments as
127# well as instance attribute assignments. Finally, because we allow
128# class specialization via inheritance (e.g., see the L1Cache class in
129# the simple-4cpu.py example), we must do parameter checking even on
130# class instantiation. To provide all these features, we use a
131# metaclass to define most of the SimObject parameter behavior for
132# this class hierarchy.
133#
134#####################################################################
135
1# Copyright (c) 2004-2005 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# Authors: Steve Reinhardt
28# Nathan Binkert
29
30import os, re, sys, types, inspect
31
32import m5
33from m5 import panic
34from convert import *
35from multidict import multidict
36
37noDot = False
38try:
39 import pydot
40except:
41 noDot = True
42
43class Singleton(type):
44 def __call__(cls, *args, **kwargs):
45 if hasattr(cls, '_instance'):
46 return cls._instance
47
48 cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
49 return cls._instance
50
51#####################################################################
52#
53# M5 Python Configuration Utility
54#
55# The basic idea is to write simple Python programs that build Python
56# objects corresponding to M5 SimObjects for the desired simulation
57# configuration. For now, the Python emits a .ini file that can be
58# parsed by M5. In the future, some tighter integration between M5
59# and the Python interpreter may allow bypassing the .ini file.
60#
61# Each SimObject class in M5 is represented by a Python class with the
62# same name. The Python inheritance tree mirrors the M5 C++ tree
63# (e.g., SimpleCPU derives from BaseCPU in both cases, and all
64# SimObjects inherit from a single SimObject base class). To specify
65# an instance of an M5 SimObject in a configuration, the user simply
66# instantiates the corresponding Python object. The parameters for
67# that SimObject are given by assigning to attributes of the Python
68# object, either using keyword assignment in the constructor or in
69# separate assignment statements. For example:
70#
71# cache = BaseCache(size='64KB')
72# cache.hit_latency = 3
73# cache.assoc = 8
74#
75# The magic lies in the mapping of the Python attributes for SimObject
76# classes to the actual SimObject parameter specifications. This
77# allows parameter validity checking in the Python code. Continuing
78# the example above, the statements "cache.blurfl=3" or
79# "cache.assoc='hello'" would both result in runtime errors in Python,
80# since the BaseCache object has no 'blurfl' parameter and the 'assoc'
81# parameter requires an integer, respectively. This magic is done
82# primarily by overriding the special __setattr__ method that controls
83# assignment to object attributes.
84#
85# Once a set of Python objects have been instantiated in a hierarchy,
86# calling 'instantiate(obj)' (where obj is the root of the hierarchy)
87# will generate a .ini file. See simple-4cpu.py for an example
88# (corresponding to m5-test/simple-4cpu.ini).
89#
90#####################################################################
91
92#####################################################################
93#
94# ConfigNode/SimObject classes
95#
96# The Python class hierarchy rooted by ConfigNode (which is the base
97# class of SimObject, which in turn is the base class of all other M5
98# SimObject classes) has special attribute behavior. In general, an
99# object in this hierarchy has three categories of attribute-like
100# things:
101#
102# 1. Regular Python methods and variables. These must start with an
103# underscore to be treated normally.
104#
105# 2. SimObject parameters. These values are stored as normal Python
106# attributes, but all assignments to these attributes are checked
107# against the pre-defined set of parameters stored in the class's
108# _params dictionary. Assignments to attributes that do not
109# correspond to predefined parameters, or that are not of the correct
110# type, incur runtime errors.
111#
112# 3. Hierarchy children. The child nodes of a ConfigNode are stored
113# in the node's _children dictionary, but can be accessed using the
114# Python attribute dot-notation (just as they are printed out by the
115# simulator). Children cannot be created using attribute assigment;
116# they must be added by specifying the parent node in the child's
117# constructor or using the '+=' operator.
118
119# The SimObject parameters are the most complex, for a few reasons.
120# First, both parameter descriptions and parameter values are
121# inherited. Thus parameter description lookup must go up the
122# inheritance chain like normal attribute lookup, but this behavior
123# must be explicitly coded since the lookup occurs in each class's
124# _params attribute. Second, because parameter values can be set
125# on SimObject classes (to implement default values), the parameter
126# checking behavior must be enforced on class attribute assignments as
127# well as instance attribute assignments. Finally, because we allow
128# class specialization via inheritance (e.g., see the L1Cache class in
129# the simple-4cpu.py example), we must do parameter checking even on
130# class instantiation. To provide all these features, we use a
131# metaclass to define most of the SimObject parameter behavior for
132# this class hierarchy.
133#
134#####################################################################
135
136
137# dict to look up SimObjects based on path
138instanceDict = {}
139
136def isSimObject(value):
137 return isinstance(value, SimObject)
138
139def isSimObjectClass(value):
140 try:
141 return issubclass(value, SimObject)
142 except TypeError:
143 # happens if value is not a class at all
144 return False
145
146def isSimObjectSequence(value):
147 if not isinstance(value, (list, tuple)) or len(value) == 0:
148 return False
149
150 for val in value:
151 if not isNullPointer(val) and not isSimObject(val):
152 return False
153
154 return True
155
156def isSimObjectClassSequence(value):
157 if not isinstance(value, (list, tuple)) or len(value) == 0:
158 return False
159
160 for val in value:
161 if not isNullPointer(val) and not isSimObjectClass(val):
162 return False
163
164 return True
165
166def isSimObjectOrSequence(value):
167 return isSimObject(value) or isSimObjectSequence(value)
168
169def isSimObjectClassOrSequence(value):
170 return isSimObjectClass(value) or isSimObjectClassSequence(value)
171
172def isNullPointer(value):
173 return isinstance(value, NullSimObject)
174
175# Apply method to object.
176# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>)
177def applyMethod(obj, meth, *args, **kwargs):
178 return getattr(obj, meth)(*args, **kwargs)
179
180# If the first argument is an (non-sequence) object, apply the named
181# method with the given arguments. If the first argument is a
182# sequence, apply the method to each element of the sequence (a la
183# 'map').
184def applyOrMap(objOrSeq, meth, *args, **kwargs):
185 if not isinstance(objOrSeq, (list, tuple)):
186 return applyMethod(objOrSeq, meth, *args, **kwargs)
187 else:
188 return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq]
189
190
191# The metaclass for ConfigNode (and thus for everything that derives
192# from ConfigNode, including SimObject). This class controls how new
193# classes that derive from ConfigNode are instantiated, and provides
194# inherited class behavior (just like a class controls how instances
195# of that class are instantiated, and provides inherited instance
196# behavior).
197class MetaSimObject(type):
198 # Attributes that can be set only at initialization time
199 init_keywords = { 'abstract' : types.BooleanType,
200 'type' : types.StringType }
201 # Attributes that can be set any time
202 keywords = { 'check' : types.FunctionType,
140def isSimObject(value):
141 return isinstance(value, SimObject)
142
143def isSimObjectClass(value):
144 try:
145 return issubclass(value, SimObject)
146 except TypeError:
147 # happens if value is not a class at all
148 return False
149
150def isSimObjectSequence(value):
151 if not isinstance(value, (list, tuple)) or len(value) == 0:
152 return False
153
154 for val in value:
155 if not isNullPointer(val) and not isSimObject(val):
156 return False
157
158 return True
159
160def isSimObjectClassSequence(value):
161 if not isinstance(value, (list, tuple)) or len(value) == 0:
162 return False
163
164 for val in value:
165 if not isNullPointer(val) and not isSimObjectClass(val):
166 return False
167
168 return True
169
170def isSimObjectOrSequence(value):
171 return isSimObject(value) or isSimObjectSequence(value)
172
173def isSimObjectClassOrSequence(value):
174 return isSimObjectClass(value) or isSimObjectClassSequence(value)
175
176def isNullPointer(value):
177 return isinstance(value, NullSimObject)
178
179# Apply method to object.
180# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>)
181def applyMethod(obj, meth, *args, **kwargs):
182 return getattr(obj, meth)(*args, **kwargs)
183
184# If the first argument is an (non-sequence) object, apply the named
185# method with the given arguments. If the first argument is a
186# sequence, apply the method to each element of the sequence (a la
187# 'map').
188def applyOrMap(objOrSeq, meth, *args, **kwargs):
189 if not isinstance(objOrSeq, (list, tuple)):
190 return applyMethod(objOrSeq, meth, *args, **kwargs)
191 else:
192 return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq]
193
194
195# The metaclass for ConfigNode (and thus for everything that derives
196# from ConfigNode, including SimObject). This class controls how new
197# classes that derive from ConfigNode are instantiated, and provides
198# inherited class behavior (just like a class controls how instances
199# of that class are instantiated, and provides inherited instance
200# behavior).
201class MetaSimObject(type):
202 # Attributes that can be set only at initialization time
203 init_keywords = { 'abstract' : types.BooleanType,
204 'type' : types.StringType }
205 # Attributes that can be set any time
206 keywords = { 'check' : types.FunctionType,
203 'children' : types.ListType }
207 'children' : types.ListType,
208 'ccObject' : types.ObjectType }
204
205 # __new__ is called before __init__, and is where the statements
206 # in the body of the class definition get loaded into the class's
207 # __dict__. We intercept this to filter out parameter assignments
208 # and only allow "private" attributes to be passed to the base
209 # __new__ (starting with underscore).
210 def __new__(mcls, name, bases, dict):
211 if dict.has_key('_init_dict'):
212 # must have been called from makeSubclass() rather than
213 # via Python class declaration; bypass filtering process.
214 cls_dict = dict
215 else:
216 # Copy "private" attributes (including special methods
217 # such as __new__) to the official dict. Everything else
218 # goes in _init_dict to be filtered in __init__.
219 cls_dict = {}
220 for key,val in dict.items():
221 if key.startswith('_'):
222 cls_dict[key] = val
223 del dict[key]
224 cls_dict['_init_dict'] = dict
225 return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
226
227 # subclass initialization
228 def __init__(cls, name, bases, dict):
229 # calls type.__init__()... I think that's a no-op, but leave
230 # it here just in case it's not.
231 super(MetaSimObject, cls).__init__(name, bases, dict)
232
233 # initialize required attributes
234 cls._params = multidict()
235 cls._values = multidict()
209
210 # __new__ is called before __init__, and is where the statements
211 # in the body of the class definition get loaded into the class's
212 # __dict__. We intercept this to filter out parameter assignments
213 # and only allow "private" attributes to be passed to the base
214 # __new__ (starting with underscore).
215 def __new__(mcls, name, bases, dict):
216 if dict.has_key('_init_dict'):
217 # must have been called from makeSubclass() rather than
218 # via Python class declaration; bypass filtering process.
219 cls_dict = dict
220 else:
221 # Copy "private" attributes (including special methods
222 # such as __new__) to the official dict. Everything else
223 # goes in _init_dict to be filtered in __init__.
224 cls_dict = {}
225 for key,val in dict.items():
226 if key.startswith('_'):
227 cls_dict[key] = val
228 del dict[key]
229 cls_dict['_init_dict'] = dict
230 return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
231
232 # subclass initialization
233 def __init__(cls, name, bases, dict):
234 # calls type.__init__()... I think that's a no-op, but leave
235 # it here just in case it's not.
236 super(MetaSimObject, cls).__init__(name, bases, dict)
237
238 # initialize required attributes
239 cls._params = multidict()
240 cls._values = multidict()
241 cls._ports = multidict()
236 cls._instantiated = False # really instantiated or subclassed
237 cls._anon_subclass_counter = 0
238
239 # We don't support multiple inheritance. If you want to, you
240 # must fix multidict to deal with it properly.
241 if len(bases) > 1:
242 raise TypeError, "SimObjects do not support multiple inheritance"
243
244 base = bases[0]
245
246 # the only time the following is not true is when we define
247 # the SimObject class itself
248 if isinstance(base, MetaSimObject):
249 cls._params.parent = base._params
250 cls._values.parent = base._values
242 cls._instantiated = False # really instantiated or subclassed
243 cls._anon_subclass_counter = 0
244
245 # We don't support multiple inheritance. If you want to, you
246 # must fix multidict to deal with it properly.
247 if len(bases) > 1:
248 raise TypeError, "SimObjects do not support multiple inheritance"
249
250 base = bases[0]
251
252 # the only time the following is not true is when we define
253 # the SimObject class itself
254 if isinstance(base, MetaSimObject):
255 cls._params.parent = base._params
256 cls._values.parent = base._values
257 cls._ports.parent = base._ports
251 base._instantiated = True
252
253 # now process the _init_dict items
254 for key,val in cls._init_dict.items():
255 if isinstance(val, (types.FunctionType, types.TypeType)):
256 type.__setattr__(cls, key, val)
257
258 # param descriptions
259 elif isinstance(val, ParamDesc):
260 cls._new_param(key, val)
261
258 base._instantiated = True
259
260 # now process the _init_dict items
261 for key,val in cls._init_dict.items():
262 if isinstance(val, (types.FunctionType, types.TypeType)):
263 type.__setattr__(cls, key, val)
264
265 # param descriptions
266 elif isinstance(val, ParamDesc):
267 cls._new_param(key, val)
268
269 # port objects
270 elif isinstance(val, Port):
271 cls._ports[key] = val
272
262 # init-time-only keywords
263 elif cls.init_keywords.has_key(key):
264 cls._set_keyword(key, val, cls.init_keywords[key])
265
266 # default: use normal path (ends up in __setattr__)
267 else:
268 setattr(cls, key, val)
269
270 # Pull the deep-copy memoization dict out of the class dict if
271 # it's there...
272 memo = cls.__dict__.get('_memo', {})
273
274 # Handle SimObject values
275 for key,val in cls._values.iteritems():
276 # SimObject instances need to be promoted to classes.
277 # Existing classes should not have any instance values, so
278 # these can only occur at the lowest level dict (the
279 # parameters just being set in this class definition).
280 if isSimObjectOrSequence(val):
281 assert(val == cls._values.local[key])
282 cls._values[key] = applyOrMap(val, 'makeClass', memo)
283 # SimObject classes need to be subclassed so that
284 # parameters that get set at this level only affect this
285 # level and derivatives.
286 elif isSimObjectClassOrSequence(val):
287 assert(not cls._values.local.has_key(key))
288 cls._values[key] = applyOrMap(val, 'makeSubclass', {}, memo)
289
290
291 def _set_keyword(cls, keyword, val, kwtype):
292 if not isinstance(val, kwtype):
293 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
294 (keyword, type(val), kwtype)
295 if isinstance(val, types.FunctionType):
296 val = classmethod(val)
297 type.__setattr__(cls, keyword, val)
298
299 def _new_param(cls, name, value):
300 cls._params[name] = value
301 if hasattr(value, 'default'):
302 setattr(cls, name, value.default)
303
304 # Set attribute (called on foo.attr = value when foo is an
305 # instance of class cls).
306 def __setattr__(cls, attr, value):
307 # normal processing for private attributes
308 if attr.startswith('_'):
309 type.__setattr__(cls, attr, value)
310 return
311
312 if cls.keywords.has_key(attr):
313 cls._set_keyword(attr, value, cls.keywords[attr])
314 return
315
273 # init-time-only keywords
274 elif cls.init_keywords.has_key(key):
275 cls._set_keyword(key, val, cls.init_keywords[key])
276
277 # default: use normal path (ends up in __setattr__)
278 else:
279 setattr(cls, key, val)
280
281 # Pull the deep-copy memoization dict out of the class dict if
282 # it's there...
283 memo = cls.__dict__.get('_memo', {})
284
285 # Handle SimObject values
286 for key,val in cls._values.iteritems():
287 # SimObject instances need to be promoted to classes.
288 # Existing classes should not have any instance values, so
289 # these can only occur at the lowest level dict (the
290 # parameters just being set in this class definition).
291 if isSimObjectOrSequence(val):
292 assert(val == cls._values.local[key])
293 cls._values[key] = applyOrMap(val, 'makeClass', memo)
294 # SimObject classes need to be subclassed so that
295 # parameters that get set at this level only affect this
296 # level and derivatives.
297 elif isSimObjectClassOrSequence(val):
298 assert(not cls._values.local.has_key(key))
299 cls._values[key] = applyOrMap(val, 'makeSubclass', {}, memo)
300
301
302 def _set_keyword(cls, keyword, val, kwtype):
303 if not isinstance(val, kwtype):
304 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
305 (keyword, type(val), kwtype)
306 if isinstance(val, types.FunctionType):
307 val = classmethod(val)
308 type.__setattr__(cls, keyword, val)
309
310 def _new_param(cls, name, value):
311 cls._params[name] = value
312 if hasattr(value, 'default'):
313 setattr(cls, name, value.default)
314
315 # Set attribute (called on foo.attr = value when foo is an
316 # instance of class cls).
317 def __setattr__(cls, attr, value):
318 # normal processing for private attributes
319 if attr.startswith('_'):
320 type.__setattr__(cls, attr, value)
321 return
322
323 if cls.keywords.has_key(attr):
324 cls._set_keyword(attr, value, cls.keywords[attr])
325 return
326
327 if cls._ports.has_key(attr):
328 self._ports[attr].connect(self, attr, value)
329 return
330
316 # must be SimObject param
317 param = cls._params.get(attr, None)
318 if param:
319 # It's ok: set attribute by delegating to 'object' class.
320 if isSimObjectOrSequence(value) and cls._instantiated:
321 raise AttributeError, \
322 "Cannot set SimObject parameter '%s' after\n" \
323 " class %s has been instantiated or subclassed" \
324 % (attr, cls.__name__)
325 try:
326 cls._values[attr] = param.convert(value)
327 except Exception, e:
328 msg = "%s\nError setting param %s.%s to %s\n" % \
329 (e, cls.__name__, attr, value)
330 e.args = (msg, )
331 raise
332 # I would love to get rid of this
333 elif isSimObjectOrSequence(value):
334 cls._values[attr] = value
335 else:
336 raise AttributeError, \
337 "Class %s has no parameter %s" % (cls.__name__, attr)
338
339 def __getattr__(cls, attr):
340 if cls._values.has_key(attr):
341 return cls._values[attr]
342
343 raise AttributeError, \
344 "object '%s' has no attribute '%s'" % (cls.__name__, attr)
345
346 # Create a subclass of this class. Basically a function interface
347 # to the standard Python class definition mechanism, primarily for
348 # internal use. 'memo' dict param supports "deep copy" (really
349 # "deep subclass") operations... within a given operation,
350 # multiple references to a class should result in a single
351 # subclass object with multiple references to it (as opposed to
352 # mutiple unique subclasses).
353 def makeSubclass(cls, init_dict, memo = {}):
354 subcls = memo.get(cls)
355 if not subcls:
356 name = cls.__name__ + '_' + str(cls._anon_subclass_counter)
357 cls._anon_subclass_counter += 1
358 subcls = MetaSimObject(name, (cls,),
359 { '_init_dict': init_dict, '_memo': memo })
360 return subcls
361
362# The ConfigNode class is the root of the special hierarchy. Most of
363# the code in this class deals with the configuration hierarchy itself
364# (parent/child node relationships).
365class SimObject(object):
366 # Specify metaclass. Any class inheriting from SimObject will
367 # get this metaclass.
368 __metaclass__ = MetaSimObject
369
370 # __new__ operator allocates new instances of the class. We
371 # override it here just to support "deep instantiation" operation
372 # via the _memo dict. When recursively instantiating an object
373 # hierarchy we want to make sure that each class is instantiated
374 # only once, and that if there are multiple references to the same
375 # original class, we end up with the corresponding instantiated
376 # references all pointing to the same instance.
377 def __new__(cls, _memo = None, **kwargs):
378 if _memo is not None and _memo.has_key(cls):
379 # return previously instantiated object
380 assert(len(kwargs) == 0)
381 return _memo[cls]
382 else:
383 # Need a new one... if it needs to be memoized, this will
384 # happen in __init__. We defer the insertion until then
385 # so __init__ can use the memo dict to tell whether or not
386 # to perform the initialization.
387 return super(SimObject, cls).__new__(cls, **kwargs)
388
389 # Initialize new instance previously allocated by __new__. For
390 # objects with SimObject-valued params, we need to recursively
391 # instantiate the classes represented by those param values as
392 # well (in a consistent "deep copy"-style fashion; see comment
393 # above).
394 def __init__(self, _memo = None, **kwargs):
395 if _memo is not None:
396 # We're inside a "deep instantiation"
397 assert(isinstance(_memo, dict))
398 assert(len(kwargs) == 0)
399 if _memo.has_key(self.__class__):
400 # __new__ returned an existing, already initialized
401 # instance, so there's nothing to do here
402 assert(_memo[self.__class__] == self)
403 return
404 # no pre-existing object, so remember this one here
405 _memo[self.__class__] = self
406 else:
407 # This is a new top-level instantiation... don't memoize
408 # this objcet, but prepare to memoize any recursively
409 # instantiated objects.
410 _memo = {}
411
412 self.__class__._instantiated = True
413
414 self._children = {}
415 # Inherit parameter values from class using multidict so
416 # individual value settings can be overridden.
417 self._values = multidict(self.__class__._values)
418 # For SimObject-valued parameters, the class should have
419 # classes (not instances) for the values. We need to
420 # instantiate these classes rather than just inheriting the
421 # class object.
422 for key,val in self.__class__._values.iteritems():
423 if isSimObjectClass(val):
424 setattr(self, key, val(_memo))
425 elif isSimObjectClassSequence(val) and len(val):
426 setattr(self, key, [ v(_memo) for v in val ])
427 # apply attribute assignments from keyword args, if any
428 for key,val in kwargs.iteritems():
429 setattr(self, key, val)
430
331 # must be SimObject param
332 param = cls._params.get(attr, None)
333 if param:
334 # It's ok: set attribute by delegating to 'object' class.
335 if isSimObjectOrSequence(value) and cls._instantiated:
336 raise AttributeError, \
337 "Cannot set SimObject parameter '%s' after\n" \
338 " class %s has been instantiated or subclassed" \
339 % (attr, cls.__name__)
340 try:
341 cls._values[attr] = param.convert(value)
342 except Exception, e:
343 msg = "%s\nError setting param %s.%s to %s\n" % \
344 (e, cls.__name__, attr, value)
345 e.args = (msg, )
346 raise
347 # I would love to get rid of this
348 elif isSimObjectOrSequence(value):
349 cls._values[attr] = value
350 else:
351 raise AttributeError, \
352 "Class %s has no parameter %s" % (cls.__name__, attr)
353
354 def __getattr__(cls, attr):
355 if cls._values.has_key(attr):
356 return cls._values[attr]
357
358 raise AttributeError, \
359 "object '%s' has no attribute '%s'" % (cls.__name__, attr)
360
361 # Create a subclass of this class. Basically a function interface
362 # to the standard Python class definition mechanism, primarily for
363 # internal use. 'memo' dict param supports "deep copy" (really
364 # "deep subclass") operations... within a given operation,
365 # multiple references to a class should result in a single
366 # subclass object with multiple references to it (as opposed to
367 # mutiple unique subclasses).
368 def makeSubclass(cls, init_dict, memo = {}):
369 subcls = memo.get(cls)
370 if not subcls:
371 name = cls.__name__ + '_' + str(cls._anon_subclass_counter)
372 cls._anon_subclass_counter += 1
373 subcls = MetaSimObject(name, (cls,),
374 { '_init_dict': init_dict, '_memo': memo })
375 return subcls
376
377# The ConfigNode class is the root of the special hierarchy. Most of
378# the code in this class deals with the configuration hierarchy itself
379# (parent/child node relationships).
380class SimObject(object):
381 # Specify metaclass. Any class inheriting from SimObject will
382 # get this metaclass.
383 __metaclass__ = MetaSimObject
384
385 # __new__ operator allocates new instances of the class. We
386 # override it here just to support "deep instantiation" operation
387 # via the _memo dict. When recursively instantiating an object
388 # hierarchy we want to make sure that each class is instantiated
389 # only once, and that if there are multiple references to the same
390 # original class, we end up with the corresponding instantiated
391 # references all pointing to the same instance.
392 def __new__(cls, _memo = None, **kwargs):
393 if _memo is not None and _memo.has_key(cls):
394 # return previously instantiated object
395 assert(len(kwargs) == 0)
396 return _memo[cls]
397 else:
398 # Need a new one... if it needs to be memoized, this will
399 # happen in __init__. We defer the insertion until then
400 # so __init__ can use the memo dict to tell whether or not
401 # to perform the initialization.
402 return super(SimObject, cls).__new__(cls, **kwargs)
403
404 # Initialize new instance previously allocated by __new__. For
405 # objects with SimObject-valued params, we need to recursively
406 # instantiate the classes represented by those param values as
407 # well (in a consistent "deep copy"-style fashion; see comment
408 # above).
409 def __init__(self, _memo = None, **kwargs):
410 if _memo is not None:
411 # We're inside a "deep instantiation"
412 assert(isinstance(_memo, dict))
413 assert(len(kwargs) == 0)
414 if _memo.has_key(self.__class__):
415 # __new__ returned an existing, already initialized
416 # instance, so there's nothing to do here
417 assert(_memo[self.__class__] == self)
418 return
419 # no pre-existing object, so remember this one here
420 _memo[self.__class__] = self
421 else:
422 # This is a new top-level instantiation... don't memoize
423 # this objcet, but prepare to memoize any recursively
424 # instantiated objects.
425 _memo = {}
426
427 self.__class__._instantiated = True
428
429 self._children = {}
430 # Inherit parameter values from class using multidict so
431 # individual value settings can be overridden.
432 self._values = multidict(self.__class__._values)
433 # For SimObject-valued parameters, the class should have
434 # classes (not instances) for the values. We need to
435 # instantiate these classes rather than just inheriting the
436 # class object.
437 for key,val in self.__class__._values.iteritems():
438 if isSimObjectClass(val):
439 setattr(self, key, val(_memo))
440 elif isSimObjectClassSequence(val) and len(val):
441 setattr(self, key, [ v(_memo) for v in val ])
442 # apply attribute assignments from keyword args, if any
443 for key,val in kwargs.iteritems():
444 setattr(self, key, val)
445
446 self._ccObject = None # pointer to C++ object
447 self._port_map = {} # map of port connections
448
431 # Use this instance as a template to create a new class.
432 def makeClass(self, memo = {}):
433 cls = memo.get(self)
434 if not cls:
435 cls = self.__class__.makeSubclass(self._values.local)
436 memo[self] = cls
437 return cls
438
439 # Direct instantiation of instances (cloning) is no longer
440 # allowed; must generate class from instance first.
441 def __call__(self, **kwargs):
442 raise TypeError, "cannot instantiate SimObject; "\
443 "use makeClass() to make class first"
444
445 def __getattr__(self, attr):
449 # Use this instance as a template to create a new class.
450 def makeClass(self, memo = {}):
451 cls = memo.get(self)
452 if not cls:
453 cls = self.__class__.makeSubclass(self._values.local)
454 memo[self] = cls
455 return cls
456
457 # Direct instantiation of instances (cloning) is no longer
458 # allowed; must generate class from instance first.
459 def __call__(self, **kwargs):
460 raise TypeError, "cannot instantiate SimObject; "\
461 "use makeClass() to make class first"
462
463 def __getattr__(self, attr):
464 if self._ports.has_key(attr):
465 # return reference that can be assigned to another port
466 # via __setattr__
467 return self._ports[attr].makeRef(self, attr)
468
446 if self._values.has_key(attr):
447 return self._values[attr]
448
449 raise AttributeError, "object '%s' has no attribute '%s'" \
450 % (self.__class__.__name__, attr)
451
452 # Set attribute (called on foo.attr = value when foo is an
453 # instance of class cls).
454 def __setattr__(self, attr, value):
455 # normal processing for private attributes
456 if attr.startswith('_'):
457 object.__setattr__(self, attr, value)
458 return
459
469 if self._values.has_key(attr):
470 return self._values[attr]
471
472 raise AttributeError, "object '%s' has no attribute '%s'" \
473 % (self.__class__.__name__, attr)
474
475 # Set attribute (called on foo.attr = value when foo is an
476 # instance of class cls).
477 def __setattr__(self, attr, value):
478 # normal processing for private attributes
479 if attr.startswith('_'):
480 object.__setattr__(self, attr, value)
481 return
482
483 if self._ports.has_key(attr):
484 # set up port connection
485 self._ports[attr].connect(self, attr, value)
486 return
487
460 # must be SimObject param
461 param = self._params.get(attr, None)
462 if param:
463 # It's ok: set attribute by delegating to 'object' class.
464 try:
465 value = param.convert(value)
466 except Exception, e:
467 msg = "%s\nError setting param %s.%s to %s\n" % \
468 (e, self.__class__.__name__, attr, value)
469 e.args = (msg, )
470 raise
471 # I would love to get rid of this
472 elif isSimObjectOrSequence(value):
473 pass
474 else:
475 raise AttributeError, "Class %s has no parameter %s" \
476 % (self.__class__.__name__, attr)
477
478 # clear out old child with this name, if any
479 self.clear_child(attr)
480
481 if isSimObject(value):
482 value.set_path(self, attr)
483 elif isSimObjectSequence(value):
484 value = SimObjVector(value)
485 [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)]
486
487 self._values[attr] = value
488
489 # this hack allows tacking a '[0]' onto parameters that may or may
490 # not be vectors, and always getting the first element (e.g. cpus)
491 def __getitem__(self, key):
492 if key == 0:
493 return self
494 raise TypeError, "Non-zero index '%s' to SimObject" % key
495
496 # clear out children with given name, even if it's a vector
497 def clear_child(self, name):
498 if not self._children.has_key(name):
499 return
500 child = self._children[name]
501 if isinstance(child, SimObjVector):
502 for i in xrange(len(child)):
503 del self._children["s%d" % (name, i)]
504 del self._children[name]
505
506 def add_child(self, name, value):
507 self._children[name] = value
508
509 def set_path(self, parent, name):
510 if not hasattr(self, '_parent'):
511 self._parent = parent
512 self._name = name
513 parent.add_child(name, self)
514
515 def path(self):
516 if not hasattr(self, '_parent'):
517 return 'root'
518 ppath = self._parent.path()
519 if ppath == 'root':
520 return self._name
521 return ppath + "." + self._name
522
523 def __str__(self):
524 return self.path()
525
526 def ini_str(self):
527 return self.path()
528
529 def find_any(self, ptype):
530 if isinstance(self, ptype):
531 return self, True
532
533 found_obj = None
534 for child in self._children.itervalues():
535 if isinstance(child, ptype):
536 if found_obj != None and child != found_obj:
537 raise AttributeError, \
538 'parent.any matched more than one: %s %s' % \
539 (found_obj.path, child.path)
540 found_obj = child
541 # search param space
542 for pname,pdesc in self._params.iteritems():
543 if issubclass(pdesc.ptype, ptype):
544 match_obj = self._values[pname]
545 if found_obj != None and found_obj != match_obj:
546 raise AttributeError, \
547 'parent.any matched more than one: %s' % obj.path
548 found_obj = match_obj
549 return found_obj, found_obj != None
550
551 def unproxy(self, base):
552 return self
553
554 def print_ini(self):
555 print '[' + self.path() + ']' # .ini section header
556
488 # must be SimObject param
489 param = self._params.get(attr, None)
490 if param:
491 # It's ok: set attribute by delegating to 'object' class.
492 try:
493 value = param.convert(value)
494 except Exception, e:
495 msg = "%s\nError setting param %s.%s to %s\n" % \
496 (e, self.__class__.__name__, attr, value)
497 e.args = (msg, )
498 raise
499 # I would love to get rid of this
500 elif isSimObjectOrSequence(value):
501 pass
502 else:
503 raise AttributeError, "Class %s has no parameter %s" \
504 % (self.__class__.__name__, attr)
505
506 # clear out old child with this name, if any
507 self.clear_child(attr)
508
509 if isSimObject(value):
510 value.set_path(self, attr)
511 elif isSimObjectSequence(value):
512 value = SimObjVector(value)
513 [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)]
514
515 self._values[attr] = value
516
517 # this hack allows tacking a '[0]' onto parameters that may or may
518 # not be vectors, and always getting the first element (e.g. cpus)
519 def __getitem__(self, key):
520 if key == 0:
521 return self
522 raise TypeError, "Non-zero index '%s' to SimObject" % key
523
524 # clear out children with given name, even if it's a vector
525 def clear_child(self, name):
526 if not self._children.has_key(name):
527 return
528 child = self._children[name]
529 if isinstance(child, SimObjVector):
530 for i in xrange(len(child)):
531 del self._children["s%d" % (name, i)]
532 del self._children[name]
533
534 def add_child(self, name, value):
535 self._children[name] = value
536
537 def set_path(self, parent, name):
538 if not hasattr(self, '_parent'):
539 self._parent = parent
540 self._name = name
541 parent.add_child(name, self)
542
543 def path(self):
544 if not hasattr(self, '_parent'):
545 return 'root'
546 ppath = self._parent.path()
547 if ppath == 'root':
548 return self._name
549 return ppath + "." + self._name
550
551 def __str__(self):
552 return self.path()
553
554 def ini_str(self):
555 return self.path()
556
557 def find_any(self, ptype):
558 if isinstance(self, ptype):
559 return self, True
560
561 found_obj = None
562 for child in self._children.itervalues():
563 if isinstance(child, ptype):
564 if found_obj != None and child != found_obj:
565 raise AttributeError, \
566 'parent.any matched more than one: %s %s' % \
567 (found_obj.path, child.path)
568 found_obj = child
569 # search param space
570 for pname,pdesc in self._params.iteritems():
571 if issubclass(pdesc.ptype, ptype):
572 match_obj = self._values[pname]
573 if found_obj != None and found_obj != match_obj:
574 raise AttributeError, \
575 'parent.any matched more than one: %s' % obj.path
576 found_obj = match_obj
577 return found_obj, found_obj != None
578
579 def unproxy(self, base):
580 return self
581
582 def print_ini(self):
583 print '[' + self.path() + ']' # .ini section header
584
585 instanceDict[self.path()] = self
586
557 if hasattr(self, 'type') and not isinstance(self, ParamContext):
558 print 'type=%s' % self.type
559
560 child_names = self._children.keys()
561 child_names.sort()
562 np_child_names = [c for c in child_names \
563 if not isinstance(self._children[c], ParamContext)]
564 if len(np_child_names):
565 print 'children=%s' % ' '.join(np_child_names)
566
567 param_names = self._params.keys()
568 param_names.sort()
569 for param in param_names:
570 value = self._values.get(param, None)
571 if value != None:
572 if isproxy(value):
573 try:
574 value = value.unproxy(self)
575 except:
576 print >> sys.stderr, \
577 "Error in unproxying param '%s' of %s" % \
578 (param, self.path())
579 raise
580 setattr(self, param, value)
581 print '%s=%s' % (param, self._values[param].ini_str())
582
583 print # blank line between objects
584
585 for child in child_names:
586 self._children[child].print_ini()
587
587 if hasattr(self, 'type') and not isinstance(self, ParamContext):
588 print 'type=%s' % self.type
589
590 child_names = self._children.keys()
591 child_names.sort()
592 np_child_names = [c for c in child_names \
593 if not isinstance(self._children[c], ParamContext)]
594 if len(np_child_names):
595 print 'children=%s' % ' '.join(np_child_names)
596
597 param_names = self._params.keys()
598 param_names.sort()
599 for param in param_names:
600 value = self._values.get(param, None)
601 if value != None:
602 if isproxy(value):
603 try:
604 value = value.unproxy(self)
605 except:
606 print >> sys.stderr, \
607 "Error in unproxying param '%s' of %s" % \
608 (param, self.path())
609 raise
610 setattr(self, param, value)
611 print '%s=%s' % (param, self._values[param].ini_str())
612
613 print # blank line between objects
614
615 for child in child_names:
616 self._children[child].print_ini()
617
618 # Call C++ to create C++ object corresponding to this object and
619 # (recursively) all its children
620 def createCCObject(self):
621 if self._ccObject:
622 return
623 self._ccObject = -1
624 self._ccObject = m5.main.createSimObject(self.path())
625 for child in self._children.itervalues():
626 child.createCCObject()
627
628 # Create C++ port connections corresponding to the connections in
629 # _port_map (& recursively for all children)
630 def connectPorts(self):
631 for portRef in self._port_map.itervalues():
632 applyOrMap(portRef, 'ccConnect')
633 for child in self._children.itervalues():
634 child.connectPorts()
635
588 # generate output file for 'dot' to display as a pretty graph.
589 # this code is currently broken.
590 def outputDot(self, dot):
591 label = "{%s|" % self.path
592 if isSimObject(self.realtype):
593 label += '%s|' % self.type
594
595 if self.children:
596 # instantiate children in same order they were added for
597 # backward compatibility (else we can end up with cpu1
598 # before cpu0).
599 for c in self.children:
600 dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
601
602 simobjs = []
603 for param in self.params:
604 try:
605 if param.value is None:
606 raise AttributeError, 'Parameter with no value'
607
608 value = param.value
609 string = param.string(value)
610 except Exception, e:
611 msg = 'exception in %s:%s\n%s' % (self.name, param.name, e)
612 e.args = (msg, )
613 raise
614
615 if isSimObject(param.ptype) and string != "Null":
616 simobjs.append(string)
617 else:
618 label += '%s = %s\\n' % (param.name, string)
619
620 for so in simobjs:
621 label += "|<%s> %s" % (so, so)
622 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
623 tailport="w"))
624 label += '}'
625 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
626
627 # recursively dump out children
628 for c in self.children:
629 c.outputDot(dot)
630
631class ParamContext(SimObject):
632 pass
633
634#####################################################################
635#
636# Proxy object support.
637#
638#####################################################################
639
640class BaseProxy(object):
641 def __init__(self, search_self, search_up):
642 self._search_self = search_self
643 self._search_up = search_up
644 self._multiplier = None
645
646 def __setattr__(self, attr, value):
647 if not attr.startswith('_'):
648 raise AttributeError, 'cannot set attribute on proxy object'
649 super(BaseProxy, self).__setattr__(attr, value)
650
651 # support multiplying proxies by constants
652 def __mul__(self, other):
653 if not isinstance(other, (int, long, float)):
654 raise TypeError, "Proxy multiplier must be integer"
655 if self._multiplier == None:
656 self._multiplier = other
657 else:
658 # support chained multipliers
659 self._multiplier *= other
660 return self
661
662 __rmul__ = __mul__
663
664 def _mulcheck(self, result):
665 if self._multiplier == None:
666 return result
667 return result * self._multiplier
668
669 def unproxy(self, base):
670 obj = base
671 done = False
672
673 if self._search_self:
674 result, done = self.find(obj)
675
676 if self._search_up:
677 while not done:
678 try: obj = obj._parent
679 except: break
680
681 result, done = self.find(obj)
682
683 if not done:
684 raise AttributeError, "Can't resolve proxy '%s' from '%s'" % \
685 (self.path(), base.path())
686
687 if isinstance(result, BaseProxy):
688 if result == self:
689 raise RuntimeError, "Cycle in unproxy"
690 result = result.unproxy(obj)
691
692 return self._mulcheck(result)
693
694 def getindex(obj, index):
695 if index == None:
696 return obj
697 try:
698 obj = obj[index]
699 except TypeError:
700 if index != 0:
701 raise
702 # if index is 0 and item is not subscriptable, just
703 # use item itself (so cpu[0] works on uniprocessors)
704 return obj
705 getindex = staticmethod(getindex)
706
707 def set_param_desc(self, pdesc):
708 self._pdesc = pdesc
709
710class AttrProxy(BaseProxy):
711 def __init__(self, search_self, search_up, attr):
712 super(AttrProxy, self).__init__(search_self, search_up)
713 self._attr = attr
714 self._modifiers = []
715
716 def __getattr__(self, attr):
717 # python uses __bases__ internally for inheritance
718 if attr.startswith('_'):
719 return super(AttrProxy, self).__getattr__(self, attr)
720 if hasattr(self, '_pdesc'):
721 raise AttributeError, "Attribute reference on bound proxy"
722 self._modifiers.append(attr)
723 return self
724
725 # support indexing on proxies (e.g., Self.cpu[0])
726 def __getitem__(self, key):
727 if not isinstance(key, int):
728 raise TypeError, "Proxy object requires integer index"
729 self._modifiers.append(key)
730 return self
731
732 def find(self, obj):
733 try:
734 val = getattr(obj, self._attr)
735 except:
736 return None, False
737 while isproxy(val):
738 val = val.unproxy(obj)
739 for m in self._modifiers:
740 if isinstance(m, str):
741 val = getattr(val, m)
742 elif isinstance(m, int):
743 val = val[m]
744 else:
745 assert("Item must be string or integer")
746 while isproxy(val):
747 val = val.unproxy(obj)
748 return val, True
749
750 def path(self):
751 p = self._attr
752 for m in self._modifiers:
753 if isinstance(m, str):
754 p += '.%s' % m
755 elif isinstance(m, int):
756 p += '[%d]' % m
757 else:
758 assert("Item must be string or integer")
759 return p
760
761class AnyProxy(BaseProxy):
762 def find(self, obj):
763 return obj.find_any(self._pdesc.ptype)
764
765 def path(self):
766 return 'any'
767
768def isproxy(obj):
769 if isinstance(obj, (BaseProxy, EthernetAddr)):
770 return True
771 elif isinstance(obj, (list, tuple)):
772 for v in obj:
773 if isproxy(v):
774 return True
775 return False
776
777class ProxyFactory(object):
778 def __init__(self, search_self, search_up):
779 self.search_self = search_self
780 self.search_up = search_up
781
782 def __getattr__(self, attr):
783 if attr == 'any':
784 return AnyProxy(self.search_self, self.search_up)
785 else:
786 return AttrProxy(self.search_self, self.search_up, attr)
787
788# global objects for handling proxies
789Parent = ProxyFactory(search_self = False, search_up = True)
790Self = ProxyFactory(search_self = True, search_up = False)
791
792#####################################################################
793#
794# Parameter description classes
795#
796# The _params dictionary in each class maps parameter names to
797# either a Param or a VectorParam object. These objects contain the
798# parameter description string, the parameter type, and the default
799# value (loaded from the PARAM section of the .odesc files). The
800# _convert() method on these objects is used to force whatever value
801# is assigned to the parameter to the appropriate type.
802#
803# Note that the default values are loaded into the class's attribute
804# space when the parameter dictionary is initialized (in
805# MetaConfigNode._setparams()); after that point they aren't used.
806#
807#####################################################################
808
809# Dummy base class to identify types that are legitimate for SimObject
810# parameters.
811class ParamValue(object):
812
813 # default for printing to .ini file is regular string conversion.
814 # will be overridden in some cases
815 def ini_str(self):
816 return str(self)
817
818 # allows us to blithely call unproxy() on things without checking
819 # if they're really proxies or not
820 def unproxy(self, base):
821 return self
822
823# Regular parameter description.
824class ParamDesc(object):
825 def __init__(self, ptype_str, ptype, *args, **kwargs):
826 self.ptype_str = ptype_str
827 # remember ptype only if it is provided
828 if ptype != None:
829 self.ptype = ptype
830
831 if args:
832 if len(args) == 1:
833 self.desc = args[0]
834 elif len(args) == 2:
835 self.default = args[0]
836 self.desc = args[1]
837 else:
838 raise TypeError, 'too many arguments'
839
840 if kwargs.has_key('desc'):
841 assert(not hasattr(self, 'desc'))
842 self.desc = kwargs['desc']
843 del kwargs['desc']
844
845 if kwargs.has_key('default'):
846 assert(not hasattr(self, 'default'))
847 self.default = kwargs['default']
848 del kwargs['default']
849
850 if kwargs:
851 raise TypeError, 'extra unknown kwargs %s' % kwargs
852
853 if not hasattr(self, 'desc'):
854 raise TypeError, 'desc attribute missing'
855
856 def __getattr__(self, attr):
857 if attr == 'ptype':
858 try:
859 ptype = eval(self.ptype_str, m5.objects.__dict__)
860 if not isinstance(ptype, type):
861 panic("Param qualifier is not a type: %s" % self.ptype)
862 self.ptype = ptype
863 return ptype
864 except NameError:
865 pass
866 raise AttributeError, "'%s' object has no attribute '%s'" % \
867 (type(self).__name__, attr)
868
869 def convert(self, value):
870 if isinstance(value, BaseProxy):
871 value.set_param_desc(self)
872 return value
873 if not hasattr(self, 'ptype') and isNullPointer(value):
874 # deferred evaluation of SimObject; continue to defer if
875 # we're just assigning a null pointer
876 return value
877 if isinstance(value, self.ptype):
878 return value
879 if isNullPointer(value) and issubclass(self.ptype, SimObject):
880 return value
881 return self.ptype(value)
882
883# Vector-valued parameter description. Just like ParamDesc, except
884# that the value is a vector (list) of the specified type instead of a
885# single value.
886
887class VectorParamValue(list):
888 def ini_str(self):
889 return ' '.join([v.ini_str() for v in self])
890
891 def unproxy(self, base):
892 return [v.unproxy(base) for v in self]
893
894class SimObjVector(VectorParamValue):
895 def print_ini(self):
896 for v in self:
897 v.print_ini()
898
899class VectorParamDesc(ParamDesc):
900 # Convert assigned value to appropriate type. If the RHS is not a
901 # list or tuple, it generates a single-element list.
902 def convert(self, value):
903 if isinstance(value, (list, tuple)):
904 # list: coerce each element into new list
905 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
906 if isSimObjectSequence(tmp_list):
907 return SimObjVector(tmp_list)
908 else:
909 return VectorParamValue(tmp_list)
910 else:
911 # singleton: leave it be (could coerce to a single-element
912 # list here, but for some historical reason we don't...
913 return ParamDesc.convert(self, value)
914
915
916class ParamFactory(object):
917 def __init__(self, param_desc_class, ptype_str = None):
918 self.param_desc_class = param_desc_class
919 self.ptype_str = ptype_str
920
921 def __getattr__(self, attr):
922 if self.ptype_str:
923 attr = self.ptype_str + '.' + attr
924 return ParamFactory(self.param_desc_class, attr)
925
926 # E.g., Param.Int(5, "number of widgets")
927 def __call__(self, *args, **kwargs):
928 caller_frame = inspect.currentframe().f_back
929 ptype = None
930 try:
931 ptype = eval(self.ptype_str,
932 caller_frame.f_globals, caller_frame.f_locals)
933 if not isinstance(ptype, type):
934 raise TypeError, \
935 "Param qualifier is not a type: %s" % ptype
936 except NameError:
937 # if name isn't defined yet, assume it's a SimObject, and
938 # try to resolve it later
939 pass
940 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
941
942Param = ParamFactory(ParamDesc)
943VectorParam = ParamFactory(VectorParamDesc)
944
945#####################################################################
946#
947# Parameter Types
948#
949# Though native Python types could be used to specify parameter types
950# (the 'ptype' field of the Param and VectorParam classes), it's more
951# flexible to define our own set of types. This gives us more control
952# over how Python expressions are converted to values (via the
953# __init__() constructor) and how these values are printed out (via
954# the __str__() conversion method). Eventually we'll need these types
955# to correspond to distinct C++ types as well.
956#
957#####################################################################
958
959# superclass for "numeric" parameter values, to emulate math
960# operations in a type-safe way. e.g., a Latency times an int returns
961# a new Latency object.
962class NumericParamValue(ParamValue):
963 def __str__(self):
964 return str(self.value)
965
966 def __float__(self):
967 return float(self.value)
968
969 # hook for bounds checking
970 def _check(self):
971 return
972
973 def __mul__(self, other):
974 newobj = self.__class__(self)
975 newobj.value *= other
976 newobj._check()
977 return newobj
978
979 __rmul__ = __mul__
980
981 def __div__(self, other):
982 newobj = self.__class__(self)
983 newobj.value /= other
984 newobj._check()
985 return newobj
986
987 def __sub__(self, other):
988 newobj = self.__class__(self)
989 newobj.value -= other
990 newobj._check()
991 return newobj
992
993class Range(ParamValue):
994 type = int # default; can be overridden in subclasses
995 def __init__(self, *args, **kwargs):
996
997 def handle_kwargs(self, kwargs):
998 if 'end' in kwargs:
999 self.second = self.type(kwargs.pop('end'))
1000 elif 'size' in kwargs:
1001 self.second = self.first + self.type(kwargs.pop('size')) - 1
1002 else:
1003 raise TypeError, "Either end or size must be specified"
1004
1005 if len(args) == 0:
1006 self.first = self.type(kwargs.pop('start'))
1007 handle_kwargs(self, kwargs)
1008
1009 elif len(args) == 1:
1010 if kwargs:
1011 self.first = self.type(args[0])
1012 handle_kwargs(self, kwargs)
1013 elif isinstance(args[0], Range):
1014 self.first = self.type(args[0].first)
1015 self.second = self.type(args[0].second)
1016 else:
1017 self.first = self.type(0)
1018 self.second = self.type(args[0]) - 1
1019
1020 elif len(args) == 2:
1021 self.first = self.type(args[0])
1022 self.second = self.type(args[1])
1023 else:
1024 raise TypeError, "Too many arguments specified"
1025
1026 if kwargs:
1027 raise TypeError, "too many keywords: %s" % kwargs.keys()
1028
1029 def __str__(self):
1030 return '%s:%s' % (self.first, self.second)
1031
1032# Metaclass for bounds-checked integer parameters. See CheckedInt.
1033class CheckedIntType(type):
1034 def __init__(cls, name, bases, dict):
1035 super(CheckedIntType, cls).__init__(name, bases, dict)
1036
1037 # CheckedInt is an abstract base class, so we actually don't
1038 # want to do any processing on it... the rest of this code is
1039 # just for classes that derive from CheckedInt.
1040 if name == 'CheckedInt':
1041 return
1042
1043 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1044 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1045 panic("CheckedInt subclass %s must define either\n" \
1046 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1047 % name);
1048 if cls.unsigned:
1049 cls.min = 0
1050 cls.max = 2 ** cls.size - 1
1051 else:
1052 cls.min = -(2 ** (cls.size - 1))
1053 cls.max = (2 ** (cls.size - 1)) - 1
1054
1055# Abstract superclass for bounds-checked integer parameters. This
1056# class is subclassed to generate parameter classes with specific
1057# bounds. Initialization of the min and max bounds is done in the
1058# metaclass CheckedIntType.__init__.
1059class CheckedInt(NumericParamValue):
1060 __metaclass__ = CheckedIntType
1061
1062 def _check(self):
1063 if not self.min <= self.value <= self.max:
1064 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1065 (self.min, self.value, self.max)
1066
1067 def __init__(self, value):
1068 if isinstance(value, str):
1069 self.value = toInteger(value)
1070 elif isinstance(value, (int, long, float)):
1071 self.value = long(value)
1072 self._check()
1073
1074class Int(CheckedInt): size = 32; unsigned = False
1075class Unsigned(CheckedInt): size = 32; unsigned = True
1076
1077class Int8(CheckedInt): size = 8; unsigned = False
1078class UInt8(CheckedInt): size = 8; unsigned = True
1079class Int16(CheckedInt): size = 16; unsigned = False
1080class UInt16(CheckedInt): size = 16; unsigned = True
1081class Int32(CheckedInt): size = 32; unsigned = False
1082class UInt32(CheckedInt): size = 32; unsigned = True
1083class Int64(CheckedInt): size = 64; unsigned = False
1084class UInt64(CheckedInt): size = 64; unsigned = True
1085
1086class Counter(CheckedInt): size = 64; unsigned = True
1087class Tick(CheckedInt): size = 64; unsigned = True
1088class TcpPort(CheckedInt): size = 16; unsigned = True
1089class UdpPort(CheckedInt): size = 16; unsigned = True
1090
1091class Percent(CheckedInt): min = 0; max = 100
1092
1093class Float(ParamValue, float):
1094 pass
1095
1096class MemorySize(CheckedInt):
1097 size = 64
1098 unsigned = True
1099 def __init__(self, value):
1100 if isinstance(value, MemorySize):
1101 self.value = value.value
1102 else:
1103 self.value = toMemorySize(value)
1104 self._check()
1105
1106class MemorySize32(CheckedInt):
1107 size = 32
1108 unsigned = True
1109 def __init__(self, value):
1110 if isinstance(value, MemorySize):
1111 self.value = value.value
1112 else:
1113 self.value = toMemorySize(value)
1114 self._check()
1115
1116class Addr(CheckedInt):
1117 size = 64
1118 unsigned = True
1119 def __init__(self, value):
1120 if isinstance(value, Addr):
1121 self.value = value.value
1122 else:
1123 try:
1124 self.value = toMemorySize(value)
1125 except TypeError:
1126 self.value = long(value)
1127 self._check()
1128
1129class AddrRange(Range):
1130 type = Addr
1131
1132# String-valued parameter. Just mixin the ParamValue class
1133# with the built-in str class.
1134class String(ParamValue,str):
1135 pass
1136
1137# Boolean parameter type. Python doesn't let you subclass bool, since
1138# it doesn't want to let you create multiple instances of True and
1139# False. Thus this is a little more complicated than String.
1140class Bool(ParamValue):
1141 def __init__(self, value):
1142 try:
1143 self.value = toBool(value)
1144 except TypeError:
1145 self.value = bool(value)
1146
1147 def __str__(self):
1148 return str(self.value)
1149
1150 def ini_str(self):
1151 if self.value:
1152 return 'true'
1153 return 'false'
1154
1155def IncEthernetAddr(addr, val = 1):
1156 bytes = map(lambda x: int(x, 16), addr.split(':'))
1157 bytes[5] += val
1158 for i in (5, 4, 3, 2, 1):
1159 val,rem = divmod(bytes[i], 256)
1160 bytes[i] = rem
1161 if val == 0:
1162 break
1163 bytes[i - 1] += val
1164 assert(bytes[0] <= 255)
1165 return ':'.join(map(lambda x: '%02x' % x, bytes))
1166
1167class NextEthernetAddr(object):
1168 addr = "00:90:00:00:00:01"
1169
1170 def __init__(self, inc = 1):
1171 self.value = NextEthernetAddr.addr
1172 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
1173
1174class EthernetAddr(ParamValue):
1175 def __init__(self, value):
1176 if value == NextEthernetAddr:
1177 self.value = value
1178 return
1179
1180 if not isinstance(value, str):
1181 raise TypeError, "expected an ethernet address and didn't get one"
1182
1183 bytes = value.split(':')
1184 if len(bytes) != 6:
1185 raise TypeError, 'invalid ethernet address %s' % value
1186
1187 for byte in bytes:
1188 if not 0 <= int(byte) <= 256:
1189 raise TypeError, 'invalid ethernet address %s' % value
1190
1191 self.value = value
1192
1193 def unproxy(self, base):
1194 if self.value == NextEthernetAddr:
1195 self.addr = self.value().value
1196 return self
1197
1198 def __str__(self):
1199 if self.value == NextEthernetAddr:
1200 if hasattr(self, 'addr'):
1201 return self.addr
1202 else:
1203 return "NextEthernetAddr (unresolved)"
1204 else:
1205 return self.value
1206
1207# Special class for NULL pointers. Note the special check in
1208# make_param_value() above that lets these be assigned where a
1209# SimObject is required.
1210# only one copy of a particular node
1211class NullSimObject(object):
1212 __metaclass__ = Singleton
1213
1214 def __call__(cls):
1215 return cls
1216
1217 def _instantiate(self, parent = None, path = ''):
1218 pass
1219
1220 def ini_str(self):
1221 return 'Null'
1222
1223 def unproxy(self, base):
1224 return self
1225
1226 def set_path(self, parent, name):
1227 pass
1228 def __str__(self):
1229 return 'Null'
1230
1231# The only instance you'll ever need...
1232Null = NULL = NullSimObject()
1233
1234# Enumerated types are a little more complex. The user specifies the
1235# type as Enum(foo) where foo is either a list or dictionary of
1236# alternatives (typically strings, but not necessarily so). (In the
1237# long run, the integer value of the parameter will be the list index
1238# or the corresponding dictionary value. For now, since we only check
1239# that the alternative is valid and then spit it into a .ini file,
1240# there's not much point in using the dictionary.)
1241
1242# What Enum() must do is generate a new type encapsulating the
1243# provided list/dictionary so that specific values of the parameter
1244# can be instances of that type. We define two hidden internal
1245# classes (_ListEnum and _DictEnum) to serve as base classes, then
1246# derive the new type from the appropriate base class on the fly.
1247
1248
1249# Metaclass for Enum types
1250class MetaEnum(type):
1251 def __init__(cls, name, bases, init_dict):
1252 if init_dict.has_key('map'):
1253 if not isinstance(cls.map, dict):
1254 raise TypeError, "Enum-derived class attribute 'map' " \
1255 "must be of type dict"
1256 # build list of value strings from map
1257 cls.vals = cls.map.keys()
1258 cls.vals.sort()
1259 elif init_dict.has_key('vals'):
1260 if not isinstance(cls.vals, list):
1261 raise TypeError, "Enum-derived class attribute 'vals' " \
1262 "must be of type list"
1263 # build string->value map from vals sequence
1264 cls.map = {}
1265 for idx,val in enumerate(cls.vals):
1266 cls.map[val] = idx
1267 else:
1268 raise TypeError, "Enum-derived class must define "\
1269 "attribute 'map' or 'vals'"
1270
1271 super(MetaEnum, cls).__init__(name, bases, init_dict)
1272
1273 def cpp_declare(cls):
1274 s = 'enum %s {\n ' % cls.__name__
1275 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1276 s += '\n};\n'
1277 return s
1278
1279# Base class for enum types.
1280class Enum(ParamValue):
1281 __metaclass__ = MetaEnum
1282 vals = []
1283
1284 def __init__(self, value):
1285 if value not in self.map:
1286 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1287 % (value, self.vals)
1288 self.value = value
1289
1290 def __str__(self):
1291 return self.value
1292
1293ticks_per_sec = None
1294
1295# how big does a rounding error need to be before we warn about it?
1296frequency_tolerance = 0.001 # 0.1%
1297
1298# convert a floting-point # of ticks to integer, and warn if rounding
1299# discards too much precision
1300def tick_check(float_ticks):
1301 if float_ticks == 0:
1302 return 0
1303 int_ticks = int(round(float_ticks))
1304 err = (float_ticks - int_ticks) / float_ticks
1305 if err > frequency_tolerance:
1306 print >> sys.stderr, "Warning: rounding error > tolerance"
1307 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
1308 #raise ValueError
1309 return int_ticks
1310
1311def getLatency(value):
1312 if isinstance(value, Latency) or isinstance(value, Clock):
1313 return value.value
1314 elif isinstance(value, Frequency) or isinstance(value, RootClock):
1315 return 1 / value.value
1316 elif isinstance(value, str):
1317 try:
1318 return toLatency(value)
1319 except ValueError:
1320 try:
1321 return 1 / toFrequency(value)
1322 except ValueError:
1323 pass # fall through
1324 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
1325
1326
1327class Latency(NumericParamValue):
1328 def __init__(self, value):
1329 self.value = getLatency(value)
1330
1331 def __getattr__(self, attr):
1332 if attr in ('latency', 'period'):
1333 return self
1334 if attr == 'frequency':
1335 return Frequency(self)
1336 raise AttributeError, "Latency object has no attribute '%s'" % attr
1337
1338 # convert latency to ticks
1339 def ini_str(self):
1340 return str(tick_check(self.value * ticks_per_sec))
1341
1342class Frequency(NumericParamValue):
1343 def __init__(self, value):
1344 self.value = 1 / getLatency(value)
1345
1346 def __getattr__(self, attr):
1347 if attr == 'frequency':
1348 return self
1349 if attr in ('latency', 'period'):
1350 return Latency(self)
1351 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1352
1353 # convert frequency to ticks per period
1354 def ini_str(self):
1355 return self.period.ini_str()
1356
1357# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
1358# We can't inherit from Frequency because we don't want it to be directly
1359# assignable to a regular Frequency parameter.
1360class RootClock(ParamValue):
1361 def __init__(self, value):
1362 self.value = 1 / getLatency(value)
1363
1364 def __getattr__(self, attr):
1365 if attr == 'frequency':
1366 return Frequency(self)
1367 if attr in ('latency', 'period'):
1368 return Latency(self)
1369 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1370
1371 def ini_str(self):
1372 return str(tick_check(self.value))
1373
1374# A generic frequency and/or Latency value. Value is stored as a latency,
1375# but to avoid ambiguity this object does not support numeric ops (* or /).
1376# An explicit conversion to a Latency or Frequency must be made first.
1377class Clock(ParamValue):
1378 def __init__(self, value):
1379 self.value = getLatency(value)
1380
1381 def __getattr__(self, attr):
1382 if attr == 'frequency':
1383 return Frequency(self)
1384 if attr in ('latency', 'period'):
1385 return Latency(self)
1386 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1387
1388 def ini_str(self):
1389 return self.period.ini_str()
1390
1391class NetworkBandwidth(float,ParamValue):
1392 def __new__(cls, value):
1393 val = toNetworkBandwidth(value) / 8.0
1394 return super(cls, NetworkBandwidth).__new__(cls, val)
1395
1396 def __str__(self):
1397 return str(self.val)
1398
1399 def ini_str(self):
1400 return '%f' % (ticks_per_sec / float(self))
1401
1402class MemoryBandwidth(float,ParamValue):
1403 def __new__(self, value):
1404 val = toMemoryBandwidth(value)
1405 return super(cls, MemoryBandwidth).__new__(cls, val)
1406
1407 def __str__(self):
1408 return str(self.val)
1409
1410 def ini_str(self):
1411 return '%f' % (ticks_per_sec / float(self))
1412
1413#
1414# "Constants"... handy aliases for various values.
1415#
1416
1417# Some memory range specifications use this as a default upper bound.
1418MaxAddr = Addr.max
1419MaxTick = Tick.max
1420AllMemory = AddrRange(0, MaxAddr)
1421
636 # generate output file for 'dot' to display as a pretty graph.
637 # this code is currently broken.
638 def outputDot(self, dot):
639 label = "{%s|" % self.path
640 if isSimObject(self.realtype):
641 label += '%s|' % self.type
642
643 if self.children:
644 # instantiate children in same order they were added for
645 # backward compatibility (else we can end up with cpu1
646 # before cpu0).
647 for c in self.children:
648 dot.add_edge(pydot.Edge(self.path,c.path, style="bold"))
649
650 simobjs = []
651 for param in self.params:
652 try:
653 if param.value is None:
654 raise AttributeError, 'Parameter with no value'
655
656 value = param.value
657 string = param.string(value)
658 except Exception, e:
659 msg = 'exception in %s:%s\n%s' % (self.name, param.name, e)
660 e.args = (msg, )
661 raise
662
663 if isSimObject(param.ptype) and string != "Null":
664 simobjs.append(string)
665 else:
666 label += '%s = %s\\n' % (param.name, string)
667
668 for so in simobjs:
669 label += "|<%s> %s" % (so, so)
670 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so,
671 tailport="w"))
672 label += '}'
673 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label))
674
675 # recursively dump out children
676 for c in self.children:
677 c.outputDot(dot)
678
679class ParamContext(SimObject):
680 pass
681
682#####################################################################
683#
684# Proxy object support.
685#
686#####################################################################
687
688class BaseProxy(object):
689 def __init__(self, search_self, search_up):
690 self._search_self = search_self
691 self._search_up = search_up
692 self._multiplier = None
693
694 def __setattr__(self, attr, value):
695 if not attr.startswith('_'):
696 raise AttributeError, 'cannot set attribute on proxy object'
697 super(BaseProxy, self).__setattr__(attr, value)
698
699 # support multiplying proxies by constants
700 def __mul__(self, other):
701 if not isinstance(other, (int, long, float)):
702 raise TypeError, "Proxy multiplier must be integer"
703 if self._multiplier == None:
704 self._multiplier = other
705 else:
706 # support chained multipliers
707 self._multiplier *= other
708 return self
709
710 __rmul__ = __mul__
711
712 def _mulcheck(self, result):
713 if self._multiplier == None:
714 return result
715 return result * self._multiplier
716
717 def unproxy(self, base):
718 obj = base
719 done = False
720
721 if self._search_self:
722 result, done = self.find(obj)
723
724 if self._search_up:
725 while not done:
726 try: obj = obj._parent
727 except: break
728
729 result, done = self.find(obj)
730
731 if not done:
732 raise AttributeError, "Can't resolve proxy '%s' from '%s'" % \
733 (self.path(), base.path())
734
735 if isinstance(result, BaseProxy):
736 if result == self:
737 raise RuntimeError, "Cycle in unproxy"
738 result = result.unproxy(obj)
739
740 return self._mulcheck(result)
741
742 def getindex(obj, index):
743 if index == None:
744 return obj
745 try:
746 obj = obj[index]
747 except TypeError:
748 if index != 0:
749 raise
750 # if index is 0 and item is not subscriptable, just
751 # use item itself (so cpu[0] works on uniprocessors)
752 return obj
753 getindex = staticmethod(getindex)
754
755 def set_param_desc(self, pdesc):
756 self._pdesc = pdesc
757
758class AttrProxy(BaseProxy):
759 def __init__(self, search_self, search_up, attr):
760 super(AttrProxy, self).__init__(search_self, search_up)
761 self._attr = attr
762 self._modifiers = []
763
764 def __getattr__(self, attr):
765 # python uses __bases__ internally for inheritance
766 if attr.startswith('_'):
767 return super(AttrProxy, self).__getattr__(self, attr)
768 if hasattr(self, '_pdesc'):
769 raise AttributeError, "Attribute reference on bound proxy"
770 self._modifiers.append(attr)
771 return self
772
773 # support indexing on proxies (e.g., Self.cpu[0])
774 def __getitem__(self, key):
775 if not isinstance(key, int):
776 raise TypeError, "Proxy object requires integer index"
777 self._modifiers.append(key)
778 return self
779
780 def find(self, obj):
781 try:
782 val = getattr(obj, self._attr)
783 except:
784 return None, False
785 while isproxy(val):
786 val = val.unproxy(obj)
787 for m in self._modifiers:
788 if isinstance(m, str):
789 val = getattr(val, m)
790 elif isinstance(m, int):
791 val = val[m]
792 else:
793 assert("Item must be string or integer")
794 while isproxy(val):
795 val = val.unproxy(obj)
796 return val, True
797
798 def path(self):
799 p = self._attr
800 for m in self._modifiers:
801 if isinstance(m, str):
802 p += '.%s' % m
803 elif isinstance(m, int):
804 p += '[%d]' % m
805 else:
806 assert("Item must be string or integer")
807 return p
808
809class AnyProxy(BaseProxy):
810 def find(self, obj):
811 return obj.find_any(self._pdesc.ptype)
812
813 def path(self):
814 return 'any'
815
816def isproxy(obj):
817 if isinstance(obj, (BaseProxy, EthernetAddr)):
818 return True
819 elif isinstance(obj, (list, tuple)):
820 for v in obj:
821 if isproxy(v):
822 return True
823 return False
824
825class ProxyFactory(object):
826 def __init__(self, search_self, search_up):
827 self.search_self = search_self
828 self.search_up = search_up
829
830 def __getattr__(self, attr):
831 if attr == 'any':
832 return AnyProxy(self.search_self, self.search_up)
833 else:
834 return AttrProxy(self.search_self, self.search_up, attr)
835
836# global objects for handling proxies
837Parent = ProxyFactory(search_self = False, search_up = True)
838Self = ProxyFactory(search_self = True, search_up = False)
839
840#####################################################################
841#
842# Parameter description classes
843#
844# The _params dictionary in each class maps parameter names to
845# either a Param or a VectorParam object. These objects contain the
846# parameter description string, the parameter type, and the default
847# value (loaded from the PARAM section of the .odesc files). The
848# _convert() method on these objects is used to force whatever value
849# is assigned to the parameter to the appropriate type.
850#
851# Note that the default values are loaded into the class's attribute
852# space when the parameter dictionary is initialized (in
853# MetaConfigNode._setparams()); after that point they aren't used.
854#
855#####################################################################
856
857# Dummy base class to identify types that are legitimate for SimObject
858# parameters.
859class ParamValue(object):
860
861 # default for printing to .ini file is regular string conversion.
862 # will be overridden in some cases
863 def ini_str(self):
864 return str(self)
865
866 # allows us to blithely call unproxy() on things without checking
867 # if they're really proxies or not
868 def unproxy(self, base):
869 return self
870
871# Regular parameter description.
872class ParamDesc(object):
873 def __init__(self, ptype_str, ptype, *args, **kwargs):
874 self.ptype_str = ptype_str
875 # remember ptype only if it is provided
876 if ptype != None:
877 self.ptype = ptype
878
879 if args:
880 if len(args) == 1:
881 self.desc = args[0]
882 elif len(args) == 2:
883 self.default = args[0]
884 self.desc = args[1]
885 else:
886 raise TypeError, 'too many arguments'
887
888 if kwargs.has_key('desc'):
889 assert(not hasattr(self, 'desc'))
890 self.desc = kwargs['desc']
891 del kwargs['desc']
892
893 if kwargs.has_key('default'):
894 assert(not hasattr(self, 'default'))
895 self.default = kwargs['default']
896 del kwargs['default']
897
898 if kwargs:
899 raise TypeError, 'extra unknown kwargs %s' % kwargs
900
901 if not hasattr(self, 'desc'):
902 raise TypeError, 'desc attribute missing'
903
904 def __getattr__(self, attr):
905 if attr == 'ptype':
906 try:
907 ptype = eval(self.ptype_str, m5.objects.__dict__)
908 if not isinstance(ptype, type):
909 panic("Param qualifier is not a type: %s" % self.ptype)
910 self.ptype = ptype
911 return ptype
912 except NameError:
913 pass
914 raise AttributeError, "'%s' object has no attribute '%s'" % \
915 (type(self).__name__, attr)
916
917 def convert(self, value):
918 if isinstance(value, BaseProxy):
919 value.set_param_desc(self)
920 return value
921 if not hasattr(self, 'ptype') and isNullPointer(value):
922 # deferred evaluation of SimObject; continue to defer if
923 # we're just assigning a null pointer
924 return value
925 if isinstance(value, self.ptype):
926 return value
927 if isNullPointer(value) and issubclass(self.ptype, SimObject):
928 return value
929 return self.ptype(value)
930
931# Vector-valued parameter description. Just like ParamDesc, except
932# that the value is a vector (list) of the specified type instead of a
933# single value.
934
935class VectorParamValue(list):
936 def ini_str(self):
937 return ' '.join([v.ini_str() for v in self])
938
939 def unproxy(self, base):
940 return [v.unproxy(base) for v in self]
941
942class SimObjVector(VectorParamValue):
943 def print_ini(self):
944 for v in self:
945 v.print_ini()
946
947class VectorParamDesc(ParamDesc):
948 # Convert assigned value to appropriate type. If the RHS is not a
949 # list or tuple, it generates a single-element list.
950 def convert(self, value):
951 if isinstance(value, (list, tuple)):
952 # list: coerce each element into new list
953 tmp_list = [ ParamDesc.convert(self, v) for v in value ]
954 if isSimObjectSequence(tmp_list):
955 return SimObjVector(tmp_list)
956 else:
957 return VectorParamValue(tmp_list)
958 else:
959 # singleton: leave it be (could coerce to a single-element
960 # list here, but for some historical reason we don't...
961 return ParamDesc.convert(self, value)
962
963
964class ParamFactory(object):
965 def __init__(self, param_desc_class, ptype_str = None):
966 self.param_desc_class = param_desc_class
967 self.ptype_str = ptype_str
968
969 def __getattr__(self, attr):
970 if self.ptype_str:
971 attr = self.ptype_str + '.' + attr
972 return ParamFactory(self.param_desc_class, attr)
973
974 # E.g., Param.Int(5, "number of widgets")
975 def __call__(self, *args, **kwargs):
976 caller_frame = inspect.currentframe().f_back
977 ptype = None
978 try:
979 ptype = eval(self.ptype_str,
980 caller_frame.f_globals, caller_frame.f_locals)
981 if not isinstance(ptype, type):
982 raise TypeError, \
983 "Param qualifier is not a type: %s" % ptype
984 except NameError:
985 # if name isn't defined yet, assume it's a SimObject, and
986 # try to resolve it later
987 pass
988 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs)
989
990Param = ParamFactory(ParamDesc)
991VectorParam = ParamFactory(VectorParamDesc)
992
993#####################################################################
994#
995# Parameter Types
996#
997# Though native Python types could be used to specify parameter types
998# (the 'ptype' field of the Param and VectorParam classes), it's more
999# flexible to define our own set of types. This gives us more control
1000# over how Python expressions are converted to values (via the
1001# __init__() constructor) and how these values are printed out (via
1002# the __str__() conversion method). Eventually we'll need these types
1003# to correspond to distinct C++ types as well.
1004#
1005#####################################################################
1006
1007# superclass for "numeric" parameter values, to emulate math
1008# operations in a type-safe way. e.g., a Latency times an int returns
1009# a new Latency object.
1010class NumericParamValue(ParamValue):
1011 def __str__(self):
1012 return str(self.value)
1013
1014 def __float__(self):
1015 return float(self.value)
1016
1017 # hook for bounds checking
1018 def _check(self):
1019 return
1020
1021 def __mul__(self, other):
1022 newobj = self.__class__(self)
1023 newobj.value *= other
1024 newobj._check()
1025 return newobj
1026
1027 __rmul__ = __mul__
1028
1029 def __div__(self, other):
1030 newobj = self.__class__(self)
1031 newobj.value /= other
1032 newobj._check()
1033 return newobj
1034
1035 def __sub__(self, other):
1036 newobj = self.__class__(self)
1037 newobj.value -= other
1038 newobj._check()
1039 return newobj
1040
1041class Range(ParamValue):
1042 type = int # default; can be overridden in subclasses
1043 def __init__(self, *args, **kwargs):
1044
1045 def handle_kwargs(self, kwargs):
1046 if 'end' in kwargs:
1047 self.second = self.type(kwargs.pop('end'))
1048 elif 'size' in kwargs:
1049 self.second = self.first + self.type(kwargs.pop('size')) - 1
1050 else:
1051 raise TypeError, "Either end or size must be specified"
1052
1053 if len(args) == 0:
1054 self.first = self.type(kwargs.pop('start'))
1055 handle_kwargs(self, kwargs)
1056
1057 elif len(args) == 1:
1058 if kwargs:
1059 self.first = self.type(args[0])
1060 handle_kwargs(self, kwargs)
1061 elif isinstance(args[0], Range):
1062 self.first = self.type(args[0].first)
1063 self.second = self.type(args[0].second)
1064 else:
1065 self.first = self.type(0)
1066 self.second = self.type(args[0]) - 1
1067
1068 elif len(args) == 2:
1069 self.first = self.type(args[0])
1070 self.second = self.type(args[1])
1071 else:
1072 raise TypeError, "Too many arguments specified"
1073
1074 if kwargs:
1075 raise TypeError, "too many keywords: %s" % kwargs.keys()
1076
1077 def __str__(self):
1078 return '%s:%s' % (self.first, self.second)
1079
1080# Metaclass for bounds-checked integer parameters. See CheckedInt.
1081class CheckedIntType(type):
1082 def __init__(cls, name, bases, dict):
1083 super(CheckedIntType, cls).__init__(name, bases, dict)
1084
1085 # CheckedInt is an abstract base class, so we actually don't
1086 # want to do any processing on it... the rest of this code is
1087 # just for classes that derive from CheckedInt.
1088 if name == 'CheckedInt':
1089 return
1090
1091 if not (hasattr(cls, 'min') and hasattr(cls, 'max')):
1092 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')):
1093 panic("CheckedInt subclass %s must define either\n" \
1094 " 'min' and 'max' or 'size' and 'unsigned'\n" \
1095 % name);
1096 if cls.unsigned:
1097 cls.min = 0
1098 cls.max = 2 ** cls.size - 1
1099 else:
1100 cls.min = -(2 ** (cls.size - 1))
1101 cls.max = (2 ** (cls.size - 1)) - 1
1102
1103# Abstract superclass for bounds-checked integer parameters. This
1104# class is subclassed to generate parameter classes with specific
1105# bounds. Initialization of the min and max bounds is done in the
1106# metaclass CheckedIntType.__init__.
1107class CheckedInt(NumericParamValue):
1108 __metaclass__ = CheckedIntType
1109
1110 def _check(self):
1111 if not self.min <= self.value <= self.max:
1112 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \
1113 (self.min, self.value, self.max)
1114
1115 def __init__(self, value):
1116 if isinstance(value, str):
1117 self.value = toInteger(value)
1118 elif isinstance(value, (int, long, float)):
1119 self.value = long(value)
1120 self._check()
1121
1122class Int(CheckedInt): size = 32; unsigned = False
1123class Unsigned(CheckedInt): size = 32; unsigned = True
1124
1125class Int8(CheckedInt): size = 8; unsigned = False
1126class UInt8(CheckedInt): size = 8; unsigned = True
1127class Int16(CheckedInt): size = 16; unsigned = False
1128class UInt16(CheckedInt): size = 16; unsigned = True
1129class Int32(CheckedInt): size = 32; unsigned = False
1130class UInt32(CheckedInt): size = 32; unsigned = True
1131class Int64(CheckedInt): size = 64; unsigned = False
1132class UInt64(CheckedInt): size = 64; unsigned = True
1133
1134class Counter(CheckedInt): size = 64; unsigned = True
1135class Tick(CheckedInt): size = 64; unsigned = True
1136class TcpPort(CheckedInt): size = 16; unsigned = True
1137class UdpPort(CheckedInt): size = 16; unsigned = True
1138
1139class Percent(CheckedInt): min = 0; max = 100
1140
1141class Float(ParamValue, float):
1142 pass
1143
1144class MemorySize(CheckedInt):
1145 size = 64
1146 unsigned = True
1147 def __init__(self, value):
1148 if isinstance(value, MemorySize):
1149 self.value = value.value
1150 else:
1151 self.value = toMemorySize(value)
1152 self._check()
1153
1154class MemorySize32(CheckedInt):
1155 size = 32
1156 unsigned = True
1157 def __init__(self, value):
1158 if isinstance(value, MemorySize):
1159 self.value = value.value
1160 else:
1161 self.value = toMemorySize(value)
1162 self._check()
1163
1164class Addr(CheckedInt):
1165 size = 64
1166 unsigned = True
1167 def __init__(self, value):
1168 if isinstance(value, Addr):
1169 self.value = value.value
1170 else:
1171 try:
1172 self.value = toMemorySize(value)
1173 except TypeError:
1174 self.value = long(value)
1175 self._check()
1176
1177class AddrRange(Range):
1178 type = Addr
1179
1180# String-valued parameter. Just mixin the ParamValue class
1181# with the built-in str class.
1182class String(ParamValue,str):
1183 pass
1184
1185# Boolean parameter type. Python doesn't let you subclass bool, since
1186# it doesn't want to let you create multiple instances of True and
1187# False. Thus this is a little more complicated than String.
1188class Bool(ParamValue):
1189 def __init__(self, value):
1190 try:
1191 self.value = toBool(value)
1192 except TypeError:
1193 self.value = bool(value)
1194
1195 def __str__(self):
1196 return str(self.value)
1197
1198 def ini_str(self):
1199 if self.value:
1200 return 'true'
1201 return 'false'
1202
1203def IncEthernetAddr(addr, val = 1):
1204 bytes = map(lambda x: int(x, 16), addr.split(':'))
1205 bytes[5] += val
1206 for i in (5, 4, 3, 2, 1):
1207 val,rem = divmod(bytes[i], 256)
1208 bytes[i] = rem
1209 if val == 0:
1210 break
1211 bytes[i - 1] += val
1212 assert(bytes[0] <= 255)
1213 return ':'.join(map(lambda x: '%02x' % x, bytes))
1214
1215class NextEthernetAddr(object):
1216 addr = "00:90:00:00:00:01"
1217
1218 def __init__(self, inc = 1):
1219 self.value = NextEthernetAddr.addr
1220 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc)
1221
1222class EthernetAddr(ParamValue):
1223 def __init__(self, value):
1224 if value == NextEthernetAddr:
1225 self.value = value
1226 return
1227
1228 if not isinstance(value, str):
1229 raise TypeError, "expected an ethernet address and didn't get one"
1230
1231 bytes = value.split(':')
1232 if len(bytes) != 6:
1233 raise TypeError, 'invalid ethernet address %s' % value
1234
1235 for byte in bytes:
1236 if not 0 <= int(byte) <= 256:
1237 raise TypeError, 'invalid ethernet address %s' % value
1238
1239 self.value = value
1240
1241 def unproxy(self, base):
1242 if self.value == NextEthernetAddr:
1243 self.addr = self.value().value
1244 return self
1245
1246 def __str__(self):
1247 if self.value == NextEthernetAddr:
1248 if hasattr(self, 'addr'):
1249 return self.addr
1250 else:
1251 return "NextEthernetAddr (unresolved)"
1252 else:
1253 return self.value
1254
1255# Special class for NULL pointers. Note the special check in
1256# make_param_value() above that lets these be assigned where a
1257# SimObject is required.
1258# only one copy of a particular node
1259class NullSimObject(object):
1260 __metaclass__ = Singleton
1261
1262 def __call__(cls):
1263 return cls
1264
1265 def _instantiate(self, parent = None, path = ''):
1266 pass
1267
1268 def ini_str(self):
1269 return 'Null'
1270
1271 def unproxy(self, base):
1272 return self
1273
1274 def set_path(self, parent, name):
1275 pass
1276 def __str__(self):
1277 return 'Null'
1278
1279# The only instance you'll ever need...
1280Null = NULL = NullSimObject()
1281
1282# Enumerated types are a little more complex. The user specifies the
1283# type as Enum(foo) where foo is either a list or dictionary of
1284# alternatives (typically strings, but not necessarily so). (In the
1285# long run, the integer value of the parameter will be the list index
1286# or the corresponding dictionary value. For now, since we only check
1287# that the alternative is valid and then spit it into a .ini file,
1288# there's not much point in using the dictionary.)
1289
1290# What Enum() must do is generate a new type encapsulating the
1291# provided list/dictionary so that specific values of the parameter
1292# can be instances of that type. We define two hidden internal
1293# classes (_ListEnum and _DictEnum) to serve as base classes, then
1294# derive the new type from the appropriate base class on the fly.
1295
1296
1297# Metaclass for Enum types
1298class MetaEnum(type):
1299 def __init__(cls, name, bases, init_dict):
1300 if init_dict.has_key('map'):
1301 if not isinstance(cls.map, dict):
1302 raise TypeError, "Enum-derived class attribute 'map' " \
1303 "must be of type dict"
1304 # build list of value strings from map
1305 cls.vals = cls.map.keys()
1306 cls.vals.sort()
1307 elif init_dict.has_key('vals'):
1308 if not isinstance(cls.vals, list):
1309 raise TypeError, "Enum-derived class attribute 'vals' " \
1310 "must be of type list"
1311 # build string->value map from vals sequence
1312 cls.map = {}
1313 for idx,val in enumerate(cls.vals):
1314 cls.map[val] = idx
1315 else:
1316 raise TypeError, "Enum-derived class must define "\
1317 "attribute 'map' or 'vals'"
1318
1319 super(MetaEnum, cls).__init__(name, bases, init_dict)
1320
1321 def cpp_declare(cls):
1322 s = 'enum %s {\n ' % cls.__name__
1323 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals])
1324 s += '\n};\n'
1325 return s
1326
1327# Base class for enum types.
1328class Enum(ParamValue):
1329 __metaclass__ = MetaEnum
1330 vals = []
1331
1332 def __init__(self, value):
1333 if value not in self.map:
1334 raise TypeError, "Enum param got bad value '%s' (not in %s)" \
1335 % (value, self.vals)
1336 self.value = value
1337
1338 def __str__(self):
1339 return self.value
1340
1341ticks_per_sec = None
1342
1343# how big does a rounding error need to be before we warn about it?
1344frequency_tolerance = 0.001 # 0.1%
1345
1346# convert a floting-point # of ticks to integer, and warn if rounding
1347# discards too much precision
1348def tick_check(float_ticks):
1349 if float_ticks == 0:
1350 return 0
1351 int_ticks = int(round(float_ticks))
1352 err = (float_ticks - int_ticks) / float_ticks
1353 if err > frequency_tolerance:
1354 print >> sys.stderr, "Warning: rounding error > tolerance"
1355 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks)
1356 #raise ValueError
1357 return int_ticks
1358
1359def getLatency(value):
1360 if isinstance(value, Latency) or isinstance(value, Clock):
1361 return value.value
1362 elif isinstance(value, Frequency) or isinstance(value, RootClock):
1363 return 1 / value.value
1364 elif isinstance(value, str):
1365 try:
1366 return toLatency(value)
1367 except ValueError:
1368 try:
1369 return 1 / toFrequency(value)
1370 except ValueError:
1371 pass # fall through
1372 raise ValueError, "Invalid Frequency/Latency value '%s'" % value
1373
1374
1375class Latency(NumericParamValue):
1376 def __init__(self, value):
1377 self.value = getLatency(value)
1378
1379 def __getattr__(self, attr):
1380 if attr in ('latency', 'period'):
1381 return self
1382 if attr == 'frequency':
1383 return Frequency(self)
1384 raise AttributeError, "Latency object has no attribute '%s'" % attr
1385
1386 # convert latency to ticks
1387 def ini_str(self):
1388 return str(tick_check(self.value * ticks_per_sec))
1389
1390class Frequency(NumericParamValue):
1391 def __init__(self, value):
1392 self.value = 1 / getLatency(value)
1393
1394 def __getattr__(self, attr):
1395 if attr == 'frequency':
1396 return self
1397 if attr in ('latency', 'period'):
1398 return Latency(self)
1399 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1400
1401 # convert frequency to ticks per period
1402 def ini_str(self):
1403 return self.period.ini_str()
1404
1405# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz).
1406# We can't inherit from Frequency because we don't want it to be directly
1407# assignable to a regular Frequency parameter.
1408class RootClock(ParamValue):
1409 def __init__(self, value):
1410 self.value = 1 / getLatency(value)
1411
1412 def __getattr__(self, attr):
1413 if attr == 'frequency':
1414 return Frequency(self)
1415 if attr in ('latency', 'period'):
1416 return Latency(self)
1417 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1418
1419 def ini_str(self):
1420 return str(tick_check(self.value))
1421
1422# A generic frequency and/or Latency value. Value is stored as a latency,
1423# but to avoid ambiguity this object does not support numeric ops (* or /).
1424# An explicit conversion to a Latency or Frequency must be made first.
1425class Clock(ParamValue):
1426 def __init__(self, value):
1427 self.value = getLatency(value)
1428
1429 def __getattr__(self, attr):
1430 if attr == 'frequency':
1431 return Frequency(self)
1432 if attr in ('latency', 'period'):
1433 return Latency(self)
1434 raise AttributeError, "Frequency object has no attribute '%s'" % attr
1435
1436 def ini_str(self):
1437 return self.period.ini_str()
1438
1439class NetworkBandwidth(float,ParamValue):
1440 def __new__(cls, value):
1441 val = toNetworkBandwidth(value) / 8.0
1442 return super(cls, NetworkBandwidth).__new__(cls, val)
1443
1444 def __str__(self):
1445 return str(self.val)
1446
1447 def ini_str(self):
1448 return '%f' % (ticks_per_sec / float(self))
1449
1450class MemoryBandwidth(float,ParamValue):
1451 def __new__(self, value):
1452 val = toMemoryBandwidth(value)
1453 return super(cls, MemoryBandwidth).__new__(cls, val)
1454
1455 def __str__(self):
1456 return str(self.val)
1457
1458 def ini_str(self):
1459 return '%f' % (ticks_per_sec / float(self))
1460
1461#
1462# "Constants"... handy aliases for various values.
1463#
1464
1465# Some memory range specifications use this as a default upper bound.
1466MaxAddr = Addr.max
1467MaxTick = Tick.max
1468AllMemory = AddrRange(0, MaxAddr)
1469
1470
1422#####################################################################
1471#####################################################################
1472#
1473# Port objects
1474#
1475# Ports are used to interconnect objects in the memory system.
1476#
1477#####################################################################
1423
1478
1479# Port reference: encapsulates a reference to a particular port on a
1480# particular SimObject.
1481class PortRef(object):
1482 def __init__(self, simobj, name, isVec):
1483 self.simobj = simobj
1484 self.name = name
1485 self.index = -1
1486 self.isVec = isVec # is this a vector port?
1487 self.peer = None # not associated with another port yet
1488 self.ccConnected = False # C++ port connection done?
1489
1490 # Set peer port reference. Called via __setattr__ as a result of
1491 # a port assignment, e.g., "obj1.port1 = obj2.port2".
1492 def setPeer(self, other):
1493 if self.isVec:
1494 curMap = self.simobj._port_map.get(self.name, [])
1495 self.index = len(curMap)
1496 curMap.append(other)
1497 else:
1498 curMap = self.simobj._port_map.get(self.name)
1499 if curMap and not self.isVec:
1500 print "warning: overwriting port", self.simobj, self.name
1501 curMap = other
1502 self.simobj._port_map[self.name] = curMap
1503 self.peer = other
1504
1505 # Call C++ to create corresponding port connection between C++ objects
1506 def ccConnect(self):
1507 if self.ccConnected: # already done this
1508 return
1509 peer = self.peer
1510 m5.main.connectPorts(self.simobj._ccObject, self.name, self.index,
1511 peer.simobj._ccObject, peer.name, peer.index)
1512 self.ccConnected = True
1513 peer.ccConnected = True
1514
1515# Port description object. Like a ParamDesc object, this represents a
1516# logical port in the SimObject class, not a particular port on a
1517# SimObject instance. The latter are represented by PortRef objects.
1518class Port(object):
1519 def __init__(self, desc):
1520 self.desc = desc
1521 self.isVec = False
1522
1523 # Generate a PortRef for this port on the given SimObject with the
1524 # given name
1525 def makeRef(self, simobj, name):
1526 return PortRef(simobj, name, self.isVec)
1527
1528 # Connect an instance of this port (on the given SimObject with
1529 # the given name) with the port described by the supplied PortRef
1530 def connect(self, simobj, name, ref):
1531 myRef = self.makeRef(simobj, name)
1532 myRef.setPeer(ref)
1533 ref.setPeer(myRef)
1534
1535# VectorPort description object. Like Port, but represents a vector
1536# of connections (e.g., as on a Bus).
1537class VectorPort(Port):
1538 def __init__(self, desc):
1539 Port.__init__(self, desc)
1540 self.isVec = True
1541
1542#####################################################################
1543
1424# __all__ defines the list of symbols that get exported when
1425# 'from config import *' is invoked. Try to keep this reasonably
1426# short to avoid polluting other namespaces.
1427__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam',
1428 'Parent', 'Self',
1429 'Enum', 'Bool', 'String', 'Float',
1430 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1431 'Int32', 'UInt32', 'Int64', 'UInt64',
1432 'Counter', 'Addr', 'Tick', 'Percent',
1433 'TcpPort', 'UdpPort', 'EthernetAddr',
1434 'MemorySize', 'MemorySize32',
1435 'Latency', 'Frequency', 'RootClock', 'Clock',
1436 'NetworkBandwidth', 'MemoryBandwidth',
1437 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory',
1438 'Null', 'NULL',
1544# __all__ defines the list of symbols that get exported when
1545# 'from config import *' is invoked. Try to keep this reasonably
1546# short to avoid polluting other namespaces.
1547__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam',
1548 'Parent', 'Self',
1549 'Enum', 'Bool', 'String', 'Float',
1550 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16',
1551 'Int32', 'UInt32', 'Int64', 'UInt64',
1552 'Counter', 'Addr', 'Tick', 'Percent',
1553 'TcpPort', 'UdpPort', 'EthernetAddr',
1554 'MemorySize', 'MemorySize32',
1555 'Latency', 'Frequency', 'RootClock', 'Clock',
1556 'NetworkBandwidth', 'MemoryBandwidth',
1557 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory',
1558 'Null', 'NULL',
1439 'NextEthernetAddr']
1559 'NextEthernetAddr',
1560 'Port', 'VectorPort']
1440
1561