Skip to content
Snippets Groups Projects
Commit 9e23924a authored by Oscar Gustafsson's avatar Oscar Gustafsson :bicyclist:
Browse files

Added better plotting and slack computations

parent c200e914
No related branches found
No related tags found
2 merge requests!71Better schedule, including plotting,!69Some basic functionality for plotting a schedule
This commit is part of merge request !71. Comments created here will be created in the context of that merge request.
...@@ -248,13 +248,6 @@ class Operation(GraphComponent, SignalSourceProvider): ...@@ -248,13 +248,6 @@ class Operation(GraphComponent, SignalSourceProvider):
""" """
raise NotImplementedError raise NotImplementedError
@abstractmethod
def set_execution_time(self, latency: int) -> None:
"""Sets the execution time of the operation to the specified integer
value. The execution time cannot be a negative integer.
"""
raise NotImplementedError
@property @property
@abstractmethod @abstractmethod
def execution_time(self) -> int: def execution_time(self) -> int:
...@@ -263,6 +256,14 @@ class Operation(GraphComponent, SignalSourceProvider): ...@@ -263,6 +256,14 @@ class Operation(GraphComponent, SignalSourceProvider):
""" """
raise NotImplementedError raise NotImplementedError
@execution_time.setter
@abstractmethod
def execution_time(self, latency: int) -> None:
"""Sets the execution time of the operation to the specified integer
value. The execution time cannot be a negative integer.
"""
raise NotImplementedError
@abstractmethod @abstractmethod
def get_plot_coordinates(self) -> Tuple[List[List[Number]], List[List[Number]]]: def get_plot_coordinates(self) -> Tuple[List[List[Number]], List[List[Number]]]:
"""Get a tuple constaining coordinates for the two polygons outlining """Get a tuple constaining coordinates for the two polygons outlining
...@@ -556,6 +557,7 @@ class AbstractOperation(Operation, AbstractGraphComponent): ...@@ -556,6 +557,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
new_component.input(i).latency_offset = inp.latency_offset new_component.input(i).latency_offset = inp.latency_offset
for i, outp in enumerate(self.outputs): for i, outp in enumerate(self.outputs):
new_component.output(i).latency_offset = outp.latency_offset new_component.output(i).latency_offset = outp.latency_offset
new_component.execution_time = self._execution_time
return new_component return new_component
def inputs_required_for_output(self, output_index: int) -> Iterable[int]: def inputs_required_for_output(self, output_index: int) -> Iterable[int]:
...@@ -654,8 +656,9 @@ class AbstractOperation(Operation, AbstractGraphComponent): ...@@ -654,8 +656,9 @@ class AbstractOperation(Operation, AbstractGraphComponent):
raise ValueError("No execution time specified.") raise ValueError("No execution time specified.")
return self._execution_time return self._execution_time
def set_execution_time(self, execution_time: int) -> None: @execution_time.setter
assert execution_time >= 0, "Negative execution time entered." def execution_time(self, execution_time: int) -> None:
assert execution_time is None or execution_time >= 0 , "Negative execution time entered."
self._execution_time = execution_time self._execution_time = execution_time
def get_plot_coordinates(self) -> Tuple[List[List[Number]], List[List[Number]]]: def get_plot_coordinates(self) -> Tuple[List[List[Number]], List[List[Number]]]:
......
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
Contains the schema class for scheduling operations in an SFG. Contains the schema class for scheduling operations in an SFG.
""" """
from collections import defaultdict
from typing import Dict, List, Optional from typing import Dict, List, Optional
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
from scipy import interpolate from scipy import interpolate
import sys
from b_asic.signal_flow_graph import SFG from b_asic.signal_flow_graph import SFG
from b_asic.graph_component import GraphID from b_asic.graph_component import GraphID
...@@ -28,7 +30,7 @@ class Schema: ...@@ -28,7 +30,7 @@ class Schema:
"""Construct a Schema from an SFG.""" """Construct a Schema from an SFG."""
self._sfg = sfg self._sfg = sfg
self._start_times = dict() self._start_times = dict()
self._laps = dict() self._laps = defaultdict(lambda: 0)
self._cyclic = cyclic self._cyclic = cyclic
self._resolution = resolution self._resolution = resolution
...@@ -38,12 +40,7 @@ class Schema: ...@@ -38,12 +40,7 @@ class Schema:
raise NotImplementedError( raise NotImplementedError(
f"No algorithm with name: {scheduling_alg} defined.") f"No algorithm with name: {scheduling_alg} defined.")
max_end_time = 0 max_end_time = self._get_max_end_time()
for op_id, op_start_time in self._start_times.items():
op = self._sfg.find_by_id(op_id)
for outport in op.outputs:
max_end_time = max(
max_end_time, op_start_time + outport.latency_offset)
if not self._cyclic: if not self._cyclic:
if schedule_time is None: if schedule_time is None:
...@@ -59,15 +56,68 @@ class Schema: ...@@ -59,15 +56,68 @@ class Schema:
assert op_id in self._start_times, "No operation with the specified op_id in this schema." assert op_id in self._start_times, "No operation with the specified op_id in this schema."
return self._start_times[op_id] return self._start_times[op_id]
def _get_max_end_time(self) -> int:
max_end_time = 0
for op_id, op_start_time in self._start_times.items():
op = self._sfg.find_by_id(op_id)
for outport in op.outputs:
max_end_time = max(
max_end_time, op_start_time + outport.latency_offset)
return max_end_time
def forward_slack(self, op_id: GraphID) -> int: def forward_slack(self, op_id: GraphID) -> int:
raise NotImplementedError assert op_id in self._start_times, "No operation with the specified op_id in this schema."
start_time = self._start_times[op_id]
op = self._sfg.find_by_id(op_id)
slack = sys.maxsize
for output_port in op.outputs:
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
def backward_slack(self, op_id: GraphID) -> int: def backward_slack(self, op_id: GraphID) -> int:
raise NotImplementedError assert op_id in self._start_times, "No operation with the specified op_id in this schema."
start_time = self._start_times[op_id]
op = self._sfg.find_by_id(op_id)
slack = sys.maxsize
for input_port in op.inputs:
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
def print_slacks(self) -> None: def print_slacks(self) -> None:
raise NotImplementedError raise NotImplementedError
def set_schedule_time(self, time: int) -> None:
assert self._get_max_end_time() < time, "New schedule time to short."
self._schedule_time = time
@property
def schedule_time(self) -> int:
return self._schedule_time
def _remove_delays(self) -> None:
delay_list = self._sfg.find_by_type_name(Delay.type_name())
while delay_list:
delay_op = delay_list[0]
delay_input_id = delay_op.input(0).signals[0].graph_id
delay_output_ids = [sig.graph_id for sig in delay_op.output(0).signals]
self._sfg = self._sfg.remove_operation(delay_op.graph_id)
for output_id in delay_output_ids:
self._laps[output_id] += 1 + self._laps[delay_input_id]
del self._laps[delay_input_id]
delay_list = self._sfg.find_by_type_name(Delay.type_name())
def _schedule_asap(self) -> None: def _schedule_asap(self) -> None:
pl = self._sfg.get_precedence_list() pl = self._sfg.get_precedence_list()
...@@ -90,7 +140,6 @@ class Schema: ...@@ -90,7 +140,6 @@ class Schema:
# Schedule the operation if it doesn't have a start time yet. # Schedule the operation if it doesn't have a start time yet.
op_start_time = 0 op_start_time = 0
for inport in op.inputs: for inport in op.inputs:
print(inport.operation.graph_id)
assert len( assert len(
inport.signals) == 1, "Error in scheduling, dangling input port detected." inport.signals) == 1, "Error in scheduling, dangling input port detected."
assert inport.signals[0].source is not None, "Error in scheduling, signal with no source detected." assert inport.signals[0].source is not None, "Error in scheduling, signal with no source detected."
...@@ -114,6 +163,8 @@ class Schema: ...@@ -114,6 +163,8 @@ class Schema:
op_start_time, op_start_time_from_in) op_start_time, op_start_time_from_in)
self._start_times[op.graph_id] = op_start_time self._start_times[op.graph_id] = op_start_time
self._remove_delays()
print(self._laps)
def plot_schedule(self) -> None: def plot_schedule(self) -> None:
def _draw_arrow2(start, end): def _draw_arrow2(start, end):
...@@ -137,10 +188,13 @@ class Schema: ...@@ -137,10 +188,13 @@ class Schema:
out = interpolate.splev(u3, tck) out = interpolate.splev(u3, tck)
plt.plot(out[0], out[1], color='black') plt.plot(out[0], out[1], color='black')
def _draw_arrow(start, end): def _draw_arrow(start, end, name="", laps=0):
if end[0] < start[0]: # Wrap around if end[0] < start[0]: # Wrap around
plt.plot([start[0], self._schedule_time], [start[1], start[1]], color='black') plt.plot([start[0], self._schedule_time + 0.2], [start[1], start[1]], color='black')
plt.plot([0, end[0]], [end[1], end[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')
plt.text(-0.2, end[1], "{}: {}".format(name, laps), verticalalignment='center', horizontalalignment='right')
elif end[0] == start[0]: elif end[0] == start[0]:
_draw_spline([start[0], start[0] + 0.2, start[0] + 0.2, start[0] - 0.2, start[0] - 0.2, start[0]], _draw_spline([start[0], start[0] + 0.2, start[0] + 0.2, start[0] - 0.2, start[0] - 0.2, start[0]],
[start[1], start[1], (start[1] + end[1])/2, (start[1] + end[1])/2, end[1], end[1]]) [start[1], start[1], (start[1] + end[1])/2, (start[1] + end[1])/2, end[1], end[1]])
...@@ -148,15 +202,15 @@ class Schema: ...@@ -148,15 +202,15 @@ class Schema:
_draw_spline([start[0], (start[0] + end[0])/2, (start[0] + end[0])/2, end[0]], _draw_spline([start[0], (start[0] + end[0])/2, (start[0] + end[0])/2, end[0]],
[start[1], start[1], end[1], end[1]]) [start[1], start[1], end[1], end[1]])
def _draw_offset_arrow(start, end, start_offset, end_offset): def _draw_offset_arrow(start, end, start_offset, end_offset, name="", laps=0):
_draw_arrow([start[0] + start_offset[0], start[1] + start_offset[1]], _draw_arrow([start[0] + start_offset[0], start[1] + start_offset[1]],
[end[0] + end_offset[0], end[1] + end_offset[1]]) [end[0] + end_offset[0], end[1] + end_offset[1]], name=name, laps=laps)
ypos = 0.5 ypos = 0.5
ytickpositions = [] ytickpositions = []
yticklabels = [] yticklabels = []
plt.figure() plt.figure()
plt.grid() plt.grid(zorder=0.5)
ypositions = dict() ypositions = dict()
for op_id, op_start_time in self._start_times.items(): for op_id, op_start_time in self._start_times.items():
op = self._sfg.find_by_id(op_id) op = self._sfg.find_by_id(op_id)
...@@ -169,7 +223,7 @@ class Schema: ...@@ -169,7 +223,7 @@ class Schema:
x = np.array(_x) x = np.array(_x)
y = np.array(_y) y = np.array(_y)
plt.plot(x + op_start_time, y + ypos, color='black', linewidth=3, alpha=0.5) plt.plot(x + op_start_time, y + ypos, color='black', linewidth=3, alpha=0.5)
ytickpositions.append(ypos + 0.75) ytickpositions.append(ypos + 0.5)
yticklabels.append(self._sfg.find_by_id(op_id).name) yticklabels.append(self._sfg.find_by_id(op_id).name)
ypositions[op_id] = ypos ypositions[op_id] = ypos
ypos += 1.5 ypos += 1.5
...@@ -181,15 +235,18 @@ class Schema: ...@@ -181,15 +235,18 @@ class Schema:
for output_port in op.outputs: for output_port in op.outputs:
for output_signal in output_port.signals: for output_signal in output_port.signals:
dest_op = output_signal.destination.operation dest_op = output_signal.destination.operation
if dest_op.type_name() != Delay.type_name() and dest_op.type_name() != Output.type_name(): if dest_op.type_name() and dest_op.type_name() != Output.type_name():
dest_start_time = self._start_times[dest_op.graph_id] dest_start_time = self._start_times[dest_op.graph_id]
dest_ypos = ypositions[dest_op.graph_id] dest_ypos = ypositions[dest_op.graph_id]
dest_in_coords, _ = output_signal.destination.operation.get_io_coordinates() dest_in_coords, _ = output_signal.destination.operation.get_io_coordinates()
_draw_offset_arrow(out_coords[output_port.index], _draw_offset_arrow(out_coords[output_port.index],
dest_in_coords[output_signal.destination.index], dest_in_coords[output_signal.destination.index],
[op_start_time, source_ypos], [op_start_time, source_ypos],
[dest_start_time, dest_ypos]) [dest_start_time, dest_ypos], name=op_id,
laps=self._laps[output_signal.graph_id])
plt.yticks(ytickpositions, yticklabels) plt.yticks(ytickpositions, yticklabels)
plt.axis([0, self._schedule_time, 0, ypos]) plt.axis([-1, self._schedule_time+1, 0, ypos])
plt.plot([0, 0], [0, ypos], linestyle='--', color='black')
plt.plot([self._schedule_time, self._schedule_time], [0, ypos], linestyle='--', color='black')
plt.show() plt.show()
...@@ -10,6 +10,7 @@ from io import StringIO ...@@ -10,6 +10,7 @@ from io import StringIO
from queue import PriorityQueue from queue import PriorityQueue
import itertools as it import itertools as it
from graphviz import Digraph from graphviz import Digraph
import re
from b_asic.port import SignalSourceProvider, OutputPort from b_asic.port import SignalSourceProvider, OutputPort
from b_asic.operation import Operation, AbstractOperation, ResultKey, DelayMap, MutableResultMap, MutableDelayMap from b_asic.operation import Operation, AbstractOperation, ResultKey, DelayMap, MutableResultMap, MutableDelayMap
...@@ -345,14 +346,9 @@ class SFG(AbstractOperation): ...@@ -345,14 +346,9 @@ class SFG(AbstractOperation):
Keyword arguments: Keyword arguments:
type_name: The type_name of the desired components. type_name: The type_name of the desired components.
""" """
i = self.id_number_offset + 1 reg = "{}[0-9]+".format(type_name)
components = [] p = re.compile(reg)
found_comp = self.find_by_id(type_name + str(i)) components = [val for key, val in self._components_by_id.items() if p.match(key)]
while found_comp is not None:
components.append(found_comp)
i += 1
found_comp = self.find_by_id(type_name + str(i))
return components return components
def find_by_id(self, graph_id: GraphID) -> Optional[GraphComponent]: def find_by_id(self, graph_id: GraphID) -> Optional[GraphComponent]:
...@@ -652,7 +648,7 @@ class SFG(AbstractOperation): ...@@ -652,7 +648,7 @@ class SFG(AbstractOperation):
def set_execution_time_of_type(self, type_name: TypeName, execution_time: int) -> None: def set_execution_time_of_type(self, type_name: TypeName, execution_time: int) -> None:
"""Set the execution time of all components with the given type name.""" """Set the execution time of all components with the given type name."""
for op in self.find_by_type_name(type_name): for op in self.find_by_type_name(type_name):
op.set_execution_time(execution_time) op.execution_time = execution_time
def set_latency_offsets_of_type(self, type_name: TypeName, latency_offsets: Dict[str, int]) -> None: def set_latency_offsets_of_type(self, type_name: TypeName, latency_offsets: Dict[str, int]) -> None:
"""Set the latency offset of all components with the given type name.""" """Set the latency offset of all components with the given type name."""
...@@ -692,9 +688,10 @@ class SFG(AbstractOperation): ...@@ -692,9 +688,10 @@ class SFG(AbstractOperation):
assert original_component not in self._original_components_to_new, "Tried to add duplicate SFG component" assert original_component not in self._original_components_to_new, "Tried to add duplicate SFG component"
new_component = original_component.copy_component() new_component = original_component.copy_component()
self._original_components_to_new[original_component] = new_component self._original_components_to_new[original_component] = new_component
new_id = self._graph_id_generator.next_id(new_component.type_name()) if not new_component.graph_id or new_component.graph_id in self._components_by_id:
new_component.graph_id = new_id new_id = self._graph_id_generator.next_id(new_component.type_name())
self._components_by_id[new_id] = new_component new_component.graph_id = new_id
self._components_by_id[new_component.graph_id] = new_component
self._components_by_name[new_component.name].append(new_component) self._components_by_name[new_component.name].append(new_component)
return new_component return new_component
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment