| # Copyright 2017 ARM Limited |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from __future__ import division |
| import csv |
| import re |
| |
| from devlib.platform.gem5 import Gem5SimulationPlatform |
| from devlib.instrument import Instrument, CONTINUOUS, MeasurementsCsv |
| from devlib.exception import TargetError, HostError |
| |
| |
| class Gem5PowerInstrument(Instrument): |
| ''' |
| Instrument enabling power monitoring in gem5 |
| ''' |
| |
| mode = CONTINUOUS |
| roi_label = 'power_instrument' |
| site_mapping = {'timestamp': 'sim_seconds'} |
| |
| def __init__(self, target, power_sites): |
| ''' |
| Parameter power_sites is a list of gem5 identifiers for power values. |
| One example of such a field: |
| system.cluster0.cores0.power_model.static_power |
| ''' |
| if not isinstance(target.platform, Gem5SimulationPlatform): |
| raise TargetError('Gem5PowerInstrument requires a gem5 platform') |
| if not target.has('gem5stats'): |
| raise TargetError('Gem5StatsModule is not loaded') |
| super(Gem5PowerInstrument, self).__init__(target) |
| |
| # power_sites is assumed to be a list later |
| if isinstance(power_sites, list): |
| self.power_sites = power_sites |
| else: |
| self.power_sites = [power_sites] |
| self.add_channel('timestamp', 'time') |
| for field in self.power_sites: |
| self.add_channel(field, 'power') |
| self.target.gem5stats.book_roi(self.roi_label) |
| self.sample_period_ns = 10000000 |
| # Sample rate must remain unset as gem5 does not provide samples |
| # at regular intervals therefore the reported timestamp should be used. |
| self.sample_rate_hz = None |
| self.target.gem5stats.start_periodic_dump(0, self.sample_period_ns) |
| self._base_stats_dump = 0 |
| |
| def start(self): |
| self.target.gem5stats.roi_start(self.roi_label) |
| |
| def stop(self): |
| self.target.gem5stats.roi_end(self.roi_label) |
| |
| def get_data(self, outfile): |
| active_sites = [c.site for c in self.active_channels] |
| with open(outfile, 'wb') as wfh: |
| writer = csv.writer(wfh) |
| writer.writerow([c.label for c in self.active_channels]) # headers |
| sites_to_match = [self.site_mapping.get(s, s) for s in active_sites] |
| for rec, rois in self.target.gem5stats.match_iter(sites_to_match, |
| [self.roi_label], self._base_stats_dump): |
| writer.writerow([rec[s] for s in active_sites]) |
| return MeasurementsCsv(outfile, self.active_channels, self.sample_rate_hz) |
| |
| def reset(self, sites=None, kinds=None, channels=None): |
| super(Gem5PowerInstrument, self).reset(sites, kinds, channels) |
| self._base_stats_dump = self.target.gem5stats.next_dump_no() |
| |