# 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", f"../../../{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(f"{router} Power: ", 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(f"{link}.nls0 Power: ", power) frequency = getClock(link + ".nls1", config) power = dsent.computeLinkPower(frequency) print(f"{link}.nls1 Power: ", 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(f"{sys.argv[1]}/{sys.argv[2]}/config.ini") parseStats( f"{sys.argv[1]}/{sys.argv[2]}/stats.txt", 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()