summaryrefslogtreecommitdiff
path: root/configs/example/read_config.py
diff options
context:
space:
mode:
Diffstat (limited to 'configs/example/read_config.py')
-rw-r--r--configs/example/read_config.py531
1 files changed, 531 insertions, 0 deletions
diff --git a/configs/example/read_config.py b/configs/example/read_config.py
new file mode 100644
index 000000000..ecb2950e6
--- /dev/null
+++ b/configs/example/read_config.py
@@ -0,0 +1,531 @@
+# Copyright (c) 2014 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: Andrew Bardsley
+
+# This script allows .ini and .json system config file generated from a
+# previous gem5 run to be read in and instantiated.
+#
+# This may be useful as a way of allowing variant run scripts (say,
+# with more complicated than usual checkpointing/stats dumping/
+# simulation control) to read pre-described systems from config scripts
+# with better system-description capabilities. Splitting scripts
+# between system construction and run control may allow better
+# debugging.
+
+import argparse
+import ConfigParser
+import inspect
+import json
+import re
+import sys
+
+import m5
+import m5.ticks as ticks
+
+sim_object_classes_by_name = {
+ cls.__name__: cls for cls in m5.objects.__dict__.itervalues()
+ if inspect.isclass(cls) and issubclass(cls, m5.objects.SimObject) }
+
+# Add some parsing functions to Param classes to handle reading in .ini
+# file elements. This could be moved into src/python/m5/params.py if
+# reading .ini files from Python proves to be useful
+
+def no_parser(cls, flags, param):
+ raise Exception('Can\'t parse string: %s for parameter'
+ ' class: %s' % (str(param), cls.__name__))
+
+def simple_parser(suffix='', cast=lambda i: i):
+ def body(cls, flags, param):
+ return cls(cast(param + suffix))
+ return body
+
+# def tick_parser(cast=m5.objects.Latency): # lambda i: i):
+def tick_parser(cast=lambda i: i):
+ def body(cls, flags, param):
+ old_param = param
+ ret = cls(cast(str(param) + 't'))
+ return ret
+ return body
+
+def addr_range_parser(cls, flags, param):
+ sys.stdout.flush()
+ low, high = param.split(':')
+ return m5.objects.AddrRange(long(low), long(high))
+
+def memory_bandwidth_parser(cls, flags, param):
+ # The string will be in tick/byte
+ # Convert to byte/tick
+ value = 1.0 / float(param)
+ # Convert to byte/s
+ value = ticks.fromSeconds(value)
+ return cls('%fB/s' % value)
+
+# These parameters have trickier parsing from .ini files than might be
+# expected
+param_parsers = {
+ 'Bool': simple_parser(),
+ 'ParamValue': no_parser,
+ 'NumericParamValue': simple_parser(cast=long),
+ 'TickParamValue': tick_parser(),
+ 'Frequency': tick_parser(cast=m5.objects.Latency),
+ 'Voltage': simple_parser(suffix='V'),
+ 'Enum': simple_parser(),
+ 'MemorySize': simple_parser(suffix='B'),
+ 'MemorySize32': simple_parser(suffix='B'),
+ 'AddrRange': addr_range_parser,
+ 'String': simple_parser(),
+ 'MemoryBandwidth': memory_bandwidth_parser,
+ 'Time': simple_parser()
+ }
+
+for name, parser in param_parsers.iteritems():
+ setattr(m5.params.__dict__[name], 'parse_ini', classmethod(parser))
+
+class PortConnection(object):
+ """This class is similar to m5.params.PortRef but with just enough
+ information for ConfigManager"""
+
+ def __init__(self, object_name, port_name, index):
+ self.object_name = object_name
+ self.port_name = port_name
+ self.index = index
+
+ @classmethod
+ def from_string(cls, str):
+ m = re.match('(.*)\.([^.\[]+)(\[(\d+)\])?', str)
+ object_name, port_name, whole_index, index = m.groups()
+ if index is not None:
+ index = int(index)
+ else:
+ index = 0
+
+ return PortConnection(object_name, port_name, index)
+
+ def __str__(self):
+ return '%s.%s[%d]' % (self.object_name, self.port_name, self.index)
+
+ def __cmp__(self, right):
+ return cmp((self.object_name, self.port_name, self.index),
+ (right.object_name, right.port_name, right.index))
+
+def to_list(v):
+ """Convert any non list to a singleton list"""
+ if isinstance(v, list):
+ return v
+ else:
+ return [v]
+
+class ConfigManager(object):
+ """Manager for parsing a Root configuration from a config file"""
+ def __init__(self, config):
+ self.config = config
+ self.objects_by_name = {}
+ self.flags = config.get_flags()
+
+ def find_object(self, object_name):
+ """Find and configure (with just non-SimObject parameters)
+ a single object"""
+
+ if object_name == 'Null':
+ return NULL
+
+ if object_name in self.objects_by_name:
+ return self.objects_by_name[object_name]
+
+ object_type = self.config.get_param(object_name, 'type')
+
+ if object_type not in sim_object_classes_by_name:
+ raise Exception('No SimObject type %s is available to'
+ ' build: %s' % (object_type, object_name))
+
+ object_class = sim_object_classes_by_name[object_type]
+
+ parsed_params = {}
+
+ for param_name, param in object_class._params.iteritems():
+ if issubclass(param.ptype, m5.params.ParamValue):
+ if isinstance(param, m5.params.VectorParamDesc):
+ param_values = self.config.get_param_vector(object_name,
+ param_name)
+
+ param_value = [ param.ptype.parse_ini(self.flags, value)
+ for value in param_values ]
+ else:
+ param_value = param.ptype.parse_ini(
+ self.flags, self.config.get_param(object_name,
+ param_name))
+
+ parsed_params[param_name] = param_value
+
+ obj = object_class(**parsed_params)
+ self.objects_by_name[object_name] = obj
+
+ return obj
+
+ def fill_in_simobj_parameters(self, object_name, obj):
+ """Fill in all references to other SimObjects in an objects
+ parameters. This relies on all referenced objects having been
+ created"""
+
+ if object_name == 'Null':
+ return NULL
+
+ for param_name, param in obj.__class__._params.iteritems():
+ if issubclass(param.ptype, m5.objects.SimObject):
+ if isinstance(param, m5.params.VectorParamDesc):
+ param_values = self.config.get_param_vector(object_name,
+ param_name)
+
+ setattr(obj, param_name, [ self.objects_by_name[name]
+ for name in param_values ])
+ else:
+ param_value = self.config.get_param(object_name,
+ param_name)
+
+ if param_value != 'Null':
+ setattr(obj, param_name, self.objects_by_name[
+ param_value])
+
+ return obj
+
+ def fill_in_children(self, object_name, obj):
+ """Fill in the children of this object. This relies on all the
+ referenced objects having been created"""
+
+ children = self.config.get_object_children(object_name)
+
+ for child_name, child_paths in children:
+ param = obj.__class__._params.get(child_name, None)
+
+ if isinstance(child_paths, list):
+ child_list = [ self.objects_by_name[path]
+ for path in child_paths ]
+ else:
+ child_list = self.objects_by_name[child_paths]
+
+ obj.add_child(child_name, child_list)
+
+ for path in to_list(child_paths):
+ self.fill_in_children(path, self.objects_by_name[path])
+
+ return obj
+
+ def parse_port_name(self, port):
+ """Parse the name of a port"""
+
+ m = re.match('(.*)\.([^.\[]+)(\[(\d+)\])?', port)
+ peer, peer_port, whole_index, index = m.groups()
+ if index is not None:
+ index = int(index)
+ else:
+ index = 0
+
+ return (peer, self.objects_by_name[peer], peer_port, index)
+
+ def gather_port_connections(self, object_name, obj):
+ """Gather all the port-to-port connections from the named object.
+ Returns a list of (PortConnection, PortConnection) with unordered
+ (wrt. master/slave) connection information"""
+
+ if object_name == 'Null':
+ return NULL
+
+ parsed_ports = []
+ for port_name, port in obj.__class__._ports.iteritems():
+ # Assume that unnamed ports are unconnected
+ peers = self.config.get_port_peers(object_name, port_name)
+
+ for index, peer in zip(xrange(0, len(peers)), peers):
+ parsed_ports.append((
+ PortConnection(object_name, port.name, index),
+ PortConnection.from_string(peer)))
+
+ return parsed_ports
+
+ def bind_ports(self, connections):
+ """Bind all ports from the given connection list. Note that the
+ connection list *must* list all connections with both (slave,master)
+ and (master,slave) orderings"""
+
+ # Markup a dict of how many connections are made to each port.
+ # This will be used to check that the next-to-be-made connection
+ # has a suitable port index
+ port_bind_indices = {}
+ for from_port, to_port in connections:
+ port_bind_indices[
+ (from_port.object_name, from_port.port_name)] = 0
+
+ def port_has_correct_index(port):
+ return port_bind_indices[
+ (port.object_name, port.port_name)] == port.index
+
+ def increment_port_index(port):
+ port_bind_indices[
+ (port.object_name, port.port_name)] += 1
+
+ # Step through the sorted connections. Exactly one of
+ # each (slave,master) and (master,slave) pairs will be
+ # bindable because the connections are sorted.
+ # For example: port_bind_indices
+ # left right left right
+ # a.b[0] -> d.f[1] 0 0 X
+ # a.b[1] -> e.g 0 0 BIND!
+ # e.g -> a.b[1] 1 X 0
+ # d.f[0] -> f.h 0 0 BIND!
+ # d.f[1] -> a.b[0] 1 0 BIND!
+ connections_to_make = []
+ for connection in sorted(connections):
+ from_port, to_port = connection
+
+ if (port_has_correct_index(from_port) and
+ port_has_correct_index(to_port)):
+
+ connections_to_make.append((from_port, to_port))
+
+ increment_port_index(from_port)
+ increment_port_index(to_port)
+
+ # Exactly half of the connections (ie. all of them, one per
+ # direction) must now have been made
+ if (len(connections_to_make) * 2) != len(connections):
+ raise Exception('Port bindings can\'t be ordered')
+
+ # Actually do the binding
+ for from_port, to_port in connections_to_make:
+ from_object = self.objects_by_name[from_port.object_name]
+ to_object = self.objects_by_name[to_port.object_name]
+
+ setattr(from_object, from_port.port_name,
+ getattr(to_object, to_port.port_name))
+
+ def find_all_objects(self):
+ """Find and build all SimObjects from the config file and connect
+ their ports together as described. Does not instantiate system"""
+
+ # Build SimObjects for all sections of the config file
+ # populating not-SimObject-valued parameters
+ for object_name in self.config.get_all_object_names():
+ self.find_object(object_name)
+
+ # Add children to objects in the hierarchy from root
+ self.fill_in_children('root', self.find_object('root'))
+
+ # Now fill in SimObject-valued parameters in the knowledge that
+ # this won't be interpreted as becoming the parent of objects
+ # which are already in the root hierarchy
+ for name, obj in self.objects_by_name.iteritems():
+ self.fill_in_simobj_parameters(name, obj)
+
+ # Gather a list of all port-to-port connections
+ connections = []
+ for name, obj in self.objects_by_name.iteritems():
+ connections += self.gather_port_connections(name, obj)
+
+ # Find an acceptable order to bind those port connections and
+ # bind them
+ self.bind_ports(connections)
+
+class ConfigFile(object):
+ def get_flags(self):
+ return set()
+
+ def load(self, config_file):
+ """Load the named config file"""
+ pass
+
+ def get_all_object_names(self):
+ """Get a list of all the SimObject paths in the configuration"""
+ pass
+
+ def get_param(self, object_name, param_name):
+ """Get a single param or SimObject reference from the configuration
+ as a string"""
+ pass
+
+ def get_param_vector(self, object_name, param_name):
+ """Get a vector param or vector of SimObject references from the
+ configuration as a list of strings"""
+ pass
+
+ def get_object_children(self, object_name):
+ """Get a list of (name, paths) for each child of this object.
+ paths is either a single string object path or a list of object
+ paths"""
+ pass
+
+ def get_port_peers(self, object_name, port_name):
+ """Get the list of connected port names (in the string form
+ object.port(\[index\])?) of the port object_name.port_name"""
+ pass
+
+class ConfigIniFile(ConfigFile):
+ def __init__(self):
+ self.parser = ConfigParser.ConfigParser()
+
+ def load(self, config_file):
+ self.parser.read(config_file)
+
+ def get_all_object_names(self):
+ return self.parser.sections()
+
+ def get_param(self, object_name, param_name):
+ return self.parser.get(object_name, param_name)
+
+ def get_param_vector(self, object_name, param_name):
+ return self.parser.get(object_name, param_name).split()
+
+ def get_object_children(self, object_name):
+ if self.parser.has_option(object_name, 'children'):
+ children = self.parser.get(object_name, 'children')
+ child_names = children.split()
+ else:
+ child_names = []
+
+ def make_path(child_name):
+ if object_name == 'root':
+ return child_name
+ else:
+ return '%s.%s' % (object_name, child_name)
+
+ return [ (name, make_path(name)) for name in child_names ]
+
+ def get_port_peers(self, object_name, port_name):
+ if self.parser.has_option(object_name, port_name):
+ peer_string = self.parser.get(object_name, port_name)
+ return peer_string.split()
+ else:
+ return []
+
+class ConfigJsonFile(ConfigFile):
+ def __init__(self):
+ pass
+
+ def is_sim_object(self, node):
+ return isinstance(node, dict) and 'path' in node
+
+ def find_all_objects(self, node):
+ if self.is_sim_object(node):
+ self.object_dicts[node['path']] = node
+
+ if isinstance(node, list):
+ for elem in node:
+ self.find_all_objects(elem)
+ elif isinstance(node, dict):
+ for elem in node.itervalues():
+ self.find_all_objects(elem)
+
+ def load(self, config_file):
+ root = json.load(open(config_file, 'r'))
+ self.object_dicts = {}
+ self.find_all_objects(root)
+
+ def get_all_object_names(self):
+ return sorted(self.object_dicts.keys())
+
+ def parse_param_string(self, node):
+ if node is None:
+ return "Null"
+ elif self.is_sim_object(node):
+ return node['path']
+ else:
+ return str(node)
+
+ def get_param(self, object_name, param_name):
+ obj = self.object_dicts[object_name]
+
+ return self.parse_param_string(obj[param_name])
+
+ def get_param_vector(self, object_name, param_name):
+ obj = self.object_dicts[object_name]
+
+ return [ self.parse_param_string(p) for p in obj[param_name] ]
+
+ def get_object_children(self, object_name):
+ """It is difficult to tell which elements are children in the
+ JSON file as there is no explicit 'children' node. Take any
+ element which is a full SimObject description or a list of
+ SimObject descriptions. This will not work with a mixed list of
+ references and descriptions but that's a scenario that isn't
+ possible (very likely?) with gem5's binding/naming rules"""
+ obj = self.object_dicts[object_name]
+
+ children = []
+ for name, node in obj.iteritems():
+ if self.is_sim_object(node):
+ children.append((name, node['path']))
+ elif isinstance(node, list) and node != [] and all([
+ self.is_sim_object(e) for e in node ]):
+ children.append((name, [ e['path'] for e in node ]))
+
+ return children
+
+ def get_port_peers(self, object_name, port_name):
+ """Get the 'peer' element of any node with 'peer' and 'role'
+ elements"""
+ obj = self.object_dicts[object_name]
+
+ peers = []
+ if port_name in obj and 'peer' in obj[port_name] and \
+ 'role' in obj[port_name]:
+ peers = to_list(obj[port_name]['peer'])
+
+ return peers
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('config_file', metavar='config-file.ini',
+ help='.ini configuration file to load and run')
+
+args = parser.parse_args(sys.argv[1:])
+
+if args.config_file.endswith('.ini'):
+ config = ConfigIniFile()
+ config.load(args.config_file)
+else:
+ config = ConfigJsonFile()
+ config.load(args.config_file)
+
+ticks.fixGlobalFrequency()
+
+mgr = ConfigManager(config)
+
+mgr.find_all_objects()
+
+m5.instantiate()
+
+exit_event = m5.simulate()
+print 'Exiting @ tick %i because %s' % (
+ m5.curTick(), exit_event.getCause())