1# Copyright (c) 2017 ARM Limited
2# All rights reserved
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Redistribution and use in source and binary forms, with or without
14# modification, are permitted provided that the following conditions are
15# met: redistributions of source code must retain the above copyright
16# notice, this list of conditions and the following disclaimer;
17# redistributions in binary form must reproduce the above copyright
18# notice, this list of conditions and the following disclaimer in the
19# documentation and/or other materials provided with the distribution;
20# neither the name of the copyright holders nor the names of its
21# contributors may be used to endorse or promote products derived from
22# this software without specific prior written permission.
23#
24# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35#
36# Authors: Radhika Jagtap
37
38import matplotlib
39matplotlib.use('Agg')
40import matplotlib.pyplot as plt
41from matplotlib.font_manager import FontProperties
42import numpy as np
43import os
44
45# global results dict
46results = {}
47idleResults = {}
48
49# global vars for bank utilisation and seq_bytes values swept in the experiment
50bankUtilValues = []
51seqBytesValues = []
52delayValues = []
53
54# settings for 3 values of bank util and 3 values of seq_bytes
55stackHeight = 6.0
56stackWidth = 18.0
57barWidth = 0.5
58plotFontSize = 18
59
60States = ['IDLE', 'ACT', 'REF', 'ACT_PDN', 'PRE_PDN', 'SREF']
61
62EnergyStates = ['ACT_E',
63'PRE_E',
64'READ_E',
65'REF_E',
66'ACT_BACK_E',
67'PRE_BACK_E',
68'ACT_PDN_E',
69'PRE_PDN_E',
70'SREF_E']
71
72StackColors = {
73'IDLE' : 'black',       # time spent in states
74'ACT' : 'lightskyblue',
75'REF' : 'limegreen',
76'ACT_PDN' : 'crimson',
77'PRE_PDN' : 'orange',
78'SREF' : 'gold',
79'ACT_E' : 'lightskyblue',  # energy of states
80'PRE_E' : 'black',
81'READ_E' : 'white',
82'REF_E' : 'limegreen',
83'ACT_BACK_E' : 'lightgray',
84'PRE_BACK_E' : 'gray',
85'ACT_PDN_E' : 'crimson',
86'PRE_PDN_E' : 'orange',
87'SREF_E' : 'gold'
88}
89
90StatToKey = {
91'system.mem_ctrls_0.actEnergy'          : 'ACT_E',
92'system.mem_ctrls_0.preEnergy'          : 'PRE_E',
93'system.mem_ctrls_0.readEnergy'         : 'READ_E',
94'system.mem_ctrls_0.refreshEnergy'      : 'REF_E',
95'system.mem_ctrls_0.actBackEnergy'      : 'ACT_BACK_E',
96'system.mem_ctrls_0.preBackEnergy'      : 'PRE_BACK_E',
97'system.mem_ctrls_0.actPowerDownEnergy' : 'ACT_PDN_E',
98'system.mem_ctrls_0.prePowerDownEnergy' : 'PRE_PDN_E',
99'system.mem_ctrls_0.selfRefreshEnergy'  : 'SREF_E'
100}
101# Skipping write energy, the example script issues 100% reads by default
102# 'system.mem_ctrls_0.writeEnergy' : "WRITE"
103
104def plotLowPStates(plot_dir, stats_fname, bank_util_list, seqbytes_list,
105                   delay_list):
106    """
107    plotLowPStates generates plots by parsing statistics output by the DRAM
108    sweep simulation described in the the configs/dram/low_power_sweep.py
109    script.
110
111    The function outputs eps format images for the following plots
112    (1) time spent in the DRAM Power states as a stacked bar chart
113    (2) energy consumed by the DRAM Power states as a stacked bar chart
114    (3) idle plot for the last stats dump corresponding to an idle period
115
116    For all plots, the time and energy values of the first rank (i.e. rank0)
117    are plotted because the way the script is written means stats across ranks
118    are similar.
119
120    @param plot_dir: the dir to output the plots
121    @param stats_fname: the stats file name of the low power sweep sim
122    @param bank_util_list: list of bank utilisation values (e.g. [1, 4, 8])
123    @param seqbytes_list: list of seq_bytes values (e.g. [64, 456, 512])
124    @param delay_list: list of itt max multipliers (e.g. [1, 20, 200])
125
126    """
127    stats_file = open(stats_fname, 'r')
128
129    global bankUtilValues
130    bankUtilValues = bank_util_list
131
132    global seqBytesValues
133    seqBytesValues = seqbytes_list
134
135    global delayValues
136    delayValues = delay_list
137    initResults()
138
139    # throw away the first two lines of the stats file
140    stats_file.readline()
141    stats_file.readline() # the 'Begin' line
142
143    #######################################
144    # Parse stats file and gather results
145    ########################################
146
147    for delay in delayValues:
148        for bank_util in bankUtilValues:
149            for seq_bytes in seqBytesValues:
150
151                for line in stats_file:
152                    if 'Begin' in line:
153                        break
154
155                    if len(line.strip()) == 0:
156                        continue
157
158                    #### state time values ####
159                    if 'system.mem_ctrls_0.memoryStateTime' in line:
160                        # remove leading and trailing white spaces
161                        line = line.strip()
162                        # Example format:
163                        # 'system.mem_ctrls_0.memoryStateTime::ACT    1000000'
164                        statistic, stime = line.split()[0:2]
165                        # Now grab the state, i.e. 'ACT'
166                        state = statistic.split('::')[1]
167                        # store the value of the stat in the results dict
168                        results[delay][bank_util][seq_bytes][state] = \
169                            int(stime)
170                    #### state energy values ####
171                    elif line.strip().split()[0] in StatToKey.keys():
172                        # Example format:
173                        # system.mem_ctrls_0.actEnergy                 35392980
174                        statistic, e_val = line.strip().split()[0:2]
175                        senergy = int(float(e_val))
176                        state = StatToKey[statistic]
177                        # store the value of the stat in the results dict
178                        results[delay][bank_util][seq_bytes][state] = senergy
179
180    # To add last traffic gen idle period stats to the results dict
181    for line in stats_file:
182        if 'system.mem_ctrls_0.memoryStateTime' in line:
183            line = line.strip() # remove leading and trailing white spaces
184            # Example format:
185            # 'system.mem_ctrls_0.memoryStateTime::ACT    1000000'
186            statistic, stime = line.split()[0:2]
187            # Now grab the state energy, .e.g 'ACT'
188            state = statistic.split('::')[1]
189            idleResults[state] = int(stime)
190            if state == 'ACT_PDN':
191                break
192
193    ########################################
194    # Call plot functions
195    ########################################
196    # one plot per delay value
197    for delay in delayValues:
198        plot_path = plot_dir + delay + '-'
199
200        plotStackedStates(delay, States, 'IDLE', stateTimePlotName(plot_path),
201                          'Time (ps) spent in a power state')
202        plotStackedStates(delay, EnergyStates, 'ACT_E',
203                          stateEnergyPlotName(plot_path),
204                          'Energy (pJ) of a power state')
205    plotIdle(plot_dir)
206
207def plotIdle(plot_dir):
208    """
209    Create a bar chart for the time spent in power states during the idle phase
210
211    @param plot_dir: the dir to output the plots
212    """
213    fig, ax = plt.subplots()
214    width = 0.35
215    ind = np.arange(len(States))
216    l1 = ax.bar(ind, map(lambda x : idleResults[x], States), width)
217
218    ax.xaxis.set_ticks(ind + width/2)
219    ax.xaxis.set_ticklabels(States)
220    ax.set_ylabel('Time (ps) spent in a power state')
221    fig.suptitle("Idle 50 us")
222
223    print "saving plot:", idlePlotName(plot_dir)
224    plt.savefig(idlePlotName(plot_dir), format='eps')
225    plt.close(fig)
226
227def plotStackedStates(delay, states_list, bottom_state, plot_name, ylabel_str):
228    """
229    Create a stacked bar chart for the list that is passed in as arg, which
230    is either time spent or energy consumed in power states.
231
232    @param delay: one plot is output per delay value
233    @param states_list: list of either time or energy state names
234    @param bottom_state: the bottom-most component of the stacked bar
235    @param plot_name: the file name of the image to write the plot to
236    @param ylabel_str: Y-axis label depending on plotting time or energy
237    """
238    fig, ax = plt.subplots(1, len(bankUtilValues), sharey=True)
239    fig.set_figheight(stackHeight)
240    fig.set_figwidth(stackWidth)
241    width = barWidth
242    plt.rcParams.update({'font.size': plotFontSize})
243
244    # Get the number of seq_bytes values
245    N = len(seqBytesValues)
246    ind = np.arange(N)
247
248    for sub_idx, bank_util in enumerate(bankUtilValues):
249
250        l_states = {}
251        p_states = {}
252
253        # Must have a bottom of the stack first
254        state = bottom_state
255
256        l_states[state] = map(lambda x: results[delay][bank_util][x][state],
257                              seqBytesValues)
258        p_states[state] = ax[sub_idx].bar(ind, l_states[state], width,
259                                          color=StackColors[state])
260
261        time_sum = l_states[state]
262        for state in states_list[1:]:
263            l_states[state] = map(lambda x:
264                                  results[delay][bank_util][x][state],
265                                  seqBytesValues)
266            # Now add on top of the bottom = sum of values up until now
267            p_states[state] = ax[sub_idx].bar(ind, l_states[state], width,
268                                              color=StackColors[state],
269                                              bottom=time_sum)
270            # Now add the bit of the stack that we just ploted to the bottom
271            # resulting in a new bottom for the next iteration
272            time_sum = [prev_sum + new_s for prev_sum, new_s in \
273                zip(time_sum, l_states[state])]
274
275        ax[sub_idx].set_title('Bank util %s' % bank_util)
276        ax[sub_idx].xaxis.set_ticks(ind + width/2.)
277        ax[sub_idx].xaxis.set_ticklabels(seqBytesValues, rotation=45)
278        ax[sub_idx].set_xlabel('Seq. bytes')
279        if bank_util == bankUtilValues[0]:
280            ax[sub_idx].set_ylabel(ylabel_str)
281
282    myFontSize='small'
283    fontP = FontProperties()
284    fontP.set_size(myFontSize)
285    fig.legend(map(lambda x: p_states[x], states_list), states_list,
286               prop=fontP)
287
288    plt.savefig(plot_name,  format='eps', bbox_inches='tight')
289    print "saving plot:", plot_name
290    plt.close(fig)
291
292# These plat name functions are also called in the main script
293def idlePlotName(plot_dir):
294    return (plot_dir + 'idle.eps')
295
296def stateTimePlotName(plot_dir):
297    return (plot_dir + 'state-time.eps')
298
299def stateEnergyPlotName(plot_dir):
300    return (plot_dir + 'state-energy.eps')
301
302def initResults():
303    for delay in delayValues:
304        results[delay] = {}
305        for bank_util in bankUtilValues:
306            results[delay][bank_util] = {}
307            for seq_bytes in seqBytesValues:
308                results[delay][bank_util][seq_bytes] = {}
309