diff --git a/util/on-chip-network-power-area.py b/util/on-chip-network-power-area.py new file mode 100644 index 000000000..9b7f66acc --- /dev/null +++ b/util/on-chip-network-power-area.py @@ -0,0 +1,217 @@ +# Copyright (c) 2014 Mark D. Hill and David A. Wood +# All rights reserved. +# +# 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. + + +from ConfigParser import ConfigParser +import string, sys, subprocess, os + +# Compile DSENT to generate the Python module and then import it. +# This script assumes it is executed from the gem5 root. +print("Attempting compilation") +from subprocess import call + +src_dir = 'ext/dsent' +build_dir = 'build/ext/dsent' + +if not os.path.exists(build_dir): + os.makedirs(build_dir) +os.chdir(build_dir) + +error = call(['cmake', '../../../%s' % src_dir]) +if error: + print("Failed to run cmake") + exit(-1) + +error = call(['make']) +if error: + print("Failed to run make") + exit(-1) + +print("Compiled dsent") +os.chdir("../../../") +sys.path.append("build/ext/dsent") +import dsent + +# Parse gem5 config.ini file for the configuration parameters related to +# the on-chip network. +def parseConfig(config_file): + config = ConfigParser() + if not config.read(config_file): + print("ERROR: config file '", config_file, "' not found") + sys.exit(1) + + if not config.has_section("system.ruby.network"): + print("ERROR: Ruby network not found in '", config_file) + sys.exit(1) + + if config.get("system.ruby.network", "type") != "GarnetNetwork_d" : + print("ERROR: Garnet network not used in '", config_file) + sys.exit(1) + + number_of_virtual_networks = config.getint("system.ruby.network", + "number_of_virtual_networks") + vcs_per_vnet = config.getint("system.ruby.network", "vcs_per_vnet") + + buffers_per_data_vc = config.getint("system.ruby.network", + "buffers_per_data_vc") + buffers_per_control_vc = config.getint("system.ruby.network", + "buffers_per_ctrl_vc") + + ni_flit_size_bits = 8 * config.getint("system.ruby.network", + "ni_flit_size") + + routers = config.get("system.ruby.network", "routers").split() + int_links = config.get("system.ruby.network", "int_links").split() + ext_links = config.get("system.ruby.network", "ext_links").split() + + return (config, number_of_virtual_networks, vcs_per_vnet, + buffers_per_data_vc, buffers_per_control_vc, ni_flit_size_bits, + routers, int_links, ext_links) + + +def getClock(obj, config): + if config.get(obj, "type") == "SrcClockDomain": + return config.getint(obj, "clock") + + if config.get(obj, "type") == "DerivedClockDomain": + source = config.get(obj, "clk_domain") + divider = config.getint(obj, "clk_divider") + return getClock(source, config) / divider + + source = config.get(obj, "clk_domain") + return getClock(source, config) + + +## Compute the power consumed by the given router +def computeRouterPowerAndArea(router, stats_file, config, int_links, ext_links, + number_of_virtual_networks, vcs_per_vnet, + buffers_per_data_vc, buffers_per_control_vc, + ni_flit_size_bits): + frequency = getClock(router, config) + num_ports = 0 + + for int_link in int_links: + if config.get(int_link, "node_a") == router or \ + config.get(int_link, "node_b") == router: + num_ports += 1 + + for ext_link in ext_links: + if config.get(ext_link, "int_node") == router: + num_ports += 1 + + power = dsent.computeRouterPowerAndArea(frequency, num_ports, num_ports, + number_of_virtual_networks, + vcs_per_vnet, buffers_per_data_vc, + ni_flit_size_bits) + + print("%s Power: " % router, power) + + +## Compute the power consumed by the given link +def computeLinkPower(link, stats_file, config, sim_seconds): + frequency = getClock(link + ".nls0", config) + power = dsent.computeLinkPower(frequency) + print("%s.nls0 Power: " % link, power) + + frequency = getClock(link + ".nls1", config) + power = dsent.computeLinkPower(frequency) + print("%s.nls1 Power: " % link, power) + + +def parseStats(stats_file, config, router_config_file, link_config_file, + routers, int_links, ext_links, number_of_virtual_networks, + vcs_per_vnet, buffers_per_data_vc, buffers_per_control_vc, + ni_flit_size_bits): + + # Open the stats.txt file and parse it to for the required numbers + # and the number of routers. + try: + stats_handle = open(stats_file, 'r') + stats_handle.close() + except IOError: + print("Failed to open ", stats_file, " for reading") + exit(-1) + + # Now parse the stats + pattern = "sim_seconds" + lines = string.split(subprocess.check_output( + ["grep", pattern, stats_file]), '\n', -1) + assert len(lines) >= 1 + + ## Assume that the first line is the one required + [l1,l2,l3] = lines[0].partition(" ") + l4 = l3.strip().partition(" ") + simulation_length_in_seconds = float(l4[0]) + + # Initialize DSENT with a configuration file + dsent.initialize(router_config_file) + + # Compute the power consumed by the routers + for router in routers: + computeRouterPowerAndArea(router, stats_file, config, int_links, + ext_links, number_of_virtual_networks, + vcs_per_vnet, buffers_per_data_vc, + buffers_per_control_vc, ni_flit_size_bits) + + # Finalize DSENT + dsent.finalize() + + # Initialize DSENT with a configuration file + dsent.initialize(link_config_file) + + # Compute the power consumed by the links + for link in int_links: + computeLinkPower(link, stats_file, config, + simulation_length_in_seconds) + for link in ext_links: + computeLinkPower(link, stats_file, config, + simulation_length_in_seconds) + + # Finalize DSENT + dsent.finalize() + +# This script parses the config.ini and the stats.txt from a run and +# generates the power and the area of the on-chip network using DSENT +def main(): + if len(sys.argv) != 5: + print("Usage: ", sys.argv[0], " " \ + " ") + exit(-1) + + print("WARNING: configuration files for DSENT and McPAT are separate. " \ + "Changes made to one are not reflected in the other.") + + (config, number_of_virtual_networks, vcs_per_vnet, buffers_per_data_vc, + buffers_per_control_vc, ni_flit_size_bits, routers, int_links, + ext_links) = parseConfig("%s/%s/config.ini" % (sys.argv[1], sys.argv[2])) + + parseStats("%s/%s/stats.txt" % (sys.argv[1], sys.argv[2]), config, + sys.argv[3], sys.argv[4], routers, int_links, ext_links, + number_of_virtual_networks, vcs_per_vnet, buffers_per_data_vc, + buffers_per_control_vc, ni_flit_size_bits) + +if __name__ == "__main__": + main()