SimObject.py revision 1605
110448Snilay@cs.wisc.edu# Copyright (c) 2004 The Regents of The University of Michigan 210448Snilay@cs.wisc.edu# All rights reserved. 310448Snilay@cs.wisc.edu# 410448Snilay@cs.wisc.edu# Redistribution and use in source and binary forms, with or without 510448Snilay@cs.wisc.edu# modification, are permitted provided that the following conditions are 610448Snilay@cs.wisc.edu# met: redistributions of source code must retain the above copyright 710448Snilay@cs.wisc.edu# notice, this list of conditions and the following disclaimer; 810448Snilay@cs.wisc.edu# redistributions in binary form must reproduce the above copyright 910448Snilay@cs.wisc.edu# notice, this list of conditions and the following disclaimer in the 1010448Snilay@cs.wisc.edu# documentation and/or other materials provided with the distribution; 1110448Snilay@cs.wisc.edu# neither the name of the copyright holders nor the names of its 1210448Snilay@cs.wisc.edu# contributors may be used to endorse or promote products derived from 1310448Snilay@cs.wisc.edu# this software without specific prior written permission. 1410448Snilay@cs.wisc.edu# 1510448Snilay@cs.wisc.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1610448Snilay@cs.wisc.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1710448Snilay@cs.wisc.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1810448Snilay@cs.wisc.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1910448Snilay@cs.wisc.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2010448Snilay@cs.wisc.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2110448Snilay@cs.wisc.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2210448Snilay@cs.wisc.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2310448Snilay@cs.wisc.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2410448Snilay@cs.wisc.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2510448Snilay@cs.wisc.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2610448Snilay@cs.wisc.edu 2710448Snilay@cs.wisc.edufrom __future__ import generators 2810448Snilay@cs.wisc.eduimport os, re, sys, types, inspect 2910448Snilay@cs.wisc.edu 3010448Snilay@cs.wisc.edufrom m5 import panic, env 3110448Snilay@cs.wisc.edufrom convert import * 3210448Snilay@cs.wisc.edufrom multidict import multidict 3310448Snilay@cs.wisc.edu 3410448Snilay@cs.wisc.edunoDot = False 3510448Snilay@cs.wisc.edutry: 3610448Snilay@cs.wisc.edu import pydot 3710448Snilay@cs.wisc.eduexcept: 3810448Snilay@cs.wisc.edu noDot = True 3910448Snilay@cs.wisc.edu 4010448Snilay@cs.wisc.educlass Singleton(type): 4110448Snilay@cs.wisc.edu def __call__(cls, *args, **kwargs): 4210448Snilay@cs.wisc.edu if hasattr(cls, '_instance'): 4310448Snilay@cs.wisc.edu return cls._instance 4410448Snilay@cs.wisc.edu 4510448Snilay@cs.wisc.edu cls._instance = super(Singleton, cls).__call__(*args, **kwargs) 4610448Snilay@cs.wisc.edu return cls._instance 4710448Snilay@cs.wisc.edu 4810448Snilay@cs.wisc.edu##################################################################### 4910448Snilay@cs.wisc.edu# 5010448Snilay@cs.wisc.edu# M5 Python Configuration Utility 5110448Snilay@cs.wisc.edu# 5210448Snilay@cs.wisc.edu# The basic idea is to write simple Python programs that build Python 5310448Snilay@cs.wisc.edu# objects corresponding to M5 SimObjects for the deisred simulation 5410448Snilay@cs.wisc.edu# configuration. For now, the Python emits a .ini file that can be 5510448Snilay@cs.wisc.edu# parsed by M5. In the future, some tighter integration between M5 5610448Snilay@cs.wisc.edu# and the Python interpreter may allow bypassing the .ini file. 5710448Snilay@cs.wisc.edu# 5810448Snilay@cs.wisc.edu# Each SimObject class in M5 is represented by a Python class with the 5910448Snilay@cs.wisc.edu# same name. The Python inheritance tree mirrors the M5 C++ tree 6010448Snilay@cs.wisc.edu# (e.g., SimpleCPU derives from BaseCPU in both cases, and all 6110448Snilay@cs.wisc.edu# SimObjects inherit from a single SimObject base class). To specify 6210448Snilay@cs.wisc.edu# an instance of an M5 SimObject in a configuration, the user simply 6310448Snilay@cs.wisc.edu# instantiates the corresponding Python object. The parameters for 6410448Snilay@cs.wisc.edu# that SimObject are given by assigning to attributes of the Python 6510448Snilay@cs.wisc.edu# object, either using keyword assignment in the constructor or in 6610448Snilay@cs.wisc.edu# separate assignment statements. For example: 6710448Snilay@cs.wisc.edu# 6810448Snilay@cs.wisc.edu# cache = BaseCache('my_cache', root, size='64KB') 6910448Snilay@cs.wisc.edu# cache.hit_latency = 3 7010448Snilay@cs.wisc.edu# cache.assoc = 8 7110448Snilay@cs.wisc.edu# 7210448Snilay@cs.wisc.edu# (The first two constructor arguments specify the name of the created 7310448Snilay@cs.wisc.edu# cache and its parent node in the hierarchy.) 7410448Snilay@cs.wisc.edu# 7510448Snilay@cs.wisc.edu# The magic lies in the mapping of the Python attributes for SimObject 7610448Snilay@cs.wisc.edu# classes to the actual SimObject parameter specifications. This 7710448Snilay@cs.wisc.edu# allows parameter validity checking in the Python code. Continuing 7810448Snilay@cs.wisc.edu# the example above, the statements "cache.blurfl=3" or 7910448Snilay@cs.wisc.edu# "cache.assoc='hello'" would both result in runtime errors in Python, 8010448Snilay@cs.wisc.edu# since the BaseCache object has no 'blurfl' parameter and the 'assoc' 8110448Snilay@cs.wisc.edu# parameter requires an integer, respectively. This magic is done 8210448Snilay@cs.wisc.edu# primarily by overriding the special __setattr__ method that controls 8310448Snilay@cs.wisc.edu# assignment to object attributes. 8410448Snilay@cs.wisc.edu# 8510448Snilay@cs.wisc.edu# The Python module provides another class, ConfigNode, which is a 8610448Snilay@cs.wisc.edu# superclass of SimObject. ConfigNode implements the parent/child 8710448Snilay@cs.wisc.edu# relationship for building the configuration hierarchy tree. 8810448Snilay@cs.wisc.edu# Concrete instances of ConfigNode can be used to group objects in the 8910448Snilay@cs.wisc.edu# hierarchy, but do not correspond to SimObjects themselves (like a 9010448Snilay@cs.wisc.edu# .ini section with "children=" but no "type=". 9110448Snilay@cs.wisc.edu# 9210448Snilay@cs.wisc.edu# Once a set of Python objects have been instantiated in a hierarchy, 9310448Snilay@cs.wisc.edu# calling 'instantiate(obj)' (where obj is the root of the hierarchy) 9410448Snilay@cs.wisc.edu# will generate a .ini file. See simple-4cpu.py for an example 9510448Snilay@cs.wisc.edu# (corresponding to m5-test/simple-4cpu.ini). 9610448Snilay@cs.wisc.edu# 9710448Snilay@cs.wisc.edu##################################################################### 9810448Snilay@cs.wisc.edu 9910448Snilay@cs.wisc.edu##################################################################### 10010448Snilay@cs.wisc.edu# 10110448Snilay@cs.wisc.edu# ConfigNode/SimObject classes 10210448Snilay@cs.wisc.edu# 10310448Snilay@cs.wisc.edu# The Python class hierarchy rooted by ConfigNode (which is the base 10410448Snilay@cs.wisc.edu# class of SimObject, which in turn is the base class of all other M5 10510448Snilay@cs.wisc.edu# SimObject classes) has special attribute behavior. In general, an 10610448Snilay@cs.wisc.edu# object in this hierarchy has three categories of attribute-like 10710448Snilay@cs.wisc.edu# things: 10810448Snilay@cs.wisc.edu# 10910448Snilay@cs.wisc.edu# 1. Regular Python methods and variables. These must start with an 11010448Snilay@cs.wisc.edu# underscore to be treated normally. 11110448Snilay@cs.wisc.edu# 11210448Snilay@cs.wisc.edu# 2. SimObject parameters. These values are stored as normal Python 11310448Snilay@cs.wisc.edu# attributes, but all assignments to these attributes are checked 11410448Snilay@cs.wisc.edu# against the pre-defined set of parameters stored in the class's 11510448Snilay@cs.wisc.edu# _params dictionary. Assignments to attributes that do not 11610448Snilay@cs.wisc.edu# correspond to predefined parameters, or that are not of the correct 11710448Snilay@cs.wisc.edu# type, incur runtime errors. 11810448Snilay@cs.wisc.edu# 11910448Snilay@cs.wisc.edu# 3. Hierarchy children. The child nodes of a ConfigNode are stored 12010448Snilay@cs.wisc.edu# in the node's _children dictionary, but can be accessed using the 12110448Snilay@cs.wisc.edu# Python attribute dot-notation (just as they are printed out by the 12210448Snilay@cs.wisc.edu# simulator). Children cannot be created using attribute assigment; 12310448Snilay@cs.wisc.edu# they must be added by specifying the parent node in the child's 12410448Snilay@cs.wisc.edu# constructor or using the '+=' operator. 12510448Snilay@cs.wisc.edu 12610448Snilay@cs.wisc.edu# The SimObject parameters are the most complex, for a few reasons. 12710448Snilay@cs.wisc.edu# First, both parameter descriptions and parameter values are 12810448Snilay@cs.wisc.edu# inherited. Thus parameter description lookup must go up the 12910448Snilay@cs.wisc.edu# inheritance chain like normal attribute lookup, but this behavior 13010448Snilay@cs.wisc.edu# must be explicitly coded since the lookup occurs in each class's 13110448Snilay@cs.wisc.edu# _params attribute. Second, because parameter values can be set 13210448Snilay@cs.wisc.edu# on SimObject classes (to implement default values), the parameter 13310448Snilay@cs.wisc.edu# checking behavior must be enforced on class attribute assignments as 13410448Snilay@cs.wisc.edu# well as instance attribute assignments. Finally, because we allow 13510448Snilay@cs.wisc.edu# class specialization via inheritance (e.g., see the L1Cache class in 13610448Snilay@cs.wisc.edu# the simple-4cpu.py example), we must do parameter checking even on 13710448Snilay@cs.wisc.edu# class instantiation. To provide all these features, we use a 13810448Snilay@cs.wisc.edu# metaclass to define most of the SimObject parameter behavior for 13910448Snilay@cs.wisc.edu# this class hierarchy. 14010448Snilay@cs.wisc.edu# 14110448Snilay@cs.wisc.edu##################################################################### 14210448Snilay@cs.wisc.edu 14310448Snilay@cs.wisc.educlass Proxy(object): 14410448Snilay@cs.wisc.edu def __init__(self, path): 14510448Snilay@cs.wisc.edu self._object = None 14610448Snilay@cs.wisc.edu if path == 'any': 14710448Snilay@cs.wisc.edu self._path = None 14810448Snilay@cs.wisc.edu else: 14910448Snilay@cs.wisc.edu # path is a list of (attr,index) tuples 15010448Snilay@cs.wisc.edu self._path = [(path,None)] 15110448Snilay@cs.wisc.edu self._index = None 15210448Snilay@cs.wisc.edu self._multiplier = None 15310448Snilay@cs.wisc.edu 15410448Snilay@cs.wisc.edu def __getattr__(self, attr): 15510448Snilay@cs.wisc.edu # python uses __bases__ internally for inheritance 15610448Snilay@cs.wisc.edu if attr == '__bases__': 15710448Snilay@cs.wisc.edu return super(Proxy, self).__getattr__(self, attr) 15810448Snilay@cs.wisc.edu if (self._path == None): 15910448Snilay@cs.wisc.edu panic("Can't add attributes to 'any' proxy") 16010448Snilay@cs.wisc.edu self._path.append((attr,None)) 16110448Snilay@cs.wisc.edu return self 16210448Snilay@cs.wisc.edu 16310448Snilay@cs.wisc.edu def __setattr__(self, attr, value): 16410448Snilay@cs.wisc.edu if not attr.startswith('_'): 16510448Snilay@cs.wisc.edu raise AttributeError, 'cannot set attribute %s' % attr 16610448Snilay@cs.wisc.edu super(Proxy, self).__setattr__(attr, value) 16710448Snilay@cs.wisc.edu 16810448Snilay@cs.wisc.edu # support indexing on proxies (e.g., parent.cpu[0]) 16910448Snilay@cs.wisc.edu def __getitem__(self, key): 17010448Snilay@cs.wisc.edu if not isinstance(key, int): 17110448Snilay@cs.wisc.edu raise TypeError, "Proxy object requires integer index" 17210448Snilay@cs.wisc.edu if self._path == None: 17310448Snilay@cs.wisc.edu raise IndexError, "Index applied to 'any' proxy" 17410448Snilay@cs.wisc.edu # replace index portion of last path element with new index 17510448Snilay@cs.wisc.edu self._path[-1] = (self._path[-1][0], key) 17610448Snilay@cs.wisc.edu return self 17710448Snilay@cs.wisc.edu 17810448Snilay@cs.wisc.edu # support multiplying proxies by constants 17910448Snilay@cs.wisc.edu def __mul__(self, other): 18010448Snilay@cs.wisc.edu if not isinstance(other, int): 18110448Snilay@cs.wisc.edu raise TypeError, "Proxy multiplier must be integer" 18210448Snilay@cs.wisc.edu if self._multiplier == None: 18310448Snilay@cs.wisc.edu self._multiplier = other 18410448Snilay@cs.wisc.edu else: 18510448Snilay@cs.wisc.edu # support chained multipliers 18610448Snilay@cs.wisc.edu self._multiplier *= other 18710448Snilay@cs.wisc.edu return self 18810448Snilay@cs.wisc.edu 18910448Snilay@cs.wisc.edu def _mulcheck(self, result): 19010448Snilay@cs.wisc.edu if self._multiplier == None: 19110448Snilay@cs.wisc.edu return result 19210448Snilay@cs.wisc.edu if not isinstance(result, int): 19310448Snilay@cs.wisc.edu raise TypeError, "Proxy with multiplier resolves to " \ 19410448Snilay@cs.wisc.edu "non-integer value" 19510448Snilay@cs.wisc.edu return result * self._multiplier 19610448Snilay@cs.wisc.edu 19710448Snilay@cs.wisc.edu def unproxy(self, base, ptype): 19810448Snilay@cs.wisc.edu obj = base 19910448Snilay@cs.wisc.edu done = False 20010448Snilay@cs.wisc.edu while not done: 20110448Snilay@cs.wisc.edu if obj is None: 20210448Snilay@cs.wisc.edu raise AttributeError, \ 20310448Snilay@cs.wisc.edu 'Parent of %s type %s not found at path %s' \ 20410448Snilay@cs.wisc.edu % (base.name, ptype, self._path) 20510448Snilay@cs.wisc.edu 20610448Snilay@cs.wisc.edu result, done = obj.find(ptype, self._path) 20710448Snilay@cs.wisc.edu obj = obj.parent 20810448Snilay@cs.wisc.edu 20910448Snilay@cs.wisc.edu if isinstance(result, Proxy): 21010448Snilay@cs.wisc.edu result = result.unproxy(obj, ptype) 21110448Snilay@cs.wisc.edu 21210448Snilay@cs.wisc.edu return self._mulcheck(result) 21310448Snilay@cs.wisc.edu 214 def getindex(obj, index): 215 if index == None: 216 return obj 217 try: 218 obj = obj[index] 219 except TypeError: 220 if index != 0: 221 raise 222 # if index is 0 and item is not subscriptable, just 223 # use item itself (so cpu[0] works on uniprocessors) 224 return obj 225 getindex = staticmethod(getindex) 226 227class ProxyFactory(object): 228 def __getattr__(self, attr): 229 return Proxy(attr) 230 231# global object for handling parent.foo proxies 232parent = ProxyFactory() 233 234def isSubClass(value, cls): 235 try: 236 return issubclass(value, cls) 237 except: 238 return False 239 240def isConfigNode(value): 241 try: 242 return issubclass(value, ConfigNode) 243 except: 244 return False 245 246def isSimObject(value): 247 try: 248 return issubclass(value, SimObject) 249 except: 250 return False 251 252def isSimObjSequence(value): 253 if not isinstance(value, (list, tuple)): 254 return False 255 256 for val in value: 257 if not isNullPointer(val) and not isConfigNode(val): 258 return False 259 260 return True 261 262def isParamContext(value): 263 try: 264 return issubclass(value, ParamContext) 265 except: 266 return False 267 268 269class_decorator = 'M5M5_SIMOBJECT_' 270expr_decorator = 'M5M5_EXPRESSION_' 271dot_decorator = '_M5M5_DOT_' 272 273# 'Global' map of legitimate types for SimObject parameters. 274param_types = {} 275 276# Dummy base class to identify types that are legitimate for SimObject 277# parameters. 278class ParamType(object): 279 pass 280 281# Add types defined in given context (dict or module) that are derived 282# from ParamType to param_types map. 283def add_param_types(ctx): 284 if isinstance(ctx, types.DictType): 285 source_dict = ctx 286 elif isinstance(ctx, types.ModuleType): 287 source_dict = ctx.__dict__ 288 else: 289 raise TypeError, \ 290 "m5.config.add_param_types requires dict or module as arg" 291 for key,val in source_dict.iteritems(): 292 if isinstance(val, type) and issubclass(val, ParamType): 293 param_types[key] = val 294 295# The metaclass for ConfigNode (and thus for everything that derives 296# from ConfigNode, including SimObject). This class controls how new 297# classes that derive from ConfigNode are instantiated, and provides 298# inherited class behavior (just like a class controls how instances 299# of that class are instantiated, and provides inherited instance 300# behavior). 301class MetaConfigNode(type): 302 # Attributes that can be set only at initialization time 303 init_keywords = {} 304 # Attributes that can be set any time 305 keywords = { 'check' : types.FunctionType, 306 'children' : types.ListType } 307 308 # __new__ is called before __init__, and is where the statements 309 # in the body of the class definition get loaded into the class's 310 # __dict__. We intercept this to filter out parameter assignments 311 # and only allow "private" attributes to be passed to the base 312 # __new__ (starting with underscore). 313 def __new__(mcls, name, bases, dict): 314 # Copy "private" attributes (including special methods such as __new__) 315 # to the official dict. Everything else goes in _init_dict to be 316 # filtered in __init__. 317 cls_dict = {} 318 for key,val in dict.items(): 319 if key.startswith('_'): 320 cls_dict[key] = val 321 del dict[key] 322 cls_dict['_init_dict'] = dict 323 return super(MetaConfigNode, mcls).__new__(mcls, name, bases, cls_dict) 324 325 # initialization 326 def __init__(cls, name, bases, dict): 327 super(MetaConfigNode, cls).__init__(name, bases, dict) 328 329 # initialize required attributes 330 cls._params = multidict() 331 cls._values = multidict() 332 cls._param_types = {} 333 cls._bases = [c for c in cls.__mro__ if isConfigNode(c)] 334 cls._anon_subclass_counter = 0 335 336 # We don't support multiple inheritence. If you want to, you 337 # must fix multidict to deal with it properly. 338 cnbase = [ base for base in bases if isConfigNode(base) ] 339 if len(cnbase) == 1: 340 # If your parent has a value in it that's a config node, clone 341 # it. Do this now so if we update any of the values' 342 # attributes we are updating the clone and not the original. 343 for key,val in cnbase[0]._values.iteritems(): 344 345 # don't clone if (1) we're about to overwrite it with 346 # a local setting or (2) we've already cloned a copy 347 # from an earlier (more derived) base 348 if cls._init_dict.has_key(key) or cls._values.has_key(key): 349 continue 350 351 if isConfigNode(val): 352 cls._values[key] = val() 353 elif isSimObjSequence(val) and len(val): 354 cls._values[key] = [ v() for v in val ] 355 356 cls._params.parent = cnbase[0]._params 357 cls._values.parent = cnbase[0]._values 358 359 elif len(cnbase) > 1: 360 panic("""\ 361The config hierarchy only supports single inheritence of SimObject 362classes. You're trying to derive from: 363%s""" % str(cnbase)) 364 365 # process param types from _init_dict, as these may be needed 366 # by param descriptions also in _init_dict 367 for key,val in cls._init_dict.items(): 368 if isinstance(val, type) and issubclass(val, ParamType): 369 cls._param_types[key] = val 370 if not issubclass(val, ConfigNode): 371 del cls._init_dict[key] 372 373 # now process remaining _init_dict items 374 for key,val in cls._init_dict.items(): 375 # param descriptions 376 if isinstance(val, ParamBase): 377 cls._new_param(key, val) 378 379 # init-time-only keywords 380 elif cls.init_keywords.has_key(key): 381 cls._set_keyword(key, val, cls.init_keywords[key]) 382 383 # See description of decorators in the importer.py file. 384 # We just strip off the expr_decorator now since we don't 385 # need from this point on. 386 elif key.startswith(expr_decorator): 387 key = key[len(expr_decorator):] 388 # because it had dots into a list so that we can find the 389 # proper variable to modify. 390 key = key.split(dot_decorator) 391 c = cls 392 for item in key[:-1]: 393 c = getattr(c, item) 394 setattr(c, key[-1], val) 395 396 # default: use normal path (ends up in __setattr__) 397 else: 398 setattr(cls, key, val) 399 400 def _set_keyword(cls, keyword, val, kwtype): 401 if not isinstance(val, kwtype): 402 raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ 403 (keyword, type(val), kwtype) 404 if isinstance(val, types.FunctionType): 405 val = classmethod(val) 406 type.__setattr__(cls, keyword, val) 407 408 def _new_param(cls, name, value): 409 cls._params[name] = value 410 if hasattr(value, 'default'): 411 cls._values[name] = value.default 412 # try to resolve local param types in local param_types scope 413 value.maybe_resolve_type(cls._param_types) 414 415 # Set attribute (called on foo.attr = value when foo is an 416 # instance of class cls). 417 def __setattr__(cls, attr, value): 418 # normal processing for private attributes 419 if attr.startswith('_'): 420 type.__setattr__(cls, attr, value) 421 return 422 423 if cls.keywords.has_key(attr): 424 cls._set_keyword(attr, value, cls.keywords[attr]) 425 return 426 427 # must be SimObject param 428 param = cls._params.get(attr, None) 429 if param: 430 # It's ok: set attribute by delegating to 'object' class. 431 # Note the use of param.make_value() to verify/canonicalize 432 # the assigned value 433 try: 434 param.valid(value) 435 except Exception, e: 436 msg = "%s\nError setting param %s.%s to %s\n" % \ 437 (e, cls.__name__, attr, value) 438 e.args = (msg, ) 439 raise 440 cls._values[attr] = value 441 elif isConfigNode(value) or isSimObjSequence(value): 442 cls._values[attr] = value 443 else: 444 raise AttributeError, \ 445 "Class %s has no parameter %s" % (cls.__name__, attr) 446 447 def __getattr__(cls, attr): 448 if cls._params.has_key(attr) or cls._values.has_key(attr): 449 return Value(cls, attr) 450 451 if attr == '_cpp_param_decl' and hasattr(cls, 'type'): 452 return cls.type + '*' 453 454 raise AttributeError, \ 455 "object '%s' has no attribute '%s'" % (cls.__name__, attr) 456 457 def add_child(cls, instance, name, child): 458 if isNullPointer(child) or instance.top_child_names.has_key(name): 459 return 460 461 if isinstance(child, (list, tuple)): 462 kid = [] 463 for i,c in enumerate(child): 464 n = '%s%d' % (name, i) 465 k = c.instantiate(n, instance) 466 467 instance.children.append(k) 468 instance.child_names[n] = k 469 instance.child_objects[c] = k 470 kid.append(k) 471 else: 472 kid = child.instantiate(name, instance) 473 instance.children.append(kid) 474 instance.child_names[name] = kid 475 instance.child_objects[child] = kid 476 477 instance.top_child_names[name] = kid 478 479 # Print instance info to .ini file. 480 def instantiate(cls, name, parent = None): 481 instance = Node(name, cls, parent, isParamContext(cls)) 482 483 if hasattr(cls, 'check'): 484 cls.check() 485 486 for key,value in cls._values.iteritems(): 487 if isConfigNode(value): 488 cls.add_child(instance, key, value) 489 if isinstance(value, (list, tuple)): 490 vals = [ v for v in value if isConfigNode(v) ] 491 if len(vals): 492 cls.add_child(instance, key, vals) 493 494 for pname,param in cls._params.iteritems(): 495 value = cls._values.get(pname, None) 496 if value is None: 497 panic('Error getting %s from %s' % (pname, name)) 498 499 try: 500 if isConfigNode(value): 501 value = instance.child_objects[value] 502 elif isinstance(value, (list, tuple)): 503 v = [] 504 for val in value: 505 if isConfigNode(val): 506 v.append(instance.child_objects[val]) 507 else: 508 v.append(val) 509 value = v 510 511 p = NodeParam(pname, param, value) 512 instance.params.append(p) 513 instance.param_names[pname] = p 514 except Exception, e: 515 msg = 'Exception while evaluating %s.%s\n%s' % \ 516 (instance.path, pname, e) 517 e.args = (msg, ) 518 raise 519 520 return instance 521 522 def _convert(cls, value): 523 realvalue = value 524 if isinstance(value, Node): 525 realvalue = value.realtype 526 527 if isinstance(realvalue, Proxy): 528 return value 529 530 if realvalue == None or isNullPointer(realvalue): 531 return value 532 533 if isSubClass(realvalue, cls): 534 return value 535 536 raise TypeError, 'object %s type %s wrong type, should be %s' % \ 537 (repr(realvalue), realvalue, cls) 538 539 def _string(cls, value): 540 if isNullPointer(value): 541 return 'Null' 542 return Node._string(value) 543 544# The ConfigNode class is the root of the special hierarchy. Most of 545# the code in this class deals with the configuration hierarchy itself 546# (parent/child node relationships). 547class ConfigNode(object): 548 # Specify metaclass. Any class inheriting from ConfigNode will 549 # get this metaclass. 550 __metaclass__ = MetaConfigNode 551 552 def __new__(cls, **kwargs): 553 name = cls.__name__ + ("_%d" % cls._anon_subclass_counter) 554 cls._anon_subclass_counter += 1 555 return cls.__metaclass__(name, (cls, ), kwargs) 556 557class ParamContext(ConfigNode,ParamType): 558 pass 559 560class MetaSimObject(MetaConfigNode): 561 # init_keywords and keywords are inherited from MetaConfigNode, 562 # with overrides/additions 563 init_keywords = MetaConfigNode.init_keywords 564 init_keywords.update({ 'abstract' : types.BooleanType, 565 'type' : types.StringType }) 566 567 keywords = MetaConfigNode.keywords 568 # no additional keywords 569 570 cpp_classes = [] 571 572 # initialization 573 def __init__(cls, name, bases, dict): 574 super(MetaSimObject, cls).__init__(name, bases, dict) 575 576 if hasattr(cls, 'type'): 577 if name == 'SimObject': 578 cls._cpp_base = None 579 elif hasattr(cls._bases[1], 'type'): 580 cls._cpp_base = cls._bases[1].type 581 else: 582 panic("SimObject %s derives from a non-C++ SimObject %s "\ 583 "(no 'type')" % (cls, cls_bases[1].__name__)) 584 585 # This class corresponds to a C++ class: put it on the global 586 # list of C++ objects to generate param structs, etc. 587 MetaSimObject.cpp_classes.append(cls) 588 589 def _cpp_decl(cls): 590 name = cls.__name__ 591 code = "" 592 code += "\n".join([e.cpp_declare() for e in cls._param_types.values()]) 593 code += "\n" 594 param_names = cls._params.keys() 595 param_names.sort() 596 code += "struct Params" 597 if cls._cpp_base: 598 code += " : public %s::Params" % cls._cpp_base 599 code += " {\n " 600 code += "\n ".join([cls._params[pname].cpp_decl(pname) \ 601 for pname in param_names]) 602 code += "\n};\n" 603 return code 604 605class NodeParam(object): 606 def __init__(self, name, param, value): 607 self.name = name 608 self.param = param 609 self.ptype = param.ptype 610 self.convert = param.convert 611 self.string = param.string 612 self.value = value 613 614class Node(object): 615 all = {} 616 def __init__(self, name, realtype, parent, paramcontext): 617 self.name = name 618 self.realtype = realtype 619 if isSimObject(realtype): 620 self.type = realtype.type 621 else: 622 self.type = None 623 self.parent = parent 624 self.children = [] 625 self.child_names = {} 626 self.child_objects = {} 627 self.top_child_names = {} 628 self.params = [] 629 self.param_names = {} 630 self.paramcontext = paramcontext 631 632 path = [ self.name ] 633 node = self.parent 634 while node is not None: 635 if node.name != 'root': 636 path.insert(0, node.name) 637 else: 638 assert(node.parent is None) 639 node = node.parent 640 self.path = '.'.join(path) 641 642 def find(self, realtype, path): 643 if not path: 644 if issubclass(self.realtype, realtype): 645 return self, True 646 647 obj = None 648 for child in self.children: 649 if issubclass(child.realtype, realtype): 650 if obj is not None: 651 raise AttributeError, \ 652 'parent.any matched more than one: %s %s' % \ 653 (obj.path, child.path) 654 obj = child 655 return obj, obj is not None 656 657 try: 658 obj = self 659 for (node,index) in path[:-1]: 660 if obj.child_names.has_key(node): 661 obj = obj.child_names[node] 662 else: 663 obj = obj.top_child_names[node] 664 obj = Proxy.getindex(obj, index) 665 666 (last,index) = path[-1] 667 if obj.child_names.has_key(last): 668 value = obj.child_names[last] 669 return Proxy.getindex(value, index), True 670 elif obj.top_child_names.has_key(last): 671 value = obj.top_child_names[last] 672 return Proxy.getindex(value, index), True 673 elif obj.param_names.has_key(last): 674 value = obj.param_names[last] 675 realtype._convert(value.value) 676 return Proxy.getindex(value.value, index), True 677 except KeyError: 678 pass 679 680 return None, False 681 682 def unproxy(self, param, ptype): 683 if not isinstance(param, Proxy): 684 return param 685 return param.unproxy(self, ptype) 686 687 def fixup(self): 688 self.all[self.path] = self 689 690 for param in self.params: 691 ptype = param.ptype 692 pval = param.value 693 694 try: 695 if isinstance(pval, (list, tuple)): 696 param.value = [ self.unproxy(pv, ptype) for pv in pval ] 697 else: 698 param.value = self.unproxy(pval, ptype) 699 except Exception, e: 700 msg = 'Error while fixing up %s:%s\n%s' % \ 701 (self.path, param.name, e) 702 e.args = (msg, ) 703 raise 704 705 for child in self.children: 706 assert(child != self) 707 child.fixup() 708 709 # print type and parameter values to .ini file 710 def display(self): 711 print '[' + self.path + ']' # .ini section header 712 713 if isSimObject(self.realtype): 714 print 'type = %s' % self.type 715 716 if self.children: 717 # instantiate children in same order they were added for 718 # backward compatibility (else we can end up with cpu1 719 # before cpu0). Changing ordering can also influence timing 720 # in the current memory system, as caches get added to a bus 721 # in different orders which affects their priority in the 722 # case of simulataneous requests. 723 self.children.sort(lambda x,y: cmp(x.name, y.name)) 724 children = [ c.name for c in self.children if not c.paramcontext] 725 print 'children =', ' '.join(children) 726 727 self.params.sort(lambda x,y: cmp(x.name, y.name)) 728 for param in self.params: 729 try: 730 if param.value is None: 731 raise AttributeError, 'Parameter with no value' 732 733 value = param.convert(param.value) 734 string = param.string(value) 735 except Exception, e: 736 msg = 'exception in %s:%s\n%s' % (self.path, param.name, e) 737 e.args = (msg, ) 738 raise 739 740 print '%s = %s' % (param.name, string) 741 742 print 743 744 # recursively dump out children 745 for c in self.children: 746 c.display() 747 748 # print type and parameter values to .ini file 749 def outputDot(self, dot): 750 label = "{%s|" % self.path 751 if isSimObject(self.realtype): 752 label += '%s|' % self.type 753 754 if self.children: 755 # instantiate children in same order they were added for 756 # backward compatibility (else we can end up with cpu1 757 # before cpu0). 758 for c in self.children: 759 dot.add_edge(pydot.Edge(self.path,c.path, style="bold")) 760 761 simobjs = [] 762 for param in self.params: 763 try: 764 if param.value is None: 765 raise AttributeError, 'Parameter with no value' 766 767 value = param.convert(param.value) 768 string = param.string(value) 769 except Exception, e: 770 msg = 'exception in %s:%s\n%s' % (self.name, param.name, e) 771 e.args = (msg, ) 772 raise 773 774 if isConfigNode(param.ptype) and string != "Null": 775 simobjs.append(string) 776 else: 777 label += '%s = %s\\n' % (param.name, string) 778 779 for so in simobjs: 780 label += "|<%s> %s" % (so, so) 781 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, 782 tailport="w")) 783 label += '}' 784 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label)) 785 786 # recursively dump out children 787 for c in self.children: 788 c.outputDot(dot) 789 790 def _string(cls, value): 791 if not isinstance(value, Node): 792 raise AttributeError, 'expecting %s got %s' % (Node, value) 793 return value.path 794 _string = classmethod(_string) 795 796##################################################################### 797# 798# Parameter description classes 799# 800# The _params dictionary in each class maps parameter names to 801# either a Param or a VectorParam object. These objects contain the 802# parameter description string, the parameter type, and the default 803# value (loaded from the PARAM section of the .odesc files). The 804# _convert() method on these objects is used to force whatever value 805# is assigned to the parameter to the appropriate type. 806# 807# Note that the default values are loaded into the class's attribute 808# space when the parameter dictionary is initialized (in 809# MetaConfigNode._setparams()); after that point they aren't used. 810# 811##################################################################### 812 813def isNullPointer(value): 814 return isinstance(value, NullSimObject) 815 816class Value(object): 817 def __init__(self, obj, attr): 818 super(Value, self).__setattr__('attr', attr) 819 super(Value, self).__setattr__('obj', obj) 820 821 def _getattr(self): 822 return self.obj._values.get(self.attr) 823 824 def __setattr__(self, attr, value): 825 setattr(self._getattr(), attr, value) 826 827 def __getattr__(self, attr): 828 return getattr(self._getattr(), attr) 829 830 def __getitem__(self, index): 831 return self._getattr().__getitem__(index) 832 833 def __call__(self, *args, **kwargs): 834 return self._getattr().__call__(*args, **kwargs) 835 836 def __nonzero__(self): 837 return bool(self._getattr()) 838 839 def __str__(self): 840 return str(self._getattr()) 841 842 def __len__(self): 843 return len(self._getattr()) 844 845# Regular parameter. 846class ParamBase(object): 847 def __init__(self, ptype, *args, **kwargs): 848 if isinstance(ptype, types.StringType): 849 self.ptype_string = ptype 850 elif isinstance(ptype, type): 851 self.ptype = ptype 852 else: 853 raise TypeError, "Param type is not a type (%s)" % ptype 854 855 if args: 856 if len(args) == 1: 857 self.desc = args[0] 858 elif len(args) == 2: 859 self.default = args[0] 860 self.desc = args[1] 861 else: 862 raise TypeError, 'too many arguments' 863 864 if kwargs.has_key('desc'): 865 assert(not hasattr(self, 'desc')) 866 self.desc = kwargs['desc'] 867 del kwargs['desc'] 868 869 if kwargs.has_key('default'): 870 assert(not hasattr(self, 'default')) 871 self.default = kwargs['default'] 872 del kwargs['default'] 873 874 if kwargs: 875 raise TypeError, 'extra unknown kwargs %s' % kwargs 876 877 if not hasattr(self, 'desc'): 878 raise TypeError, 'desc attribute missing' 879 880 def maybe_resolve_type(self, context): 881 # check if already resolved... don't use hasattr(), 882 # as that calls __getattr__() 883 if self.__dict__.has_key('ptype'): 884 return 885 try: 886 self.ptype = context[self.ptype_string] 887 except KeyError: 888 # no harm in trying... we'll try again later using global scope 889 pass 890 891 def __getattr__(self, attr): 892 if attr == 'ptype': 893 try: 894 self.ptype = param_types[self.ptype_string] 895 return self.ptype 896 except: 897 panic("undefined Param type %s" % self.ptype_string) 898 else: 899 raise AttributeError, "'%s' object has no attribute '%s'" % \ 900 (type(self).__name__, attr) 901 902 def valid(self, value): 903 if not isinstance(value, Proxy): 904 self.ptype._convert(value) 905 906 def convert(self, value): 907 return self.ptype._convert(value) 908 909 def string(self, value): 910 return self.ptype._string(value) 911 912 def set(self, name, instance, value): 913 instance.__dict__[name] = value 914 915 def cpp_decl(self, name): 916 return '%s %s;' % (self.ptype._cpp_param_decl, name) 917 918class ParamFactory(object): 919 def __init__(self, type): 920 self.ptype = type 921 922 # E.g., Param.Int(5, "number of widgets") 923 def __call__(self, *args, **kwargs): 924 return ParamBase(self.ptype, *args, **kwargs) 925 926 # Strange magic to theoretically allow dotted names as Param classes, 927 # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar 928 def __getattr__(self, attr): 929 if attr == '__bases__': 930 raise AttributeError, '' 931 cls = type(self) 932 return cls(attr) 933 934 def __setattr__(self, attr, value): 935 if attr != 'ptype': 936 raise AttributeError, \ 937 'Attribute %s not available in %s' % (attr, self.__class__) 938 super(ParamFactory, self).__setattr__(attr, value) 939 940Param = ParamFactory(None) 941 942# Vector-valued parameter description. Just like Param, except that 943# the value is a vector (list) of the specified type instead of a 944# single value. 945class VectorParamBase(ParamBase): 946 def __init__(self, type, *args, **kwargs): 947 ParamBase.__init__(self, type, *args, **kwargs) 948 949 def valid(self, value): 950 if value == None: 951 return True 952 953 if isinstance(value, (list, tuple)): 954 for val in value: 955 if not isinstance(val, Proxy): 956 self.ptype._convert(val) 957 elif not isinstance(value, Proxy): 958 self.ptype._convert(value) 959 960 # Convert assigned value to appropriate type. If the RHS is not a 961 # list or tuple, it generates a single-element list. 962 def convert(self, value): 963 if value == None: 964 return [] 965 966 if isinstance(value, (list, tuple)): 967 # list: coerce each element into new list 968 return [ self.ptype._convert(v) for v in value ] 969 else: 970 # singleton: coerce & wrap in a list 971 return self.ptype._convert(value) 972 973 def string(self, value): 974 if isinstance(value, (list, tuple)): 975 return ' '.join([ self.ptype._string(v) for v in value]) 976 else: 977 return self.ptype._string(value) 978 979 def cpp_decl(self, name): 980 return 'std::vector<%s> %s;' % (self.ptype._cpp_param_decl, name) 981 982class VectorParamFactory(ParamFactory): 983 # E.g., VectorParam.Int(5, "number of widgets") 984 def __call__(self, *args, **kwargs): 985 return VectorParamBase(self.ptype, *args, **kwargs) 986 987VectorParam = VectorParamFactory(None) 988 989##################################################################### 990# 991# Parameter Types 992# 993# Though native Python types could be used to specify parameter types 994# (the 'ptype' field of the Param and VectorParam classes), it's more 995# flexible to define our own set of types. This gives us more control 996# over how Python expressions are converted to values (via the 997# __init__() constructor) and how these values are printed out (via 998# the __str__() conversion method). Eventually we'll need these types 999# to correspond to distinct C++ types as well. 1000# 1001##################################################################### 1002 1003class MetaRange(type): 1004 def __init__(cls, name, bases, dict): 1005 super(MetaRange, cls).__init__(name, bases, dict) 1006 if name == 'Range': 1007 return 1008 cls._cpp_param_decl = 'Range<%s>' % cls.type._cpp_param_decl 1009 1010 def _convert(cls, value): 1011 if not isinstance(value, Range): 1012 raise TypeError, 'value %s is not a Pair' % value 1013 value = cls(value) 1014 value.first = cls.type._convert(value.first) 1015 value.second = cls.type._convert(value.second) 1016 return value 1017 1018 def _string(cls, value): 1019 first = int(value.first) 1020 second = int(value.second) 1021 if value.extend: 1022 second += first 1023 if not value.inclusive: 1024 second -= 1 1025 return '%s:%s' % (cls.type._string(first), cls.type._string(second)) 1026 1027class Range(ParamType): 1028 __metaclass__ = MetaRange 1029 def __init__(self, *args, **kwargs): 1030 if len(args) == 0: 1031 self.first = kwargs.pop('start') 1032 1033 if 'end' in kwargs: 1034 self.second = kwargs.pop('end') 1035 self.inclusive = True 1036 self.extend = False 1037 elif 'size' in kwargs: 1038 self.second = kwargs.pop('size') 1039 self.inclusive = False 1040 self.extend = True 1041 else: 1042 raise TypeError, "Either end or size must be specified" 1043 1044 elif len(args) == 1: 1045 if kwargs: 1046 self.first = args[0] 1047 if 'end' in kwargs: 1048 self.second = kwargs.pop('end') 1049 self.inclusive = True 1050 self.extend = False 1051 elif 'size' in kwargs: 1052 self.second = kwargs.pop('size') 1053 self.inclusive = False 1054 self.extend = True 1055 else: 1056 raise TypeError, "Either end or size must be specified" 1057 elif isinstance(args[0], Range): 1058 self.first = args[0].first 1059 self.second = args[0].second 1060 self.inclusive = args[0].inclusive 1061 self.extend = args[0].extend 1062 else: 1063 self.first = 0 1064 self.second = args[0] 1065 self.inclusive = False 1066 self.extend = True 1067 1068 elif len(args) == 2: 1069 self.first, self.second = args 1070 self.inclusive = True 1071 self.extend = False 1072 else: 1073 raise TypeError, "Too many arguments specified" 1074 1075 if kwargs: 1076 raise TypeError, "too many keywords: %s" % kwargs.keys() 1077 1078# Metaclass for bounds-checked integer parameters. See CheckedInt. 1079class CheckedIntType(type): 1080 def __init__(cls, name, bases, dict): 1081 super(CheckedIntType, cls).__init__(name, bases, dict) 1082 1083 # CheckedInt is an abstract base class, so we actually don't 1084 # want to do any processing on it... the rest of this code is 1085 # just for classes that derive from CheckedInt. 1086 if name == 'CheckedInt': 1087 return 1088 1089 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 1090 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 1091 panic("CheckedInt subclass %s must define either\n" \ 1092 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 1093 % name); 1094 if cls.unsigned: 1095 cls.min = 0 1096 cls.max = 2 ** cls.size - 1 1097 else: 1098 cls.min = -(2 ** (cls.size - 1)) 1099 cls.max = (2 ** (cls.size - 1)) - 1 1100 1101 cls._cpp_param_decl = cls.cppname 1102 1103 def _convert(cls, value): 1104 if isinstance(value, bool): 1105 return int(value) 1106 1107 if not isinstance(value, (int, long, float, str)): 1108 raise TypeError, 'Integer param of invalid type %s' % type(value) 1109 1110 if isinstance(value, float): 1111 value = long(value) 1112 elif isinstance(value, str): 1113 value = toInteger(value) 1114 1115 if not cls.min <= value <= cls.max: 1116 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 1117 (cls.min, value, cls.max) 1118 1119 return value 1120 1121 def _string(cls, value): 1122 return str(value) 1123 1124# Abstract superclass for bounds-checked integer parameters. This 1125# class is subclassed to generate parameter classes with specific 1126# bounds. Initialization of the min and max bounds is done in the 1127# metaclass CheckedIntType.__init__. 1128class CheckedInt(long,ParamType): 1129 __metaclass__ = CheckedIntType 1130 1131class Int(CheckedInt): cppname = 'int'; size = 32; unsigned = False 1132class Unsigned(CheckedInt): cppname = 'unsigned'; size = 32; unsigned = True 1133 1134class Int8(CheckedInt): cppname = 'int8_t'; size = 8; unsigned = False 1135class UInt8(CheckedInt): cppname = 'uint8_t'; size = 8; unsigned = True 1136class Int16(CheckedInt): cppname = 'int16_t'; size = 16; unsigned = False 1137class UInt16(CheckedInt): cppname = 'uint16_t'; size = 16; unsigned = True 1138class Int32(CheckedInt): cppname = 'int32_t'; size = 32; unsigned = False 1139class UInt32(CheckedInt): cppname = 'uint32_t'; size = 32; unsigned = True 1140class Int64(CheckedInt): cppname = 'int64_t'; size = 64; unsigned = False 1141class UInt64(CheckedInt): cppname = 'uint64_t'; size = 64; unsigned = True 1142 1143class Counter(CheckedInt): cppname = 'Counter'; size = 64; unsigned = True 1144class Tick(CheckedInt): cppname = 'Tick'; size = 64; unsigned = True 1145 1146class Percent(CheckedInt): cppname = 'int'; min = 0; max = 100 1147 1148class MemorySize(CheckedInt): 1149 cppname = 'uint64_t' 1150 size = 64 1151 unsigned = True 1152 def __new__(cls, value): 1153 return super(MemorySize, cls).__new__(cls, toMemorySize(value)) 1154 1155 def _convert(cls, value): 1156 return cls(value) 1157 _convert = classmethod(_convert) 1158 1159 def _string(cls, value): 1160 return '%d' % value 1161 _string = classmethod(_string) 1162 1163class Addr(MemorySize): 1164 pass 1165 1166class AddrRange(Range): 1167 type = Addr 1168 1169# Boolean parameter type. 1170class Bool(ParamType): 1171 _cpp_param_decl = 'bool' 1172 #def __new__(cls, value): 1173 # return super(MemorySize, cls).__new__(cls, toBool(value)) 1174 1175 def _convert(cls, value): 1176 return toBool(value) 1177 _convert = classmethod(_convert) 1178 1179 def _string(cls, value): 1180 if value: 1181 return "true" 1182 else: 1183 return "false" 1184 _string = classmethod(_string) 1185 1186# String-valued parameter. 1187class String(ParamType): 1188 _cpp_param_decl = 'string' 1189 1190 # Constructor. Value must be Python string. 1191 def _convert(cls,value): 1192 if value is None: 1193 return '' 1194 if isinstance(value, str): 1195 return value 1196 1197 raise TypeError, \ 1198 "String param got value %s %s" % (repr(value), type(value)) 1199 _convert = classmethod(_convert) 1200 1201 # Generate printable string version. Not too tricky. 1202 def _string(cls, value): 1203 return value 1204 _string = classmethod(_string) 1205 1206def IncEthernetAddr(addr, val = 1): 1207 bytes = map(lambda x: int(x, 16), addr.split(':')) 1208 bytes[5] += val 1209 for i in (5, 4, 3, 2, 1): 1210 val,rem = divmod(bytes[i], 256) 1211 bytes[i] = rem 1212 if val == 0: 1213 break 1214 bytes[i - 1] += val 1215 assert(bytes[0] <= 255) 1216 return ':'.join(map(lambda x: '%02x' % x, bytes)) 1217 1218class NextEthernetAddr(object): 1219 __metaclass__ = Singleton 1220 addr = "00:90:00:00:00:01" 1221 1222 def __init__(self, inc = 1): 1223 self.value = self.addr 1224 self.addr = IncEthernetAddr(self.addr, inc) 1225 1226class EthernetAddr(ParamType): 1227 _cpp_param_decl = 'EthAddr' 1228 1229 def _convert(cls, value): 1230 if value == NextEthernetAddr: 1231 return value 1232 1233 if not isinstance(value, str): 1234 raise TypeError, "expected an ethernet address and didn't get one" 1235 1236 bytes = value.split(':') 1237 if len(bytes) != 6: 1238 raise TypeError, 'invalid ethernet address %s' % value 1239 1240 for byte in bytes: 1241 if not 0 <= int(byte) <= 256: 1242 raise TypeError, 'invalid ethernet address %s' % value 1243 1244 return value 1245 _convert = classmethod(_convert) 1246 1247 def _string(cls, value): 1248 if value == NextEthernetAddr: 1249 value = value().value 1250 return value 1251 _string = classmethod(_string) 1252 1253# Special class for NULL pointers. Note the special check in 1254# make_param_value() above that lets these be assigned where a 1255# SimObject is required. 1256# only one copy of a particular node 1257class NullSimObject(object): 1258 __metaclass__ = Singleton 1259 1260 def __call__(cls): 1261 return cls 1262 1263 def _instantiate(self, parent = None, path = ''): 1264 pass 1265 1266 def _convert(cls, value): 1267 if value == Nxone: 1268 return 1269 1270 if isinstance(value, cls): 1271 return value 1272 1273 raise TypeError, 'object %s %s of the wrong type, should be %s' % \ 1274 (repr(value), type(value), cls) 1275 _convert = classmethod(_convert) 1276 1277 def _string(): 1278 return 'NULL' 1279 _string = staticmethod(_string) 1280 1281# The only instance you'll ever need... 1282Null = NULL = NullSimObject() 1283 1284# Enumerated types are a little more complex. The user specifies the 1285# type as Enum(foo) where foo is either a list or dictionary of 1286# alternatives (typically strings, but not necessarily so). (In the 1287# long run, the integer value of the parameter will be the list index 1288# or the corresponding dictionary value. For now, since we only check 1289# that the alternative is valid and then spit it into a .ini file, 1290# there's not much point in using the dictionary.) 1291 1292# What Enum() must do is generate a new type encapsulating the 1293# provided list/dictionary so that specific values of the parameter 1294# can be instances of that type. We define two hidden internal 1295# classes (_ListEnum and _DictEnum) to serve as base classes, then 1296# derive the new type from the appropriate base class on the fly. 1297 1298 1299# Metaclass for Enum types 1300class MetaEnum(type): 1301 def __init__(cls, name, bases, init_dict): 1302 if init_dict.has_key('map'): 1303 if not isinstance(cls.map, dict): 1304 raise TypeError, "Enum-derived class attribute 'map' " \ 1305 "must be of type dict" 1306 # build list of value strings from map 1307 cls.vals = cls.map.keys() 1308 cls.vals.sort() 1309 elif init_dict.has_key('vals'): 1310 if not isinstance(cls.vals, list): 1311 raise TypeError, "Enum-derived class attribute 'vals' " \ 1312 "must be of type list" 1313 # build string->value map from vals sequence 1314 cls.map = {} 1315 for idx,val in enumerate(cls.vals): 1316 cls.map[val] = idx 1317 else: 1318 raise TypeError, "Enum-derived class must define "\ 1319 "attribute 'map' or 'vals'" 1320 1321 cls._cpp_param_decl = name 1322 1323 super(MetaEnum, cls).__init__(name, bases, init_dict) 1324 1325 def cpp_declare(cls): 1326 s = 'enum %s {\n ' % cls.__name__ 1327 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 1328 s += '\n};\n' 1329 return s 1330 1331# Base class for enum types. 1332class Enum(ParamType): 1333 __metaclass__ = MetaEnum 1334 vals = [] 1335 1336 def _convert(self, value): 1337 if value not in self.map: 1338 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 1339 % (value, self.vals) 1340 return value 1341 _convert = classmethod(_convert) 1342 1343 # Generate printable string version of value. 1344 def _string(self, value): 1345 return str(value) 1346 _string = classmethod(_string) 1347# 1348# "Constants"... handy aliases for various values. 1349# 1350 1351class Frequency(int,ParamType): 1352 _cpp_param_decl = 'Tick' 1353 1354 def __new__(cls, value): 1355 if isinstance(value, basestring): 1356 val = int(env['FREQUENCY'] / toFrequency(value)) 1357 else: 1358 val = toFrequency(value) 1359 return super(cls, Frequency).__new__(cls, val) 1360 1361 def _convert(cls, value): 1362 return cls(value) 1363 _convert = classmethod(_convert) 1364 1365 def _string(cls, value): 1366 return '%d' % value 1367 _string = classmethod(_string) 1368 1369class Latency(int,ParamType): 1370 _cpp_param_decl = 'Tick' 1371 def __new__(cls, value): 1372 if isinstance(value, basestring): 1373 val = int(env['FREQUENCY'] * toLatency(value)) 1374 else: 1375 val = toLatency(value) 1376 return super(cls, Latency).__new__(cls, val) 1377 1378 def _convert(cls, value): 1379 return cls(value) 1380 _convert = classmethod(_convert) 1381 1382 def _string(cls, value): 1383 return '%d' % value 1384 _string = classmethod(_string) 1385 1386 1387# Some memory range specifications use this as a default upper bound. 1388MaxAddr = Addr.max 1389MaxTick = Tick.max 1390AllMemory = AddrRange(0, MaxAddr) 1391 1392##################################################################### 1393 1394# The final hook to generate .ini files. Called from configuration 1395# script once config is built. 1396def instantiate(root): 1397 instance = root.instantiate('root') 1398 instance.fixup() 1399 instance.display() 1400 if not noDot: 1401 dot = pydot.Dot() 1402 instance.outputDot(dot) 1403 dot.orientation = "portrait" 1404 dot.size = "8.5,11" 1405 dot.ranksep="equally" 1406 dot.rank="samerank" 1407 dot.write("config.dot") 1408 dot.write_ps("config.ps") 1409 1410# SimObject is a minimal extension of ConfigNode, implementing a 1411# hierarchy node that corresponds to an M5 SimObject. It prints out a 1412# "type=" line to indicate its SimObject class, prints out the 1413# assigned parameters corresponding to its class, and allows 1414# parameters to be set by keyword in the constructor. Note that most 1415# of the heavy lifting for the SimObject param handling is done in the 1416# MetaConfigNode metaclass. 1417class SimObject(ConfigNode, ParamType): 1418 __metaclass__ = MetaSimObject 1419 type = 'SimObject' 1420 1421 1422# __all__ defines the list of symbols that get exported when 1423# 'from config import *' is invoked. Try to keep this reasonably 1424# short to avoid polluting other namespaces. 1425__all__ = ['ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam', 1426 'parent', 'Enum', 1427 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1428 'Int32', 'UInt32', 'Int64', 'UInt64', 1429 'Counter', 'Addr', 'Tick', 'Percent', 1430 'MemorySize', 'Frequency', 'Latency', 1431 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 'NULL', 1432 'NextEthernetAddr', 'instantiate'] 1433