1 # Copyright (c) 2014 Mark D. Hill and David A. Wood
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.
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.
28 from ConfigParser
import ConfigParser
29 import string
, sys
, subprocess
, os
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
37 build_dir
= 'build/ext/dsent'
39 if not os
.path
.exists(build_dir
):
40 os
.makedirs(build_dir
)
43 error
= call(['cmake', '../../../%s' % src_dir
])
45 print("Failed to run cmake")
48 error
= call(['make'])
50 print("Failed to run make")
53 print("Compiled dsent")
55 sys
.path
.append("build/ext/dsent")
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")
66 if not config
.has_section("system.ruby.network"):
67 print("ERROR: Ruby network not found in '", config_file
)
70 if config
.get("system.ruby.network", "type") != "GarnetNetwork_d" :
71 print("ERROR: Garnet network not used in '", config_file
)
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")
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")
83 ni_flit_size_bits
= 8 * config
.getint("system.ruby.network",
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()
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
)
95 def getClock(obj
, config
):
96 if config
.get(obj
, "type") == "SrcClockDomain":
97 return config
.getint(obj
, "clock")
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
104 source
= config
.get(obj
, "clk_domain")
105 return getClock(source
, config
)
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
,
113 frequency
= getClock(router
, config
)
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
:
121 for ext_link
in ext_links
:
122 if config
.get(ext_link
, "int_node") == router
:
125 power
= dsent
.computeRouterPowerAndArea(frequency
, num_ports
, num_ports
,
126 number_of_virtual_networks
,
127 vcs_per_vnet
, buffers_per_data_vc
,
130 print("%s Power: " % router
, power
)
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
)
139 frequency
= getClock(link
+ ".nls1", config
)
140 power
= dsent
.computeLinkPower(frequency
)
141 print("%s.nls1 Power: " % link
, power
)
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
,
149 # Open the stats.txt file and parse it to for the required numbers
150 # and the number of routers.
152 stats_handle
= open(stats_file
, 'r')
155 print("Failed to open ", stats_file
, " for reading")
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
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])
169 # Initialize DSENT with a configuration file
170 dsent
.initialize(router_config_file
)
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
)
182 # Initialize DSENT with a configuration file
183 dsent
.initialize(link_config_file
)
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
)
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
199 if len(sys
.argv
) != 5:
200 print("Usage: ", sys
.argv
[0], " <gem5 root directory> " \
201 "<simulation directory> <router config file> <link config file>")
204 print("WARNING: configuration files for DSENT and McPAT are separate. " \
205 "Changes made to one are not reflected in the other.")
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]))
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
)
216 if __name__
== "__main__":