config.py revision 2712:aa0891b4a110
112841Sgabeblack@google.com# Copyright (c) 2004-2005 The Regents of The University of Michigan 212841Sgabeblack@google.com# All rights reserved. 312841Sgabeblack@google.com# 412841Sgabeblack@google.com# Redistribution and use in source and binary forms, with or without 512841Sgabeblack@google.com# modification, are permitted provided that the following conditions are 612841Sgabeblack@google.com# met: redistributions of source code must retain the above copyright 712841Sgabeblack@google.com# notice, this list of conditions and the following disclaimer; 812841Sgabeblack@google.com# redistributions in binary form must reproduce the above copyright 912841Sgabeblack@google.com# notice, this list of conditions and the following disclaimer in the 1012841Sgabeblack@google.com# documentation and/or other materials provided with the distribution; 1112841Sgabeblack@google.com# neither the name of the copyright holders nor the names of its 1212841Sgabeblack@google.com# contributors may be used to endorse or promote products derived from 1312841Sgabeblack@google.com# this software without specific prior written permission. 1412841Sgabeblack@google.com# 1512841Sgabeblack@google.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1612841Sgabeblack@google.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1712841Sgabeblack@google.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1812841Sgabeblack@google.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1912841Sgabeblack@google.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2012841Sgabeblack@google.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2112841Sgabeblack@google.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2212841Sgabeblack@google.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2312841Sgabeblack@google.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2412841Sgabeblack@google.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2512841Sgabeblack@google.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2612841Sgabeblack@google.com# 2712841Sgabeblack@google.com# Authors: Steve Reinhardt 2812841Sgabeblack@google.com# Nathan Binkert 2912841Sgabeblack@google.com 3012841Sgabeblack@google.comimport os, re, sys, types, inspect 3112841Sgabeblack@google.com 3212841Sgabeblack@google.comimport m5 3312841Sgabeblack@google.comfrom m5 import panic 3412841Sgabeblack@google.comfrom convert import * 3513054Sgabeblack@google.comfrom multidict import multidict 3612841Sgabeblack@google.com 3713245Sgabeblack@google.comnoDot = False 3812841Sgabeblack@google.comtry: 3912841Sgabeblack@google.com import pydot 4012841Sgabeblack@google.comexcept: 4112841Sgabeblack@google.com noDot = True 4212841Sgabeblack@google.com 4312841Sgabeblack@google.comclass Singleton(type): 4412841Sgabeblack@google.com def __call__(cls, *args, **kwargs): 4512841Sgabeblack@google.com if hasattr(cls, '_instance'): 4612841Sgabeblack@google.com return cls._instance 4712841Sgabeblack@google.com 4812841Sgabeblack@google.com cls._instance = super(Singleton, cls).__call__(*args, **kwargs) 4912841Sgabeblack@google.com return cls._instance 5012841Sgabeblack@google.com 5113054Sgabeblack@google.com##################################################################### 5213054Sgabeblack@google.com# 5313054Sgabeblack@google.com# M5 Python Configuration Utility 5413054Sgabeblack@google.com# 5513054Sgabeblack@google.com# The basic idea is to write simple Python programs that build Python 5613054Sgabeblack@google.com# objects corresponding to M5 SimObjects for the desired simulation 5712841Sgabeblack@google.com# configuration. For now, the Python emits a .ini file that can be 5812841Sgabeblack@google.com# parsed by M5. In the future, some tighter integration between M5 5912868Sgabeblack@google.com# and the Python interpreter may allow bypassing the .ini file. 6012868Sgabeblack@google.com# 6113054Sgabeblack@google.com# Each SimObject class in M5 is represented by a Python class with the 6213054Sgabeblack@google.com# same name. The Python inheritance tree mirrors the M5 C++ tree 6312868Sgabeblack@google.com# (e.g., SimpleCPU derives from BaseCPU in both cases, and all 6412868Sgabeblack@google.com# SimObjects inherit from a single SimObject base class). To specify 6513054Sgabeblack@google.com# an instance of an M5 SimObject in a configuration, the user simply 6613054Sgabeblack@google.com# instantiates the corresponding Python object. The parameters for 6712868Sgabeblack@google.com# that SimObject are given by assigning to attributes of the Python 6812868Sgabeblack@google.com# object, either using keyword assignment in the constructor or in 6913054Sgabeblack@google.com# separate assignment statements. For example: 7013054Sgabeblack@google.com# 7112868Sgabeblack@google.com# cache = BaseCache(size='64KB') 7212868Sgabeblack@google.com# cache.hit_latency = 3 7313054Sgabeblack@google.com# cache.assoc = 8 7413054Sgabeblack@google.com# 7512868Sgabeblack@google.com# The magic lies in the mapping of the Python attributes for SimObject 7612868Sgabeblack@google.com# classes to the actual SimObject parameter specifications. This 7713054Sgabeblack@google.com# allows parameter validity checking in the Python code. Continuing 7813054Sgabeblack@google.com# the example above, the statements "cache.blurfl=3" or 7912868Sgabeblack@google.com# "cache.assoc='hello'" would both result in runtime errors in Python, 8012868Sgabeblack@google.com# since the BaseCache object has no 'blurfl' parameter and the 'assoc' 8113054Sgabeblack@google.com# parameter requires an integer, respectively. This magic is done 8213054Sgabeblack@google.com# primarily by overriding the special __setattr__ method that controls 8312868Sgabeblack@google.com# assignment to object attributes. 8412868Sgabeblack@google.com# 8512841Sgabeblack@google.com# Once a set of Python objects have been instantiated in a hierarchy, 8613054Sgabeblack@google.com# calling 'instantiate(obj)' (where obj is the root of the hierarchy) 8712841Sgabeblack@google.com# will generate a .ini file. See simple-4cpu.py for an example 8813054Sgabeblack@google.com# (corresponding to m5-test/simple-4cpu.ini). 8913054Sgabeblack@google.com# 9013054Sgabeblack@google.com##################################################################### 9113054Sgabeblack@google.com 9213054Sgabeblack@google.com##################################################################### 9313054Sgabeblack@google.com# 9413054Sgabeblack@google.com# ConfigNode/SimObject classes 9513054Sgabeblack@google.com# 9613054Sgabeblack@google.com# The Python class hierarchy rooted by ConfigNode (which is the base 9712841Sgabeblack@google.com# class of SimObject, which in turn is the base class of all other M5 9812841Sgabeblack@google.com# SimObject classes) has special attribute behavior. In general, an 9913054Sgabeblack@google.com# object in this hierarchy has three categories of attribute-like 10012841Sgabeblack@google.com# things: 10113054Sgabeblack@google.com# 10212841Sgabeblack@google.com# 1. Regular Python methods and variables. These must start with an 10312841Sgabeblack@google.com# underscore to be treated normally. 10412841Sgabeblack@google.com# 10513054Sgabeblack@google.com# 2. SimObject parameters. These values are stored as normal Python 10612841Sgabeblack@google.com# attributes, but all assignments to these attributes are checked 10713054Sgabeblack@google.com# against the pre-defined set of parameters stored in the class's 10812841Sgabeblack@google.com# _params dictionary. Assignments to attributes that do not 10912841Sgabeblack@google.com# correspond to predefined parameters, or that are not of the correct 11013054Sgabeblack@google.com# type, incur runtime errors. 11112841Sgabeblack@google.com# 11213054Sgabeblack@google.com# 3. Hierarchy children. The child nodes of a ConfigNode are stored 11312841Sgabeblack@google.com# in the node's _children dictionary, but can be accessed using the 11412841Sgabeblack@google.com# Python attribute dot-notation (just as they are printed out by the 11513245Sgabeblack@google.com# simulator). Children cannot be created using attribute assigment; 11613245Sgabeblack@google.com# they must be added by specifying the parent node in the child's 11713245Sgabeblack@google.com# constructor or using the '+=' operator. 11813245Sgabeblack@google.com 11913245Sgabeblack@google.com# The SimObject parameters are the most complex, for a few reasons. 12013245Sgabeblack@google.com# First, both parameter descriptions and parameter values are 12113245Sgabeblack@google.com# inherited. Thus parameter description lookup must go up the 12213245Sgabeblack@google.com# inheritance chain like normal attribute lookup, but this behavior 12312841Sgabeblack@google.com# must be explicitly coded since the lookup occurs in each class's 12413054Sgabeblack@google.com# _params attribute. Second, because parameter values can be set 12513054Sgabeblack@google.com# on SimObject classes (to implement default values), the parameter 12612841Sgabeblack@google.com# checking behavior must be enforced on class attribute assignments as 12713054Sgabeblack@google.com# well as instance attribute assignments. Finally, because we allow 12812841Sgabeblack@google.com# class specialization via inheritance (e.g., see the L1Cache class in 12912841Sgabeblack@google.com# the simple-4cpu.py example), we must do parameter checking even on 13012841Sgabeblack@google.com# class instantiation. To provide all these features, we use a 13113054Sgabeblack@google.com# metaclass to define most of the SimObject parameter behavior for 13212841Sgabeblack@google.com# this class hierarchy. 13313054Sgabeblack@google.com# 13413054Sgabeblack@google.com##################################################################### 13512841Sgabeblack@google.com 13612841Sgabeblack@google.comdef isSimObject(value): 13712841Sgabeblack@google.com return isinstance(value, SimObject) 13813245Sgabeblack@google.com 13913245Sgabeblack@google.comdef isSimObjectClass(value): 14013245Sgabeblack@google.com try: 14113245Sgabeblack@google.com return issubclass(value, SimObject) 14213245Sgabeblack@google.com except TypeError: 14313245Sgabeblack@google.com # happens if value is not a class at all 14412841Sgabeblack@google.com return False 14513054Sgabeblack@google.com 14613054Sgabeblack@google.comdef isSimObjSequence(value): 14713245Sgabeblack@google.com if not isinstance(value, (list, tuple)): 14813245Sgabeblack@google.com return False 14912841Sgabeblack@google.com 15013054Sgabeblack@google.com for val in value: 15113054Sgabeblack@google.com if not isNullPointer(val) and not isSimObject(val): 15212841Sgabeblack@google.com return False 15312841Sgabeblack@google.com 15412841Sgabeblack@google.com return True 15512841Sgabeblack@google.com 15613245Sgabeblack@google.comdef isSimObjClassSequence(value): 15712841Sgabeblack@google.com if not isinstance(value, (list, tuple)): 15813245Sgabeblack@google.com return False 15913245Sgabeblack@google.com 16013245Sgabeblack@google.com for val in value: 16113245Sgabeblack@google.com if not isNullPointer(val) and not isSimObjectClass(val): 16212841Sgabeblack@google.com return False 16312841Sgabeblack@google.com 16412841Sgabeblack@google.com return True 16512841Sgabeblack@google.com 16612841Sgabeblack@google.comdef isNullPointer(value): 16712841Sgabeblack@google.com return isinstance(value, NullSimObject) 16813054Sgabeblack@google.com 16913054Sgabeblack@google.com# The metaclass for ConfigNode (and thus for everything that derives 17013054Sgabeblack@google.com# from ConfigNode, including SimObject). This class controls how new 17113054Sgabeblack@google.com# classes that derive from ConfigNode are instantiated, and provides 17213054Sgabeblack@google.com# inherited class behavior (just like a class controls how instances 17313054Sgabeblack@google.com# of that class are instantiated, and provides inherited instance 17412841Sgabeblack@google.com# behavior). 17513054Sgabeblack@google.comclass MetaSimObject(type): 17613054Sgabeblack@google.com # Attributes that can be set only at initialization time 17713054Sgabeblack@google.com init_keywords = { 'abstract' : types.BooleanType, 17813054Sgabeblack@google.com 'type' : types.StringType } 17913054Sgabeblack@google.com # Attributes that can be set any time 18013054Sgabeblack@google.com keywords = { 'check' : types.FunctionType, 18112841Sgabeblack@google.com 'children' : types.ListType } 18212841Sgabeblack@google.com 18312868Sgabeblack@google.com # __new__ is called before __init__, and is where the statements 18412868Sgabeblack@google.com # in the body of the class definition get loaded into the class's 18513054Sgabeblack@google.com # __dict__. We intercept this to filter out parameter assignments 18613054Sgabeblack@google.com # and only allow "private" attributes to be passed to the base 18713054Sgabeblack@google.com # __new__ (starting with underscore). 18813054Sgabeblack@google.com def __new__(mcls, name, bases, dict): 18913054Sgabeblack@google.com if dict.has_key('_init_dict'): 19012868Sgabeblack@google.com # must have been called from makeSubclass() rather than 19112868Sgabeblack@google.com # via Python class declaration; bypass filtering process. 19213054Sgabeblack@google.com cls_dict = dict 19313054Sgabeblack@google.com else: 19413054Sgabeblack@google.com # Copy "private" attributes (including special methods 19513054Sgabeblack@google.com # such as __new__) to the official dict. Everything else 19613054Sgabeblack@google.com # goes in _init_dict to be filtered in __init__. 19712868Sgabeblack@google.com cls_dict = {} 19812868Sgabeblack@google.com for key,val in dict.items(): 19913054Sgabeblack@google.com if key.startswith('_'): 20013054Sgabeblack@google.com cls_dict[key] = val 20113054Sgabeblack@google.com del dict[key] 20213054Sgabeblack@google.com cls_dict['_init_dict'] = dict 20313054Sgabeblack@google.com return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) 20412868Sgabeblack@google.com 20512868Sgabeblack@google.com # subclass initialization 20613054Sgabeblack@google.com def __init__(cls, name, bases, dict): 20713054Sgabeblack@google.com # calls type.__init__()... I think that's a no-op, but leave 20813054Sgabeblack@google.com # it here just in case it's not. 20913054Sgabeblack@google.com super(MetaSimObject, cls).__init__(name, bases, dict) 21013054Sgabeblack@google.com 21112868Sgabeblack@google.com # initialize required attributes 21212868Sgabeblack@google.com cls._params = multidict() 21313054Sgabeblack@google.com cls._values = multidict() 21413054Sgabeblack@google.com cls._instantiated = False # really instantiated or subclassed 21513054Sgabeblack@google.com cls._anon_subclass_counter = 0 21613054Sgabeblack@google.com 21713054Sgabeblack@google.com # We don't support multiple inheritance. If you want to, you 21812868Sgabeblack@google.com # must fix multidict to deal with it properly. 21912868Sgabeblack@google.com if len(bases) > 1: 22013054Sgabeblack@google.com raise TypeError, "SimObjects do not support multiple inheritance" 22113054Sgabeblack@google.com 22213054Sgabeblack@google.com base = bases[0] 22313054Sgabeblack@google.com 22413054Sgabeblack@google.com # the only time the following is not true is when we define 22512868Sgabeblack@google.com # the SimObject class itself 22612868Sgabeblack@google.com if isinstance(base, MetaSimObject): 22712841Sgabeblack@google.com cls._params.parent = base._params 22813054Sgabeblack@google.com cls._values.parent = base._values 22912841Sgabeblack@google.com base._instantiated = True 23013054Sgabeblack@google.com 23113054Sgabeblack@google.com # now process the _init_dict items 23213054Sgabeblack@google.com for key,val in cls._init_dict.items(): 23313054Sgabeblack@google.com if isinstance(val, (types.FunctionType, types.TypeType)): 23413054Sgabeblack@google.com type.__setattr__(cls, key, val) 23513054Sgabeblack@google.com 23613054Sgabeblack@google.com # param descriptions 23713054Sgabeblack@google.com elif isinstance(val, ParamDesc): 23813054Sgabeblack@google.com cls._new_param(key, val) 23912841Sgabeblack@google.com 24012841Sgabeblack@google.com # init-time-only keywords 24113054Sgabeblack@google.com elif cls.init_keywords.has_key(key): 24212841Sgabeblack@google.com cls._set_keyword(key, val, cls.init_keywords[key]) 24313054Sgabeblack@google.com 24412841Sgabeblack@google.com # default: use normal path (ends up in __setattr__) 24512841Sgabeblack@google.com else: 24612841Sgabeblack@google.com setattr(cls, key, val) 24713054Sgabeblack@google.com 24812841Sgabeblack@google.com # Pull the deep-copy memoization dict out of the class dict if 24913054Sgabeblack@google.com # it's there... 25012841Sgabeblack@google.com memo = cls.__dict__.get('_memo', {}) 25112841Sgabeblack@google.com 25213054Sgabeblack@google.com # Handle SimObject values 25312841Sgabeblack@google.com for key,val in cls._values.iteritems(): 25413054Sgabeblack@google.com # SimObject instances need to be promoted to classes. 25512841Sgabeblack@google.com # Existing classes should not have any instance values, so 25612841Sgabeblack@google.com # these can only occur at the lowest level dict (the 25713245Sgabeblack@google.com # parameters just being set in this class definition). 25813245Sgabeblack@google.com if isSimObject(val): 25913245Sgabeblack@google.com assert(val == cls._values.local[key]) 26013245Sgabeblack@google.com cls._values[key] = val.makeClass(memo) 26113245Sgabeblack@google.com elif isSimObjSequence(val) and len(val): 26213245Sgabeblack@google.com assert(val == cls._values.local[key]) 26313245Sgabeblack@google.com cls._values[key] = [ v.makeClass(memo) for v in val ] 26413245Sgabeblack@google.com # SimObject classes need to be subclassed so that 26512841Sgabeblack@google.com # parameters that get set at this level only affect this 26613054Sgabeblack@google.com # level and derivatives. 26713054Sgabeblack@google.com elif isSimObjectClass(val): 26812841Sgabeblack@google.com assert(not cls._values.local.has_key(key)) 26913054Sgabeblack@google.com cls._values[key] = val.makeSubclass({}, memo) 27012841Sgabeblack@google.com elif isSimObjClassSequence(val) and len(val): 27112841Sgabeblack@google.com assert(not cls._values.local.has_key(key)) 27212841Sgabeblack@google.com cls._values[key] = [ v.makeSubclass({}, memo) for v in val ] 27313054Sgabeblack@google.com 27412841Sgabeblack@google.com 27512841Sgabeblack@google.com def _set_keyword(cls, keyword, val, kwtype): 27612841Sgabeblack@google.com if not isinstance(val, kwtype): 27712841Sgabeblack@google.com raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ 27813054Sgabeblack@google.com (keyword, type(val), kwtype) 27912841Sgabeblack@google.com if isinstance(val, types.FunctionType): 28012841Sgabeblack@google.com val = classmethod(val) 28112841Sgabeblack@google.com type.__setattr__(cls, keyword, val) 28212841Sgabeblack@google.com 28313054Sgabeblack@google.com def _new_param(cls, name, value): 28412841Sgabeblack@google.com cls._params[name] = value 28512841Sgabeblack@google.com if hasattr(value, 'default'): 28613054Sgabeblack@google.com setattr(cls, name, value.default) 28713054Sgabeblack@google.com 28813054Sgabeblack@google.com # Set attribute (called on foo.attr = value when foo is an 28912841Sgabeblack@google.com # instance of class cls). 29013054Sgabeblack@google.com def __setattr__(cls, attr, value): 29113054Sgabeblack@google.com # normal processing for private attributes 29213054Sgabeblack@google.com if attr.startswith('_'): 29312841Sgabeblack@google.com type.__setattr__(cls, attr, value) 29412841Sgabeblack@google.com return 29512841Sgabeblack@google.com 29613245Sgabeblack@google.com if cls.keywords.has_key(attr): 29713245Sgabeblack@google.com cls._set_keyword(attr, value, cls.keywords[attr]) 29813245Sgabeblack@google.com return 29913245Sgabeblack@google.com 30013245Sgabeblack@google.com # must be SimObject param 30113245Sgabeblack@google.com param = cls._params.get(attr, None) 30212841Sgabeblack@google.com if param: 30313054Sgabeblack@google.com # It's ok: set attribute by delegating to 'object' class. 30413054Sgabeblack@google.com if (isSimObject(value) or isSimObjSequence(value)) \ 30513054Sgabeblack@google.com and cls._instantiated: 30613054Sgabeblack@google.com raise AttributeError, \ 30713245Sgabeblack@google.com "Cannot set SimObject parameter '%s' after\n" \ 30813245Sgabeblack@google.com " class %s has been instantiated or subclassed" \ 30912841Sgabeblack@google.com % (attr, cls.__name__) 31013054Sgabeblack@google.com try: 31113054Sgabeblack@google.com cls._values[attr] = param.convert(value) 31212841Sgabeblack@google.com except Exception, e: 31312841Sgabeblack@google.com msg = "%s\nError setting param %s.%s to %s\n" % \ 31412841Sgabeblack@google.com (e, cls.__name__, attr, value) 31512841Sgabeblack@google.com e.args = (msg, ) 31613245Sgabeblack@google.com raise 31713245Sgabeblack@google.com # I would love to get rid of this 31812841Sgabeblack@google.com elif isSimObject(value) or isSimObjSequence(value): 31913245Sgabeblack@google.com cls._values[attr] = value 32013245Sgabeblack@google.com else: 32113245Sgabeblack@google.com raise AttributeError, \ 32213245Sgabeblack@google.com "Class %s has no parameter %s" % (cls.__name__, attr) 32312841Sgabeblack@google.com 32412841Sgabeblack@google.com def __getattr__(cls, attr): 32512841Sgabeblack@google.com if cls._values.has_key(attr): 32612841Sgabeblack@google.com return cls._values[attr] 32712841Sgabeblack@google.com 32812841Sgabeblack@google.com raise AttributeError, \ 32912841Sgabeblack@google.com "object '%s' has no attribute '%s'" % (cls.__name__, attr) 33013054Sgabeblack@google.com 33113054Sgabeblack@google.com # Create a subclass of this class. Basically a function interface 33213054Sgabeblack@google.com # to the standard Python class definition mechanism, primarily for 33313054Sgabeblack@google.com # internal use. 'memo' dict param supports "deep copy" (really 33413054Sgabeblack@google.com # "deep subclass") operations... within a given operation, 33513054Sgabeblack@google.com # multiple references to a class should result in a single 33612841Sgabeblack@google.com # subclass object with multiple references to it (as opposed to 33713054Sgabeblack@google.com # mutiple unique subclasses). 33813054Sgabeblack@google.com def makeSubclass(cls, init_dict, memo = {}): 33913054Sgabeblack@google.com subcls = memo.get(cls) 34013054Sgabeblack@google.com if not subcls: 34113054Sgabeblack@google.com name = cls.__name__ + '_' + str(cls._anon_subclass_counter) 34212841Sgabeblack@google.com cls._anon_subclass_counter += 1 34312841Sgabeblack@google.com subcls = MetaSimObject(name, (cls,), 34412841Sgabeblack@google.com { '_init_dict': init_dict, '_memo': memo }) 34512868Sgabeblack@google.com return subcls 34612868Sgabeblack@google.com 34713054Sgabeblack@google.com# The ConfigNode class is the root of the special hierarchy. Most of 34813054Sgabeblack@google.com# the code in this class deals with the configuration hierarchy itself 34913054Sgabeblack@google.com# (parent/child node relationships). 35013054Sgabeblack@google.comclass SimObject(object): 35113054Sgabeblack@google.com # Specify metaclass. Any class inheriting from SimObject will 35212868Sgabeblack@google.com # get this metaclass. 35312868Sgabeblack@google.com __metaclass__ = MetaSimObject 35412868Sgabeblack@google.com 35513054Sgabeblack@google.com # __new__ operator allocates new instances of the class. We 35613054Sgabeblack@google.com # override it here just to support "deep instantiation" operation 35713054Sgabeblack@google.com # via the _memo dict. When recursively instantiating an object 35813054Sgabeblack@google.com # hierarchy we want to make sure that each class is instantiated 35913054Sgabeblack@google.com # only once, and that if there are multiple references to the same 36012868Sgabeblack@google.com # original class, we end up with the corresponding instantiated 36112868Sgabeblack@google.com # references all pointing to the same instance. 36213054Sgabeblack@google.com def __new__(cls, _memo = None, **kwargs): 36313054Sgabeblack@google.com if _memo is not None and _memo.has_key(cls): 36413054Sgabeblack@google.com # return previously instantiated object 36513054Sgabeblack@google.com assert(len(kwargs) == 0) 36613054Sgabeblack@google.com return _memo[cls] 36712868Sgabeblack@google.com else: 36812868Sgabeblack@google.com # Need a new one... if it needs to be memoized, this will 36912868Sgabeblack@google.com # happen in __init__. We defer the insertion until then 37013054Sgabeblack@google.com # so __init__ can use the memo dict to tell whether or not 37113054Sgabeblack@google.com # to perform the initialization. 37213054Sgabeblack@google.com return super(SimObject, cls).__new__(cls, **kwargs) 37313054Sgabeblack@google.com 37413054Sgabeblack@google.com # Initialize new instance previously allocated by __new__. For 37512868Sgabeblack@google.com # objects with SimObject-valued params, we need to recursively 37612868Sgabeblack@google.com # instantiate the classes represented by those param values as 37713054Sgabeblack@google.com # well (in a consistent "deep copy"-style fashion; see comment 37813054Sgabeblack@google.com # above). 37913054Sgabeblack@google.com def __init__(self, _memo = None, **kwargs): 38013054Sgabeblack@google.com if _memo is not None: 38113054Sgabeblack@google.com # We're inside a "deep instantiation" 38212868Sgabeblack@google.com assert(isinstance(_memo, dict)) 38312868Sgabeblack@google.com assert(len(kwargs) == 0) 38412868Sgabeblack@google.com if _memo.has_key(self.__class__): 38513054Sgabeblack@google.com # __new__ returned an existing, already initialized 38613054Sgabeblack@google.com # instance, so there's nothing to do here 38713054Sgabeblack@google.com assert(_memo[self.__class__] == self) 38813054Sgabeblack@google.com return 38913054Sgabeblack@google.com # no pre-existing object, so remember this one here 39012868Sgabeblack@google.com _memo[self.__class__] = self 39112868Sgabeblack@google.com else: 39212841Sgabeblack@google.com # This is a new top-level instantiation... don't memoize 39313054Sgabeblack@google.com # this objcet, but prepare to memoize any recursively 39412841Sgabeblack@google.com # instantiated objects. 39513054Sgabeblack@google.com _memo = {} 39613054Sgabeblack@google.com 39712841Sgabeblack@google.com self.__class__._instantiated = True 39812841Sgabeblack@google.com 39913054Sgabeblack@google.com self._children = {} 40013054Sgabeblack@google.com # Inherit parameter values from class using multidict so 40113054Sgabeblack@google.com # individual value settings can be overridden. 40213054Sgabeblack@google.com self._values = multidict(self.__class__._values) 40312841Sgabeblack@google.com # For SimObject-valued parameters, the class should have 40413054Sgabeblack@google.com # classes (not instances) for the values. We need to 40513054Sgabeblack@google.com # instantiate these classes rather than just inheriting the 40613054Sgabeblack@google.com # class object. 40713054Sgabeblack@google.com for key,val in self.__class__._values.iteritems(): 40813054Sgabeblack@google.com if isSimObjectClass(val): 40913054Sgabeblack@google.com setattr(self, key, val(_memo)) 41012841Sgabeblack@google.com elif isSimObjClassSequence(val) and len(val): 41112841Sgabeblack@google.com setattr(self, key, [ v(_memo) for v in val ]) 41212841Sgabeblack@google.com # apply attribute assignments from keyword args, if any 41313054Sgabeblack@google.com for key,val in kwargs.iteritems(): 41412841Sgabeblack@google.com setattr(self, key, val) 41513054Sgabeblack@google.com 41612841Sgabeblack@google.com # Use this instance as a template to create a new class. 41712841Sgabeblack@google.com def makeClass(self, memo = {}): 41813054Sgabeblack@google.com cls = memo.get(self) 41912841Sgabeblack@google.com if not cls: 42013054Sgabeblack@google.com cls = self.__class__.makeSubclass(self._values.local) 42112841Sgabeblack@google.com memo[self] = cls 42212841Sgabeblack@google.com return cls 42313245Sgabeblack@google.com 42413245Sgabeblack@google.com # Direct instantiation of instances (cloning) is no longer 42513245Sgabeblack@google.com # allowed; must generate class from instance first. 42613245Sgabeblack@google.com def __call__(self, **kwargs): 42713245Sgabeblack@google.com raise TypeError, "cannot instantiate SimObject; "\ 42813245Sgabeblack@google.com "use makeClass() to make class first" 42913245Sgabeblack@google.com 43013245Sgabeblack@google.com def __getattr__(self, attr): 43112841Sgabeblack@google.com if self._values.has_key(attr): 43213054Sgabeblack@google.com return self._values[attr] 43313054Sgabeblack@google.com 43412841Sgabeblack@google.com raise AttributeError, "object '%s' has no attribute '%s'" \ 43513054Sgabeblack@google.com % (self.__class__.__name__, attr) 43612841Sgabeblack@google.com 43712841Sgabeblack@google.com # Set attribute (called on foo.attr = value when foo is an 43812841Sgabeblack@google.com # instance of class cls). 43913054Sgabeblack@google.com def __setattr__(self, attr, value): 44012841Sgabeblack@google.com # normal processing for private attributes 44113054Sgabeblack@google.com if attr.startswith('_'): 44213054Sgabeblack@google.com object.__setattr__(self, attr, value) 44312841Sgabeblack@google.com return 44413054Sgabeblack@google.com 44513054Sgabeblack@google.com # must be SimObject param 44613054Sgabeblack@google.com param = self._params.get(attr, None) 44712841Sgabeblack@google.com if param: 44813054Sgabeblack@google.com # It's ok: set attribute by delegating to 'object' class. 44913054Sgabeblack@google.com try: 45013054Sgabeblack@google.com value = param.convert(value) 45112841Sgabeblack@google.com except Exception, e: 45212841Sgabeblack@google.com msg = "%s\nError setting param %s.%s to %s\n" % \ 45312841Sgabeblack@google.com (e, self.__class__.__name__, attr, value) 45413245Sgabeblack@google.com e.args = (msg, ) 45513245Sgabeblack@google.com raise 45613245Sgabeblack@google.com # I would love to get rid of this 45713245Sgabeblack@google.com elif isSimObject(value) or isSimObjSequence(value): 45813245Sgabeblack@google.com pass 45913245Sgabeblack@google.com else: 46012841Sgabeblack@google.com raise AttributeError, "Class %s has no parameter %s" \ 46113054Sgabeblack@google.com % (self.__class__.__name__, attr) 46213054Sgabeblack@google.com 46313054Sgabeblack@google.com # clear out old child with this name, if any 46413054Sgabeblack@google.com self.clear_child(attr) 46513054Sgabeblack@google.com 46613245Sgabeblack@google.com if isSimObject(value): 46713245Sgabeblack@google.com value.set_path(self, attr) 46812841Sgabeblack@google.com elif isSimObjSequence(value): 46913054Sgabeblack@google.com value = SimObjVector(value) 47013054Sgabeblack@google.com [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)] 47112841Sgabeblack@google.com 47212841Sgabeblack@google.com self._values[attr] = value 47312841Sgabeblack@google.com 47412841Sgabeblack@google.com # this hack allows tacking a '[0]' onto parameters that may or may 47513245Sgabeblack@google.com # not be vectors, and always getting the first element (e.g. cpus) 47613245Sgabeblack@google.com def __getitem__(self, key): 47712841Sgabeblack@google.com if key == 0: 47813245Sgabeblack@google.com return self 47913245Sgabeblack@google.com raise TypeError, "Non-zero index '%s' to SimObject" % key 48013245Sgabeblack@google.com 48113245Sgabeblack@google.com # clear out children with given name, even if it's a vector 48212841Sgabeblack@google.com def clear_child(self, name): 48312841Sgabeblack@google.com if not self._children.has_key(name): 48412841Sgabeblack@google.com return 48512841Sgabeblack@google.com child = self._children[name] 48612841Sgabeblack@google.com if isinstance(child, SimObjVector): 487 for i in xrange(len(child)): 488 del self._children["s%d" % (name, i)] 489 del self._children[name] 490 491 def add_child(self, name, value): 492 self._children[name] = value 493 494 def set_path(self, parent, name): 495 if not hasattr(self, '_parent'): 496 self._parent = parent 497 self._name = name 498 parent.add_child(name, self) 499 500 def path(self): 501 if not hasattr(self, '_parent'): 502 return 'root' 503 ppath = self._parent.path() 504 if ppath == 'root': 505 return self._name 506 return ppath + "." + self._name 507 508 def __str__(self): 509 return self.path() 510 511 def ini_str(self): 512 return self.path() 513 514 def find_any(self, ptype): 515 if isinstance(self, ptype): 516 return self, True 517 518 found_obj = None 519 for child in self._children.itervalues(): 520 if isinstance(child, ptype): 521 if found_obj != None and child != found_obj: 522 raise AttributeError, \ 523 'parent.any matched more than one: %s %s' % \ 524 (found_obj.path, child.path) 525 found_obj = child 526 # search param space 527 for pname,pdesc in self._params.iteritems(): 528 if issubclass(pdesc.ptype, ptype): 529 match_obj = self._values[pname] 530 if found_obj != None and found_obj != match_obj: 531 raise AttributeError, \ 532 'parent.any matched more than one: %s' % obj.path 533 found_obj = match_obj 534 return found_obj, found_obj != None 535 536 def unproxy(self, base): 537 return self 538 539 def print_ini(self): 540 print '[' + self.path() + ']' # .ini section header 541 542 if hasattr(self, 'type') and not isinstance(self, ParamContext): 543 print 'type=%s' % self.type 544 545 child_names = self._children.keys() 546 child_names.sort() 547 np_child_names = [c for c in child_names \ 548 if not isinstance(self._children[c], ParamContext)] 549 if len(np_child_names): 550 print 'children=%s' % ' '.join(np_child_names) 551 552 param_names = self._params.keys() 553 param_names.sort() 554 for param in param_names: 555 value = self._values.get(param, None) 556 if value != None: 557 if isproxy(value): 558 try: 559 value = value.unproxy(self) 560 except: 561 print >> sys.stderr, \ 562 "Error in unproxying param '%s' of %s" % \ 563 (param, self.path()) 564 raise 565 setattr(self, param, value) 566 print '%s=%s' % (param, self._values[param].ini_str()) 567 568 print # blank line between objects 569 570 for child in child_names: 571 self._children[child].print_ini() 572 573 # generate output file for 'dot' to display as a pretty graph. 574 # this code is currently broken. 575 def outputDot(self, dot): 576 label = "{%s|" % self.path 577 if isSimObject(self.realtype): 578 label += '%s|' % self.type 579 580 if self.children: 581 # instantiate children in same order they were added for 582 # backward compatibility (else we can end up with cpu1 583 # before cpu0). 584 for c in self.children: 585 dot.add_edge(pydot.Edge(self.path,c.path, style="bold")) 586 587 simobjs = [] 588 for param in self.params: 589 try: 590 if param.value is None: 591 raise AttributeError, 'Parameter with no value' 592 593 value = param.value 594 string = param.string(value) 595 except Exception, e: 596 msg = 'exception in %s:%s\n%s' % (self.name, param.name, e) 597 e.args = (msg, ) 598 raise 599 600 if isSimObject(param.ptype) and string != "Null": 601 simobjs.append(string) 602 else: 603 label += '%s = %s\\n' % (param.name, string) 604 605 for so in simobjs: 606 label += "|<%s> %s" % (so, so) 607 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, 608 tailport="w")) 609 label += '}' 610 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label)) 611 612 # recursively dump out children 613 for c in self.children: 614 c.outputDot(dot) 615 616class ParamContext(SimObject): 617 pass 618 619##################################################################### 620# 621# Proxy object support. 622# 623##################################################################### 624 625class BaseProxy(object): 626 def __init__(self, search_self, search_up): 627 self._search_self = search_self 628 self._search_up = search_up 629 self._multiplier = None 630 631 def __setattr__(self, attr, value): 632 if not attr.startswith('_'): 633 raise AttributeError, 'cannot set attribute on proxy object' 634 super(BaseProxy, self).__setattr__(attr, value) 635 636 # support multiplying proxies by constants 637 def __mul__(self, other): 638 if not isinstance(other, (int, long, float)): 639 raise TypeError, "Proxy multiplier must be integer" 640 if self._multiplier == None: 641 self._multiplier = other 642 else: 643 # support chained multipliers 644 self._multiplier *= other 645 return self 646 647 __rmul__ = __mul__ 648 649 def _mulcheck(self, result): 650 if self._multiplier == None: 651 return result 652 return result * self._multiplier 653 654 def unproxy(self, base): 655 obj = base 656 done = False 657 658 if self._search_self: 659 result, done = self.find(obj) 660 661 if self._search_up: 662 while not done: 663 try: obj = obj._parent 664 except: break 665 666 result, done = self.find(obj) 667 668 if not done: 669 raise AttributeError, "Can't resolve proxy '%s' from '%s'" % \ 670 (self.path(), base.path()) 671 672 if isinstance(result, BaseProxy): 673 if result == self: 674 raise RuntimeError, "Cycle in unproxy" 675 result = result.unproxy(obj) 676 677 return self._mulcheck(result) 678 679 def getindex(obj, index): 680 if index == None: 681 return obj 682 try: 683 obj = obj[index] 684 except TypeError: 685 if index != 0: 686 raise 687 # if index is 0 and item is not subscriptable, just 688 # use item itself (so cpu[0] works on uniprocessors) 689 return obj 690 getindex = staticmethod(getindex) 691 692 def set_param_desc(self, pdesc): 693 self._pdesc = pdesc 694 695class AttrProxy(BaseProxy): 696 def __init__(self, search_self, search_up, attr): 697 super(AttrProxy, self).__init__(search_self, search_up) 698 self._attr = attr 699 self._modifiers = [] 700 701 def __getattr__(self, attr): 702 # python uses __bases__ internally for inheritance 703 if attr.startswith('_'): 704 return super(AttrProxy, self).__getattr__(self, attr) 705 if hasattr(self, '_pdesc'): 706 raise AttributeError, "Attribute reference on bound proxy" 707 self._modifiers.append(attr) 708 return self 709 710 # support indexing on proxies (e.g., Self.cpu[0]) 711 def __getitem__(self, key): 712 if not isinstance(key, int): 713 raise TypeError, "Proxy object requires integer index" 714 self._modifiers.append(key) 715 return self 716 717 def find(self, obj): 718 try: 719 val = getattr(obj, self._attr) 720 except: 721 return None, False 722 while isproxy(val): 723 val = val.unproxy(obj) 724 for m in self._modifiers: 725 if isinstance(m, str): 726 val = getattr(val, m) 727 elif isinstance(m, int): 728 val = val[m] 729 else: 730 assert("Item must be string or integer") 731 while isproxy(val): 732 val = val.unproxy(obj) 733 return val, True 734 735 def path(self): 736 p = self._attr 737 for m in self._modifiers: 738 if isinstance(m, str): 739 p += '.%s' % m 740 elif isinstance(m, int): 741 p += '[%d]' % m 742 else: 743 assert("Item must be string or integer") 744 return p 745 746class AnyProxy(BaseProxy): 747 def find(self, obj): 748 return obj.find_any(self._pdesc.ptype) 749 750 def path(self): 751 return 'any' 752 753def isproxy(obj): 754 if isinstance(obj, (BaseProxy, EthernetAddr)): 755 return True 756 elif isinstance(obj, (list, tuple)): 757 for v in obj: 758 if isproxy(v): 759 return True 760 return False 761 762class ProxyFactory(object): 763 def __init__(self, search_self, search_up): 764 self.search_self = search_self 765 self.search_up = search_up 766 767 def __getattr__(self, attr): 768 if attr == 'any': 769 return AnyProxy(self.search_self, self.search_up) 770 else: 771 return AttrProxy(self.search_self, self.search_up, attr) 772 773# global objects for handling proxies 774Parent = ProxyFactory(search_self = False, search_up = True) 775Self = ProxyFactory(search_self = True, search_up = False) 776 777##################################################################### 778# 779# Parameter description classes 780# 781# The _params dictionary in each class maps parameter names to 782# either a Param or a VectorParam object. These objects contain the 783# parameter description string, the parameter type, and the default 784# value (loaded from the PARAM section of the .odesc files). The 785# _convert() method on these objects is used to force whatever value 786# is assigned to the parameter to the appropriate type. 787# 788# Note that the default values are loaded into the class's attribute 789# space when the parameter dictionary is initialized (in 790# MetaConfigNode._setparams()); after that point they aren't used. 791# 792##################################################################### 793 794# Dummy base class to identify types that are legitimate for SimObject 795# parameters. 796class ParamValue(object): 797 798 # default for printing to .ini file is regular string conversion. 799 # will be overridden in some cases 800 def ini_str(self): 801 return str(self) 802 803 # allows us to blithely call unproxy() on things without checking 804 # if they're really proxies or not 805 def unproxy(self, base): 806 return self 807 808# Regular parameter description. 809class ParamDesc(object): 810 def __init__(self, ptype_str, ptype, *args, **kwargs): 811 self.ptype_str = ptype_str 812 # remember ptype only if it is provided 813 if ptype != None: 814 self.ptype = ptype 815 816 if args: 817 if len(args) == 1: 818 self.desc = args[0] 819 elif len(args) == 2: 820 self.default = args[0] 821 self.desc = args[1] 822 else: 823 raise TypeError, 'too many arguments' 824 825 if kwargs.has_key('desc'): 826 assert(not hasattr(self, 'desc')) 827 self.desc = kwargs['desc'] 828 del kwargs['desc'] 829 830 if kwargs.has_key('default'): 831 assert(not hasattr(self, 'default')) 832 self.default = kwargs['default'] 833 del kwargs['default'] 834 835 if kwargs: 836 raise TypeError, 'extra unknown kwargs %s' % kwargs 837 838 if not hasattr(self, 'desc'): 839 raise TypeError, 'desc attribute missing' 840 841 def __getattr__(self, attr): 842 if attr == 'ptype': 843 try: 844 ptype = eval(self.ptype_str, m5.objects.__dict__) 845 if not isinstance(ptype, type): 846 panic("Param qualifier is not a type: %s" % self.ptype) 847 self.ptype = ptype 848 return ptype 849 except NameError: 850 pass 851 raise AttributeError, "'%s' object has no attribute '%s'" % \ 852 (type(self).__name__, attr) 853 854 def convert(self, value): 855 if isinstance(value, BaseProxy): 856 value.set_param_desc(self) 857 return value 858 if not hasattr(self, 'ptype') and isNullPointer(value): 859 # deferred evaluation of SimObject; continue to defer if 860 # we're just assigning a null pointer 861 return value 862 if isinstance(value, self.ptype): 863 return value 864 if isNullPointer(value) and issubclass(self.ptype, SimObject): 865 return value 866 return self.ptype(value) 867 868# Vector-valued parameter description. Just like ParamDesc, except 869# that the value is a vector (list) of the specified type instead of a 870# single value. 871 872class VectorParamValue(list): 873 def ini_str(self): 874 return ' '.join([v.ini_str() for v in self]) 875 876 def unproxy(self, base): 877 return [v.unproxy(base) for v in self] 878 879class SimObjVector(VectorParamValue): 880 def print_ini(self): 881 for v in self: 882 v.print_ini() 883 884class VectorParamDesc(ParamDesc): 885 # Convert assigned value to appropriate type. If the RHS is not a 886 # list or tuple, it generates a single-element list. 887 def convert(self, value): 888 if isinstance(value, (list, tuple)): 889 # list: coerce each element into new list 890 tmp_list = [ ParamDesc.convert(self, v) for v in value ] 891 if isSimObjSequence(tmp_list): 892 return SimObjVector(tmp_list) 893 else: 894 return VectorParamValue(tmp_list) 895 else: 896 # singleton: leave it be (could coerce to a single-element 897 # list here, but for some historical reason we don't... 898 return ParamDesc.convert(self, value) 899 900 901class ParamFactory(object): 902 def __init__(self, param_desc_class, ptype_str = None): 903 self.param_desc_class = param_desc_class 904 self.ptype_str = ptype_str 905 906 def __getattr__(self, attr): 907 if self.ptype_str: 908 attr = self.ptype_str + '.' + attr 909 return ParamFactory(self.param_desc_class, attr) 910 911 # E.g., Param.Int(5, "number of widgets") 912 def __call__(self, *args, **kwargs): 913 caller_frame = inspect.currentframe().f_back 914 ptype = None 915 try: 916 ptype = eval(self.ptype_str, 917 caller_frame.f_globals, caller_frame.f_locals) 918 if not isinstance(ptype, type): 919 raise TypeError, \ 920 "Param qualifier is not a type: %s" % ptype 921 except NameError: 922 # if name isn't defined yet, assume it's a SimObject, and 923 # try to resolve it later 924 pass 925 return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) 926 927Param = ParamFactory(ParamDesc) 928VectorParam = ParamFactory(VectorParamDesc) 929 930##################################################################### 931# 932# Parameter Types 933# 934# Though native Python types could be used to specify parameter types 935# (the 'ptype' field of the Param and VectorParam classes), it's more 936# flexible to define our own set of types. This gives us more control 937# over how Python expressions are converted to values (via the 938# __init__() constructor) and how these values are printed out (via 939# the __str__() conversion method). Eventually we'll need these types 940# to correspond to distinct C++ types as well. 941# 942##################################################################### 943 944# superclass for "numeric" parameter values, to emulate math 945# operations in a type-safe way. e.g., a Latency times an int returns 946# a new Latency object. 947class NumericParamValue(ParamValue): 948 def __str__(self): 949 return str(self.value) 950 951 def __float__(self): 952 return float(self.value) 953 954 # hook for bounds checking 955 def _check(self): 956 return 957 958 def __mul__(self, other): 959 newobj = self.__class__(self) 960 newobj.value *= other 961 newobj._check() 962 return newobj 963 964 __rmul__ = __mul__ 965 966 def __div__(self, other): 967 newobj = self.__class__(self) 968 newobj.value /= other 969 newobj._check() 970 return newobj 971 972 def __sub__(self, other): 973 newobj = self.__class__(self) 974 newobj.value -= other 975 newobj._check() 976 return newobj 977 978class Range(ParamValue): 979 type = int # default; can be overridden in subclasses 980 def __init__(self, *args, **kwargs): 981 982 def handle_kwargs(self, kwargs): 983 if 'end' in kwargs: 984 self.second = self.type(kwargs.pop('end')) 985 elif 'size' in kwargs: 986 self.second = self.first + self.type(kwargs.pop('size')) - 1 987 else: 988 raise TypeError, "Either end or size must be specified" 989 990 if len(args) == 0: 991 self.first = self.type(kwargs.pop('start')) 992 handle_kwargs(self, kwargs) 993 994 elif len(args) == 1: 995 if kwargs: 996 self.first = self.type(args[0]) 997 handle_kwargs(self, kwargs) 998 elif isinstance(args[0], Range): 999 self.first = self.type(args[0].first) 1000 self.second = self.type(args[0].second) 1001 else: 1002 self.first = self.type(0) 1003 self.second = self.type(args[0]) - 1 1004 1005 elif len(args) == 2: 1006 self.first = self.type(args[0]) 1007 self.second = self.type(args[1]) 1008 else: 1009 raise TypeError, "Too many arguments specified" 1010 1011 if kwargs: 1012 raise TypeError, "too many keywords: %s" % kwargs.keys() 1013 1014 def __str__(self): 1015 return '%s:%s' % (self.first, self.second) 1016 1017# Metaclass for bounds-checked integer parameters. See CheckedInt. 1018class CheckedIntType(type): 1019 def __init__(cls, name, bases, dict): 1020 super(CheckedIntType, cls).__init__(name, bases, dict) 1021 1022 # CheckedInt is an abstract base class, so we actually don't 1023 # want to do any processing on it... the rest of this code is 1024 # just for classes that derive from CheckedInt. 1025 if name == 'CheckedInt': 1026 return 1027 1028 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 1029 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 1030 panic("CheckedInt subclass %s must define either\n" \ 1031 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 1032 % name); 1033 if cls.unsigned: 1034 cls.min = 0 1035 cls.max = 2 ** cls.size - 1 1036 else: 1037 cls.min = -(2 ** (cls.size - 1)) 1038 cls.max = (2 ** (cls.size - 1)) - 1 1039 1040# Abstract superclass for bounds-checked integer parameters. This 1041# class is subclassed to generate parameter classes with specific 1042# bounds. Initialization of the min and max bounds is done in the 1043# metaclass CheckedIntType.__init__. 1044class CheckedInt(NumericParamValue): 1045 __metaclass__ = CheckedIntType 1046 1047 def _check(self): 1048 if not self.min <= self.value <= self.max: 1049 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 1050 (self.min, self.value, self.max) 1051 1052 def __init__(self, value): 1053 if isinstance(value, str): 1054 self.value = toInteger(value) 1055 elif isinstance(value, (int, long, float)): 1056 self.value = long(value) 1057 self._check() 1058 1059class Int(CheckedInt): size = 32; unsigned = False 1060class Unsigned(CheckedInt): size = 32; unsigned = True 1061 1062class Int8(CheckedInt): size = 8; unsigned = False 1063class UInt8(CheckedInt): size = 8; unsigned = True 1064class Int16(CheckedInt): size = 16; unsigned = False 1065class UInt16(CheckedInt): size = 16; unsigned = True 1066class Int32(CheckedInt): size = 32; unsigned = False 1067class UInt32(CheckedInt): size = 32; unsigned = True 1068class Int64(CheckedInt): size = 64; unsigned = False 1069class UInt64(CheckedInt): size = 64; unsigned = True 1070 1071class Counter(CheckedInt): size = 64; unsigned = True 1072class Tick(CheckedInt): size = 64; unsigned = True 1073class TcpPort(CheckedInt): size = 16; unsigned = True 1074class UdpPort(CheckedInt): size = 16; unsigned = True 1075 1076class Percent(CheckedInt): min = 0; max = 100 1077 1078class Float(ParamValue, float): 1079 pass 1080 1081class MemorySize(CheckedInt): 1082 size = 64 1083 unsigned = True 1084 def __init__(self, value): 1085 if isinstance(value, MemorySize): 1086 self.value = value.value 1087 else: 1088 self.value = toMemorySize(value) 1089 self._check() 1090 1091class MemorySize32(CheckedInt): 1092 size = 32 1093 unsigned = True 1094 def __init__(self, value): 1095 if isinstance(value, MemorySize): 1096 self.value = value.value 1097 else: 1098 self.value = toMemorySize(value) 1099 self._check() 1100 1101class Addr(CheckedInt): 1102 size = 64 1103 unsigned = True 1104 def __init__(self, value): 1105 if isinstance(value, Addr): 1106 self.value = value.value 1107 else: 1108 try: 1109 self.value = toMemorySize(value) 1110 except TypeError: 1111 self.value = long(value) 1112 self._check() 1113 1114class AddrRange(Range): 1115 type = Addr 1116 1117# String-valued parameter. Just mixin the ParamValue class 1118# with the built-in str class. 1119class String(ParamValue,str): 1120 pass 1121 1122# Boolean parameter type. Python doesn't let you subclass bool, since 1123# it doesn't want to let you create multiple instances of True and 1124# False. Thus this is a little more complicated than String. 1125class Bool(ParamValue): 1126 def __init__(self, value): 1127 try: 1128 self.value = toBool(value) 1129 except TypeError: 1130 self.value = bool(value) 1131 1132 def __str__(self): 1133 return str(self.value) 1134 1135 def ini_str(self): 1136 if self.value: 1137 return 'true' 1138 return 'false' 1139 1140def IncEthernetAddr(addr, val = 1): 1141 bytes = map(lambda x: int(x, 16), addr.split(':')) 1142 bytes[5] += val 1143 for i in (5, 4, 3, 2, 1): 1144 val,rem = divmod(bytes[i], 256) 1145 bytes[i] = rem 1146 if val == 0: 1147 break 1148 bytes[i - 1] += val 1149 assert(bytes[0] <= 255) 1150 return ':'.join(map(lambda x: '%02x' % x, bytes)) 1151 1152class NextEthernetAddr(object): 1153 addr = "00:90:00:00:00:01" 1154 1155 def __init__(self, inc = 1): 1156 self.value = NextEthernetAddr.addr 1157 NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) 1158 1159class EthernetAddr(ParamValue): 1160 def __init__(self, value): 1161 if value == NextEthernetAddr: 1162 self.value = value 1163 return 1164 1165 if not isinstance(value, str): 1166 raise TypeError, "expected an ethernet address and didn't get one" 1167 1168 bytes = value.split(':') 1169 if len(bytes) != 6: 1170 raise TypeError, 'invalid ethernet address %s' % value 1171 1172 for byte in bytes: 1173 if not 0 <= int(byte) <= 256: 1174 raise TypeError, 'invalid ethernet address %s' % value 1175 1176 self.value = value 1177 1178 def unproxy(self, base): 1179 if self.value == NextEthernetAddr: 1180 self.addr = self.value().value 1181 return self 1182 1183 def __str__(self): 1184 if self.value == NextEthernetAddr: 1185 if hasattr(self, 'addr'): 1186 return self.addr 1187 else: 1188 return "NextEthernetAddr (unresolved)" 1189 else: 1190 return self.value 1191 1192# Special class for NULL pointers. Note the special check in 1193# make_param_value() above that lets these be assigned where a 1194# SimObject is required. 1195# only one copy of a particular node 1196class NullSimObject(object): 1197 __metaclass__ = Singleton 1198 1199 def __call__(cls): 1200 return cls 1201 1202 def _instantiate(self, parent = None, path = ''): 1203 pass 1204 1205 def ini_str(self): 1206 return 'Null' 1207 1208 def unproxy(self, base): 1209 return self 1210 1211 def set_path(self, parent, name): 1212 pass 1213 def __str__(self): 1214 return 'Null' 1215 1216# The only instance you'll ever need... 1217Null = NULL = NullSimObject() 1218 1219# Enumerated types are a little more complex. The user specifies the 1220# type as Enum(foo) where foo is either a list or dictionary of 1221# alternatives (typically strings, but not necessarily so). (In the 1222# long run, the integer value of the parameter will be the list index 1223# or the corresponding dictionary value. For now, since we only check 1224# that the alternative is valid and then spit it into a .ini file, 1225# there's not much point in using the dictionary.) 1226 1227# What Enum() must do is generate a new type encapsulating the 1228# provided list/dictionary so that specific values of the parameter 1229# can be instances of that type. We define two hidden internal 1230# classes (_ListEnum and _DictEnum) to serve as base classes, then 1231# derive the new type from the appropriate base class on the fly. 1232 1233 1234# Metaclass for Enum types 1235class MetaEnum(type): 1236 def __init__(cls, name, bases, init_dict): 1237 if init_dict.has_key('map'): 1238 if not isinstance(cls.map, dict): 1239 raise TypeError, "Enum-derived class attribute 'map' " \ 1240 "must be of type dict" 1241 # build list of value strings from map 1242 cls.vals = cls.map.keys() 1243 cls.vals.sort() 1244 elif init_dict.has_key('vals'): 1245 if not isinstance(cls.vals, list): 1246 raise TypeError, "Enum-derived class attribute 'vals' " \ 1247 "must be of type list" 1248 # build string->value map from vals sequence 1249 cls.map = {} 1250 for idx,val in enumerate(cls.vals): 1251 cls.map[val] = idx 1252 else: 1253 raise TypeError, "Enum-derived class must define "\ 1254 "attribute 'map' or 'vals'" 1255 1256 super(MetaEnum, cls).__init__(name, bases, init_dict) 1257 1258 def cpp_declare(cls): 1259 s = 'enum %s {\n ' % cls.__name__ 1260 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 1261 s += '\n};\n' 1262 return s 1263 1264# Base class for enum types. 1265class Enum(ParamValue): 1266 __metaclass__ = MetaEnum 1267 vals = [] 1268 1269 def __init__(self, value): 1270 if value not in self.map: 1271 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 1272 % (value, self.vals) 1273 self.value = value 1274 1275 def __str__(self): 1276 return self.value 1277 1278ticks_per_sec = None 1279 1280# how big does a rounding error need to be before we warn about it? 1281frequency_tolerance = 0.001 # 0.1% 1282 1283# convert a floting-point # of ticks to integer, and warn if rounding 1284# discards too much precision 1285def tick_check(float_ticks): 1286 if float_ticks == 0: 1287 return 0 1288 int_ticks = int(round(float_ticks)) 1289 err = (float_ticks - int_ticks) / float_ticks 1290 if err > frequency_tolerance: 1291 print >> sys.stderr, "Warning: rounding error > tolerance" 1292 print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) 1293 #raise ValueError 1294 return int_ticks 1295 1296def getLatency(value): 1297 if isinstance(value, Latency) or isinstance(value, Clock): 1298 return value.value 1299 elif isinstance(value, Frequency) or isinstance(value, RootClock): 1300 return 1 / value.value 1301 elif isinstance(value, str): 1302 try: 1303 return toLatency(value) 1304 except ValueError: 1305 try: 1306 return 1 / toFrequency(value) 1307 except ValueError: 1308 pass # fall through 1309 raise ValueError, "Invalid Frequency/Latency value '%s'" % value 1310 1311 1312class Latency(NumericParamValue): 1313 def __init__(self, value): 1314 self.value = getLatency(value) 1315 1316 def __getattr__(self, attr): 1317 if attr in ('latency', 'period'): 1318 return self 1319 if attr == 'frequency': 1320 return Frequency(self) 1321 raise AttributeError, "Latency object has no attribute '%s'" % attr 1322 1323 # convert latency to ticks 1324 def ini_str(self): 1325 return str(tick_check(self.value * ticks_per_sec)) 1326 1327class Frequency(NumericParamValue): 1328 def __init__(self, value): 1329 self.value = 1 / getLatency(value) 1330 1331 def __getattr__(self, attr): 1332 if attr == 'frequency': 1333 return self 1334 if attr in ('latency', 'period'): 1335 return Latency(self) 1336 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1337 1338 # convert frequency to ticks per period 1339 def ini_str(self): 1340 return self.period.ini_str() 1341 1342# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). 1343# We can't inherit from Frequency because we don't want it to be directly 1344# assignable to a regular Frequency parameter. 1345class RootClock(ParamValue): 1346 def __init__(self, value): 1347 self.value = 1 / getLatency(value) 1348 1349 def __getattr__(self, attr): 1350 if attr == 'frequency': 1351 return Frequency(self) 1352 if attr in ('latency', 'period'): 1353 return Latency(self) 1354 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1355 1356 def ini_str(self): 1357 return str(tick_check(self.value)) 1358 1359# A generic frequency and/or Latency value. Value is stored as a latency, 1360# but to avoid ambiguity this object does not support numeric ops (* or /). 1361# An explicit conversion to a Latency or Frequency must be made first. 1362class Clock(ParamValue): 1363 def __init__(self, value): 1364 self.value = getLatency(value) 1365 1366 def __getattr__(self, attr): 1367 if attr == 'frequency': 1368 return Frequency(self) 1369 if attr in ('latency', 'period'): 1370 return Latency(self) 1371 raise AttributeError, "Frequency object has no attribute '%s'" % attr 1372 1373 def ini_str(self): 1374 return self.period.ini_str() 1375 1376class NetworkBandwidth(float,ParamValue): 1377 def __new__(cls, value): 1378 val = toNetworkBandwidth(value) / 8.0 1379 return super(cls, NetworkBandwidth).__new__(cls, val) 1380 1381 def __str__(self): 1382 return str(self.val) 1383 1384 def ini_str(self): 1385 return '%f' % (ticks_per_sec / float(self)) 1386 1387class MemoryBandwidth(float,ParamValue): 1388 def __new__(self, value): 1389 val = toMemoryBandwidth(value) 1390 return super(cls, MemoryBandwidth).__new__(cls, val) 1391 1392 def __str__(self): 1393 return str(self.val) 1394 1395 def ini_str(self): 1396 return '%f' % (ticks_per_sec / float(self)) 1397 1398# 1399# "Constants"... handy aliases for various values. 1400# 1401 1402# Some memory range specifications use this as a default upper bound. 1403MaxAddr = Addr.max 1404MaxTick = Tick.max 1405AllMemory = AddrRange(0, MaxAddr) 1406 1407##################################################################### 1408 1409# __all__ defines the list of symbols that get exported when 1410# 'from config import *' is invoked. Try to keep this reasonably 1411# short to avoid polluting other namespaces. 1412__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', 1413 'Parent', 'Self', 1414 'Enum', 'Bool', 'String', 'Float', 1415 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1416 'Int32', 'UInt32', 'Int64', 'UInt64', 1417 'Counter', 'Addr', 'Tick', 'Percent', 1418 'TcpPort', 'UdpPort', 'EthernetAddr', 1419 'MemorySize', 'MemorySize32', 1420 'Latency', 'Frequency', 'RootClock', 'Clock', 1421 'NetworkBandwidth', 'MemoryBandwidth', 1422 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', 1423 'Null', 'NULL', 1424 'NextEthernetAddr'] 1425 1426