From 87267394f63aad602a201701399787a9c0e5d796 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Sun, 17 Jan 2016 13:43:18 +0000 Subject: [PATCH] libs/utils/energy.py: Initial integration of AEP Energy Meter: This patch integrates the initial version of the ARM Energy Probe AEP Energy Meter class. The AEP delivers sample voltage, power and current values (10Khz). The AEP Energy Meter uses only the power sample values and calculates the energy value by multiplying the average power value with the time difference value between the call of the reset() and the report() method. Current limitations: - The AEP Energy Meter class is tailored to measure DIE energy on the Hikey board for now. - It implements only the reset() and report()(sample()) method (e.g. used by the eas rfc test: tests/eas/rfc.py). - It uses only AEP channel 1 (hardcoded). - It uses a hardcoded value for shunt resistor (33 mOhm). - It uses 'LITTLE' channel tag to let the existing tooling work with the Hikey board even though we actually measure DIE energy. The AEP Energy Meter operates on top of the devlib EnergyProbeInstrument which itself uses the caiman binary to operate the AEP. The EnergyProbeInstrument figures out if the caiman binary is not installed and issues an appropriate error message in this case on the host. This patch bases on an initial version by Leo Yan . Signed-off-by: Dietmar Eggemann --- libs/utils/energy.py | 94 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/libs/utils/energy.py b/libs/utils/energy.py index 935718518..a7d3de02a 100644 --- a/libs/utils/energy.py +++ b/libs/utils/energy.py @@ -18,6 +18,7 @@ import devlib import json import logging +import time # Default energy measurements for each board DEFAULT_ENERGY_METER = { @@ -38,6 +39,11 @@ DEFAULT_ENERGY_METER = { 'sites' : [ 'a53', 'a57' ], 'kinds' : [ 'energy' ] } + }, + + # Hikey: by default use AEP + 'hikey' : { + 'instrument' : 'aep', } } @@ -66,6 +72,8 @@ class EnergyMeter(object): if emeter['instrument'] == 'hwmon': EnergyMeter._meter = HWMon(target, emeter['conf']) + elif emeter['instrument'] == 'aep': + EnergyMeter._meter = Aep(target) return EnergyMeter._meter def sample(self): @@ -168,6 +176,92 @@ class HWMon(EnergyMeter): if 'big' not in clusters_nrg: logging.warning('%14s - No energy data for big cluster', 'EnergyMeter') + + # Dump data as JSON file + nrg_file = '{}/{}'.format(out_dir, out_file) + with open(nrg_file, 'w') as ofile: + json.dump(clusters_nrg, ofile, sort_keys=True, indent=4) + + return (clusters_nrg, nrg_file) + +class Aep(EnergyMeter): + + def __init__(self, target): + super(Aep, self).__init__(target) + + # Energy readings + self.readings = {} + + # Time (start and diff) for power measurment + self.time = {} + + # Initialize instrument + # Only one channel (first AEP channel: pc1 ... probe channel 1) is used + self._aep = devlib.EnergyProbeInstrument(self._target, labels=["pc1"], resistor_values=[0.033]) + + # Configure channels for energy measurements + logging.debug('EnergyMeter - Enabling channels') + self._aep.reset() + + # Logging enabled channels + logging.info('%14s - Channels selected for energy sampling:\n%s', + 'EnergyMeter', str(self._aep.active_channels)) + + def __calc_nrg(self, samples): + + power = {'sum' : 0, 'count' : 0, 'avg' : 0} + + for s in samples: + power['sum'] += s[1].value # s[1] ... power value of channel 1 + power['count'] += 1 + + power['avg'] = power['sum'] / power['count'] + + nrg = power['avg'] * self.time['diff'] + + logging.debug('avg power: %.6f count: %s time: %.6f nrg: %.6f', + power['avg'], power['count'], self.time['diff'] , nrg) + return nrg + + def sample(self): + if self._aep is None: + return + + self.time['diff'] = time.time() - self.time['start'] + self._aep.stop() + + csv_data = self._aep.get_data("/tmp/aep.csv") + samples = csv_data.measurements() + + value = self.__calc_nrg(samples) + + self.readings['last'] = value + self.readings['delta'] = value + self.readings['total'] = value + + logging.debug('SAMPLE: %s', self.readings) + return self.readings + + def reset(self): + if self._aep is None: + return + + logging.debug('RESET: %s', self.readings) + + self._aep.start() + self.time['start'] = time.time() + + def report(self, out_dir, out_file='energy.json'): + if self._aep is None: + return + + # Retrieve energy consumption data + nrg = self.sample() + + # Reformat data for output generation + clusters_nrg = {} + clusters_nrg['LITTLE'] = '{:.6f}'.format(self.readings['total']) + # Dump data as JSON file nrg_file = '{}/{}'.format(out_dir, out_file) with open(nrg_file, 'w') as ofile: -- GitLab