diff options
-rwxr-xr-x | SConstruct | 3 | ||||
-rw-r--r-- | configs/example/read_config.py | 531 | ||||
-rwxr-xr-x | src/SConscript | 67 | ||||
-rw-r--r-- | src/python/m5/SimObject.py | 278 | ||||
-rw-r--r-- | src/python/m5/params.py | 150 | ||||
-rw-r--r-- | src/sim/SConscript | 4 | ||||
-rw-r--r-- | src/sim/cxx_config.cc | 45 | ||||
-rw-r--r-- | src/sim/cxx_config.hh | 241 | ||||
-rw-r--r-- | src/sim/cxx_config_ini.cc | 104 | ||||
-rw-r--r-- | src/sim/cxx_config_ini.hh | 87 | ||||
-rw-r--r-- | src/sim/cxx_manager.cc | 740 | ||||
-rw-r--r-- | src/sim/cxx_manager.hh | 314 | ||||
-rw-r--r-- | util/cxx_config/Makefile | 65 | ||||
-rw-r--r-- | util/cxx_config/README | 44 | ||||
-rw-r--r-- | util/cxx_config/main.cc | 319 | ||||
-rw-r--r-- | util/cxx_config/stats.cc | 115 | ||||
-rw-r--r-- | util/cxx_config/stats.hh | 61 |
17 files changed, 3168 insertions, 0 deletions
diff --git a/SConstruct b/SConstruct index 1078a3e96..8fb649143 100755 --- a/SConstruct +++ b/SConstruct @@ -173,6 +173,9 @@ AddLocalOption('--colors', dest='use_colors', action='store_true', help="Add color to abbreviated scons output") AddLocalOption('--no-colors', dest='use_colors', action='store_false', help="Don't add color to abbreviated scons output") +AddLocalOption('--with-cxx-config', dest='with_cxx_config', + action='store_true', + help="Build with support for C++-based configuration") AddLocalOption('--default', dest='default', type='string', action='store', help='Override which build_opts file to use for defaults') AddLocalOption('--ignore-style', dest='ignore_style', action='store_true', 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()) diff --git a/src/SConscript b/src/SConscript index ef729cb33..8fe22d9ec 100755 --- a/src/SConscript +++ b/src/SConscript @@ -579,6 +579,18 @@ def createSimObjectParamStruct(target, source, env): obj.cxx_param_decl(code) code.write(target[0].abspath) +def createSimObjectCxxConfig(is_header): + def body(target, source, env): + assert len(target) == 1 and len(source) == 1 + + name = str(source[0].get_contents()) + obj = sim_objects[name] + + code = code_formatter() + obj.cxx_config_param_file(code, is_header) + code.write(target[0].abspath) + return body + def createParamSwigWrapper(target, source, env): assert len(target) == 1 and len(source) == 1 @@ -644,6 +656,61 @@ for name,simobj in sorted(sim_objects.iteritems()): env.Depends(hh_file, depends + extra_deps) env.Depends(SWIG, hh_file) +# C++ parameter description files +if GetOption('with_cxx_config'): + for name,simobj in sorted(sim_objects.iteritems()): + py_source = PySource.modules[simobj.__module__] + extra_deps = [ py_source.tnode ] + + cxx_config_hh_file = File('cxx_config/%s.hh' % name) + cxx_config_cc_file = File('cxx_config/%s.cc' % name) + env.Command(cxx_config_hh_file, Value(name), + MakeAction(createSimObjectCxxConfig(True), + Transform("CXXCPRHH"))) + env.Command(cxx_config_cc_file, Value(name), + MakeAction(createSimObjectCxxConfig(False), + Transform("CXXCPRCC"))) + env.Depends(cxx_config_hh_file, depends + extra_deps + + [File('params/%s.hh' % name), File('sim/cxx_config.hh')]) + env.Depends(cxx_config_cc_file, depends + extra_deps + + [cxx_config_hh_file]) + Source(cxx_config_cc_file) + + cxx_config_init_cc_file = File('cxx_config/init.cc') + + def createCxxConfigInitCC(target, source, env): + assert len(target) == 1 and len(source) == 1 + + code = code_formatter() + + for name,simobj in sorted(sim_objects.iteritems()): + if not hasattr(simobj, 'abstract') or not simobj.abstract: + code('#include "cxx_config/${name}.hh"') + code() + code('void cxxConfigInit()') + code('{') + code.indent() + for name,simobj in sorted(sim_objects.iteritems()): + not_abstract = not hasattr(simobj, 'abstract') or \ + not simobj.abstract + if not_abstract and 'type' in simobj.__dict__: + code('cxx_config_directory["${name}"] = ' + '${name}CxxConfigParams::makeDirectoryEntry();') + code.dedent() + code('}') + code.write(target[0].abspath) + + py_source = PySource.modules[simobj.__module__] + extra_deps = [ py_source.tnode ] + env.Command(cxx_config_init_cc_file, Value(name), + MakeAction(createCxxConfigInitCC, Transform("CXXCINIT"))) + cxx_param_hh_files = ["cxx_config/%s.hh" % simobj + for simobj in sorted(sim_objects.itervalues()) + if not hasattr(simobj, 'abstract') or not simobj.abstract] + Depends(cxx_config_init_cc_file, cxx_param_hh_files + + [File('sim/cxx_config.hh')]) + Source(cxx_config_init_cc_file) + # Generate any needed param SWIG wrapper files params_i_files = [] for name,param in params_to_swig.iteritems(): diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py index 9f4c2c155..40203a307 100644 --- a/src/python/m5/SimObject.py +++ b/src/python/m5/SimObject.py @@ -114,6 +114,279 @@ def public_value(key, value): isinstance(value, (FunctionType, MethodType, ModuleType, classmethod, type)) +def createCxxConfigDirectoryEntryFile(code, name, simobj, is_header): + entry_class = 'CxxConfigDirectoryEntry_%s' % name + param_class = '%sCxxConfigParams' % name + + code('#include "params/%s.hh"' % name) + + if not is_header: + for param in simobj._params.values(): + if isSimObjectClass(param.ptype): + code('#include "%s"' % param.ptype._value_dict['cxx_header']) + code('#include "params/%s.hh"' % param.ptype.__name__) + else: + param.ptype.cxx_ini_predecls(code) + + if is_header: + member_prefix = '' + end_of_decl = ';' + code('#include "sim/cxx_config.hh"') + code() + code('class ${param_class} : public CxxConfigParams,' + ' public ${name}Params') + code('{') + code(' private:') + code.indent() + code('class DirectoryEntry : public CxxConfigDirectoryEntry') + code('{') + code(' public:') + code.indent() + code('DirectoryEntry();'); + code() + code('CxxConfigParams *makeParamsObject() const') + code('{ return new ${param_class}; }') + code.dedent() + code('};') + code() + code.dedent() + code(' public:') + code.indent() + else: + member_prefix = '%s::' % param_class + end_of_decl = '' + code('#include "%s"' % simobj._value_dict['cxx_header']) + code('#include "base/str.hh"') + code('#include "cxx_config/${name}.hh"') + + if simobj._ports.values() != []: + code('#include "mem/mem_object.hh"') + code('#include "mem/port.hh"') + + code() + code('${member_prefix}DirectoryEntry::DirectoryEntry()'); + code('{') + + def cxx_bool(b): + return 'true' if b else 'false' + + code.indent() + for param in simobj._params.values(): + is_vector = isinstance(param, m5.params.VectorParamDesc) + is_simobj = issubclass(param.ptype, m5.SimObject.SimObject) + + code('parameters["%s"] = new ParamDesc("%s", %s, %s);' % + (param.name, param.name, cxx_bool(is_vector), + cxx_bool(is_simobj))); + + for port in simobj._ports.values(): + is_vector = isinstance(port, m5.params.VectorPort) + is_master = port.role == 'MASTER' + + code('ports["%s"] = new PortDesc("%s", %s, %s);' % + (port.name, port.name, cxx_bool(is_vector), + cxx_bool(is_master))) + + code.dedent() + code('}') + code() + + code('bool ${member_prefix}setSimObject(const std::string &name,') + code(' SimObject *simObject)${end_of_decl}') + + if not is_header: + code('{') + code.indent() + code('bool ret = true;') + code() + code('if (false) {') + for param in simobj._params.values(): + is_vector = isinstance(param, m5.params.VectorParamDesc) + is_simobj = issubclass(param.ptype, m5.SimObject.SimObject) + + if is_simobj and not is_vector: + code('} else if (name == "${{param.name}}") {') + code.indent() + code('this->${{param.name}} = ' + 'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);') + code('if (simObject && !this->${{param.name}})') + code(' ret = false;') + code.dedent() + code('} else {') + code(' ret = false;') + code('}') + code() + code('return ret;') + code.dedent() + code('}') + + code() + code('bool ${member_prefix}setSimObjectVector(' + 'const std::string &name,') + code(' const std::vector<SimObject *> &simObjects)${end_of_decl}') + + if not is_header: + code('{') + code.indent() + code('bool ret = true;') + code() + code('if (false) {') + for param in simobj._params.values(): + is_vector = isinstance(param, m5.params.VectorParamDesc) + is_simobj = issubclass(param.ptype, m5.SimObject.SimObject) + + if is_simobj and is_vector: + code('} else if (name == "${{param.name}}") {') + code.indent() + code('this->${{param.name}}.clear();') + code('for (auto i = simObjects.begin(); ' + 'ret && i != simObjects.end(); i ++)') + code('{') + code.indent() + code('${{param.ptype.cxx_type}} object = ' + 'dynamic_cast<${{param.ptype.cxx_type}}>(*i);') + code('if (*i && !object)') + code(' ret = false;') + code('else') + code(' this->${{param.name}}.push_back(object);') + code.dedent() + code('}') + code.dedent() + code('} else {') + code(' ret = false;') + code('}') + code() + code('return ret;') + code.dedent() + code('}') + + code() + code('void ${member_prefix}setName(const std::string &name_)' + '${end_of_decl}') + + if not is_header: + code('{') + code.indent() + code('this->name = name_;') + code('this->pyobj = NULL;') + code.dedent() + code('}') + + if is_header: + code('const std::string &${member_prefix}getName()') + code('{ return this->name; }') + + code() + code('bool ${member_prefix}setParam(const std::string &name,') + code(' const std::string &value, const Flags flags)${end_of_decl}') + + if not is_header: + code('{') + code.indent() + code('bool ret = true;') + code() + code('if (false) {') + for param in simobj._params.values(): + is_vector = isinstance(param, m5.params.VectorParamDesc) + is_simobj = issubclass(param.ptype, m5.SimObject.SimObject) + + if not is_simobj and not is_vector: + code('} else if (name == "${{param.name}}") {') + code.indent() + param.ptype.cxx_ini_parse(code, + 'value', 'this->%s' % param.name, 'ret =') + code.dedent() + code('} else {') + code(' ret = false;') + code('}') + code() + code('return ret;') + code.dedent() + code('}') + + code() + code('bool ${member_prefix}setParamVector(' + 'const std::string &name,') + code(' const std::vector<std::string> &values,') + code(' const Flags flags)${end_of_decl}') + + if not is_header: + code('{') + code.indent() + code('bool ret = true;') + code() + code('if (false) {') + for param in simobj._params.values(): + is_vector = isinstance(param, m5.params.VectorParamDesc) + is_simobj = issubclass(param.ptype, m5.SimObject.SimObject) + + if not is_simobj and is_vector: + code('} else if (name == "${{param.name}}") {') + code.indent() + code('${{param.name}}.clear();') + code('for (auto i = values.begin(); ' + 'ret && i != values.end(); i ++)') + code('{') + code.indent() + code('${{param.ptype.cxx_type}} elem;') + param.ptype.cxx_ini_parse(code, + '*i', 'elem', 'ret =') + code('if (ret)') + code(' this->${{param.name}}.push_back(elem);') + code.dedent() + code('}') + code.dedent() + code('} else {') + code(' ret = false;') + code('}') + code() + code('return ret;') + code.dedent() + code('}') + + code() + code('bool ${member_prefix}setPortConnectionCount(' + 'const std::string &name,') + code(' unsigned int count)${end_of_decl}') + + if not is_header: + code('{') + code.indent() + code('bool ret = true;') + code() + code('if (false)') + code(' ;') + for port in simobj._ports.values(): + code('else if (name == "${{port.name}}")') + code(' this->port_${{port.name}}_connection_count = count;') + code('else') + code(' ret = false;') + code() + code('return ret;') + code.dedent() + code('}') + + code() + code('SimObject *${member_prefix}simObjectCreate()${end_of_decl}') + + if not is_header: + code('{') + if hasattr(simobj, 'abstract') and simobj.abstract: + code(' return NULL;') + else: + code(' return this->create();') + code('}') + + if is_header: + code() + code('static CxxConfigDirectoryEntry' + ' *${member_prefix}makeDirectoryEntry()') + code('{ return new DirectoryEntry; }') + + if is_header: + code.dedent() + code('};') + # The metaclass for SimObject. This class controls how new classes # that derive from SimObject are instantiated, and provides inherited # class behavior (just like a class controls how instances of that @@ -583,6 +856,11 @@ struct PyObject; code('#endif // __PARAMS__${cls}__') return code + # Generate the C++ declaration/definition files for this SimObject's + # param struct to allow C++ initialisation + def cxx_config_param_file(cls, code, is_header): + createCxxConfigDirectoryEntryFile(code, cls.__name__, cls, is_header) + return code # This *temporary* definition is required to support calls from the # SimObject class definition to the MetaSimObject methods (in diff --git a/src/python/m5/params.py b/src/python/m5/params.py index f16cabaff..b7df7c660 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -120,6 +120,18 @@ class ParamValue(object): def config_value(self): return str(self) + # Prerequisites for .ini parsing with cxx_ini_parse + @classmethod + def cxx_ini_predecls(cls, code): + pass + + # parse a .ini file entry for this param from string expression + # src into lvalue dest (of the param's C++ type) + @classmethod + def cxx_ini_parse(cls, code, src, dest, ret): + code('// Unhandled param type: %s' % cls.__name__) + code('%s false;' % ret) + # allows us to blithely call unproxy() on things without checking # if they're really proxies or not def unproxy(self, base): @@ -454,6 +466,11 @@ class String(ParamValue,str): self = value return value + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s = %s;' % (dest, src)) + code('%s true;' % ret) + def getValue(self): return self @@ -500,6 +517,19 @@ class NumericParamValue(ParamValue): def config_value(self): return self.value + @classmethod + def cxx_ini_predecls(cls, code): + # Assume that base/str.hh will be included anyway + # code('#include "base/str.hh"') + pass + + # The default for parsing PODs from an .ini entry is to extract from an + # istringstream and let overloading choose the right type according to + # the dest type. + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s to_number(%s, %s);' % (ret, src, dest)) + # Metaclass for bounds-checked integer parameters. See CheckedInt. class CheckedIntType(MetaParamValue): def __init__(cls, name, bases, dict): @@ -592,6 +622,20 @@ class Cycles(CheckedInt): from m5.internal.core import Cycles return Cycles(self.value) + @classmethod + def cxx_ini_predecls(cls, code): + # Assume that base/str.hh will be included anyway + # code('#include "base/str.hh"') + pass + + @classmethod + def cxx_ini_parse(cls, code, src, dest, ret): + code('uint64_t _temp;') + code('bool _ret = to_number(%s, _temp);' % src) + code('if (_ret)') + code(' %s = Cycles(_temp);' % dest) + code('%s _ret;' % ret) + class Float(ParamValue, float): cxx_type = 'double' cmdLineSettable = True @@ -613,6 +657,14 @@ class Float(ParamValue, float): def config_value(self): return self + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <sstream>') + + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) + class MemorySize(CheckedInt): cxx_type = 'uint64_t' ex_str = '512MB' @@ -738,6 +790,24 @@ class AddrRange(ParamValue): def swig_predecls(cls, code): Addr.swig_predecls(code) + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <sstream>') + + @classmethod + def cxx_ini_parse(cls, code, src, dest, ret): + code('uint64_t _start, _end;') + code('char _sep;') + code('std::istringstream _stream(${src});') + code('_stream >> _start;') + code('_stream.get(_sep);') + code('_stream >> _end;') + code('bool _ret = !_stream.fail() &&' + '_stream.eof() && _sep == \':\';') + code('if (_ret)') + code(' ${dest} = AddrRange(_start, _end);') + code('${ret} _ret;') + def getValue(self): # Go from the Python class to the wrapped C++ class generated # by swig @@ -783,6 +853,16 @@ class Bool(ParamValue): def config_value(self): return self.value + @classmethod + def cxx_ini_predecls(cls, code): + # Assume that base/str.hh will be included anyway + # code('#include "base/str.hh"') + pass + + @classmethod + def cxx_ini_parse(cls, code, src, dest, ret): + code('%s to_bool(%s, %s);' % (ret, src, dest)) + def IncEthernetAddr(addr, val = 1): bytes = map(lambda x: int(x, 16), addr.split(':')) bytes[5] += val @@ -850,6 +930,11 @@ class EthernetAddr(ParamValue): def ini_str(self): return self.value + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s = Net::EthAddr(%s);' % (dest, src)) + code('%s true;' % ret) + # When initializing an IpAddress, pass in an existing IpAddress, a string of # the form "a.b.c.d", or an integer representing an IP. class IpAddress(ParamValue): @@ -1154,6 +1239,16 @@ class Time(ParamValue): assert false return str(self) + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <time.h>') + + @classmethod + def cxx_ini_parse(cls, code, src, dest, ret): + code('char *_parse_ret = strptime((${src}).c_str(),') + code(' "%a %b %d %H:%M:%S %Y", &(${dest}));') + code('${ret} _parse_ret && *_parse_ret == \'\\0\';'); + # Enumerated types are a little more complex. The user specifies the # type as Enum(foo) where foo is either a list or dictionary of # alternatives (typically strings, but not necessarily so). (In the @@ -1306,6 +1401,19 @@ class Enum(ParamValue): def swig_predecls(cls, code): code('%import "python/m5/internal/enum_$0.i"', cls.__name__) + @classmethod + def cxx_ini_parse(cls, code, src, dest, ret): + code('if (false) {') + for elem_name in cls.map.iterkeys(): + code('} else if (%s == "%s") {' % (src, elem_name)) + code.indent() + code('%s = Enums::%s;' % (dest, elem_name)) + code('%s true;' % ret) + code.dedent() + code('} else {') + code(' %s false;' % ret) + code('}') + def getValue(self): return int(self.map[self.value]) @@ -1336,6 +1444,16 @@ class TickParamValue(NumericParamValue): def getValue(self): return long(self.value) + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <sstream>') + + # Ticks are expressed in seconds in JSON files and in plain + # Ticks in .ini files. Switch based on a config flag + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('${ret} to_number(${src}, ${dest});') + class Latency(TickParamValue): ex_str = "100ns" @@ -1485,6 +1603,14 @@ class Voltage(float,ParamValue): def ini_str(self): return '%f' % self.getValue() + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <sstream>') + + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) + class Current(float, ParamValue): cxx_type = 'double' ex_str = "1mA" @@ -1510,6 +1636,14 @@ class Current(float, ParamValue): def ini_str(self): return '%f' % self.getValue() + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <sstream>') + + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) + class NetworkBandwidth(float,ParamValue): cxx_type = 'float' ex_str = "1Gbps" @@ -1541,6 +1675,14 @@ class NetworkBandwidth(float,ParamValue): def config_value(self): return '%f' % self.getValue() + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <sstream>') + + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) + class MemoryBandwidth(float,ParamValue): cxx_type = 'float' ex_str = "1GB/s" @@ -1571,6 +1713,14 @@ class MemoryBandwidth(float,ParamValue): def config_value(self): return '%f' % self.getValue() + @classmethod + def cxx_ini_predecls(cls, code): + code('#include <sstream>') + + @classmethod + def cxx_ini_parse(self, code, src, dest, ret): + code('%s (std::istringstream(%s) >> %s).eof();' % (ret, src, dest)) + # # "Constants"... handy aliases for various values. # diff --git a/src/sim/SConscript b/src/sim/SConscript index 7987afa00..7583b53cb 100644 --- a/src/sim/SConscript +++ b/src/sim/SConscript @@ -43,6 +43,9 @@ SimObject('SubSystem.py') Source('arguments.cc') Source('async.cc') Source('core.cc') +Source('cxx_config.cc') +Source('cxx_manager.cc') +Source('cxx_config_ini.cc') Source('debug.cc') Source('py_interact.cc', skip_no_python=True) Source('eventq.cc') @@ -76,6 +79,7 @@ if env['TARGET_ISA'] != 'null': DebugFlag('Checkpoint') DebugFlag('Config') +DebugFlag('CxxConfig') DebugFlag('Drain') DebugFlag('Event') DebugFlag('Fault') diff --git a/src/sim/cxx_config.cc b/src/sim/cxx_config.cc new file mode 100644 index 000000000..f99afac8a --- /dev/null +++ b/src/sim/cxx_config.cc @@ -0,0 +1,45 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +#include "sim/cxx_config.hh" + +const std::string CxxConfigParams::invalidName = "<invalid>"; + +/** Directory of all SimObject classes config details */ +std::map<std::string, CxxConfigDirectoryEntry *> cxx_config_directory; diff --git a/src/sim/cxx_config.hh b/src/sim/cxx_config.hh new file mode 100644 index 000000000..da2752b4b --- /dev/null +++ b/src/sim/cxx_config.hh @@ -0,0 +1,241 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +/** + * @file + * + * C++-only configuration and instantiation support. This allows a + * config to be read back from a .ini and instantiated without + * Python. Useful if you want to embed gem5 within a larger system + * without carrying the integration cost of the fully-featured + * configuration system. + * + * This file contains definitions needed to store summaries of a + * SimObject's parameter structure + */ + +#ifndef __SIM_CXX_CONFIG_HH__ +#define __SIM_CXX_CONFIG_HH__ + +#include <map> +#include <string> +#include <vector> + +#include "mem/port.hh" +#include "params/SimObject.hh" +#include "sim/sim_object.hh" + +class CxxConfigParams; + +/** Config details entry for a SimObject. Instances of this class contain + * enough configuration layout information to popular a ...Param structure + * and build a SimObject from it with the help of the 'set' functions in + * each ...Param class */ +class CxxConfigDirectoryEntry +{ + public: + /* Class to represent parameters and SimObject references within + * SimObjects */ + class ParamDesc + { + public: + const std::string name; + + /* Is this a vector or singleton parameters/SimObject */ + const bool isVector; + + /** Is this a SimObject, and so is to be set with setSimObject... + * or another from-string parameter set with setParam... */ + const bool isSimObject; + + ParamDesc(const std::string &name_, + bool isVector_, bool isSimObject_) : + name(name_), isVector(isVector_), isSimObject(isSimObject_) + { } + }; + + /** Similar to ParamDesc to describe ports */ + class PortDesc + { + public: + const std::string name; + + /* Is this a vector or singleton parameters/SimObject */ + const bool isVector; + + /** Is this a master or slave port */ + const bool isMaster; + + PortDesc(const std::string &name_, + bool isVector_, bool isMaster_) : + name(name_), isVector(isVector_), isMaster(isMaster_) + { } + }; + + /** All parameters (including SimObjects) in order */ + std::map<std::string, ParamDesc *> parameters; + + /** Ports */ + std::map<std::string, PortDesc *> ports; + + /** Make a ...Param structure for the SimObject class of this entry */ + virtual CxxConfigParams *makeParamsObject() const { return NULL; } + + virtual ~CxxConfigDirectoryEntry() { } +}; + +/** Base for peer classes of SimObjectParams derived classes with parameter + * modifying member functions. C++ configuration will offer objects of + * these classes to SimObjects as params rather than SimObjectParams + * objects */ +class CxxConfigParams +{ + private: + static const std::string invalidName; + + public: + /** Flags passable to setParam... to smooth over any parsing difference + * between different config files */ + typedef uint32_t FlagsType; + typedef ::Flags<FlagsType> Flags; + + /** Example flag */ + /* static const FlagsType MY_NEW_FLAG = 0x00000001; */ + + public: + /** Set future object's full path name */ + virtual void setName(const std::string &name_) { } + + /** Get full path name string */ + virtual const std::string &getName() { return invalidName; } + + /** Set a SimObject valued parameter with a reference to the given + * SimObject. This will return false if the parameter name is not + * valid or the object is of the wrong type */ + virtual bool setSimObject(const std::string &name, + SimObject *simObject) + { return false; } + + /** As setSimObjectVector but set a whole vector of references */ + virtual bool setSimObjectVector(const std::string &name, + const std::vector<SimObject *> &simObjects) + { return false; } + + /** Set a parameter with a value parsed from the given string. The + * parsing regime matches the format of .ini config files. Returns + * false if the parameter name is not valid or the string cannot be + * parsed as the type of the parameter */ + virtual bool setParam(const std::string &name, + const std::string &value, const Flags flags) + { return false; } + + /** As setParamVector but for parameters given as vectors pre-separated + * into elements */ + virtual bool setParamVector(const std::string &name, + const std::vector<std::string> &values, const Flags flags) + { return false; } + + /** Set the number of connections expected for the named port. Returns + * false if the port name is not valid */ + virtual bool setPortConnectionCount(const std::string &name, + unsigned int count) + { return false; } + + /** Create the associated SimObject */ + virtual SimObject *simObjectCreate() { return NULL; } + + CxxConfigParams() { } + + virtual ~CxxConfigParams() { } +}; + +/** Config file wrapper providing a common interface to CxxConfigManager */ +class CxxConfigFileBase +{ + public: + CxxConfigFileBase() { } + virtual ~CxxConfigFileBase() { } + + /** Get a single parameter value as a string returned in value. + * For booleans, the function expects "true" or "false" in value. + * For NULL SimObjects, it expects "Null" */ + virtual bool getParam(const std::string &object_name, + const std::string ¶m_name, + std::string &value) const = 0; + + /** Get a list/vector parameter */ + virtual bool getParamVector(const std::string &object_name, + const std::string ¶m_name, + std::vector<std::string> &values) const = 0; + + /** Get the peer (connected) ports of the named ports */ + virtual bool getPortPeers(const std::string &object_name, + const std::string &port_name, + std::vector<std::string> &peers) const = 0; + + /** Does an object with this path exist? */ + virtual bool objectExists(const std::string &object_name) const = 0; + + /** Get all SimObjects in the config */ + virtual void getAllObjectNames(std::vector<std::string> &list) const = 0; + + /** Get the names or paths of all the children SimObjects of this + * SimObject. If return_paths is true then full paths are returned. + * If false, only the last name component for each object is returned */ + virtual void getObjectChildren(const std::string &object_name, + std::vector<std::string> &children, + bool return_paths = false) const = 0; + + /** Load config file */ + virtual bool load(const std::string &filename) = 0; + + /** Get the flags which should be used to modify parameter parsing + * behaviour */ + virtual CxxConfigParams::Flags getFlags() const { return 0; } +}; + +/** Directory of all SimObject classes config details */ +extern std::map<std::string, CxxConfigDirectoryEntry *> + cxx_config_directory; + +/** Initialise cxx_config_directory. This is defined in the + * auto-generated .../cxx_config/init.cc */ +void cxxConfigInit(); + +#endif // __SIM_CXX_CONFIG_HH__ diff --git a/src/sim/cxx_config_ini.cc b/src/sim/cxx_config_ini.cc new file mode 100644 index 000000000..3df6d72d4 --- /dev/null +++ b/src/sim/cxx_config_ini.cc @@ -0,0 +1,104 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +#include "sim/cxx_config_ini.hh" + +bool +CxxIniFile::getParam(const std::string &object_name, + const std::string ¶m_name, + std::string &value) const +{ + return iniFile.find(object_name, param_name, value); +} + +bool +CxxIniFile::getParamVector(const std::string &object_name, + const std::string ¶m_name, + std::vector<std::string> &values) const +{ + std::string value; + bool ret = iniFile.find(object_name, param_name, value); + + if (ret) { + std::vector<std::string> sub_object_names; + + tokenize(values, value, ' ', true); + } + + return ret; +} + +bool +CxxIniFile::getPortPeers(const std::string &object_name, + const std::string &port_name, + std::vector<std::string> &peers) const +{ + return getParamVector(object_name, port_name, peers); +} + +bool +CxxIniFile::objectExists(const std::string &object) const +{ + return iniFile.sectionExists(object); +} + +void +CxxIniFile::getAllObjectNames(std::vector<std::string> &list) const +{ + iniFile.getSectionNames(list); +} + +void +CxxIniFile::getObjectChildren(const std::string &object_name, + std::vector<std::string> &children, bool return_paths) const +{ + if (!getParamVector(object_name, "children", children)) + return; + + if (return_paths && object_name != "root") { + for (auto i = children.begin(); i != children.end(); ++i) + *i = object_name + "." + *i; + } +} + +bool +CxxIniFile::load(const std::string &filename) +{ + return iniFile.load(filename); +} diff --git a/src/sim/cxx_config_ini.hh b/src/sim/cxx_config_ini.hh new file mode 100644 index 000000000..9ea61976e --- /dev/null +++ b/src/sim/cxx_config_ini.hh @@ -0,0 +1,87 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +/** + * @file + * + * .ini file reading wrapper for use with CxxConfigManager + */ + +#ifndef __SIM_CXX_CONFIG_INI_HH__ +#define __SIM_CXX_CONFIG_INI_HH__ + +#include "base/inifile.hh" +#include "base/str.hh" +#include "sim/cxx_config.hh" + +/** CxxConfigManager interface for using .ini files */ +class CxxIniFile : public CxxConfigFileBase +{ + protected: + IniFile iniFile; + + public: + CxxIniFile() { } + + /* Most of these functions work by mapping 'object' onto 'section' */ + + bool getParam(const std::string &object_name, + const std::string ¶m_name, + std::string &value) const; + + bool getParamVector(const std::string &object_name, + const std::string ¶m_name, + std::vector<std::string> &values) const; + + bool getPortPeers(const std::string &object_name, + const std::string &port_name, + std::vector<std::string> &peers) const; + + bool objectExists(const std::string &object_name) const; + + void getAllObjectNames(std::vector<std::string> &list) const; + + void getObjectChildren(const std::string &object_name, + std::vector<std::string> &children, + bool return_paths = false) const; + + bool load(const std::string &filename); +}; + +#endif // __SIM_CXX_CONFIG_INI_HH__ diff --git a/src/sim/cxx_manager.cc b/src/sim/cxx_manager.cc new file mode 100644 index 000000000..7f6a03398 --- /dev/null +++ b/src/sim/cxx_manager.cc @@ -0,0 +1,740 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +#include <cstdlib> +#include <sstream> + +#include "base/str.hh" +#include "debug/CxxConfig.hh" +#include "mem/mem_object.hh" +#include "sim/cxx_manager.hh" +#include "sim/serialize.hh" + +CxxConfigManager::CxxConfigManager(CxxConfigFileBase &configFile_) : + configFile(configFile_), flags(configFile_.getFlags()), + simObjectResolver(*this) +{ +} + +const CxxConfigDirectoryEntry & +CxxConfigManager::findObjectType(const std::string &object_name, + std::string &object_type) +{ + if (!configFile.objectExists(object_name)) + throw Exception(object_name, "Can't find sim object"); + + if (!configFile.getParam(object_name, "type", object_type)) + throw Exception(object_name, "Sim object has no 'type' field"); + + if (cxx_config_directory.find(object_type) == + cxx_config_directory.end()) + { + throw Exception(object_name, csprintf( + "No sim object type %s is available", object_type)); + } + + const CxxConfigDirectoryEntry *entry = cxx_config_directory[object_type]; + + return *entry; +} + +std::string +CxxConfigManager::rename(const std::string &from_name) +{ + for (auto i = renamings.begin(); i != renamings.end(); ++ i) { + const Renaming &renaming = *i; + + if (from_name.find(renaming.fromPrefix) == 0) { + return renaming.toPrefix + + from_name.substr(renaming.fromPrefix.length()); + } + } + + return from_name; +} + +std::string +CxxConfigManager::unRename(const std::string &to_name) +{ + for (auto i = renamings.begin(); i != renamings.end(); ++ i) { + const Renaming &renaming = *i; + + if (to_name.find(renaming.toPrefix) == 0) { + return renaming.fromPrefix + + to_name.substr(renaming.toPrefix.length()); + } + } + + return to_name; +} + +static +std::string formatParamList(const std::vector<std::string> ¶m_values) +{ + std::ostringstream params; + + auto i = param_values.begin(); + auto end_i = param_values.end(); + + params << '['; + while (i != end_i) { + params << (*i); + ++i; + + if (i != end_i) + params << ", "; + } + params << ']'; + + return params.str(); +} + +SimObject * +CxxConfigManager::findObject(const std::string &object_name, + bool visit_children) +{ + std::string instance_name = rename(object_name); + + if (object_name == "Null") + return NULL; + + /* Already constructed */ + if (objectsByName.find(instance_name) != objectsByName.end()) + return objectsByName[instance_name]; + + if (inVisit.find(instance_name) != inVisit.end()) + throw Exception(instance_name, "Cycle in configuration"); + + std::string object_type; + const CxxConfigDirectoryEntry &entry = + findObjectType(object_name, object_type); + + SimObject *object = NULL; + + CxxConfigParams *object_params = findObjectParams(object_name); + + try { + DPRINTF(CxxConfig, "Configuring sim object references for: %s" + " (%s from object %s)\n", instance_name, object_type, + object_name); + + /* Remember the path back to the top of the recursion to detect + * cycles */ + inVisit.insert(instance_name); + + /* Resolve pointed-to SimObjects by recursing into them */ + for (auto i = entry.parameters.begin(); + i != entry.parameters.end(); ++i) + { + const CxxConfigDirectoryEntry::ParamDesc *param = (*i).second; + + if (param->isSimObject) { + if (param->isVector) { + std::vector<std::string> sub_object_names; + + if (!configFile.getParamVector(object_name, param->name, + sub_object_names)) + { + throw Exception(object_name, csprintf( + "Element not found: %s", param->name)); + } + + std::vector<SimObject *> sub_objects; + + for (auto n = sub_object_names.begin(); + n != sub_object_names.end(); ++n) + { + SimObject *sub_object = findObject(*n, + visit_children); + + if (sub_object) + sub_objects.push_back(sub_object); + } + + if (!object_params->setSimObjectVector(param->name, + sub_objects)) + { + throw Exception(object_name, csprintf( + "Can't assign sim object element %s from \"%s\"", + param->name, formatParamList(sub_object_names))); + } + + DPRINTF(CxxConfig, "Setting sim object(s): %s.%s=%s\n", + object_name, param->name, + formatParamList(sub_object_names)); + } else { + std::string sub_object_name; + + if (!configFile.getParam(object_name, param->name, + sub_object_name)) + { + throw Exception(object_name, csprintf( + "Element not found: %s", param->name)); + } + + SimObject *sub_object = findObject(sub_object_name, + visit_children); + + if (sub_object) { + if (!object_params->setSimObject(param->name, + sub_object)) + { + throw Exception(object_name, csprintf( + "Can't assign sim object element %s from" + " \"%s\"", param->name, sub_object_name)); + } + } + + DPRINTF(CxxConfig, "Setting sim object(s):" + " %s.%s=%s\n", object_name, param->name, + sub_object_name); + } + } + } + + DPRINTF(CxxConfig, "Creating SimObject: %s\n", instance_name); + object = object_params->simObjectCreate(); + + if (!object) { + throw Exception(object_name, csprintf("Couldn't create object of" + " type: %s", object_type)); + } + + objectsByName[instance_name] = object; + objectParamsByName[instance_name] = object_params; + + if (visit_children) { + std::vector<std::string> children; + configFile.getObjectChildren(object_name, children, true); + + /* Visit all your children */ + for (auto i = children.begin(); i != children.end(); ++i) + findObject(*i, visit_children); + } + } catch (Exception &) { + delete object_params; + throw; + } + + /* Mark that we've exited object + * construction and so 'find'ing this object again won't be a + * configuration loop */ + inVisit.erase(object_name); + return object; +} + +CxxConfigParams * +CxxConfigManager::findObjectParams(const std::string &object_name) +{ + std::string instance_name = rename(object_name); + + /* Already constructed */ + if (objectParamsByName.find(instance_name) != objectParamsByName.end()) + return objectParamsByName[instance_name]; + + std::string object_type; + const CxxConfigDirectoryEntry &entry = + findObjectType(object_name, object_type); + + DPRINTF(CxxConfig, "Configuring parameters of object: %s (%s)\n", + instance_name, object_type); + + CxxConfigParams *object_params = entry.makeParamsObject(); + + try { + /* Fill in the implicit parameters that don't necessarily + * appear in config files */ + object_params->setName(instance_name); + + /* Fill in parameters */ + for (auto i = entry.parameters.begin(); + i != entry.parameters.end(); ++i) + { + const CxxConfigDirectoryEntry::ParamDesc *param = (*i).second; + + if (!param->isSimObject) { + /* Only handle non-SimObject parameters here (see below) */ + + if (param->isVector) { + std::vector<std::string> param_values; + + if (!configFile.getParamVector(object_name, param->name, + param_values)) + { + throw Exception(object_name, csprintf( + "Element not found for parameter: %s", + param->name)); + } + + if (!object_params->setParamVector(param->name, + param_values, flags)) + { + throw Exception(instance_name, csprintf( + "Bad parameter value: .%s=X=\"%s\"", + param->name, formatParamList(param_values))); + } + + DPRINTF(CxxConfig, "Setting parameter" + " %s.%s=%s\n", instance_name, param->name, + formatParamList(param_values)); + } else { + std::string param_value; + + if (!configFile.getParam(object_name, param->name, + param_value)) + { + throw Exception(object_name, csprintf( + "Element not found for parameter: %s", + param->name)); + } + + if (!object_params->setParam(param->name, param_value, + flags)) + { + throw Exception(instance_name, csprintf( + "Bad parameter value: .%s=X=\"%s\"", + param->name, param_value)); + } + + DPRINTF(CxxConfig, "Setting parameter %s.%s=%s\n", + instance_name, param->name, param_value); + } + } + } + + /* Find the number of ports that will need binding and set the + * appropriate port_..._connection_count parameters */ + for (auto i = entry.ports.begin(); i != entry.ports.end(); ++i) { + const CxxConfigDirectoryEntry::PortDesc *port = (*i).second; + std::vector<std::string> peers; + + if (!configFile.getPortPeers(object_name, port->name, peers)) { + DPRINTF(CxxConfig, "Port not found: %s.%s," + " assuming there are no connections\n", + instance_name, port->name); + } + + unsigned int peer_count = peers.size(); + + /* It would be more efficient to split the peer list and + * save the values for peer binding later but that would + * require another annoying intermediate structure to + * hold for little performance increase */ + + if (!object_params->setPortConnectionCount(port->name, + peer_count)) + { + throw Exception(instance_name, csprintf( + "Unconnected port: %s", port->name)); + } + + DPRINTF(CxxConfig, "Setting port connection count" + " for: %s.%s to %d\n", + instance_name, port->name, peer_count); + } + + /* Set pointed-to SimObjects to NULL */ + for (auto i = entry.parameters.begin(); + i != entry.parameters.end(); ++i) + { + const CxxConfigDirectoryEntry::ParamDesc *param = (*i).second; + + if (param->isSimObject) { + bool ret; + + DPRINTF(CxxConfig, "Nulling sim object reference: %s.%s\n", + instance_name, param->name); + + if (param->isVector) { + /* Clear the reference list. */ + std::vector<SimObject *> empty; + ret = object_params->setSimObjectVector(param->name, + empty); + } else { + ret = object_params->setSimObject(param->name, NULL); + } + + if (!ret) { + throw Exception(instance_name, csprintf( + "Error nulling sim object reference(s): %s", + param->name)); + } + } + } + } catch (Exception &) { + delete object_params; + throw; + } + + objectParamsByName[instance_name] = object_params; + + return object_params; +} + +void +CxxConfigManager::findAllObjects() +{ + std::vector<std::string> objects; + configFile.getAllObjectNames(objects); + + /* Sort the object names to get a consistent initialisation order + * even with config file reorganisation */ + std::sort(objects.begin(), objects.end()); + + for (auto i = objects.begin(); i != objects.end(); ++i) + findObject(*i); + + /* Set the traversal order for further iterators */ + objectsInOrder.clear(); + findTraversalOrder("root"); +} + +void +CxxConfigManager::findTraversalOrder(const std::string &object_name) +{ + SimObject *object = findObject(object_name); + + if (object) { + objectsInOrder.push_back(object); + + std::vector<std::string> children; + configFile.getObjectChildren(object_name, children, true); + + /* Visit all your children */ + for (auto i = children.begin(); i != children.end(); ++i) + findTraversalOrder(*i); + } +} + +void +CxxConfigManager::bindAllPorts() +{ + for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++i) + bindObjectPorts(*i); +} + +void +CxxConfigManager::bindPort( + SimObject *master_object, const std::string &master_port_name, + PortID master_port_index, + SimObject *slave_object, const std::string &slave_port_name, + PortID slave_port_index) +{ + MemObject *master_mem_object = dynamic_cast<MemObject *>(master_object); + MemObject *slave_mem_object = dynamic_cast<MemObject *>(slave_object); + + if (!master_mem_object) { + throw Exception(master_object->name(), csprintf( + "Object isn't a mem object and so can have master port:" + " %s[%d]", master_port_name, master_port_index)); + } + + if (!slave_mem_object) { + throw Exception(slave_object->name(), csprintf( + "Object isn't a mem object and so can have slave port:" + " %s[%d]", slave_port_name, slave_port_index)); + } + + /* FIXME, check slave_port_index against connection_count + * defined for port, need getPortConnectionCount and a + * getCxxConfigDirectoryEntry for each object. */ + + /* It would be nice to be able to catch the errors from these calls. */ + BaseMasterPort &master_port = master_mem_object->getMasterPort( + master_port_name, master_port_index); + BaseSlavePort &slave_port = slave_mem_object->getSlavePort( + slave_port_name, slave_port_index); + + if (master_port.isConnected()) { + throw Exception(master_object->name(), csprintf( + "Master port: %s[%d] is already connected\n", master_port_name, + master_port_index)); + } + + if (slave_port.isConnected()) { + throw Exception(slave_object->name(), csprintf( + "Slave port: %s[%d] is already connected\n", slave_port_name, + slave_port_index)); + } + + DPRINTF(CxxConfig, "Binding port %s.%s[%d]" + " to %s:%s[%d]\n", + master_object->name(), master_port_name, master_port_index, + slave_object->name(), slave_port_name, slave_port_index); + + master_port.bind(slave_port); +} + +void +CxxConfigManager::bindMasterPort(SimObject *object, + const CxxConfigDirectoryEntry::PortDesc &port, + const std::vector<std::string> &peers) +{ + unsigned int master_port_index = 0; + + for (auto peer_i = peers.begin(); peer_i != peers.end(); + ++peer_i) + { + const std::string &peer = *peer_i; + std::string slave_object_name; + std::string slave_port_name; + unsigned int slave_port_index; + + parsePort(peer, slave_object_name, slave_port_name, + slave_port_index); + + std::string slave_instance_name = rename(slave_object_name); + + if (objectsByName.find(slave_instance_name) == objectsByName.end()) { + throw Exception(object->name(), csprintf( + "Can't find slave port object: %s", slave_instance_name)); + } + + SimObject *slave_object = objectsByName[slave_instance_name]; + + bindPort(object, port.name, master_port_index, + slave_object, slave_port_name, slave_port_index); + + master_port_index++; + } +} + +void +CxxConfigManager::bindObjectPorts(SimObject *object) +{ + /* We may want to separate object->name() from the name in configuration + * later to allow (for example) repetition of fragments of configs */ + const std::string &instance_name = object->name(); + + std::string object_name = unRename(instance_name); + + std::string object_type; + const CxxConfigDirectoryEntry &entry = + findObjectType(object_name, object_type); + + DPRINTF(CxxConfig, "Binding ports of object: %s (%s)\n", + instance_name, object_type); + + for (auto i = entry.ports.begin(); i != entry.ports.end(); ++i) { + const CxxConfigDirectoryEntry::PortDesc *port = (*i).second; + + DPRINTF(CxxConfig, "Binding port: %s.%s\n", instance_name, + port->name); + + std::vector<std::string> peers; + configFile.getPortPeers(object_name, port->name, peers); + + /* Only handle master ports as binding only needs to happen once + * for each observed pair of ports */ + if (port->isMaster) { + if (!port->isVector && peers.size() > 1) { + throw Exception(instance_name, csprintf( + "Too many connections to non-vector port %s (%d)\n", + port->name, peers.size())); + } + + bindMasterPort(object, *port, peers); + } + } +} + +void +CxxConfigManager::parsePort(const std::string &inp, + std::string &path, std::string &port, unsigned int &index) +{ + std::size_t dot_i = inp.rfind('.'); + std::size_t open_square_i = inp.rfind('['); + + if (dot_i == std::string::npos) { + DPRINTF(CxxConfig, "Bad port string: %s\n", inp); + path = ""; + port = ""; + index = 0; + } else { + path = std::string(inp, 0, dot_i); + + if (open_square_i == std::string::npos) { + /* Singleton port */ + port = std::string(inp, dot_i + 1, inp.length() - dot_i); + index = 0; + } else { + /* Vectored port elemnt */ + port = std::string(inp, dot_i + 1, (open_square_i - 1) - dot_i); + index = std::atoi(inp.c_str() + open_square_i + 1); + } + } +} + +void +CxxConfigManager::forEachObject(void (SimObject::*mem_func)()) +{ + for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++i) + ((*i)->*mem_func)(); +} + +void +CxxConfigManager::instantiate(bool build_all) +{ + if (build_all) { + findAllObjects(); + bindAllPorts(); + } + + DPRINTF(CxxConfig, "Initialising all objects\n"); + forEachObject(&SimObject::init); + + DPRINTF(CxxConfig, "Registering stats\n"); + forEachObject(&SimObject::regStats); + + DPRINTF(CxxConfig, "Registering probe points\n"); + forEachObject(&SimObject::regProbePoints); + + DPRINTF(CxxConfig, "Connecting probe listeners\n"); + forEachObject(&SimObject::regProbeListeners); +} + +void +CxxConfigManager::initState() +{ + DPRINTF(CxxConfig, "Calling initState on all objects\n"); + forEachObject(&SimObject::initState); +} + +void +CxxConfigManager::startup() +{ + DPRINTF(CxxConfig, "Starting up all objects\n"); + forEachObject(&SimObject::startup); +} + +unsigned int +CxxConfigManager::drain(DrainManager *drain_manager) +{ + unsigned int ret = 0; + + for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i) + ret += (*i)->drain(drain_manager); + + return ret; +} + +void +CxxConfigManager::drainResume() +{ + forEachObject(&SimObject::drainResume); +} + +void +CxxConfigManager::serialize(std::ostream &os) +{ + for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i) { + // (*i)->nameOut(os); FIXME, change access spec. for nameOut + os << '[' << (*i)->name() << "]\n"; + (*i)->serialize(os); + } +} + +void +CxxConfigManager::loadState(Checkpoint *checkpoint) +{ + for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i) + (*i)->loadState(checkpoint); +} + +void +CxxConfigManager::deleteObjects() +{ + for (auto i = objectsInOrder.rbegin(); i != objectsInOrder.rend(); ++i) { + DPRINTF(CxxConfig, "Freeing sim object: %s\n", (*i)->name()); + delete *i; + } + + for (auto i = objectParamsByName.rbegin(); + i != objectParamsByName.rend(); ++i) + { + CxxConfigParams *params = (*i).second; + + DPRINTF(CxxConfig, "Freeing sim object params: %s\n", + params->getName()); + delete params; + } + + objectsInOrder.clear(); + objectsByName.clear(); +} + +void +CxxConfigManager::setParam(const std::string &object_name, + const std::string ¶m_name, const std::string ¶m_value) +{ + CxxConfigParams *params = findObjectParams(object_name); + + if (!params->setParam(param_name, param_value, flags)) { + throw Exception(object_name, csprintf("Bad parameter value:" + " .%s=X=\"%s\"", param_name, param_value)); + } else { + std::string instance_name = rename(object_name); + + DPRINTF(CxxConfig, "Setting parameter %s.%s=%s\n", + instance_name, param_name, param_value); + } +} + +void +CxxConfigManager::setParamVector(const std::string &object_name, + const std::string ¶m_name, + const std::vector<std::string> ¶m_values) +{ + CxxConfigParams *params = findObjectParams(object_name); + + if (!params->setParamVector(param_name, param_values, flags)) { + throw Exception(object_name, csprintf("Bad vector parameter value:" + " .%s=X=\"%s\"", param_name, formatParamList(param_values))); + } else { + std::string instance_name = rename(object_name); + + DPRINTF(CxxConfig, "Setting parameter %s.%s=\"%s\"\n", + instance_name, param_name, formatParamList(param_values)); + } +} + +void CxxConfigManager::addRenaming(const Renaming &renaming) +{ + renamings.push_back(renaming); +} diff --git a/src/sim/cxx_manager.hh b/src/sim/cxx_manager.hh new file mode 100644 index 000000000..caa115f03 --- /dev/null +++ b/src/sim/cxx_manager.hh @@ -0,0 +1,314 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +/** + * @file + * + * C++-only configuration and instantiation support. This allows a + * config to be read back from a config file and instantiated without + * Python. Useful if you want to embed gem5 within a larger system + * without carrying the integration cost of the fully-featured + * configuration system. + * + * This file contains the config loading/storing manager class + */ + +#ifndef __SIM_CXX_MANAGER_HH__ +#define __SIM_CXX_MANAGER_HH__ + +#include <list> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/cprintf.hh" +#include "sim/cxx_config.hh" + +class Checkpoint; + +/** This class allows a config file to be read into gem5 (generating the + * appropriate SimObjects) from C++ */ +class CxxConfigManager +{ + protected: + /** Configuration file being read */ + CxxConfigFileBase &configFile; + + /** Flags to pass to affect param setting */ + CxxConfigParams::Flags flags; + + public: + /** Exception for instantiate/post-instantiate errors */ + class Exception : public std::exception + { + public: + std::string name; + std::string message; + + public: + Exception(const std::string &name_, const std::string &message_) : + name(name_), message(message_) + { } + + const char *what() const throw() { return message.c_str(); } + + ~Exception() throw() { } + }; + + /** Name substitution when instantiating any object whose name starts + * with fromPrefix. Where both renamed and unrenamed names are used + * in the code, `object' as part of a name usually refers to the + * unrenamed name (the name as it appears in the config file) and + * `instance' is part of the renamed name */ + struct Renaming + { + std::string fromPrefix; + std::string toPrefix; + + Renaming(const std::string &from_prefix, + const std::string &to_prefix) : + fromPrefix(from_prefix), + toPrefix(to_prefix) + { } + }; + + public: + /** SimObject indexed by name */ + std::map<std::string, SimObject *> objectsByName; + + /** ...Params objects created by this manager */ + std::map<std::string, CxxConfigParams *> objectParamsByName; + + /** SimObjects in order. This is populated by findAllObjects */ + std::list<SimObject *> objectsInOrder; + + protected: + /** While configuring, inVisit contains names of SimObjects visited in + * this recursive configuration walk */ + std::set<std::string> inVisit; + + /** All the renamings applicable when instantiating objects */ + std::list<Renaming> renamings; + + /** Bind a single connection between two objects' ports */ + void bindPort(SimObject *masterObject, const std::string &masterPort, + PortID masterPortIndex, SimObject *slaveObject, + const std::string &slavePort, PortID slavePortIndex); + + /** Bind a single (possibly vectored) master port to peers from the + * unparsed list peers with elements in the .ini connection format: + * path(.path)*.port[index] */ + void bindMasterPort(SimObject *object, + const CxxConfigDirectoryEntry::PortDesc &port, + const std::vector<std::string> &peers); + + /** Apply the first matching renaming in renamings to the given name */ + std::string rename(const std::string &from_name); + + /** Apply the first matching renaming in reverse (toPrefix -> fromPrefix + * for the given name */ + std::string unRename(const std::string &to_name); + + protected: + /** Bind the ports of all the objects in objectInOrder order. + * Also */ + void bindAllPorts(); + + /** Class for resolving SimObject names to SimObjects usable by the + * checkpoint restore mechanism */ + class SimObjectResolver : public ::SimObjectResolver + { + protected: + CxxConfigManager &configManager; + + public: + SimObjectResolver(CxxConfigManager &configManager_) : + configManager(configManager_) + { } + + SimObject *resolveSimObject(const std::string &name) + { return &(configManager.getObject<SimObject>(name)); } + }; + + /** Singleton instance of SimObjectResolver */ + SimObjectResolver simObjectResolver; + + public: + CxxConfigManager(CxxConfigFileBase &configFile_); + + /** Find the type field for a named object and return both the + * name of the type to object_type and the object's directory + * entry as the return value */ + const CxxConfigDirectoryEntry &findObjectType( + const std::string &object_name, std::string &object_type); + + /** Add a name prefix renaming to those currently applied. Call this + * before trying to instantiate any object as the name mappings are + * not applied to the config tree read from the config file but are + * applied while processing instantiations */ + void addRenaming(const Renaming &renaming); + + public: + /** Bind the ports of a single SimObject */ + void bindObjectPorts(SimObject *object); + + /** Walk the configuration starting with object object_name and fill + * in all the elements of this object on the way. This involves: + * <ul> + * <li>Calling findObjectParams to make the ...Params object + * If findObjectParams has already been called for this object, + * the ...Params object generated by that called (stored in + * (objectParamsByName[object_name] will be used)</li> + * <li>Populating the ...Params object references to other + * SimObjects by recursively descending into the trees formed + * by SimObject references</li> + * <li>Building the final SimObject and adding it to + * objectsByName</li> + * <li>If visit_children is true, recursively visit all this + * object's children and build/find them too</li> + * </ul> + * After the first call, this function will return + * objectsByName[object_name] */ + SimObject *findObject(const std::string &object_name, + bool visit_children = false); + + /** Find the parameters for the named object. Returns NULL if the + * object isn't in the configuration. For the first call with a + * particular object name, a new CxxConfigParams descended object + * is made with the configuration file contents for this object. + * This involves populating that ...Params object with: + * <ul> + * <li>parameter values from the configuration file</li> + * <li>port connection connection counts from the connection counts + * indicated by the number of peer ports in the configuration + * file</li> + * <li>nulled (or vector<>::clear'ed) SimObject references for + * SimObject-values parameters</li> + * </ul> + * The ...Params object is then added to objectParamsByName + * After the first call, this function will return + * objectParamsByName[object_name] */ + CxxConfigParams *findObjectParams(const std::string &object_name); + + /** Populate objectsInOrder with a preorder, depth first traversal from + * the given object name down through all its children */ + void findTraversalOrder(const std::string &object_name); + + /** Find an object from objectsByName with a type-checking cast. + * This function is provided for manipulating objects after + * instantiate as it assumes the named object exists. */ + template<typename SimObjectType> + SimObjectType & + getObject(const std::string &object_name) + { + if (objectsByName.find(object_name) == objectsByName.end()) { + throw Exception("", csprintf("No sim object named: %s", + object_name)); + } + + SimObjectType *object = dynamic_cast<SimObjectType *>( + objectsByName[object_name]); + + if (!object) { + throw Exception("", csprintf("Sim object: %s has the wrong" + " type", object_name)); + } + + return *object; + } + + /** Perform mem_func on each SimObject */ + void forEachObject(void (SimObject::*mem_func)()); + + /** Find all objects by iterating over the object names in the config + * file with findObject. Also populate the traversal order */ + void findAllObjects(); + + /** Parse a port string of the form 'path(.path)*.port[index]' into + * path, port and index */ + static void parsePort(const std::string &inp, + std::string &path, std::string &port, unsigned int &index); + + /** Build all objects (if build_all is true, otherwise objects must + * have been individually findObject-ed and added to the traversal + * order) and perform all the configuration specific actions up to, + * but not including initState. + * + * If you want to set some parameters before completing instantiation, + * call findObjectParams on the objects you want to modify, then call + * instantiate */ + void instantiate(bool build_all = true); + + /** Call initState on all objects */ + void initState(); + + /** Call startup on all objects */ + void startup(); + + /** Drain all objects */ + unsigned int drain(DrainManager *drain_manager); + + /** Resume from drain */ + void drainResume(); + + /** Serialize (checkpoint) all objects to the given stream */ + void serialize(std::ostream &os); + + /** Load all objects' state from the given Checkpoint */ + void loadState(Checkpoint *checkpoint); + + /** Delete all objects and clear objectsByName and objectsByOrder */ + void deleteObjects(); + + /** Get the resolver used to map SimObject names to SimObjects for + * checkpoint restore */ + SimObjectResolver &getSimObjectResolver() { return simObjectResolver; } + + /** Convenience functions for calling set... member functions on a + * CxxConfigParams for an object. These functions throw Exception + * rather than return a bool on failure */ + void setParam(const std::string &object_name, + const std::string ¶m_name, const std::string ¶m_value); + void setParamVector(const std::string &object_name, + const std::string ¶m_name, + const std::vector<std::string> ¶m_values); +}; + +#endif // __SIM_CXX_MANAGER_HH__ diff --git a/util/cxx_config/Makefile b/util/cxx_config/Makefile new file mode 100644 index 000000000..e8f491225 --- /dev/null +++ b/util/cxx_config/Makefile @@ -0,0 +1,65 @@ +# 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. +# +# Authors: Andrew Bardsley + +ARCH = ARM +VARIANT = opt + +CXXFLAGS = -I../../build/$(ARCH) -L../../build/$(ARCH) +CXXFLAGS += -std=c++0x +LIBS = -lgem5_$(VARIANT) + +## You may also need protobuf flags +# CXXFLAGS += $(shell pkg-config --cflags --libs-only-L protobuf) +# LIBS += $(shell pkg-config --libs protobuf) + +ALL = gem5.$(VARIANT).cxx + +all: $(ALL) + +.cc.o: + $(CXX) $(CXXFLAGS) -c -o $@ $< + +stats.o: stats.cc stats.hh +main.o: main.cc stats.hh + +gem5.$(VARIANT).cxx: main.o stats.o + $(CXX) $(CXXFLAGS) -o $@ $^ $(LIBS) + +clean: + $(RM) $(ALL) + $(RM) *.o + $(RM) -r m5out diff --git a/util/cxx_config/README b/util/cxx_config/README new file mode 100644 index 000000000..a80f7ad8f --- /dev/null +++ b/util/cxx_config/README @@ -0,0 +1,44 @@ +This directory contains a demo of C++ configuration of gem5. The intention +is to provide a mechanism to allow pre-generated config.ini files generated +by Python-based gem5 to be reloaded in library-base versions of gem5 +embedded in other systems using C++ calls for simulation control. + +This demo implements a few of the simulation control mechanisms of the Python +gem5 on top of a C++ configured system. + +Read main.cc for more details of the implementation. + +To build: + +First build gem5 as a library with cxx-config support and (optionally) +without python. Also build a normal gem5 (cxx-config not needed, Python +needed): + +> cd ../.. +> scons build/ARM/gem5.opt +> scons --with-cxx-config --without-python build/ARM/libgem5_opt.so +> cd util/cxx_config + +Then edit Makefile to set the paths for PYTHON and run make + +> make + +Make a config file for the C++-configured gem5 using normal gem5 + +> ../../build/ARM/gem5.opt ../../configs/example/se.py -c \ +> ../../tests/test-progs/hello/bin/arm/linux/hello + +The binary 'gem5.opt.cxx' can now be used to load in the generated config +file from the previous normal gem5 run. + +Try: + +> ./gem5.opt.cxx m5out/config.ini + +This should print: + +> Hello world! + +The .ini file can also be read by the Python .ini file reader example: + +> ../../build/ARM/gem5.opt ../../configs/example/read_config.py m5out/config.ini diff --git a/util/cxx_config/main.cc b/util/cxx_config/main.cc new file mode 100644 index 000000000..a9a86b424 --- /dev/null +++ b/util/cxx_config/main.cc @@ -0,0 +1,319 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +/** + * @file + * + * C++-only configuration and instantiation support. This allows a + * config to be read back from a .ini and instantiated without + * Python. Useful if you want to embed gem5 within a larger system + * without carrying the integration cost of the fully-featured + * configuration system. + * + * This file contains a demonstration main using CxxConfigManager. + * Build with something like: + * + * scons --without-python build/ARM/libgem5_opt.so + * + * g++ -DTRACING_ON -std=c++0x -Ibuild/ARM src/sim/cxx_main.cc \ + * -o gem5cxx.opt -Lbuild/ARM -lgem5_opt + */ + +#include <cstdlib> +#include <iostream> +#include <sstream> + +#include "base/inifile.hh" +#include "base/statistics.hh" +#include "base/str.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "sim/cxx_config_ini.hh" +#include "sim/cxx_manager.hh" +#include "sim/init_signals.hh" +#include "sim/serialize.hh" +#include "sim/simulate.hh" +#include "sim/stat_control.hh" +#include "sim/system.hh" +#include "stats.hh" + +void +usage(const std::string &prog_name) +{ + std::cerr << "Usage: " << prog_name << ( + " <config-file.ini> [ <option> ]\n\n" + "OPTIONS:\n" + " -p <object> <param> <value> -- set a parameter\n" + " -v <object> <param> <values> -- set a vector parameter from" + " a comma\n" + " separated values string\n" + " -d <flag> -- set a debug flag (-<flag>\n" + " clear a flag)\n" + " -s <dir> <ticks> -- save checkpoint to dir after" + " the given\n" + " number of ticks\n" + " -r <dir> -- restore checkpoint to dir\n" + " -c <from> <to> <ticks> -- switch from cpu 'from' to cpu" + " 'to' after\n" + " the given number of ticks\n" + "\n" + ); + + std::exit(EXIT_FAILURE); +} + +int +main(int argc, char **argv) +{ + std::string prog_name(argv[0]); + unsigned int arg_ptr = 1; + + if (argc == 1) + usage(prog_name); + + cxxConfigInit(); + + initSignals(); + + setClockFrequency(1000000000000); + curEventQueue(getEventQueue(0)); + + Stats::initSimStats(); + Stats::registerHandlers(CxxConfig::statsReset, CxxConfig::statsDump); + + Trace::enabled = true; + setDebugFlag("Terminal"); + // setDebugFlag("CxxConfig"); + + const std::string config_file(argv[arg_ptr]); + + CxxConfigFileBase *conf = new CxxIniFile(); + + if (!conf->load(config_file.c_str())) { + std::cerr << "Can't open config file: " << config_file << '\n'; + return EXIT_FAILURE; + } + arg_ptr++; + + CxxConfigManager *config_manager = new CxxConfigManager(*conf); + + bool checkpoint_restore = false; + bool checkpoint_save = false; + bool switch_cpus = false; + std::string checkpoint_dir = ""; + std::string from_cpu = ""; + std::string to_cpu = ""; + Tick pre_run_time = 1000000; + Tick pre_switch_time = 1000000; + + try { + while (arg_ptr < argc) { + std::string option(argv[arg_ptr]); + arg_ptr++; + unsigned num_args = argc - arg_ptr; + + if (option == "-p") { + if (num_args < 3) + usage(prog_name); + config_manager->setParam(argv[arg_ptr], argv[arg_ptr + 1], + argv[arg_ptr + 2]); + arg_ptr += 3; + } else if (option == "-v") { + std::vector<std::string> values; + + if (num_args < 3) + usage(prog_name); + tokenize(values, argv[arg_ptr + 2], ','); + config_manager->setParamVector(argv[arg_ptr], + argv[arg_ptr + 1], values); + arg_ptr += 3; + } else if (option == "-d") { + if (num_args < 1) + usage(prog_name); + if (argv[arg_ptr][0] == '-') + clearDebugFlag(argv[arg_ptr] + 1); + else + setDebugFlag(argv[arg_ptr]); + arg_ptr++; + } else if (option == "-r") { + if (num_args < 1) + usage(prog_name); + checkpoint_dir = argv[arg_ptr]; + checkpoint_restore = true; + arg_ptr++; + } else if (option == "-s") { + if (num_args < 2) + usage(prog_name); + checkpoint_dir = argv[arg_ptr]; + std::istringstream(argv[arg_ptr + 1]) >> pre_run_time; + checkpoint_save = true; + arg_ptr += 2; + } else if (option == "-c") { + if (num_args < 3) + usage(prog_name); + switch_cpus = true; + from_cpu = argv[arg_ptr]; + to_cpu = argv[arg_ptr + 1]; + std::istringstream(argv[arg_ptr + 2]) >> pre_switch_time; + arg_ptr += 3; + } else { + usage(prog_name); + } + } + } catch (CxxConfigManager::Exception &e) { + std::cerr << e.name << ": " << e.message << "\n"; + return EXIT_FAILURE; + } + + if (checkpoint_save && checkpoint_restore) { + std::cerr << "Don't try and save and restore a checkpoint in the" + " same run\n"; + return EXIT_FAILURE; + } + + CxxConfig::statsEnable(); + getEventQueue(0)->dump(); + + try { + config_manager->instantiate(); + if (!checkpoint_restore) { + config_manager->initState(); + config_manager->startup(); + } + } catch (CxxConfigManager::Exception &e) { + std::cerr << "Config problem in sim object " << e.name + << ": " << e.message << "\n"; + + return EXIT_FAILURE; + } + + GlobalSimLoopExitEvent *exit_event = NULL; + + if (checkpoint_save) { + + exit_event = simulate(pre_run_time); + + DrainManager drain_manager; + unsigned int drain_count = 1; + do { + drain_count = config_manager->drain(&drain_manager); + + std::cerr << "Draining " << drain_count << '\n'; + + if (drain_count > 0) { + drain_manager.setCount(drain_count); + exit_event = simulate(); + } + } while (drain_count > 0); + + std::cerr << "Simulation stop at tick " << curTick() + << ", cause: " << exit_event->getCause() << '\n'; + + std::cerr << "Checkpointing\n"; + + /* FIXME, this should really be serialising just for + * config_manager rather than using serializeAll's ugly + * SimObject static object list */ + Serializable::serializeAll(checkpoint_dir); + + std::cerr << "Completed checkpoint\n"; + + config_manager->drainResume(); + } + + if (checkpoint_restore) { + std::cerr << "Restoring checkpoint\n"; + + Checkpoint *checkpoint = new Checkpoint(checkpoint_dir, + config_manager->getSimObjectResolver()); + + Serializable::unserializeGlobals(checkpoint); + config_manager->loadState(checkpoint); + + config_manager->drainResume(); + + std::cerr << "Restored from checkpoint\n"; + } + + if (switch_cpus) { + exit_event = simulate(pre_switch_time); + + std::cerr << "Switching CPU\n"; + + /* Assume the system is called system */ + System &system = config_manager->getObject<System>("system"); + BaseCPU &old_cpu = config_manager->getObject<BaseCPU>(from_cpu); + BaseCPU &new_cpu = config_manager->getObject<BaseCPU>(to_cpu); + + DrainManager drain_manager; + unsigned int drain_count = 1; + do { + drain_count = config_manager->drain(&drain_manager); + + std::cerr << "Draining " << drain_count << '\n'; + + if (drain_count > 0) { + drain_manager.setCount(drain_count); + exit_event = simulate(); + } + } while (drain_count > 0); + + old_cpu.switchOut(); + system.setMemoryMode(Enums::timing); + new_cpu.takeOverFrom(&old_cpu); + config_manager->drainResume(); + + std::cerr << "Switched CPU\n"; + } + + exit_event = simulate(); + + std::cerr << "Exit at tick " << curTick() + << ", cause: " << exit_event->getCause() << '\n'; + + getEventQueue(0)->dump(); + +#if TRY_CLEAN_DELETE + config_manager->deleteObjects(); +#endif + + delete config_manager; + + return EXIT_SUCCESS; +} diff --git a/util/cxx_config/stats.cc b/util/cxx_config/stats.cc new file mode 100644 index 000000000..ef5d9b5d3 --- /dev/null +++ b/util/cxx_config/stats.cc @@ -0,0 +1,115 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +/** + * @file + * + * C++-only configuration stats handling example + * + * Register with: Stats::registerHandlers(statsReset, statsDump) + */ + +#include "base/statistics.hh" +#include "stats.hh" + +namespace CxxConfig +{ + +void statsPrepare() +{ + std::list<Stats::Info *> stats = Stats::statsList(); + + /* gather_stats -> prepare */ + for (auto i = stats.begin(); i != stats.end(); ++i) + (*i)->prepare(); +} + +void statsDump() +{ + std::cerr << "Stats dump\n"; + + Stats::processDumpQueue(); + + std::list<Stats::Info *> stats = Stats::statsList(); + + statsPrepare(); + + /* gather_stats -> convert_value */ + for (auto i = stats.begin(); i != stats.end(); ++i) { + Stats::Info *stat = *i; + + Stats::ScalarInfo *scalar = dynamic_cast<Stats::ScalarInfo *>(stat); + Stats::VectorInfo *vector = dynamic_cast<Stats::VectorInfo *>(stat); + + if (scalar) { + std::cerr << "SCALAR " << stat->name << ' ' + << scalar->value() << '\n'; + } else if (vector) { + Stats::VResult results = vector->value(); + + unsigned int index = 0; + for (auto e = results.begin(); e != results.end(); ++e) { + std::cerr << "VECTOR " << stat->name << '[' << index + << "] " << (*e) << '\n'; + index++; + } + std::cerr << "VTOTAL " << stat->name << ' ' + << vector->total() << '\n'; + } else { + std::cerr << "?????? " << stat->name << '\n'; + } + } +} + +void statsReset() +{ + std::cerr << "Stats reset\n"; + + Stats::processResetQueue(); +} + +void statsEnable() +{ + std::list<Stats::Info *> stats = Stats::statsList(); + + for (auto i = stats.begin(); i != stats.end(); ++i) + (*i)->enable(); +} + +} diff --git a/util/cxx_config/stats.hh b/util/cxx_config/stats.hh new file mode 100644 index 000000000..360cb6293 --- /dev/null +++ b/util/cxx_config/stats.hh @@ -0,0 +1,61 @@ +/* + * 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. + * + * Authors: Andrew Bardsley + */ + +/** + * @file + * + * C++-only configuration stats handling example + * + * Register with: Stats::registerHandlers(statsReset, statsDump) + */ + +#ifndef __UTIL_CXX_CONFIG_STATS_H__ +#define __UTIL_CXX_CONFIG_STATS_H__ + +namespace CxxConfig +{ + +void statsDump(); +void statsReset(); +void statsEnable(); +void statsPrepare(); + +} + +#endif // __UTIL_CXX_CONFIG_STATS_H__ |