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