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