diff options
-rwxr-xr-x | bin/generate-dfa-benchmark.py | 26 | ||||
-rw-r--r-- | lib/harness.py | 5 | ||||
-rw-r--r-- | lib/runner.py | 40 |
3 files changed, 64 insertions, 7 deletions
diff --git a/bin/generate-dfa-benchmark.py b/bin/generate-dfa-benchmark.py index e157f1f..5d89a2d 100755 --- a/bin/generate-dfa-benchmark.py +++ b/bin/generate-dfa-benchmark.py @@ -29,7 +29,7 @@ Options: Override the name of the class instance used for benchmarking --mimosa=[k=v,k=v,...] - Perform energy measurements with MIMOSA. Takes precedence over --timing. + Perform energy measurements with MIMOSA. Takes precedence over --timing and --energytrace. mimosa options are key-value pairs. Possible settings with defaults: offset = 130 (mysterious 0V offset) shunt = 330 (measurement shunt in ohms) @@ -46,6 +46,9 @@ Options: Perform timing measurements using on-chip counters (no external hardware required) +--energytrace + Perform energy measurements using MSP430 EnergyTrace hardware. Includes --timing. + --trace-filter=<transition,transition,transition,...>[ <transition,transition,transition,...> ...] Only consider traces whose beginning matches one of the provided transition sequences. E.g. --trace-filter='init,foo init,bar' will only consider traces with init as first and foo or bar as second transition, @@ -161,7 +164,7 @@ def benchmark_from_runs(pta: PTA, runs: list, harness: OnboardTimerHarness, benc return outbuf def run_benchmark(application_file: str, pta: PTA, runs: list, arch: str, app: str, run_args: list, harness: object, sleep: int = 0, repeat: int = 0, run_offset: int = 0, runs_total: int = 0, dummy = False): - if 'mimosa' in opt: + if 'mimosa' in opt or 'energytrace' in opt: outbuf = benchmark_from_runs(pta, runs, harness, dummy = dummy, repeat = 1) else: outbuf = benchmark_from_runs(pta, runs, harness, dummy = dummy, repeat = repeat) @@ -201,12 +204,15 @@ def run_benchmark(application_file: str, pta: PTA, runs: list, arch: str, app: s return results - if 'mimosa' in opt: + if 'mimosa' in opt or 'energytrace' in opt: files = list() i = 0 while i < opt['repeat']: runner.flash(arch, app, run_args) - monitor = runner.get_monitor(arch, callback = harness.parser_cb, mimosa = opt['mimosa']) + if 'mimosa' in opt: + monitor = runner.get_monitor(arch, callback = harness.parser_cb, mimosa = opt['mimosa']) + elif 'energytrace' in opt: + monitor = runner.get_monitor(arch, callback = harness.parser_cb, energytrace = opt['energytrace']) sync_error = False try: @@ -268,6 +274,7 @@ if __name__ == '__main__': 'app= ' 'depth= ' 'dummy= ' + 'energytrace= ' 'instance= ' 'mimosa= ' 'repeat= ' @@ -316,6 +323,15 @@ if __name__ == '__main__': if opt['repeat'] == 0: opt['repeat'] = 1 + if 'energytrace' in opt: + if opt['energytrace'] == '': + opt['energytrace'] = dict() + else: + opt['energytrace'] = dict(map(lambda x: x.split('='), opt['energytrace'].split(','))) + opt.pop('timing', None) + if opt['repeat'] == 0: + opt['repeat'] = 1 + except getopt.GetoptError as err: print(err) sys.exit(2) @@ -369,6 +385,8 @@ if __name__ == '__main__': if 'mimosa' in opt: harness = TransitionHarness(gpio_pin = timer_pin, pta = pta, log_return_values = need_return_values, repeat = 1, post_transition_delay_us = 20) + elif 'energytrace' in opt: + harness = OnboardTimerHarness(gpio_pin = timer_pin, pta = pta, counter_limits = runner.get_counter_limits_us(opt['arch']), log_return_values = need_return_values, repeat = 1) elif 'timing' in opt: harness = OnboardTimerHarness(gpio_pin = timer_pin, pta = pta, counter_limits = runner.get_counter_limits_us(opt['arch']), log_return_values = need_return_values, repeat = opt['repeat']) diff --git a/lib/harness.py b/lib/harness.py index f39b28c..a4724f1 100644 --- a/lib/harness.py +++ b/lib/harness.py @@ -321,11 +321,12 @@ class OnboardTimerHarness(TransitionHarness): print(' Offending line: {}'.format(line)) return if log_data_target['isa'] != 'transition': - raise RuntimeError('Log mismatch: Expected transition, got {:s}'.format(log_data_target['isa'])) + raise RuntimeError('Log mismatch in benchmark id={:d} trace={:d}: transition #{:d} (ID {:d}): Expected transition, got {:s}'.format(0, + self.trace_id, self.current_transition_in_trace, transition_id, log_data_target['isa'])) if self.pta: transition = self.pta.transitions[transition_id] if transition.name != log_data_target['name']: - raise RuntimeError('Log mismatch: Expected transition {:s}, got transition {:s}'.format(log_data_target['name'], transition.name)) + raise RuntimeError('Log mismatch in benchmark id={:d} trace={:d}: transition #{:d} (ID {:d}): Expected transition {:s}, got transition {:s}'.format(0, self.trace_id, self.current_transition_in_trace, transition_id, log_data_target['name'], transition.name)) if self.log_return_values and len(transition.return_value_handlers): for handler in transition.return_value_handlers: if 'parameter' in handler: diff --git a/lib/runner.py b/lib/runner.py index 3fa104b..1525b56 100644 --- a/lib/runner.py +++ b/lib/runner.py @@ -128,6 +128,32 @@ class SerialMonitor: self.worker.stop() self.ser.close() +class EnergyTraceMonitor(SerialMonitor): + """EnergyTraceMonitor captures serial timing output and EnergyTrace energy data.""" + def __init__(self, port: str, baud: int, callback = None, voltage = 3.3): + super().__init__(port = port, baud = baud, callback = callback) + self._voltage = voltage + self._output = time.strftime('%Y%m%d-%H%M%S.etlog') + self._start_energytrace() + + def _start_energytrace(self): + cmd = ['msp430-etv', '--save', self._output, '0'] + self._logger = subprocess.Popen(cmd, + stdout = subprocess.PIPE, stderr = subprocess.PIPE, + universal_newlines = True) + + def close(self): + super().close() + self._logger.send_signal(subprocess.signal.SIGINT) + stdout, stderr = self._logger.communicate(timeout = 15) + + def get_files(self) -> list: + return [self._output] + + def get_config(self) -> dict: + return { + 'voltage' : self._voltage, + } class MIMOSAMonitor(SerialMonitor): """MIMOSAMonitor captures serial output and MIMOSA energy data for a specific amount of time.""" @@ -282,7 +308,15 @@ def get_info(arch, opts: list = []) -> list: return res.stdout.split('\n') def get_monitor(arch: str, **kwargs) -> object: - """Return a SerialMonitor or ShellMonitor, depending on "make info" output of arch.""" + """ + Return an appropriate monitor for arch, depending on "make info" output. + + Port and Baud rate are taken from "make info". + + :param arch: architecture name, e.g. 'msp430fr5994lp' or 'posix' + :param energytrace: `EnergyTraceMonitor` options. Returns an EnergyTrace monitor if not None. + :param mimosa: `MIMOSAMonitor` options. Returns a MIMOSA monitor if not None. + """ for line in get_info(arch): if 'Monitor:' in line: _, port, arg = line.split(' ') @@ -291,7 +325,11 @@ def get_monitor(arch: str, **kwargs) -> object: elif 'mimosa' in kwargs and kwargs['mimosa'] is not None: mimosa_kwargs = kwargs.pop('mimosa') return MIMOSAMonitor(port, arg, **mimosa_kwargs, **kwargs) + elif 'energytrace' in kwargs and kwargs['energytrace'] is not None: + energytrace_kwargs = kwargs.pop('energytrace') + return EnergyTraceMonitor(port, arg, **energytrace_kwargs, **kwargs) else: + kwargs.pop('energytrace', None) kwargs.pop('mimosa', None) return SerialMonitor(port, arg, **kwargs) raise RuntimeError('Monitor failure') |