util: Add a README file for the m5 utility.
[gem5.git] / util / on-chip-network-power-area.py
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
28 from configparser import ConfigParser
29 import 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.
33 print("Attempting compilation")
34 from subprocess import call
35
36 src_dir = 'ext/dsent'
37 build_dir = 'build/ext/dsent'
38
39 if not os.path.exists(build_dir):
40 os.makedirs(build_dir)
41 os.chdir(build_dir)
42
43 error = call(['cmake', '../../../%s' % src_dir])
44 if error:
45 print("Failed to run cmake")
46 exit(-1)
47
48 error = call(['make'])
49 if error:
50 print("Failed to run make")
51 exit(-1)
52
53 print("Compiled dsent")
54 os.chdir("../../../")
55 sys.path.append("build/ext/dsent")
56 import dsent
57
58 # Parse gem5 config.ini file for the configuration parameters related to
59 # the on-chip network.
60 def 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
95 def 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
109 def 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
134 def 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
144 def 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
198 def 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
216 if __name__ == "__main__":
217 main()