1# Copyright (c) 2014 Mark D. Hill and David A. Wood 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: redistributions of source code must retain the above copyright 7# notice, this list of conditions and the following disclaimer; 8# redistributions in binary form must reproduce the above copyright 9# notice, this list of conditions and the following disclaimer in the 10# documentation and/or other materials provided with the distribution; 11# neither the name of the copyright holders nor the names of its 12# contributors may be used to endorse or promote products derived from 13# this software without specific prior written permission. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 28from ConfigParser import ConfigParser 29import string, sys, subprocess, os 30 31# Compile DSENT to generate the Python module and then import it. 32# This script assumes it is executed from the gem5 root. 33print("Attempting compilation") 34from subprocess import call 35 36src_dir = 'ext/dsent' 37build_dir = 'build/ext/dsent' 38 39if not os.path.exists(build_dir): 40 os.makedirs(build_dir) 41os.chdir(build_dir) 42 43error = call(['cmake', '../../../%s' % src_dir]) 44if error: 45 print("Failed to run cmake") 46 exit(-1) 47 48error = call(['make']) 49if error: 50 print("Failed to run make") 51 exit(-1) 52 53print("Compiled dsent") 54os.chdir("../../../") 55sys.path.append("build/ext/dsent") 56import dsent 57 58# Parse gem5 config.ini file for the configuration parameters related to 59# the on-chip network. 60def parseConfig(config_file): 61 config = ConfigParser() 62 if not config.read(config_file): 63 print("ERROR: config file '", config_file, "' not found") 64 sys.exit(1) 65 66 if not config.has_section("system.ruby.network"): 67 print("ERROR: Ruby network not found in '", config_file) 68 sys.exit(1) 69 70 if config.get("system.ruby.network", "type") != "GarnetNetwork_d" : 71 print("ERROR: Garnet network not used in '", config_file) 72 sys.exit(1) 73 74 number_of_virtual_networks = config.getint("system.ruby.network", 75 "number_of_virtual_networks") 76 vcs_per_vnet = config.getint("system.ruby.network", "vcs_per_vnet") 77 78 buffers_per_data_vc = config.getint("system.ruby.network", 79 "buffers_per_data_vc") 80 buffers_per_control_vc = config.getint("system.ruby.network", 81 "buffers_per_ctrl_vc") 82 83 ni_flit_size_bits = 8 * config.getint("system.ruby.network", 84 "ni_flit_size") 85 86 routers = config.get("system.ruby.network", "routers").split() 87 int_links = config.get("system.ruby.network", "int_links").split() 88 ext_links = config.get("system.ruby.network", "ext_links").split() 89 90 return (config, number_of_virtual_networks, vcs_per_vnet, 91 buffers_per_data_vc, buffers_per_control_vc, ni_flit_size_bits, 92 routers, int_links, ext_links) 93 94 95def getClock(obj, config): 96 if config.get(obj, "type") == "SrcClockDomain": 97 return config.getint(obj, "clock") 98 99 if config.get(obj, "type") == "DerivedClockDomain": 100 source = config.get(obj, "clk_domain") 101 divider = config.getint(obj, "clk_divider") 102 return getClock(source, config) / divider 103 104 source = config.get(obj, "clk_domain") 105 return getClock(source, config) 106 107 108## Compute the power consumed by the given router 109def computeRouterPowerAndArea(router, stats_file, config, int_links, ext_links, 110 number_of_virtual_networks, vcs_per_vnet, 111 buffers_per_data_vc, buffers_per_control_vc, 112 ni_flit_size_bits): 113 frequency = getClock(router, config) 114 num_ports = 0 115 116 for int_link in int_links: 117 if config.get(int_link, "node_a") == router or \ 118 config.get(int_link, "node_b") == router: 119 num_ports += 1 120 121 for ext_link in ext_links: 122 if config.get(ext_link, "int_node") == router: 123 num_ports += 1 124 125 power = dsent.computeRouterPowerAndArea(frequency, num_ports, num_ports, 126 number_of_virtual_networks, 127 vcs_per_vnet, buffers_per_data_vc, 128 ni_flit_size_bits) 129 130 print("%s Power: " % router, power) 131 132 133## Compute the power consumed by the given link 134def computeLinkPower(link, stats_file, config, sim_seconds): 135 frequency = getClock(link + ".nls0", config) 136 power = dsent.computeLinkPower(frequency) 137 print("%s.nls0 Power: " % link, power) 138 139 frequency = getClock(link + ".nls1", config) 140 power = dsent.computeLinkPower(frequency) 141 print("%s.nls1 Power: " % link, power) 142 143 144def parseStats(stats_file, config, router_config_file, link_config_file, 145 routers, int_links, ext_links, number_of_virtual_networks, 146 vcs_per_vnet, buffers_per_data_vc, buffers_per_control_vc, 147 ni_flit_size_bits): 148 149 # Open the stats.txt file and parse it to for the required numbers 150 # and the number of routers. 151 try: 152 stats_handle = open(stats_file, 'r') 153 stats_handle.close() 154 except IOError: 155 print("Failed to open ", stats_file, " for reading") 156 exit(-1) 157 158 # Now parse the stats 159 pattern = "sim_seconds" 160 lines = string.split(subprocess.check_output( 161 ["grep", pattern, stats_file]), '\n', -1) 162 assert len(lines) >= 1 163 164 ## Assume that the first line is the one required 165 [l1,l2,l3] = lines[0].partition(" ") 166 l4 = l3.strip().partition(" ") 167 simulation_length_in_seconds = float(l4[0]) 168 169 # Initialize DSENT with a configuration file 170 dsent.initialize(router_config_file) 171 172 # Compute the power consumed by the routers 173 for router in routers: 174 computeRouterPowerAndArea(router, stats_file, config, int_links, 175 ext_links, number_of_virtual_networks, 176 vcs_per_vnet, buffers_per_data_vc, 177 buffers_per_control_vc, ni_flit_size_bits) 178 179 # Finalize DSENT 180 dsent.finalize() 181 182 # Initialize DSENT with a configuration file 183 dsent.initialize(link_config_file) 184 185 # Compute the power consumed by the links 186 for link in int_links: 187 computeLinkPower(link, stats_file, config, 188 simulation_length_in_seconds) 189 for link in ext_links: 190 computeLinkPower(link, stats_file, config, 191 simulation_length_in_seconds) 192 193 # Finalize DSENT 194 dsent.finalize() 195 196# This script parses the config.ini and the stats.txt from a run and 197# generates the power and the area of the on-chip network using DSENT 198def main(): 199 if len(sys.argv) != 5: 200 print("Usage: ", sys.argv[0], " <gem5 root directory> " \ 201 "<simulation directory> <router config file> <link config file>") 202 exit(-1) 203 204 print("WARNING: configuration files for DSENT and McPAT are separate. " \ 205 "Changes made to one are not reflected in the other.") 206 207 (config, number_of_virtual_networks, vcs_per_vnet, buffers_per_data_vc, 208 buffers_per_control_vc, ni_flit_size_bits, routers, int_links, 209 ext_links) = parseConfig("%s/%s/config.ini" % (sys.argv[1], sys.argv[2])) 210 211 parseStats("%s/%s/stats.txt" % (sys.argv[1], sys.argv[2]), config, 212 sys.argv[3], sys.argv[4], routers, int_links, ext_links, 213 number_of_virtual_networks, vcs_per_vnet, buffers_per_data_vc, 214 buffers_per_control_vc, ni_flit_size_bits) 215 216if __name__ == "__main__": 217 main() 218