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