From 2c60ea049270ac24a6e404de28a837e8efc668a2 Mon Sep 17 00:00:00 2001
From: Oscar Gustafsson <oscar.gustafsson@gmail.com>
Date: Mon, 8 Jun 2020 23:41:53 +0200
Subject: [PATCH] Better rescheduling

---
 b_asic/schema.py | 93 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 79 insertions(+), 14 deletions(-)

diff --git a/b_asic/schema.py b/b_asic/schema.py
index 4c68e86d..63829e83 100644
--- a/b_asic/schema.py
+++ b/b_asic/schema.py
@@ -68,33 +68,56 @@ class Schema:
 
     def forward_slack(self, op_id: GraphID) -> int:
         assert op_id in self._start_times, "No operation with the specified op_id in this schema."
+        slack = sys.maxsize
+        output_slacks = self._forward_slacks(op_id)
+        # Make more pythonic
+        for signal_slacks in output_slacks.values():
+            for signal_slack in signal_slacks.values():
+                slack = min(slack, signal_slack)
+        return slack
+
+    def _forward_slacks(self, op_id: GraphID) -> Dict["OutputPort", Dict["Signal", int]]:
+        ret = dict()
         start_time = self._start_times[op_id]
         op = self._sfg.find_by_id(op_id)
-        slack = sys.maxsize
         for output_port in op.outputs:
+            output_slacks = dict()
             available_time = start_time + output_port.latency_offset
 
             for signal in output_port.signals:
                 usage_time = (signal.destination.latency_offset +
                              self._start_times[signal.destination.operation.graph_id] +
                              self._schedule_time*self._laps[signal.graph_id])
-                slack = min(slack, usage_time - available_time)
-        return slack
+                output_slacks[signal] = usage_time - available_time
+            ret[output_port] = output_slacks
+        return ret
 
     def backward_slack(self, op_id: GraphID) -> int:
         assert op_id in self._start_times, "No operation with the specified op_id in this schema."
+        slack = sys.maxsize
+        input_slacks = self._backward_slacks(op_id)
+        # Make more pythonic
+        for signal_slacks in input_slacks.values():
+            for signal_slack in signal_slacks.values():
+                slack = min(slack, signal_slack)
+        return slack
+
+    def _backward_slacks(self, op_id: GraphID) -> Dict["OutputPort", Dict["Signal", int]]:
+        ret = dict()
         start_time = self._start_times[op_id]
         op = self._sfg.find_by_id(op_id)
-        slack = sys.maxsize
         for input_port in op.inputs:
+            input_slacks = dict()
             usage_time = start_time + input_port.latency_offset
 
             for signal in input_port.signals:
                 available_time = (signal.source.latency_offset +
                              self._start_times[signal.source.operation.graph_id] -
                              self._schedule_time*self._laps[signal.graph_id])
-                slack = min(slack, usage_time - available_time)
-        return slack
+                input_slacks[signal] = usage_time - available_time
+            ret[input_port] = input_slacks
+        return ret
+
 
     def slacks(self, op_id: GraphID) -> Tuple[int, int]:
         assert op_id in self._start_times, "No operation with the specified op_id in this schema."
@@ -103,29 +126,71 @@ class Schema:
     def print_slacks(self) -> None:
         raise NotImplementedError
 
-    def set_schedule_time(self, time: int) -> None:
+    def set_schedule_time(self, time: int) -> "Schema":
         assert self._get_max_end_time() < time, "New schedule time to short."
         self._schedule_time = time
+        return self
 
     @property
     def schedule_time(self) -> int:
         return self._schedule_time
 
-    def increase_time_resolution(self, factor: int) -> None:
+    def increase_time_resolution(self, factor: int) -> "Schema":
         raise NotImplementedError
 
-    def decrease_time_resolution(self, factor: int) -> None:
+    def decrease_time_resolution(self, factor: int) -> "Schema":
         raise NotImplementedError
 
-    def move_operation(self, op_id: GraphID, time: int) -> None:
+    def move_operation(self, op_id: GraphID, time: int) -> "Schema":
         assert op_id in self._start_times, "No operation with the specified op_id in this schema."
+
+        (backward_slack, forward_slack) = self.slacks(op_id)
         if time < 0:
-            if -time > self.backward_slack(op_id):
+            if -time > backward_slack:
                 raise ValueError
         else:
-            if time > self.forward_slack(op_id):
+            if time > forward_slack:
                 raise ValueError
-        self._start_times[op_id] += time
+
+        tmp_start = self._start_times[op_id] + time
+        new_start = tmp_start % self._schedule_time
+
+        # Update input laps
+        input_slacks = self._backward_slacks(op_id)
+        for in_port, signal_slacks in input_slacks.items():
+            tmp_usage = tmp_start + in_port.latency_offset
+            new_usage = tmp_usage % self._schedule_time
+            for signal, signal_slack in signal_slacks.items():
+                new_slack = signal_slack + time
+                old_laps = self._laps[signal.graph_id]
+                tmp_prev_available = tmp_usage - new_slack
+                prev_available = tmp_prev_available % self._schedule_time
+                laps = new_slack // self._schedule_time
+                if new_usage < prev_available:
+                    laps += 1
+                print([signal_slack, new_slack, old_laps, laps, new_usage, prev_available, tmp_usage, tmp_prev_available])
+                self._laps[signal.graph_id] = laps
+
+        # Update output laps
+        output_slacks = self._forward_slacks(op_id)
+        for out_port, signal_slacks in output_slacks.items():
+            tmp_available = tmp_start + out_port.latency_offset
+            new_available = tmp_available % self._schedule_time
+            for signal, signal_slack in signal_slacks.items():
+                new_slack = signal_slack - time
+                tmp_next_usage = tmp_available + new_slack
+                next_usage = tmp_next_usage % self._schedule_time
+                laps = new_slack // self._schedule_time
+                if next_usage < new_available:
+                    laps += 1
+                if new_available == 0 and new_slack > 0:
+                    laps += 1
+                self._laps[signal.graph_id] = laps
+
+
+        # Set new start time
+        self._start_times[op_id] = new_start
+        return self
 
     def _remove_delays(self) -> None:
         delay_list = self._sfg.find_by_type_name(Delay.type_name())
@@ -223,7 +288,7 @@ class Schema:
             plt.plot(out[0], out[1], color='black')
 
         def _draw_arrow(start, end, name="", laps=0):
-            if end[0] < start[0]: # Wrap around
+            if end[0] < start[0] or laps > 0: # Wrap around
                 plt.plot([start[0], self._schedule_time + 0.2], [start[1], start[1]], color='black')
                 plt.plot([-0.2, end[0]], [end[1], end[1]], color='black')
                 plt.text(self._schedule_time + 0.2, start[1], name, verticalalignment='center')
-- 
GitLab