From f48482529e34cc5d9f7d11681febb57905251b47 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Tue, 20 Apr 2021 09:10:23 +0200 Subject: add dot export of energy models --- bin/analyze-archive.py | 15 +++++++++++++++ lib/automata.py | 31 +++++++++++++++++++++++++++++-- lib/model.py | 13 +++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/bin/analyze-archive.py b/bin/analyze-archive.py index 6f5fc37..8b58478 100755 --- a/bin/analyze-archive.py +++ b/bin/analyze-archive.py @@ -614,6 +614,12 @@ if __name__ == "__main__": type=str, help="Load DFA hardware model from JSON or YAML FILE", ) + parser.add_argument( + "--export-dot", + metavar="FILE", + type=str, + help="Export PTA representation suitable for Graphviz dot to FILE", + ) parser.add_argument( "--export-energymodel", metavar="FILE", @@ -1222,4 +1228,13 @@ if __name__ == "__main__": with open(args.export_energymodel, "w") as f: json.dump(json_model, f, indent=2, sort_keys=True, cls=NpEncoder) + if args.export_dot: + if not pta: + print( + "Note: v0 measurements do not embed the PTA used for benchmark generation. Estimating PTA from recorded observations." + ) + json_model = model.to_json() + with open(args.export_dot, "w") as f: + f.write(model.to_dot()) + sys.exit(0) diff --git a/lib/automata.py b/lib/automata.py index 59be144..5989a50 100755 --- a/lib/automata.py +++ b/lib/automata.py @@ -283,7 +283,15 @@ class State: def to_dot(self) -> str: quote = '"' - return f"{quote}{self.name}{quote};\n" + label = self.name + if self.power and self.power.value: + if self.power.value < 1e3: + label += f"\\n{self.power.value : .0f} µW" + elif self.power.value < 1e6: + label += f"\\n{self.power.value * 1e-3 : .1f} mW" + else: + label += f"\\n{self.power.value * 1e-6 : .1f} W" + return f"""{quote}{self.name}{quote} [label="{label}"];\n""" class Transition: @@ -317,6 +325,7 @@ class Transition: name: str, energy: ModelFunction = StaticFunction(0), duration: ModelFunction = StaticFunction(0), + power: ModelFunction = StaticFunction(0), timeout: ModelFunction = StaticFunction(0), is_interrupt: bool = False, arguments: list = [], @@ -340,6 +349,7 @@ class Transition: self.destination = dest_state self.energy = energy self.duration = duration + self.power = power self.timeout = timeout self.is_interrupt = is_interrupt self.arguments = arguments.copy() @@ -447,9 +457,24 @@ class Transition: return ret def to_dot(self) -> str: + label = self.name + if self.duration and self.duration.value: + if self.duration.value < 1e3: + label += f"\\n{self.duration.value : .0f} µs" + elif self.duration.value < 1e6: + label += f"\\n{self.duration.value * 1e-3 : .1f} ms" + else: + label += f"\\n{self.duration.value * 1e-6 : .1f} s" + if self.power and self.power.value: + if self.power.value < 1e3: + label += f"\\n{self.power.value : .0f} µW" + elif self.power.value < 1e6: + label += f"\\n{self.power.value * 1e-3 : .1f} mW" + else: + label += f"\\n{self.power.value * 1e-6 : .1f} W" return ( '"' - + f"""{self.origin.name}" -> "{self.destination.name}" [label="{self.name}"];\n""" + + f"""{self.origin.name}" -> "{self.destination.name}" [label="{label}"];\n""" ) @@ -627,6 +652,7 @@ class PTA: transition["name"], duration=ModelFunction.from_json_maybe(transition, "duration"), energy=ModelFunction.from_json_maybe(transition, "energy"), + power=ModelFunction.from_json_maybe(transition, "power"), timeout=ModelFunction.from_json_maybe(transition, "timeout"), **kwargs, ) @@ -1205,6 +1231,7 @@ class PTA: for transition in self.transitions: try: transition.duration = model(transition.name, "duration") + transition.power = model(transition.name, "power") transition.energy = model(transition.name, "energy") if transition.is_interrupt: transition.timeout = model(transition.name, "timeout") diff --git a/lib/model.py b/lib/model.py index c89ff4f..4585dfe 100644 --- a/lib/model.py +++ b/lib/model.py @@ -815,6 +815,19 @@ class PTAModel(AnalyticModel): ) return pta.to_json() + def to_dot(self) -> str: + param_model, param_info = self.get_fitted() + pta = self.pta + if pta is None: + pta = PTA(self.states, parameters=self._parameter_names) + for transition in self.transitions: + for origin, destination in self.get_transition_states_from_traces( + transition + ): + pta.add_transition(origin, destination, transition) + pta.update(param_info) + return pta.to_dot() + def get_transition_states_from_traces(self, transition_name): if self.traces is None: return [("UNINITIALIZED", "UNINITIALIZED")] -- cgit v1.2.3