SimObject.py revision 1587
12292SN/A# Copyright (c) 2004 The Regents of The University of Michigan 22727Sktlim@umich.edu# All rights reserved. 32292SN/A# 42292SN/A# Redistribution and use in source and binary forms, with or without 52292SN/A# modification, are permitted provided that the following conditions are 62292SN/A# met: redistributions of source code must retain the above copyright 72292SN/A# notice, this list of conditions and the following disclaimer; 82292SN/A# redistributions in binary form must reproduce the above copyright 92292SN/A# notice, this list of conditions and the following disclaimer in the 102292SN/A# documentation and/or other materials provided with the distribution; 112292SN/A# neither the name of the copyright holders nor the names of its 122292SN/A# contributors may be used to endorse or promote products derived from 132292SN/A# this software without specific prior written permission. 142292SN/A# 152292SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 162292SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 172292SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 182292SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 192292SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 202292SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 212292SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 222292SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 232292SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 242292SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 252292SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 262292SN/A 272689Sktlim@umich.edufrom __future__ import generators 282689Sktlim@umich.eduimport os, re, sys, types, inspect 292292SN/A 302292SN/Afrom m5 import panic 312329SN/Afrom convert import * 322980Sgblack@eecs.umich.edufrom multidict import multidict 332329SN/A 342329SN/AnoDot = False 352292SN/Atry: 368232Snate@binkert.org import pydot 378232Snate@binkert.orgexcept: 388232Snate@binkert.org noDot = True 396221Snate@binkert.org 402292SN/Aclass Singleton(type): 416221Snate@binkert.org def __call__(cls, *args, **kwargs): 425529Snate@binkert.org if hasattr(cls, '_instance'): 434192Sktlim@umich.edu return cls._instance 444192Sktlim@umich.edu 454192Sktlim@umich.edu cls._instance = super(Singleton, cls).__call__(*args, **kwargs) 464192Sktlim@umich.edu return cls._instance 474192Sktlim@umich.edu 484192Sktlim@umich.edu##################################################################### 494192Sktlim@umich.edu# 504192Sktlim@umich.edu# M5 Python Configuration Utility 514192Sktlim@umich.edu# 524192Sktlim@umich.edu# The basic idea is to write simple Python programs that build Python 534192Sktlim@umich.edu# objects corresponding to M5 SimObjects for the deisred simulation 544192Sktlim@umich.edu# configuration. For now, the Python emits a .ini file that can be 554192Sktlim@umich.edu# parsed by M5. In the future, some tighter integration between M5 562292SN/A# and the Python interpreter may allow bypassing the .ini file. 572907Sktlim@umich.edu# 582907Sktlim@umich.edu# Each SimObject class in M5 is represented by a Python class with the 592907Sktlim@umich.edu# same name. The Python inheritance tree mirrors the M5 C++ tree 602907Sktlim@umich.edu# (e.g., SimpleCPU derives from BaseCPU in both cases, and all 617823Ssteve.reinhardt@amd.com# SimObjects inherit from a single SimObject base class). To specify 622907Sktlim@umich.edu# an instance of an M5 SimObject in a configuration, the user simply 632907Sktlim@umich.edu# instantiates the corresponding Python object. The parameters for 642907Sktlim@umich.edu# that SimObject are given by assigning to attributes of the Python 652907Sktlim@umich.edu# object, either using keyword assignment in the constructor or in 662907Sktlim@umich.edu# separate assignment statements. For example: 672907Sktlim@umich.edu# 688346Sksewell@umich.edu# cache = BaseCache('my_cache', root, size=64*K) 692907Sktlim@umich.edu# cache.hit_latency = 3 702907Sktlim@umich.edu# cache.assoc = 8 712907Sktlim@umich.edu# 722907Sktlim@umich.edu# (The first two constructor arguments specify the name of the created 732907Sktlim@umich.edu# cache and its parent node in the hierarchy.) 742907Sktlim@umich.edu# 753647Srdreslin@umich.edu# The magic lies in the mapping of the Python attributes for SimObject 763647Srdreslin@umich.edu# classes to the actual SimObject parameter specifications. This 773647Srdreslin@umich.edu# allows parameter validity checking in the Python code. Continuing 783647Srdreslin@umich.edu# the example above, the statements "cache.blurfl=3" or 793647Srdreslin@umich.edu# "cache.assoc='hello'" would both result in runtime errors in Python, 802907Sktlim@umich.edu# since the BaseCache object has no 'blurfl' parameter and the 'assoc' 813647Srdreslin@umich.edu# parameter requires an integer, respectively. This magic is done 822907Sktlim@umich.edu# primarily by overriding the special __setattr__ method that controls 832907Sktlim@umich.edu# assignment to object attributes. 842907Sktlim@umich.edu# 852907Sktlim@umich.edu# The Python module provides another class, ConfigNode, which is a 862907Sktlim@umich.edu# superclass of SimObject. ConfigNode implements the parent/child 872907Sktlim@umich.edu# relationship for building the configuration hierarchy tree. 882907Sktlim@umich.edu# Concrete instances of ConfigNode can be used to group objects in the 894986Ssaidi@eecs.umich.edu# hierarchy, but do not correspond to SimObjects themselves (like a 904986Ssaidi@eecs.umich.edu# .ini section with "children=" but no "type=". 913310Srdreslin@umich.edu# 925714Shsul@eecs.umich.edu# Once a set of Python objects have been instantiated in a hierarchy, 933310Srdreslin@umich.edu# calling 'instantiate(obj)' (where obj is the root of the hierarchy) 943310Srdreslin@umich.edu# will generate a .ini file. See simple-4cpu.py for an example 954895Sstever@eecs.umich.edu# (corresponding to m5-test/simple-4cpu.ini). 964895Sstever@eecs.umich.edu# 974895Sstever@eecs.umich.edu##################################################################### 984895Sstever@eecs.umich.edu 993310Srdreslin@umich.edu##################################################################### 1002907Sktlim@umich.edu# 1012907Sktlim@umich.edu# ConfigNode/SimObject classes 1022907Sktlim@umich.edu# 1032907Sktlim@umich.edu# The Python class hierarchy rooted by ConfigNode (which is the base 1042907Sktlim@umich.edu# class of SimObject, which in turn is the base class of all other M5 1052907Sktlim@umich.edu# SimObject classes) has special attribute behavior. In general, an 1062907Sktlim@umich.edu# object in this hierarchy has three categories of attribute-like 1073014Srdreslin@umich.edu# things: 1083014Srdreslin@umich.edu# 1093014Srdreslin@umich.edu# 1. Regular Python methods and variables. These must start with an 1103014Srdreslin@umich.edu# underscore to be treated normally. 1113014Srdreslin@umich.edu# 1124985Sktlim@umich.edu# 2. SimObject parameters. These values are stored as normal Python 1132907Sktlim@umich.edu# attributes, but all assignments to these attributes are checked 1142907Sktlim@umich.edu# against the pre-defined set of parameters stored in the class's 1152907Sktlim@umich.edu# _params dictionary. Assignments to attributes that do not 1164985Sktlim@umich.edu# correspond to predefined parameters, or that are not of the correct 1172907Sktlim@umich.edu# type, incur runtime errors. 1182907Sktlim@umich.edu# 1192907Sktlim@umich.edu# 3. Hierarchy children. The child nodes of a ConfigNode are stored 1205529Snate@binkert.org# in the node's _children dictionary, but can be accessed using the 1215494Sstever@gmail.com# Python attribute dot-notation (just as they are printed out by the 1224329Sktlim@umich.edu# simulator). Children cannot be created using attribute assigment; 1234329Sktlim@umich.edu# they must be added by specifying the parent node in the child's 1245529Snate@binkert.org# constructor or using the '+=' operator. 1252907Sktlim@umich.edu 1262292SN/A# The SimObject parameters are the most complex, for a few reasons. 1273647Srdreslin@umich.edu# First, both parameter descriptions and parameter values are 1283647Srdreslin@umich.edu# inherited. Thus parameter description lookup must go up the 1292292SN/A# inheritance chain like normal attribute lookup, but this behavior 1302292SN/A# must be explicitly coded since the lookup occurs in each class's 1312292SN/A# _params attribute. Second, because parameter values can be set 1322980Sgblack@eecs.umich.edu# on SimObject classes (to implement default values), the parameter 1332292SN/A# checking behavior must be enforced on class attribute assignments as 1342292SN/A# well as instance attribute assignments. Finally, because we allow 1352292SN/A# class specialization via inheritance (e.g., see the L1Cache class in 1362292SN/A# the simple-4cpu.py example), we must do parameter checking even on 1372292SN/A# class instantiation. To provide all these features, we use a 1382292SN/A# metaclass to define most of the SimObject parameter behavior for 1392292SN/A# this class hierarchy. 1402292SN/A# 1412292SN/A##################################################################### 1422292SN/A 1432292SN/Aclass Proxy(object): 1444329Sktlim@umich.edu def __init__(self, path): 1452292SN/A self._object = None 1462292SN/A if path == 'any': 1472292SN/A self._path = None 1482292SN/A else: 1492292SN/A # path is a list of (attr,index) tuples 1502292SN/A self._path = [(path,None)] 1512292SN/A self._index = None 1524329Sktlim@umich.edu self._multiplier = None 1532292SN/A 1548346Sksewell@umich.edu def __getattr__(self, attr): 1552292SN/A # python uses __bases__ internally for inheritance 1562292SN/A if attr == '__bases__': 1572292SN/A return super(Proxy, self).__getattr__(self, attr) 1582292SN/A if (self._path == None): 1592292SN/A panic("Can't add attributes to 'any' proxy") 1602292SN/A self._path.append((attr,None)) 1612292SN/A return self 1622292SN/A 1632292SN/A def __setattr__(self, attr, value): 1642292SN/A if not attr.startswith('_'): 1652292SN/A raise AttributeError, 'cannot set attribute %s' % attr 1662292SN/A super(Proxy, self).__setattr__(attr, value) 1674329Sktlim@umich.edu 1682292SN/A # support indexing on proxies (e.g., parent.cpu[0]) 1698346Sksewell@umich.edu def __getitem__(self, key): 1702292SN/A if not isinstance(key, int): 1712292SN/A raise TypeError, "Proxy object requires integer index" 1722292SN/A if self._path == None: 1732292SN/A raise IndexError, "Index applied to 'any' proxy" 1742292SN/A # replace index portion of last path element with new index 1752292SN/A self._path[-1] = (self._path[-1][0], key) 1762292SN/A return self 1776221Snate@binkert.org 1784329Sktlim@umich.edu # support multiplying proxies by constants 1794329Sktlim@umich.edu def __mul__(self, other): 1802907Sktlim@umich.edu if not isinstance(other, int): 1812292SN/A raise TypeError, "Proxy multiplier must be integer" 1822292SN/A if self._multiplier == None: 1832292SN/A self._multiplier = other 1842292SN/A else: 1852292SN/A # support chained multipliers 1862292SN/A self._multiplier *= other 1872292SN/A return self 1882292SN/A 1892292SN/A def _mulcheck(self, result): 1902292SN/A if self._multiplier == None: 1912292SN/A return result 1922292SN/A if not isinstance(result, int): 1932292SN/A raise TypeError, "Proxy with multiplier resolves to " \ 1942727Sktlim@umich.edu "non-integer value" 1952727Sktlim@umich.edu return result * self._multiplier 1962727Sktlim@umich.edu 1976221Snate@binkert.org def unproxy(self, base, ptype): 1982727Sktlim@umich.edu obj = base 1992727Sktlim@umich.edu done = False 2002727Sktlim@umich.edu while not done: 2012727Sktlim@umich.edu if obj is None: 2022727Sktlim@umich.edu raise AttributeError, \ 2032727Sktlim@umich.edu 'Parent of %s type %s not found at path %s' \ 2046221Snate@binkert.org % (base.name, ptype, self._path) 2052292SN/A 2062292SN/A result, done = obj.find(ptype, self._path) 2072292SN/A obj = obj.parent 2082292SN/A 2092292SN/A if isinstance(result, Proxy): 2102292SN/A result = result.unproxy(obj, ptype) 2112307SN/A 2122307SN/A return self._mulcheck(result) 2132307SN/A 2146221Snate@binkert.org def getindex(obj, index): 2152307SN/A if index == None: 2162307SN/A return obj 2172307SN/A try: 2182307SN/A obj = obj[index] 2192307SN/A except TypeError: 2202307SN/A if index != 0: 2212307SN/A raise 2222307SN/A # if index is 0 and item is not subscriptable, just 2236221Snate@binkert.org # use item itself (so cpu[0] works on uniprocessors) 2242307SN/A return obj 2252307SN/A getindex = staticmethod(getindex) 2262307SN/A 2272307SN/Aclass ProxyFactory(object): 2282307SN/A def __getattr__(self, attr): 2292292SN/A return Proxy(attr) 2306221Snate@binkert.org 2312292SN/A# global object for handling parent.foo proxies 2322292SN/Aparent = ProxyFactory() 2332292SN/A 2342292SN/Adef isSubClass(value, cls): 2352292SN/A try: 2362292SN/A return issubclass(value, cls) 2372292SN/A except: 2382292SN/A return False 2392292SN/A 2402292SN/Adef isConfigNode(value): 2412292SN/A try: 2422292SN/A return issubclass(value, ConfigNode) 2432292SN/A except: 2443867Sbinkertn@umich.edu return False 2452292SN/A 2462292SN/Adef isSimObject(value): 2472292SN/A try: 2482292SN/A return issubclass(value, SimObject) 2492292SN/A except: 2502292SN/A return False 2512292SN/A 2522292SN/Adef isSimObjSequence(value): 2532292SN/A if not isinstance(value, (list, tuple)): 2542292SN/A return False 2552292SN/A 2566221Snate@binkert.org for val in value: 2576221Snate@binkert.org if not isNullPointer(val) and not isConfigNode(val): 2583867Sbinkertn@umich.edu return False 2593867Sbinkertn@umich.edu 2606221Snate@binkert.org return True 2613867Sbinkertn@umich.edu 2623867Sbinkertn@umich.edudef isParamContext(value): 2632292SN/A try: 2642292SN/A return issubclass(value, ParamContext) 2652292SN/A except: 2662292SN/A return False 2672292SN/A 2682292SN/A 2696221Snate@binkert.orgclass_decorator = 'M5M5_SIMOBJECT_' 2702292SN/Aexpr_decorator = 'M5M5_EXPRESSION_' 2712292SN/Adot_decorator = '_M5M5_DOT_' 2722292SN/A 2732292SN/A# 'Global' map of legitimate types for SimObject parameters. 2742292SN/Aparam_types = {} 2752292SN/A 2762292SN/A# Dummy base class to identify types that are legitimate for SimObject 2776221Snate@binkert.org# parameters. 2782292SN/Aclass ParamType(object): 2792292SN/A pass 2802292SN/A 2812292SN/A# Add types defined in given context (dict or module) that are derived 2822292SN/A# from ParamType to param_types map. 2832292SN/Adef add_param_types(ctx): 2842292SN/A if isinstance(ctx, types.DictType): 2852292SN/A source_dict = ctx 2862292SN/A elif isinstance(ctx, types.ModuleType): 2876221Snate@binkert.org source_dict = ctx.__dict__ 2886221Snate@binkert.org else: 2892292SN/A raise TypeError, \ 2903867Sbinkertn@umich.edu "m5.config.add_param_types requires dict or module as arg" 2916221Snate@binkert.org for key,val in source_dict.iteritems(): 2922292SN/A if isinstance(val, type) and issubclass(val, ParamType): 2932292SN/A param_types[key] = val 2942292SN/A 2952292SN/A# The metaclass for ConfigNode (and thus for everything that derives 2962292SN/A# from ConfigNode, including SimObject). This class controls how new 2972292SN/A# classes that derive from ConfigNode are instantiated, and provides 2982292SN/A# inherited class behavior (just like a class controls how instances 2992292SN/A# of that class are instantiated, and provides inherited instance 3002292SN/A# behavior). 3016221Snate@binkert.orgclass MetaConfigNode(type): 3022292SN/A # Attributes that can be set only at initialization time 3032292SN/A init_keywords = {} 3042292SN/A # Attributes that can be set any time 3052292SN/A keywords = { 'check' : types.FunctionType, 3062292SN/A 'children' : types.ListType } 3072292SN/A 3082292SN/A # __new__ is called before __init__, and is where the statements 3092292SN/A # in the body of the class definition get loaded into the class's 3106221Snate@binkert.org # __dict__. We intercept this to filter out parameter assignments 3112292SN/A # and only allow "private" attributes to be passed to the base 3122292SN/A # __new__ (starting with underscore). 3132292SN/A def __new__(mcls, name, bases, dict): 3142292SN/A # Copy "private" attributes (including special methods such as __new__) 3152292SN/A # to the official dict. Everything else goes in _init_dict to be 3162292SN/A # filtered in __init__. 3172292SN/A cls_dict = {} 3182292SN/A for key,val in dict.items(): 3196221Snate@binkert.org if key.startswith('_'): 3202292SN/A cls_dict[key] = val 3212292SN/A del dict[key] 3222292SN/A cls_dict['_init_dict'] = dict 3232292SN/A return super(MetaConfigNode, mcls).__new__(mcls, name, bases, cls_dict) 3242292SN/A 3252292SN/A # initialization 3262292SN/A def __init__(cls, name, bases, dict): 3272292SN/A super(MetaConfigNode, cls).__init__(name, bases, dict) 3286221Snate@binkert.org 3292292SN/A # initialize required attributes 3302292SN/A cls._params = multidict() 3312292SN/A cls._values = multidict() 3322292SN/A cls._param_types = {} 3332292SN/A cls._bases = [c for c in cls.__mro__ if isConfigNode(c)] 3342292SN/A cls._anon_subclass_counter = 0 3352292SN/A 3362292SN/A # We don't support multiple inheritence. If you want to, you 3376221Snate@binkert.org # must fix multidict to deal with it properly. 3386221Snate@binkert.org sob = [ base for base in bases \ 3392292SN/A if issubclass(base, ParamType) and base != ParamType ] 3403867Sbinkertn@umich.edu 3416221Snate@binkert.org if len(sob) == 1: 3422292SN/A # If your parent has a value in it that's a config node, clone 3432292SN/A # it. Do this now so if we update any of the values' 3442329SN/A # attributes we are updating the clone and not the original. 3452329SN/A for key,val in sob[0]._values.iteritems(): 3462292SN/A 3472292SN/A # don't clone if (1) we're about to overwrite it with 3482292SN/A # a local setting or (2) we've already cloned a copy 3492292SN/A # from an earlier (more derived) base 3502292SN/A if cls._init_dict.has_key(key) or cls._values.has_key(key): 3512292SN/A continue 3522292SN/A 3532292SN/A if isConfigNode(val): 3542292SN/A cls._values[key] = val() 3552292SN/A elif isSimObjSequence(val) and len(val): 3562292SN/A cls._values[key] = [ v() for v in val ] 3576221Snate@binkert.org 3586221Snate@binkert.org cls._params.parent = sob[0]._params 3592292SN/A cls._values.parent = sob[0]._values 3603867Sbinkertn@umich.edu 3616221Snate@binkert.org elif len(sob) > 1: 3623867Sbinkertn@umich.edu panic("""\ 3632292SN/AThe config hierarchy only supports single inheritence of SimObject 3642292SN/Aclasses. You're trying to derive from: 3652292SN/A%s""" % str(sob)) 3662292SN/A 3672292SN/A # process param types from _init_dict, as these may be needed 3682292SN/A # by param descriptions also in _init_dict 3692292SN/A for key,val in cls._init_dict.items(): 3702292SN/A if isinstance(val, type) and issubclass(val, ParamType): 3712292SN/A cls._param_types[key] = val 3722292SN/A if not issubclass(val, ConfigNode): 3732292SN/A del cls._init_dict[key] 3742292SN/A 3752292SN/A # now process remaining _init_dict items 3766221Snate@binkert.org for key,val in cls._init_dict.items(): 3776221Snate@binkert.org # param descriptions 3782292SN/A if isinstance(val, _Param): 3793867Sbinkertn@umich.edu cls._new_param(key, val) 3806221Snate@binkert.org 3813867Sbinkertn@umich.edu # init-time-only keywords 3822292SN/A elif cls.init_keywords.has_key(key): 3832292SN/A cls._set_keyword(key, val, cls.init_keywords[key]) 3842292SN/A 3852292SN/A # See description of decorators in the importer.py file. 3862292SN/A # We just strip off the expr_decorator now since we don't 3872292SN/A # need from this point on. 3882292SN/A elif key.startswith(expr_decorator): 3892292SN/A key = key[len(expr_decorator):] 3902292SN/A # because it had dots into a list so that we can find the 3912292SN/A # proper variable to modify. 3922292SN/A key = key.split(dot_decorator) 3932292SN/A c = cls 3946221Snate@binkert.org for item in key[:-1]: 3956221Snate@binkert.org c = getattr(c, item) 3962292SN/A setattr(c, key[-1], val) 3973867Sbinkertn@umich.edu 3986221Snate@binkert.org # default: use normal path (ends up in __setattr__) 3993867Sbinkertn@umich.edu else: 4002292SN/A setattr(cls, key, val) 4012292SN/A 4022292SN/A def _set_keyword(cls, keyword, val, kwtype): 4032292SN/A if not isinstance(val, kwtype): 4042292SN/A raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ 4052292SN/A (keyword, type(val), kwtype) 4062292SN/A if isinstance(val, types.FunctionType): 4072292SN/A val = classmethod(val) 4082292SN/A type.__setattr__(cls, keyword, val) 4092292SN/A 4102292SN/A def _new_param(cls, name, value): 4112292SN/A cls._params[name] = value 4126221Snate@binkert.org if hasattr(value, 'default'): 4136221Snate@binkert.org cls._values[name] = value.default 4142292SN/A # try to resolve local param types in local param_types scope 4153867Sbinkertn@umich.edu value.maybe_resolve_type(cls._param_types) 4166221Snate@binkert.org 4173867Sbinkertn@umich.edu # Set attribute (called on foo.attr = value when foo is an 4182292SN/A # instance of class cls). 4192292SN/A def __setattr__(cls, attr, value): 4202292SN/A # normal processing for private attributes 4212292SN/A if attr.startswith('_'): 4222292SN/A type.__setattr__(cls, attr, value) 4232292SN/A return 4242292SN/A 4252292SN/A if cls.keywords.has_key(attr): 4262292SN/A cls._set_keyword(attr, value, cls.keywords[attr]) 4272292SN/A return 4282292SN/A 4292292SN/A # must be SimObject param 4306221Snate@binkert.org param = cls._params.get(attr, None) 4316221Snate@binkert.org if param: 4322292SN/A # It's ok: set attribute by delegating to 'object' class. 4333867Sbinkertn@umich.edu # Note the use of param.make_value() to verify/canonicalize 4346221Snate@binkert.org # the assigned value 4353867Sbinkertn@umich.edu try: 4362292SN/A param.valid(value) 4372292SN/A except Exception, e: 4382292SN/A panic("Exception: %s\nError setting param %s.%s to %s\n" % \ 4392292SN/A (e, cls.__name__, attr, value)) 4402292SN/A cls._values[attr] = value 4412292SN/A elif isConfigNode(value) or isSimObjSequence(value): 4422292SN/A cls._values[attr] = value 4432292SN/A else: 4442292SN/A raise AttributeError, \ 4452292SN/A "Class %s has no parameter %s" % (cls.__name__, attr) 4462292SN/A 4472292SN/A def __getattr__(cls, attr): 4486221Snate@binkert.org if cls._params.has_key(attr) or cls._values.has_key(attr): 4496221Snate@binkert.org return Value(cls, attr) 4502292SN/A 4513867Sbinkertn@umich.edu if attr == '_cpp_param_decl' and hasattr(cls, 'type'): 4526221Snate@binkert.org return cls.type + '*' 4533867Sbinkertn@umich.edu 4542292SN/A raise AttributeError, \ 4552292SN/A "object '%s' has no attribute '%s'" % (cls.__name__, attr) 4562292SN/A 4572292SN/A def add_child(cls, instance, name, child): 4582292SN/A if isNullPointer(child) or instance.top_child_names.has_key(name): 4592292SN/A return 4602292SN/A 4612292SN/A if isinstance(child, (list, tuple)): 4626221Snate@binkert.org kid = [] 4632292SN/A for i,c in enumerate(child): 4643870Sbinkertn@umich.edu n = '%s%d' % (name, i) 4652292SN/A k = c.instantiate(n, instance) 4662292SN/A 4672292SN/A instance.children.append(k) 4682292SN/A instance.child_names[n] = k 4692292SN/A instance.child_objects[c] = k 4702292SN/A kid.append(k) 4712292SN/A else: 4722292SN/A kid = child.instantiate(name, instance) 4732292SN/A instance.children.append(kid) 4746221Snate@binkert.org instance.child_names[name] = kid 4756221Snate@binkert.org instance.child_objects[child] = kid 4762292SN/A 4773867Sbinkertn@umich.edu instance.top_child_names[name] = kid 4786221Snate@binkert.org 4793867Sbinkertn@umich.edu # Print instance info to .ini file. 4803867Sbinkertn@umich.edu def instantiate(cls, name, parent = None): 4812292SN/A instance = Node(name, cls, parent, isParamContext(cls)) 4822292SN/A 4832292SN/A if hasattr(cls, 'check'): 4842292SN/A cls.check() 4852292SN/A 4862292SN/A for key,value in cls._values.iteritems(): 4872292SN/A if isConfigNode(value): 4882292SN/A cls.add_child(instance, key, value) 4896221Snate@binkert.org if isinstance(value, (list, tuple)): 4902292SN/A vals = [ v for v in value if isConfigNode(v) ] 4912292SN/A if len(vals): 4922292SN/A cls.add_child(instance, key, vals) 4933867Sbinkertn@umich.edu 4942292SN/A for pname,param in cls._params.iteritems(): 4952292SN/A value = cls._values.get(pname, None) 4962292SN/A if value is None: 4972292SN/A panic('Error getting %s from %s' % (pname, name)) 4982292SN/A 4992292SN/A try: 5002292SN/A if isConfigNode(value): 5012292SN/A value = instance.child_objects[value] 5022292SN/A elif isinstance(value, (list, tuple)): 5036221Snate@binkert.org v = [] 5046221Snate@binkert.org for val in value: 5052292SN/A if isConfigNode(val): 5063867Sbinkertn@umich.edu v.append(instance.child_objects[val]) 5076221Snate@binkert.org else: 5083867Sbinkertn@umich.edu v.append(val) 5092292SN/A value = v 5102292SN/A 5112292SN/A p = NodeParam(pname, param, value) 5122292SN/A instance.params.append(p) 5132292SN/A instance.param_names[pname] = p 5142292SN/A except Exception, e: 5152292SN/A raise e.__class__, 'Exception while evaluating %s.%s\n%s' % \ 5162292SN/A (instance.path, pname, e) 5172292SN/A 5186221Snate@binkert.org return instance 5192292SN/A 5202292SN/A def _convert(cls, value): 5212292SN/A realvalue = value 5223870Sbinkertn@umich.edu if isinstance(value, Node): 5232292SN/A realvalue = value.realtype 5242292SN/A 5252292SN/A if isinstance(realvalue, Proxy): 5262292SN/A return value 5272292SN/A 5282292SN/A if realvalue == None or isNullPointer(realvalue): 5292292SN/A return value 5302292SN/A 5312292SN/A if isSubClass(realvalue, cls): 5326221Snate@binkert.org return value 5336221Snate@binkert.org 5342292SN/A raise TypeError, 'object %s type %s wrong type, should be %s' % \ 5353867Sbinkertn@umich.edu (repr(realvalue), realvalue, cls) 5366221Snate@binkert.org 5373867Sbinkertn@umich.edu def _string(cls, value): 5382292SN/A if isNullPointer(value): 5392292SN/A return 'Null' 5402292SN/A return Node._string(value) 5412292SN/A 5422292SN/A# The ConfigNode class is the root of the special hierarchy. Most of 5432292SN/A# the code in this class deals with the configuration hierarchy itself 5442292SN/A# (parent/child node relationships). 5452292SN/Aclass ConfigNode(object): 5462292SN/A # Specify metaclass. Any class inheriting from ConfigNode will 5476221Snate@binkert.org # get this metaclass. 5482292SN/A __metaclass__ = MetaConfigNode 5492292SN/A 5502292SN/A def __new__(cls, **kwargs): 5513870Sbinkertn@umich.edu name = cls.__name__ + ("_%d" % cls._anon_subclass_counter) 5522292SN/A cls._anon_subclass_counter += 1 5532292SN/A return cls.__metaclass__(name, (cls, ), kwargs) 5542292SN/A 5552292SN/Aclass ParamContext(ConfigNode,ParamType): 5562292SN/A pass 5572292SN/A 5582292SN/Aclass MetaSimObject(MetaConfigNode): 5592292SN/A # init_keywords and keywords are inherited from MetaConfigNode, 5602292SN/A # with overrides/additions 5616221Snate@binkert.org init_keywords = MetaConfigNode.init_keywords 5626221Snate@binkert.org init_keywords.update({ 'abstract' : types.BooleanType, 5632292SN/A 'type' : types.StringType }) 5643867Sbinkertn@umich.edu 5656221Snate@binkert.org keywords = MetaConfigNode.keywords 5663867Sbinkertn@umich.edu # no additional keywords 5672292SN/A 5682292SN/A cpp_classes = [] 5692292SN/A 5702292SN/A # initialization 5712292SN/A def __init__(cls, name, bases, dict): 5722292SN/A super(MetaSimObject, cls).__init__(name, bases, dict) 5732292SN/A 5742292SN/A if hasattr(cls, 'type'): 5752292SN/A if name == 'SimObject': 5766221Snate@binkert.org cls._cpp_base = None 5772292SN/A elif hasattr(cls._bases[1], 'type'): 5783870Sbinkertn@umich.edu cls._cpp_base = cls._bases[1].type 5792292SN/A else: 5802292SN/A panic("SimObject %s derives from a non-C++ SimObject %s "\ 5812292SN/A "(no 'type')" % (cls, cls_bases[1].__name__)) 5822292SN/A 5832292SN/A # This class corresponds to a C++ class: put it on the global 5842292SN/A # list of C++ objects to generate param structs, etc. 5852292SN/A MetaSimObject.cpp_classes.append(cls) 5862292SN/A 5872292SN/A def _cpp_decl(cls): 5886221Snate@binkert.org name = cls.__name__ 5896221Snate@binkert.org code = "" 5902292SN/A code += "\n".join([e.cpp_declare() for e in cls._param_types.values()]) 5913867Sbinkertn@umich.edu code += "\n" 5926221Snate@binkert.org param_names = cls._params.keys() 5933867Sbinkertn@umich.edu param_names.sort() 5945557Sktlim@umich.edu code += "struct Params" 5955557Sktlim@umich.edu if cls._cpp_base: 5962292SN/A code += " : public %s::Params" % cls._cpp_base 5972292SN/A code += " {\n " 5985557Sktlim@umich.edu code += "\n ".join([cls._params[pname].cpp_decl(pname) \ 5992292SN/A for pname in param_names]) 6002292SN/A code += "\n};\n" 6012292SN/A return code 6022292SN/A 6032292SN/Aclass NodeParam(object): 6042292SN/A def __init__(self, name, param, value): 6056221Snate@binkert.org self.name = name 6066221Snate@binkert.org self.param = param 6072292SN/A self.ptype = param.ptype 6083867Sbinkertn@umich.edu self.convert = param.convert 6096221Snate@binkert.org self.string = param.string 6103867Sbinkertn@umich.edu self.value = value 6115557Sktlim@umich.edu 6125557Sktlim@umich.educlass Node(object): 6132292SN/A all = {} 6142292SN/A def __init__(self, name, realtype, parent, paramcontext): 6155557Sktlim@umich.edu self.name = name 6162292SN/A self.realtype = realtype 6172292SN/A if isSimObject(realtype): 6182292SN/A self.type = realtype.type 6192292SN/A else: 6202292SN/A self.type = None 6212292SN/A self.parent = parent 6226221Snate@binkert.org self.children = [] 6236221Snate@binkert.org self.child_names = {} 6242292SN/A self.child_objects = {} 6253867Sbinkertn@umich.edu self.top_child_names = {} 6266221Snate@binkert.org self.params = [] 6273867Sbinkertn@umich.edu self.param_names = {} 6282292SN/A self.paramcontext = paramcontext 6292292SN/A 6302292SN/A path = [ self.name ] 631 node = self.parent 632 while node is not None: 633 if node.name != 'root': 634 path.insert(0, node.name) 635 else: 636 assert(node.parent is None) 637 node = node.parent 638 self.path = '.'.join(path) 639 640 def find(self, realtype, path): 641 if not path: 642 if issubclass(self.realtype, realtype): 643 return self, True 644 645 obj = None 646 for child in self.children: 647 if issubclass(child.realtype, realtype): 648 if obj is not None: 649 raise AttributeError, \ 650 'parent.any matched more than one: %s %s' % \ 651 (obj.path, child.path) 652 obj = child 653 return obj, obj is not None 654 655 try: 656 obj = self 657 for (node,index) in path[:-1]: 658 if obj.child_names.has_key(node): 659 obj = obj.child_names[node] 660 else: 661 obj = obj.top_child_names[node] 662 obj = Proxy.getindex(obj, index) 663 664 (last,index) = path[-1] 665 if obj.child_names.has_key(last): 666 value = obj.child_names[last] 667 return Proxy.getindex(value, index), True 668 elif obj.top_child_names.has_key(last): 669 value = obj.top_child_names[last] 670 return Proxy.getindex(value, index), True 671 elif obj.param_names.has_key(last): 672 value = obj.param_names[last] 673 realtype._convert(value.value) 674 return Proxy.getindex(value.value, index), True 675 except KeyError: 676 pass 677 678 return None, False 679 680 def unproxy(self, param, ptype): 681 if not isinstance(param, Proxy): 682 return param 683 return param.unproxy(self, ptype) 684 685 def fixup(self): 686 self.all[self.path] = self 687 688 for param in self.params: 689 ptype = param.ptype 690 pval = param.value 691 692 try: 693 if isinstance(pval, (list, tuple)): 694 param.value = [ self.unproxy(pv, ptype) for pv in pval ] 695 else: 696 param.value = self.unproxy(pval, ptype) 697 except Exception, e: 698 raise e.__class__, 'Error while fixing up %s:%s\n%s' % \ 699 (self.path, param.name, e) 700 701 for child in self.children: 702 assert(child != self) 703 child.fixup() 704 705 # print type and parameter values to .ini file 706 def display(self): 707 print '[' + self.path + ']' # .ini section header 708 709 if isSimObject(self.realtype): 710 print 'type = %s' % self.type 711 712 if self.children: 713 # instantiate children in same order they were added for 714 # backward compatibility (else we can end up with cpu1 715 # before cpu0). Changing ordering can also influence timing 716 # in the current memory system, as caches get added to a bus 717 # in different orders which affects their priority in the 718 # case of simulataneous requests. 719 self.children.sort(lambda x,y: cmp(x.name, y.name)) 720 children = [ c.name for c in self.children if not c.paramcontext] 721 print 'children =', ' '.join(children) 722 723 self.params.sort(lambda x,y: cmp(x.name, y.name)) 724 for param in self.params: 725 try: 726 if param.value is None: 727 raise AttributeError, 'Parameter with no value' 728 729 value = param.convert(param.value) 730 string = param.string(value) 731 except Exception, e: 732 raise e.__class__, 'exception in %s:%s\n%s' % \ 733 (self.path, param.name, e) 734 735 print '%s = %s' % (param.name, string) 736 737 print 738 739 # recursively dump out children 740 for c in self.children: 741 c.display() 742 743 # print type and parameter values to .ini file 744 def outputDot(self, dot): 745 746 747 label = "{%s|" % self.path 748 if isSimObject(self.realtype): 749 label += '%s|' % self.type 750 751 if self.children: 752 # instantiate children in same order they were added for 753 # backward compatibility (else we can end up with cpu1 754 # before cpu0). 755 for c in self.children: 756 dot.add_edge(pydot.Edge(self.path,c.path, style="bold")) 757 758 simobjs = [] 759 for param in self.params: 760 try: 761 if param.value is None: 762 raise AttributeError, 'Parameter with no value' 763 764 value = param.convert(param.value) 765 string = param.string(value) 766 except Exception, e: 767 raise e.__class__, 'exception in %s:%s\n%s' % \ 768 (self.name, param.name, e) 769 raise 770 if isConfigNode(param.ptype) and string != "Null": 771 simobjs.append(string) 772 else: 773 label += '%s = %s\\n' % (param.name, string) 774 775 for so in simobjs: 776 label += "|<%s> %s" % (so, so) 777 dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, 778 tailport="w")) 779 label += '}' 780 dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label)) 781 782 # recursively dump out children 783 for c in self.children: 784 c.outputDot(dot) 785 786 def _string(cls, value): 787 if not isinstance(value, Node): 788 raise AttributeError, 'expecting %s got %s' % (Node, value) 789 return value.path 790 _string = classmethod(_string) 791 792##################################################################### 793# 794# Parameter description classes 795# 796# The _params dictionary in each class maps parameter names to 797# either a Param or a VectorParam object. These objects contain the 798# parameter description string, the parameter type, and the default 799# value (loaded from the PARAM section of the .odesc files). The 800# _convert() method on these objects is used to force whatever value 801# is assigned to the parameter to the appropriate type. 802# 803# Note that the default values are loaded into the class's attribute 804# space when the parameter dictionary is initialized (in 805# MetaConfigNode._setparams()); after that point they aren't used. 806# 807##################################################################### 808 809def isNullPointer(value): 810 return isinstance(value, NullSimObject) 811 812class Value(object): 813 def __init__(self, obj, attr): 814 super(Value, self).__setattr__('attr', attr) 815 super(Value, self).__setattr__('obj', obj) 816 817 def _getattr(self): 818 return self.obj._values.get(self.attr) 819 820 def __setattr__(self, attr, value): 821 setattr(self._getattr(), attr, value) 822 823 def __getattr__(self, attr): 824 return getattr(self._getattr(), attr) 825 826 def __getitem__(self, index): 827 return self._getattr().__getitem__(index) 828 829 def __call__(self, *args, **kwargs): 830 return self._getattr().__call__(*args, **kwargs) 831 832 def __nonzero__(self): 833 return bool(self._getattr()) 834 835 def __str__(self): 836 return str(self._getattr()) 837 838 def __len__(self): 839 return len(self._getattr()) 840 841# Regular parameter. 842class _Param(object): 843 def __init__(self, ptype, *args, **kwargs): 844 if isinstance(ptype, types.StringType): 845 self.ptype_string = ptype 846 elif isinstance(ptype, type): 847 self.ptype = ptype 848 else: 849 raise TypeError, "Param type is not a type (%s)" % ptype 850 851 if args: 852 if len(args) == 1: 853 self.desc = args[0] 854 elif len(args) == 2: 855 self.default = args[0] 856 self.desc = args[1] 857 else: 858 raise TypeError, 'too many arguments' 859 860 if kwargs.has_key('desc'): 861 assert(not hasattr(self, 'desc')) 862 self.desc = kwargs['desc'] 863 del kwargs['desc'] 864 865 if kwargs.has_key('default'): 866 assert(not hasattr(self, 'default')) 867 self.default = kwargs['default'] 868 del kwargs['default'] 869 870 if kwargs: 871 raise TypeError, 'extra unknown kwargs %s' % kwargs 872 873 if not hasattr(self, 'desc'): 874 raise TypeError, 'desc attribute missing' 875 876 def maybe_resolve_type(self, context): 877 # check if already resolved... don't use hasattr(), 878 # as that calls __getattr__() 879 if self.__dict__.has_key('ptype'): 880 return 881 try: 882 self.ptype = context[self.ptype_string] 883 except KeyError: 884 # no harm in trying... we'll try again later using global scope 885 pass 886 887 def __getattr__(self, attr): 888 if attr == 'ptype': 889 try: 890 self.ptype = param_types[self.ptype_string] 891 return self.ptype 892 except: 893 panic("undefined Param type %s" % self.ptype_string) 894 else: 895 raise AttributeError, "'%s' object has no attribute '%s'" % \ 896 (type(self).__name__, attr) 897 898 def valid(self, value): 899 if not isinstance(value, Proxy): 900 self.ptype._convert(value) 901 902 def convert(self, value): 903 return self.ptype._convert(value) 904 905 def string(self, value): 906 return self.ptype._string(value) 907 908 def set(self, name, instance, value): 909 instance.__dict__[name] = value 910 911 def cpp_decl(self, name): 912 return '%s %s;' % (self.ptype._cpp_param_decl, name) 913 914class _ParamProxy(object): 915 def __init__(self, type): 916 self.ptype = type 917 918 # E.g., Param.Int(5, "number of widgets") 919 def __call__(self, *args, **kwargs): 920 return _Param(self.ptype, *args, **kwargs) 921 922 # Strange magic to theoretically allow dotted names as Param classes, 923 # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar 924 def __getattr__(self, attr): 925 if attr == '__bases__': 926 raise AttributeError, '' 927 cls = type(self) 928 return cls(attr) 929 930 def __setattr__(self, attr, value): 931 if attr != 'ptype': 932 raise AttributeError, \ 933 'Attribute %s not available in %s' % (attr, self.__class__) 934 super(_ParamProxy, self).__setattr__(attr, value) 935 936 937Param = _ParamProxy(None) 938 939# Vector-valued parameter description. Just like Param, except that 940# the value is a vector (list) of the specified type instead of a 941# single value. 942class _VectorParam(_Param): 943 def __init__(self, type, *args, **kwargs): 944 _Param.__init__(self, type, *args, **kwargs) 945 946 def valid(self, value): 947 if value == None: 948 return True 949 950 if isinstance(value, (list, tuple)): 951 for val in value: 952 if not isinstance(val, Proxy): 953 self.ptype._convert(val) 954 elif not isinstance(value, Proxy): 955 self.ptype._convert(value) 956 957 # Convert assigned value to appropriate type. If the RHS is not a 958 # list or tuple, it generates a single-element list. 959 def convert(self, value): 960 if value == None: 961 return [] 962 963 if isinstance(value, (list, tuple)): 964 # list: coerce each element into new list 965 return [ self.ptype._convert(v) for v in value ] 966 else: 967 # singleton: coerce & wrap in a list 968 return self.ptype._convert(value) 969 970 def string(self, value): 971 if isinstance(value, (list, tuple)): 972 return ' '.join([ self.ptype._string(v) for v in value]) 973 else: 974 return self.ptype._string(value) 975 976 def cpp_decl(self, name): 977 return 'std::vector<%s> %s;' % (self.ptype._cpp_param_decl, name) 978 979class _VectorParamProxy(_ParamProxy): 980 # E.g., VectorParam.Int(5, "number of widgets") 981 def __call__(self, *args, **kwargs): 982 return _VectorParam(self.ptype, *args, **kwargs) 983 984VectorParam = _VectorParamProxy(None) 985 986##################################################################### 987# 988# Parameter Types 989# 990# Though native Python types could be used to specify parameter types 991# (the 'ptype' field of the Param and VectorParam classes), it's more 992# flexible to define our own set of types. This gives us more control 993# over how Python expressions are converted to values (via the 994# __init__() constructor) and how these values are printed out (via 995# the __str__() conversion method). Eventually we'll need these types 996# to correspond to distinct C++ types as well. 997# 998##################################################################### 999 1000 1001# Metaclass for bounds-checked integer parameters. See CheckedInt. 1002class CheckedIntType(type): 1003 def __init__(cls, name, bases, dict): 1004 super(CheckedIntType, cls).__init__(name, bases, dict) 1005 1006 # CheckedInt is an abstract base class, so we actually don't 1007 # want to do any processing on it... the rest of this code is 1008 # just for classes that derive from CheckedInt. 1009 if name == 'CheckedInt': 1010 return 1011 1012 if not (hasattr(cls, 'min') and hasattr(cls, 'max')): 1013 if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): 1014 panic("CheckedInt subclass %s must define either\n" \ 1015 " 'min' and 'max' or 'size' and 'unsigned'\n" \ 1016 % name); 1017 if cls.unsigned: 1018 cls.min = 0 1019 cls.max = 2 ** cls.size - 1 1020 else: 1021 cls.min = -(2 ** (cls.size - 1)) 1022 cls.max = (2 ** (cls.size - 1)) - 1 1023 1024 cls._cpp_param_decl = cls.cppname 1025 1026 def _convert(cls, value): 1027 if isinstance(value, bool): 1028 return int(value) 1029 1030 if not isinstance(value, (int, long, float, str)): 1031 raise TypeError, 'Integer param of invalid type %s' % type(value) 1032 1033 if isinstance(value, (str, float)): 1034 value = long(float(value)) 1035 1036 if not cls.min <= value <= cls.max: 1037 raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ 1038 (cls.min, value, cls.max) 1039 1040 return value 1041 1042 def _string(cls, value): 1043 return str(value) 1044 1045# Abstract superclass for bounds-checked integer parameters. This 1046# class is subclassed to generate parameter classes with specific 1047# bounds. Initialization of the min and max bounds is done in the 1048# metaclass CheckedIntType.__init__. 1049class CheckedInt(ParamType): 1050 __metaclass__ = CheckedIntType 1051 1052class Int(CheckedInt): cppname = 'int'; size = 32; unsigned = False 1053class Unsigned(CheckedInt): cppname = 'unsigned'; size = 32; unsigned = True 1054 1055class Int8(CheckedInt): cppname = 'int8_t'; size = 8; unsigned = False 1056class UInt8(CheckedInt): cppname = 'uint8_t'; size = 8; unsigned = True 1057class Int16(CheckedInt): cppname = 'int16_t'; size = 16; unsigned = False 1058class UInt16(CheckedInt): cppname = 'uint16_t'; size = 16; unsigned = True 1059class Int32(CheckedInt): cppname = 'int32_t'; size = 32; unsigned = False 1060class UInt32(CheckedInt): cppname = 'uint32_t'; size = 32; unsigned = True 1061class Int64(CheckedInt): cppname = 'int64_t'; size = 64; unsigned = False 1062class UInt64(CheckedInt): cppname = 'uint64_t'; size = 64; unsigned = True 1063 1064class Counter(CheckedInt): cppname = 'Counter'; size = 64; unsigned = True 1065class Addr(CheckedInt): cppname = 'Addr'; size = 64; unsigned = True 1066class Tick(CheckedInt): cppname = 'Tick'; size = 64; unsigned = True 1067 1068class Percent(CheckedInt): cppname = 'int'; min = 0; max = 100 1069 1070class Pair(object): 1071 def __init__(self, first, second): 1072 self.first = first 1073 self.second = second 1074 1075class MetaRange(type): 1076 def __init__(cls, name, bases, dict): 1077 super(MetaRange, cls).__init__(name, bases, dict) 1078 if name == 'Range': 1079 return 1080 cls._cpp_param_decl = 'Range<%s>' % cls.type._cpp_param_decl 1081 1082 def _convert(cls, value): 1083 if not isinstance(value, Pair): 1084 raise TypeError, 'value %s is not a Pair' % value 1085 return Pair(cls.type._convert(value.first), 1086 cls.type._convert(value.second)) 1087 1088 def _string(cls, value): 1089 return '%s:%s' % (cls.type._string(value.first), 1090 cls.type._string(value.second)) 1091 1092class Range(ParamType): 1093 __metaclass__ = MetaRange 1094 1095def RangeSize(start, size): 1096 return Pair(start, start + size - 1) 1097 1098class AddrRange(Range): type = Addr 1099 1100# Boolean parameter type. 1101class Bool(ParamType): 1102 _cpp_param_decl = 'bool' 1103 def _convert(value): 1104 t = type(value) 1105 if t == bool: 1106 return value 1107 1108 if t == int or t == long: 1109 return bool(value) 1110 1111 if t == str: 1112 v = value.lower() 1113 if v == "true" or v == "t" or v == "yes" or v == "y": 1114 return True 1115 elif v == "false" or v == "f" or v == "no" or v == "n": 1116 return False 1117 1118 raise TypeError, 'Bool parameter (%s) of invalid type %s' % (v, t) 1119 _convert = staticmethod(_convert) 1120 1121 def _string(value): 1122 if value: 1123 return "true" 1124 else: 1125 return "false" 1126 _string = staticmethod(_string) 1127 1128# String-valued parameter. 1129class String(ParamType): 1130 _cpp_param_decl = 'string' 1131 1132 # Constructor. Value must be Python string. 1133 def _convert(cls,value): 1134 if value is None: 1135 return '' 1136 if isinstance(value, str): 1137 return value 1138 1139 raise TypeError, \ 1140 "String param got value %s %s" % (repr(value), type(value)) 1141 _convert = classmethod(_convert) 1142 1143 # Generate printable string version. Not too tricky. 1144 def _string(cls, value): 1145 return value 1146 _string = classmethod(_string) 1147 1148 1149def IncEthernetAddr(addr, val = 1): 1150 bytes = map(lambda x: int(x, 16), addr.split(':')) 1151 bytes[5] += val 1152 for i in (5, 4, 3, 2, 1): 1153 val,rem = divmod(bytes[i], 256) 1154 bytes[i] = rem 1155 if val == 0: 1156 break 1157 bytes[i - 1] += val 1158 assert(bytes[0] <= 255) 1159 return ':'.join(map(lambda x: '%02x' % x, bytes)) 1160 1161class NextEthernetAddr(object): 1162 __metaclass__ = Singleton 1163 addr = "00:90:00:00:00:01" 1164 1165 def __init__(self, inc = 1): 1166 self.value = self.addr 1167 self.addr = IncEthernetAddr(self.addr, inc) 1168 1169class EthernetAddr(ParamType): 1170 _cpp_param_decl = 'EthAddr' 1171 1172 def _convert(cls, value): 1173 if value == NextEthernetAddr: 1174 return value 1175 1176 if not isinstance(value, str): 1177 raise TypeError, "expected an ethernet address and didn't get one" 1178 1179 bytes = value.split(':') 1180 if len(bytes) != 6: 1181 raise TypeError, 'invalid ethernet address %s' % value 1182 1183 for byte in bytes: 1184 if not 0 <= int(byte) <= 256: 1185 raise TypeError, 'invalid ethernet address %s' % value 1186 1187 return value 1188 _convert = classmethod(_convert) 1189 1190 def _string(cls, value): 1191 if value == NextEthernetAddr: 1192 value = value().value 1193 return value 1194 _string = classmethod(_string) 1195 1196# Special class for NULL pointers. Note the special check in 1197# make_param_value() above that lets these be assigned where a 1198# SimObject is required. 1199# only one copy of a particular node 1200class NullSimObject(object): 1201 __metaclass__ = Singleton 1202 1203 def __call__(cls): 1204 return cls 1205 1206 def _instantiate(self, parent = None, path = ''): 1207 pass 1208 1209 def _convert(cls, value): 1210 if value == Nxone: 1211 return 1212 1213 if isinstance(value, cls): 1214 return value 1215 1216 raise TypeError, 'object %s %s of the wrong type, should be %s' % \ 1217 (repr(value), type(value), cls) 1218 _convert = classmethod(_convert) 1219 1220 def _string(): 1221 return 'NULL' 1222 _string = staticmethod(_string) 1223 1224# The only instance you'll ever need... 1225Null = NULL = NullSimObject() 1226 1227# Enumerated types are a little more complex. The user specifies the 1228# type as Enum(foo) where foo is either a list or dictionary of 1229# alternatives (typically strings, but not necessarily so). (In the 1230# long run, the integer value of the parameter will be the list index 1231# or the corresponding dictionary value. For now, since we only check 1232# that the alternative is valid and then spit it into a .ini file, 1233# there's not much point in using the dictionary.) 1234 1235# What Enum() must do is generate a new type encapsulating the 1236# provided list/dictionary so that specific values of the parameter 1237# can be instances of that type. We define two hidden internal 1238# classes (_ListEnum and _DictEnum) to serve as base classes, then 1239# derive the new type from the appropriate base class on the fly. 1240 1241 1242# Metaclass for Enum types 1243class MetaEnum(type): 1244 1245 def __init__(cls, name, bases, init_dict): 1246 if init_dict.has_key('map'): 1247 if not isinstance(cls.map, dict): 1248 raise TypeError, "Enum-derived class attribute 'map' " \ 1249 "must be of type dict" 1250 # build list of value strings from map 1251 cls.vals = cls.map.keys() 1252 cls.vals.sort() 1253 elif init_dict.has_key('vals'): 1254 if not isinstance(cls.vals, list): 1255 raise TypeError, "Enum-derived class attribute 'vals' " \ 1256 "must be of type list" 1257 # build string->value map from vals sequence 1258 cls.map = {} 1259 for idx,val in enumerate(cls.vals): 1260 cls.map[val] = idx 1261 else: 1262 raise TypeError, "Enum-derived class must define "\ 1263 "attribute 'map' or 'vals'" 1264 1265 cls._cpp_param_decl = name 1266 1267 super(MetaEnum, cls).__init__(name, bases, init_dict) 1268 1269 def cpp_declare(cls): 1270 s = 'enum %s {\n ' % cls.__name__ 1271 s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) 1272 s += '\n};\n' 1273 return s 1274 1275# Base class for enum types. 1276class Enum(ParamType): 1277 __metaclass__ = MetaEnum 1278 vals = [] 1279 1280 def _convert(self, value): 1281 if value not in self.map: 1282 raise TypeError, "Enum param got bad value '%s' (not in %s)" \ 1283 % (value, self.vals) 1284 return value 1285 _convert = classmethod(_convert) 1286 1287 # Generate printable string version of value. 1288 def _string(self, value): 1289 return str(value) 1290 _string = classmethod(_string) 1291# 1292# "Constants"... handy aliases for various values. 1293# 1294 1295# Some memory range specifications use this as a default upper bound. 1296MAX_ADDR = Addr.max 1297MaxTick = Tick.max 1298 1299# For power-of-two sizing, e.g. 64*K gives an integer value 65536. 1300K = 1024 1301M = K*K 1302G = K*M 1303 1304##################################################################### 1305 1306# The final hook to generate .ini files. Called from configuration 1307# script once config is built. 1308def instantiate(root): 1309 instance = root.instantiate('root') 1310 instance.fixup() 1311 instance.display() 1312 if not noDot: 1313 dot = pydot.Dot() 1314 instance.outputDot(dot) 1315 dot.orientation = "portrait" 1316 dot.size = "8.5,11" 1317 dot.ranksep="equally" 1318 dot.rank="samerank" 1319 dot.write("config.dot") 1320 dot.write_ps("config.ps") 1321 1322# SimObject is a minimal extension of ConfigNode, implementing a 1323# hierarchy node that corresponds to an M5 SimObject. It prints out a 1324# "type=" line to indicate its SimObject class, prints out the 1325# assigned parameters corresponding to its class, and allows 1326# parameters to be set by keyword in the constructor. Note that most 1327# of the heavy lifting for the SimObject param handling is done in the 1328# MetaConfigNode metaclass. 1329class SimObject(ConfigNode, ParamType): 1330 __metaclass__ = MetaSimObject 1331 type = 'SimObject' 1332 1333 1334# __all__ defines the list of symbols that get exported when 1335# 'from config import *' is invoked. Try to keep this reasonably 1336# short to avoid polluting other namespaces. 1337__all__ = ['ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam', 1338 'parent', 'Enum', 1339 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', 1340 'Int32', 'UInt32', 'Int64', 'UInt64', 1341 'Counter', 'Addr', 'Tick', 'Percent', 1342 'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M', 1343 'NextEthernetAddr', 1344 'instantiate'] 1345