From a64358e6c07a5964eb63016c09e14732c49f95a7 Mon Sep 17 00:00:00 2001 From: Hugo Winbladh <hugwi268@student.liu.se> Date: Mon, 3 Jul 2023 20:36:38 +0000 Subject: [PATCH] Add method to reconstruct SFG from a schedule --- b_asic/schedule.py | 19 ++++++++++++++----- b_asic/scheduler_gui/main_window.py | 2 -- b_asic/signal_flow_graph.py | 29 +++++++++++++++++++++++++++++ test/test_schedule.py | 17 +++++++++++++++++ 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 2603afcb..ef0a6772 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -113,7 +113,6 @@ class Schedule: if not isinstance(sfg, SFG): raise TypeError("An SFG must be provided") - self._original_sfg = sfg() # Make a copy self._sfg = sfg self._start_times = {} self._laps = defaultdict(_laps_default) @@ -390,13 +389,14 @@ class Schedule: operation_id : GraphID The GraphID of the operation to swap. """ - self._original_sfg.swap_io_of_operation(operation_id) self._sfg.swap_io_of_operation(operation_id) @property def sfg(self) -> SFG: - """The SFG of the current schedule.""" - return self._original_sfg + """The SFG corresponding to the current schedule.""" + reconstructed_sfg = self._reintroduce_delays() + simplified_sfg = reconstructed_sfg.simplify_delay_element_placement() + return simplified_sfg @property def start_times(self) -> Dict[GraphID, int]: @@ -529,7 +529,6 @@ class Schedule: The execution time of the operation. """ self._sfg.set_execution_time_of_type(type_name, execution_time) - self._original_sfg.set_execution_time_of_type(type_name, execution_time) def move_y_location( self, graph_id: GraphID, new_y: int, insert: bool = False @@ -760,6 +759,16 @@ class Schedule: del self._laps[delay_input_id] delay_list = self._sfg.find_by_type_name(Delay.type_name()) + def _reintroduce_delays(self) -> SFG: + """ + Reintroduce delay elements to each signal according to the ``_laps`` variable. + """ + new_sfg = self._sfg() + for signal_id,lap in self._laps.items(): + for delays in range(lap): + new_sfg = new_sfg.insert_operation_after(signal_id, Delay()) + return new_sfg() + def _schedule_alap(self) -> None: """Schedule the operations using as-late-as-possible scheduling.""" precedence_list = self._sfg.get_precedence_list() diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index c1740e58..437dc009 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -439,7 +439,6 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): self.save_as() return self._schedule._sfg._graph_id_generator = None - self._schedule._original_sfg._graph_id_generator = None with open(self._file_name, 'wb') as f: pickle.dump(self._schedule, f) self._add_recent_file(self._file_name) @@ -462,7 +461,6 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): filename += '.bsc' self._file_name = filename self._schedule._sfg._graph_id_generator = None - self._schedule._original_sfg._graph_id_generator = None with open(self._file_name, 'wb') as f: pickle.dump(self._schedule, f) self._add_recent_file(self._file_name) diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index 405681ea..c2e3b43e 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -736,6 +736,35 @@ class SFG(AbstractOperation): # Recreate the newly coupled SFG so that all attributes are correct. return sfg_copy() + def simplify_delay_element_placement(self) -> "SFG": + """ + Simplify an SFG by removing some redundant delay elements. + For example two signals originating from the same starting point, each + connected to a delay element will combine into a single delay element. + + Returns a copy of the simplified SFG. + """ + + sfg_copy = self() + for delay_element in sfg_copy.find_by_type_name(Delay.type_name()): + neighboring_delays = [] + if len(delay_element.inputs[0].signals) > 0: + for signal in delay_element.inputs[0].signals[0].source.signals: + if isinstance(signal.destination.operation, Delay): + neighboring_delays.append(signal.destination.operation) + + if delay_element in neighboring_delays: + neighboring_delays.remove(delay_element) + + for delay in neighboring_delays: + for output in delay.outputs[0].signals: + output.set_source(delay_element.outputs[0]) + in_sig = delay.input(0).signals[0] + delay.input(0).remove_signal(in_sig) + in_sig.source.remove_signal(in_sig) + + return sfg_copy() + def _insert_operation_after_operation( self, output_operation: Operation, new_operation: Operation ): diff --git a/test/test_schedule.py b/test/test_schedule.py index 6990eb89..aba52d2f 100644 --- a/test/test_schedule.py +++ b/test/test_schedule.py @@ -520,6 +520,23 @@ class TestRescheduling: assert schedule._start_times["add0"] == 0 assert schedule._start_times["out0"] == 2 + def test_reintroduce_delays(self, precedence_sfg_delays, sfg_direct_form_iir_lp_filter): + precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1) + precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3) + sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 1) + sfg_direct_form_iir_lp_filter.set_latency_of_type(ConstantMultiplication.type_name(), 3) + + schedule = Schedule(precedence_sfg_delays, algorithm="ASAP") + sfg = schedule.sfg + assert precedence_sfg_delays.evaluate(5) == sfg.evaluate(5) + + schedule = Schedule(sfg_direct_form_iir_lp_filter, algorithm="ASAP") + sfg = schedule.sfg + assert sfg_direct_form_iir_lp_filter.evaluate(5) == sfg.evaluate(5) + + + + class TestTimeResolution: def test_increase_time_resolution( -- GitLab