summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <daniel.friesel@uos.de>2019-09-26 16:53:29 +0200
committerDaniel Friesel <daniel.friesel@uos.de>2019-09-26 16:53:29 +0200
commitbdd9fa8b2c08c833d398247c4e8fa9806e759c13 (patch)
treeae8bacc3c519f16917da96a4099929504e01c2ab
parent8aa9bdc2ec7832d49a7dde8ee92641df43a37398 (diff)
Use individuel MIMOSA log files per repetition; documentation
-rwxr-xr-xbin/generate-dfa-benchmark.py92
-rwxr-xr-xlib/automata.py86
-rwxr-xr-xlib/dfatool.py47
-rw-r--r--lib/harness.py87
-rw-r--r--lib/runner.py2
5 files changed, 233 insertions, 81 deletions
diff --git a/bin/generate-dfa-benchmark.py b/bin/generate-dfa-benchmark.py
index 30f2d20..9c783b7 100755
--- a/bin/generate-dfa-benchmark.py
+++ b/bin/generate-dfa-benchmark.py
@@ -28,6 +28,13 @@ Options:
--instance=<name>
Override the name of the class instance used for benchmarking
+--mimosa=[k=v,k=v,...]
+ Perform energy measurements with MIMOSA. Takes precedence over --timing.
+ mimosa options are key-value pairs. Possible settings with defaults:
+ offset = 130 (mysterious 0V offset)
+ shunt = 330 (measurement shunt in ohms)
+ voltage = 3.3 (VCC provided to DUT)
+
--sleep=<ms> (default: 0)
How long to sleep between function calls.
@@ -35,6 +42,10 @@ Options:
Decrease amount of parameter values used in state space exploration
(only use minimum and maximum for numeric values)
+--timing
+ Perform timing measurements using on-chip counters (no external hardware
+ required)
+
--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,
@@ -147,7 +158,10 @@ 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):
- outbuf = benchmark_from_runs(pta, runs, harness, dummy = dummy, repeat = repeat)
+ if 'mimosa' 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)
with open(application_file, 'w') as f:
f.write(outbuf.getvalue())
print('[MAKE] building benchmark with {:d} runs'.format(len(runs)))
@@ -180,25 +194,46 @@ def run_benchmark(application_file: str, pta: PTA, runs: list, arch: str, app: s
results.extend(run_benchmark(application_file, pta, runs[mid:], arch, app, run_args, harness.copy(), sleep, repeat, run_offset = run_offset + mid, runs_total = runs_total, dummy = dummy))
return results
- runner.flash(arch, app, run_args)
- monitor = runner.get_monitor(arch, callback = harness.parser_cb, mimosa = {'shunt': 82})
- if arch == 'posix':
- print('[RUN] Will run benchmark for {:.0f} seconds'.format(run_timeout))
- lines = monitor.run(int(run_timeout))
- return [(runs, harness, lines)]
+ if 'mimosa' in opt:
+ files = list()
+ for i in range(opt['repeat']):
+ runner.flash(arch, app, run_args)
+ monitor = runner.get_monitor(arch, callback = harness.parser_cb, mimosa = opt['mimosa'])
+
+ try:
+ slept = 0
+ while not harness.done:
+ time.sleep(5)
+ slept += 5
+ print('[RUN] {:d}/{:d} ({:.0f}%), current benchmark at {:.0f}%'.format(run_offset, runs_total, run_offset * 100 / runs_total, slept * 100 / run_timeout))
+ except KeyboardInterrupt:
+ pass
+ monitor.close()
+ files.extend(monitor.get_files())
+ harness.restart()
+
+ return [(runs, harness, monitor, files)]
+ else:
+ runner.flash(arch, app, run_args)
+ monitor = runner.get_monitor(arch, callback = harness.parser_cb)
+
+ if arch == 'posix':
+ print('[RUN] Will run benchmark for {:.0f} seconds'.format(run_timeout))
+ lines = monitor.run(int(run_timeout))
+ return [(runs, harness, lines, list())]
- try:
- slept = 0
- while repeat == 0 or not harness.done:
- time.sleep(5)
- slept += 5
- print('[RUN] {:d}/{:d} ({:.0f}%), current benchmark at {:.0f}%'.format(run_offset, runs_total, run_offset * 100 / runs_total, slept * 100 / run_timeout))
- except KeyboardInterrupt:
- pass
- monitor.close()
+ try:
+ slept = 0
+ while not harness.done:
+ time.sleep(5)
+ slept += 5
+ print('[RUN] {:d}/{:d} ({:.0f}%), current benchmark at {:.0f}%'.format(run_offset, runs_total, run_offset * 100 / runs_total, slept * 100 / run_timeout))
+ except KeyboardInterrupt:
+ pass
+ monitor.close()
- return [(runs, harness, monitor)]
+ return [(runs, harness, monitor, list())]
if __name__ == '__main__':
@@ -211,10 +246,12 @@ if __name__ == '__main__':
'depth= '
'dummy= '
'instance= '
+ 'mimosa= '
'repeat= '
'run= '
'sleep= '
'shrink '
+ 'timing '
'timer-pin= '
'trace-filter= '
)
@@ -240,13 +277,22 @@ if __name__ == '__main__':
opt['sleep'] = 0
if 'trace-filter' in opt:
- trace_filter = []
+ trace_filter = list()
for trace in opt['trace-filter'].split():
trace_filter.append(trace.split(','))
opt['trace-filter'] = trace_filter
else:
opt['trace-filter'] = None
+ if 'mimosa' in opt:
+ if opt['mimosa'] == '':
+ opt['mimosa'] = dict()
+ else:
+ opt['mimosa'] = dict(map(lambda x: x.split('='), opt['mimosa'].split(',')))
+ opt.pop('timing', None)
+ if opt['repeat'] == 0:
+ opt['repeat'] = 1
+
except getopt.GetoptError as err:
print(err)
sys.exit(2)
@@ -302,8 +348,10 @@ if __name__ == '__main__':
if next(filter(lambda x: len(x.return_value_handlers), pta.transitions), None):
need_return_values = True
- 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'])
- harness = TransitionHarness(gpio_pin = timer_pin, pta = pta, log_return_values = need_return_values, repeat = opt['repeat'], post_transition_delay_us = 20)
+ 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 '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'])
if len(args) > 1:
results = run_benchmark(args[1], pta, runs, opt['arch'], opt['app'], opt['run'].split(), harness, opt['sleep'], opt['repeat'], runs_total = len(runs), dummy = 'dummy' in opt)
@@ -312,7 +360,7 @@ if __name__ == '__main__':
'pta' : pta.to_json(),
'traces' : list(map(lambda x: x[1].traces, results)),
'raw_output' : list(map(lambda x: x[2].get_lines(), results)),
- 'files' : list(map(lambda x: x[2].get_files(), results)),
+ 'files' : list(map(lambda x: x[3], results)),
'configs' : list(map(lambda x: x[2].get_config(), results)),
}
extra_files = flatten(json_out['files'])
@@ -336,7 +384,7 @@ if __name__ == '__main__':
json.dump(json_out, f)
print(' --> {}.json'.format(output_prefix))
else:
- outbuf = benchmark_from_runs(pta, runs, harness, repeat = repeat)
+ outbuf = benchmark_from_runs(pta, runs, harness, repeat = opt['repeat'])
print(outbuf.getvalue())
sys.exit(0)
diff --git a/lib/automata.py b/lib/automata.py
index 0cc7143..2387734 100755
--- a/lib/automata.py
+++ b/lib/automata.py
@@ -27,10 +27,9 @@ class State:
u"""
Create a new PTA state.
- arguments:
- name -- state name
- power -- static state power in µW
- power_function -- parameterized state power in µW
+ :param name: state name
+ :param power: static state power in µW
+ :param power_function: parameterized state power in µW
"""
self.name = name
self.power = power
@@ -45,19 +44,25 @@ class State:
u"""
Return energy spent in state in pJ.
- arguments:
- duration -- duration in µs
- param_dict -- current parameters
+ :param duration: duration in µs
+ :param param_dict: current parameters
+ :returns: energy spent in pJ
"""
if self.power_function:
return self.power_function.eval(_dict_to_list(param_dict)) * duration
return self.power * duration
def set_random_energy_model(self, static_model = True):
+ """Set a random static energy value."""
self.power = int(np.random.sample() * 50000)
def get_transition(self, transition_name: str) -> object:
- """Return Transition object for outgoing transtion transition_name."""
+ """
+ Return Transition object for outgoing transtion transition_name.
+
+ :param transition_name: transition name
+ :returns: `Transition` object
+ """
return self.outgoing_transitions[transition_name]
def has_interrupt_transitions(self) -> bool:
@@ -73,8 +78,8 @@ class State:
Must only be called if has_interrupt_transitions returned true.
- arguments:
- parameters -- current parameter values
+ :param parameters: current parameter values
+ :returns: Transition object
"""
interrupts = filter(lambda x: x.is_interrupt, self.outgoing_transitions.values())
interrupts = sorted(interrupts, key = lambda x: x.get_timeout(parameters))
@@ -84,14 +89,15 @@ class State:
"""
Return a generator object for depth-first search over all outgoing transitions.
- arguments:
- depth -- search depth
- with_arguments -- perform dfs with function+argument transitions instead of just function transitions.
- trace_filter -- list of lists. Each sub-list is a trace. Only traces matching one of the provided sub-lists are returned.
+ :param depth: search depth
+ :param with_arguments: perform dfs with function+argument transitions instead of just function transitions.
+ :param trace_filter: list of lists. Each sub-list is a trace. Only traces matching one of the provided sub-lists are returned.
E.g. trace_filter = [['init', 'foo'], ['init', 'bar']] will only return traces with init as first and foo or bar as second element.
trace_filter = [['init', 'foo', '$'], ['init', 'bar', '$']] will only return the traces ['init', 'foo'] and ['init', 'bar'].
- sleep -- if set and non-zero: include sleep pseudo-states with <sleep> us duration
+ Note that `trace_filter` takes precedence over `depth`: traces matching `trace_filter` are generated even if their length exceeds `depth`
+ :param sleep: if set and non-zero: include sleep pseudo-states with <sleep> us duration
For the [['init', 'foo', '$'], ['init', 'bar', '$']] example above, sleep=10 results in [(None, 10), 'init', (None, 10), 'foo'] and [(None, 10), 'init', (None, 10), 'bar']
+ :returns: Generator object for depth-first search. Each access yields a list of (Transition, (arguments)) elements describing a single run through the PTA.
"""
# A '$' entry in trace_filter indicates that the trace should (successfully) terminate here regardless of `depth`.
@@ -170,7 +176,28 @@ class State:
return ret
class Transition:
- """A single PTA transition with one origin and one destination state."""
+ u"""
+ A single PTA transition with one origin and one destination state.
+
+ :param name: transition name, corresponds to driver function name
+ :param origin: origin `State`
+ :param destination: destination `State`
+ :param energy: static energy needed to execute this transition, in pJ
+ :param energy_function: parameterized transition energy `AnalyticFunction`, returning pJ
+ :param duration: transition duration, in µs
+ :param duration_function: parameterized duration `AnalyticFunction`, returning µs
+ :param timeout: transition timeout, in µs. Only set for interrupt transitions.
+ :param timeout_function: parameterized transition timeout `AnalyticFunction`, in µs. Only set for interrupt transitions.
+ :param is_interrupt: Is this an interrupt transition?
+ :param arguments: list of function argument names
+ :param argument_values: list of argument values used for benchmark generation. Each entry is a list of values for the corresponding argument
+ :param argument_combination: During benchmark generation, should arguments be combined via `cartesian` or `zip`?
+ :param param_update_function: Setter for parameters after a transition. Gets current parameter dict and function argument values as arguments, must return the new parameter dict
+ :param arg_to_param_map: dict mapping argument index to the name of the parameter affected by its value
+ :param set_param: dict mapping parameter name to their value (set as side-effect of executing the transition, not parameter-dependent)
+ :param return_value_handlers: todo
+ :param codegen: todo
+ """
def __init__(self, orig_state: State, dest_state: State, name: str,
energy: float = 0, energy_function: AnalyticFunction = None,
@@ -183,14 +210,14 @@ class Transition:
param_update_function = None,
arg_to_param_map: dict = None,
set_param = None,
- return_value_handlers: list = []):
+ return_value_handlers: list = [],
+ codegen = dict()):
"""
Create a new transition between two PTA states.
- arguments:
- orig_state -- origin state
- dest_state -- destination state
- name -- transition name, typically the same as a driver/library function name
+ :param orig_state: origin `State`
+ :param dest_state: destination `State`
+ :param name: transition name, typically the same as a driver/library function name
"""
self.name = name
self.origin = orig_state
@@ -209,18 +236,21 @@ class Transition:
self.arg_to_param_map = arg_to_param_map
self.set_param = set_param
self.return_value_handlers = return_value_handlers
+ self.codegen = codegen
for handler in self.return_value_handlers:
if 'formula' in handler:
handler['formula'] = NormalizationFunction(handler['formula'])
+
def get_duration(self, param_dict: dict = {}, args: list = []) -> float:
u"""
Return transition duration in µs.
- arguments:
- param_dict -- current parameter values
- args -- function arguments
+ :param param_dict: current parameter values
+ :param args: function arguments
+
+ :returns: transition duration in µs
"""
if self.duration_function:
return self.duration_function.eval(_dict_to_list(param_dict), args)
@@ -307,6 +337,14 @@ class PTA:
A parameterized priced timed automaton. All states are accepting.
Suitable for simulation, model storage, and (soon) benchmark generation.
+
+ :param state: dict mapping state name to `State` object
+ :param accepting_states: list of accepting state names
+ :param parameters: current parameters
+ :param parameter_normalization: TODO
+ :param codegen: TODO
+ :param initial_param_values: TODO
+ :param transitions: list of `Transition` objects
"""
def __init__(self, state_names: list = [],
diff --git a/lib/dfatool.py b/lib/dfatool.py
index 68146ae..ade685c 100755
--- a/lib/dfatool.py
+++ b/lib/dfatool.py
@@ -648,7 +648,7 @@ class RawData:
# state_duration is stored as ms, not us
return offline['us'] > state_duration * 1500
- def _measurement_is_valid_01(self, processed_data, repeat = 0):
+ def _measurement_is_valid_01(self, processed_data):
"""
Check if a dfatool v0 or v1 measurement is valid.
@@ -693,8 +693,6 @@ class RawData:
sched_trigger_count = 0
for run in traces:
sched_trigger_count += len(run['trace'])
- if repeat:
- sched_trigger_count *= repeat
if sched_trigger_count != processed_data['triggers']:
processed_data['error'] = 'got {got:d} trigger edges, expected {exp:d}'.format(
got = processed_data['triggers'],
@@ -931,20 +929,8 @@ class RawData:
elif version == 1:
- traces_by_file = list()
- mim_files_by_file = list()
with tarfile.open(filename) as tf:
- # Relies on generate-dfa-benchmark placing the .mim files
- # in the order they were created (i.e., lexically sorted)
- for member in tf.getmembers():
- _, extension = os.path.splitext(member.name)
- if extension == '.mim':
- mim_files_by_file.append({
- 'content' : tf.extractfile(member).read(),
- 'info' : member,
- })
- elif extension == '.json':
- ptalog = json.load(tf.extractfile(member))
+ ptalog = json.load(tf.extractfile(tf.getmember('ptalog.json')))
# ptalog['traces'] is a list of lists.
# The first level corresponds to the individual .mim files:
@@ -954,17 +940,22 @@ class RawData:
# sub-benchmark, so ptalog['traces'][0][0] is the first
# run, ptalog['traces'][0][1] the second, and so on
- traces_by_file.extend(ptalog['traces'])
- self.setup_by_fileno.append({
- 'mimosa_voltage' : ptalog['configs'][0]['voltage'],
- 'mimosa_shunt' : ptalog['configs'][0]['shunt'],
- 'state_duration' : ptalog['opt']['sleep']
- })
- for j, mim_file in enumerate(mim_files_by_file):
- mim_file['setup'] = self.setup_by_fileno[i]
- mim_file['expected_trace'] = ptalog['traces'][j]
- mim_file['fileno'] = i
- mim_files.extend(mim_files_by_file)
+ for j, traces in enumerate(ptalog['traces']):
+ self.traces_by_fileno.append(traces)
+ self.setup_by_fileno.append({
+ 'mimosa_voltage' : ptalog['configs'][j]['voltage'],
+ 'mimosa_shunt' : ptalog['configs'][j]['shunt'],
+ 'state_duration' : ptalog['opt']['sleep'],
+ })
+ for mim_file in ptalog['files'][j]:
+ member = tf.getmember(mim_file)
+ mim_files.append({
+ 'content' : tf.extractfile(member).read(),
+ 'fileno' : i,
+ 'info' : member,
+ 'setup' : self.setup_by_fileno[j],
+ 'expected_trace' : ptalog['traces'][j],
+ })
with Pool() as pool:
measurements = pool.map(_preprocess_measurement, mim_files)
@@ -983,7 +974,7 @@ class RawData:
measurement['energy_trace'].pop(0)
repeat = ptalog['opt']['repeat']
- if self._measurement_is_valid_01(measurement, repeat):
+ if self._measurement_is_valid_01(measurement):
self._merge_online_and_offline(measurement)
num_valid += 1
else:
diff --git a/lib/harness.py b/lib/harness.py
index d3ea481..f39b28c 100644
--- a/lib/harness.py
+++ b/lib/harness.py
@@ -11,15 +11,35 @@ import re
# generated otherwise and it should also work with AnalyticModel (which does
# not have states)
class TransitionHarness:
- """Foo."""
+ """
+ TODO
+
+ :param done: True if the specified amount of iterations have been logged.
+ :param synced: True if `parser_cb` has synchronized with UART output, i.e., the benchmark has successfully started.
+ :param traces: List of annotated PTA traces from benchmark execution. This list is updated during UART logging and should only be read back when `done` is True.
+ Uses the standard dfatool trace format: `traces` is a list of `{'id': ..., 'trace': ...}` dictionaries, each of which represents a single PTA trace (AKA
+ run). Each `trace` is in turn a list of state or transition dictionaries with the
+ following attributes:
+ * `isa`: 'state' or 'transition'
+ * `name`: state or transition name
+ * `parameter`: currently valid parameter values. If normalization is used, they are already normalized. Each parameter value is either a primitive
+ int/float/str value (-> constant for each iteration) or a list of
+ primitive values (-> set by the return value of the current run, not necessarily constan)
+ * `args`: function arguments, if isa == 'transition'
+ """
def __init__(self, gpio_pin = None, pta = None, log_return_values = False, repeat = 0, post_transition_delay_us = 0):
"""
Create a new TransitionHarness
- :param gpio_pin: multipass GPIO Pin used for transition synchronization, e.g. `GPIO::p1_0`. Optional.
+ :param gpio_pin: multipass GPIO Pin used for transition synchronization with an external measurement device, e.g. `GPIO::p1_0`. Optional.
The GPIO output is high iff a transition is executing
- :param pta: PTA object
+ :param pta: PTA object. Needed to map UART output IDs to states and transitions
:param log_return_values: Log return values of transition function calls?
+ :param repeat: How many times to run the benchmark until setting `one`, default 0.
+ When 0, `done` is never set.
+ :param post_transition_delay_us: If set, inject `arch.delay_us` after each transition, before logging the transition as completed (and releasing
+ `gpio_pin`). This artificially increases transition duration by the specified time and is useful if an external measurement device's resolution is
+ lower than the expected minimum transition duration.
"""
self.gpio_pin = gpio_pin
self.pta = pta
@@ -29,19 +49,35 @@ class TransitionHarness:
self.reset()
def copy(self):
- new_object = __class__(gpio_pin = self.gpio_pin, pta = self.pta, log_return_values = self.log_return_values, repeat = self.repeat)
+ new_object = __class__(gpio_pin = self.gpio_pin, pta = self.pta, log_return_values = self.log_return_values, repeat = self.repeat, post_transition_delay_us = self.post_transition_delay_us)
new_object.traces = self.traces.copy()
new_object.trace_id = self.trace_id
return new_object
def reset(self):
+ """
+ Reset harness for a new benchmark.
+
+ Truncates `traces`, `trace_id`, `done`, and `synced`.
+ """
self.traces = []
self.trace_id = 0
self.repetitions = 0
self.done = False
self.synced = False
+ def restart(self):
+ """
+ Reset harness for a new execution of the current benchmark.
+
+ Resets `done` and `synced`.
+ """
+ self.repetitions = 0
+ self.done = False
+ self.synced = False
+
def global_code(self):
+ """Return global (pre-`main()`) C++ code needed for tracing."""
ret = ''
if self.gpio_pin != None:
ret += '#define PTALOG_GPIO {}\n'.format(self.gpio_pin)
@@ -56,9 +92,11 @@ class TransitionHarness:
return ret
def start_benchmark(self, benchmark_id = 0):
+ """Return C++ code to signal benchmark start to harness."""
return 'ptalog.startBenchmark({:d});\n'.format(benchmark_id)
def start_trace(self):
+ """Prepare a new trace/run in the internal `.traces` structure."""
self.traces.append({
'id' : self.trace_id,
'trace' : list(),
@@ -66,6 +104,12 @@ class TransitionHarness:
self.trace_id += 1
def append_state(self, state_name, param):
+ """
+ Append a state to the current run in the internal `.traces` structure.
+
+ :param state_name: state name
+ :param param: parameter dict
+ """
self.traces[-1]['trace'].append({
'name': state_name,
'isa': 'state',
@@ -73,6 +117,13 @@ class TransitionHarness:
})
def append_transition(self, transition_name, param, args = []):
+ """
+ Append a transition to the current run in the internal `.traces` structure.
+
+ :param transition_name: transition name
+ :param param: parameter dict
+ :param args: function arguments (optional)
+ """
self.traces[-1]['trace'].append({
'name': transition_name,
'isa': 'transition',
@@ -81,9 +132,16 @@ class TransitionHarness:
})
def start_run(self):
+ """Return C++ code used to start a new run/trace."""
return 'ptalog.reset();\n'
def pass_transition(self, transition_id, transition_code, transition: object = None):
+ """
+ Return C++ code used to pass a transition, including the corresponding function call.
+
+ Tracks which transition has been executed and optionally its return value. May also inject a delay, if
+ `post_transition_delay_us` is set.
+ """
ret = 'ptalog.passTransition({:d});\n'.format(transition_id)
ret += 'ptalog.startTransition();\n'
if self.log_return_values and transition and len(transition.return_value_handlers):
@@ -170,14 +228,22 @@ class TransitionHarness:
self.current_transition_in_trace += 1
class OnboardTimerHarness(TransitionHarness):
- """Bar."""
+ """TODO
+
+ Additional parameters / changes from TransitionHarness:
+
+ :param traces: Each trace element (`.traces[*]['trace'][*]`) additionally contains
+ the dict `offline_aggregates` with the member `duration`. It contains a list of durations (in us) of the corresponding state/transition for each
+ benchmark iteration.
+ I.e. `.traces[*]['trace'][*]['offline_aggregates']['duration'] = [..., ...]`
+ """
def __init__(self, counter_limits, **kwargs):
super().__init__(**kwargs)
self.trace_length = 0
self.one_cycle_in_us, self.one_overflow_in_us, self.counter_max_overflow = counter_limits
def copy(self):
- new_harness = __class__((self.one_cycle_in_us, self.one_overflow_in_us, self.counter_max_overflow), gpio_pin = self.gpio_pin, pta = self.pta, log_return_values = self.log_return_values)
+ new_harness = __class__((self.one_cycle_in_us, self.one_overflow_in_us, self.counter_max_overflow), gpio_pin = self.gpio_pin, pta = self.pta, log_return_values = self.log_return_values, repeat = self.repeat)
new_harness.traces = self.traces.copy()
new_harness.trace_id = self.trace_id
return new_harness
@@ -217,8 +283,14 @@ class OnboardTimerHarness(TransitionHarness):
def parser_cb(self, line):
#print('[HARNESS] got line {}'.format(line))
if re.match(r'\[PTA\] benchmark start, id=(\S+)', line):
+ if self.repeat > 0 and self.repetitions == self.repeat:
+ self.done = True
+ self.synced = False
+ print('[HARNESS] done')
+ return
self.synced = True
- print('[HARNESS] synced')
+ self.repetitions += 1
+ print('[HARNESS] synced, {}/{}'.format(self.repetitions, self.repeat))
if self.synced:
res = re.match(r'\[PTA\] trace=(\S+) count=(\S+)', line)
if res:
@@ -237,6 +309,7 @@ class OnboardTimerHarness(TransitionHarness):
if overflow >= self.counter_max_overflow:
raise RuntimeError('Counter overflow ({:d}/{:d}) in benchmark id={:d} trace={:d}: transition #{:d} (ID {:d})'.format(cycles, overflow, 0, self.trace_id, self.current_transition_in_trace, transition_id))
duration_us = cycles * self.one_cycle_in_us + overflow * self.one_overflow_in_us
+ # TODO subtract 'nop' cycles
# self.traces contains transitions and states, UART output only contains transitions -> use index * 2
try:
log_data_target = self.traces[self.trace_id]['trace'][self.current_transition_in_trace * 2]
diff --git a/lib/runner.py b/lib/runner.py
index 588bd4d..25e3519 100644
--- a/lib/runner.py
+++ b/lib/runner.py
@@ -167,6 +167,8 @@ class MIMOSAMonitor(SerialMonitor):
self._mimosactl('connect')
def _stop_mimosa(self):
+ # Make sure the MIMOSA daemon has gathered all needed data
+ time.sleep(2)
self._mimosacmd(['--mimosa-stop'])
mtime_changed = True
mim_file = None