diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 86eaba91e1a7f736652e51a3ab6b01082bca66d6..c0d998ccad05ab027d78dcb8c99bca91214a489f 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -118,7 +118,7 @@ class Schedule: elif algorithm == "ALAP": self.scheduler.schedule_alap() elif algorithm == "earliest_deadline": - self.scheduler.schedule_earliest_deadline() + self.scheduler.schedule_earliest_deadline([]) elif algorithm == "provided": if start_times is None: raise ValueError("Must provide start_times when using 'provided'") @@ -1246,14 +1246,15 @@ class Scheduler: operation = outport.operation if operation.type_name() == Delay.type_name(): non_schedulable_ops.add(operation.graph_id) - elif operation.graph_id not in sched._start_times: + # elif operation.graph_id not in sched._start_times: + else: sched._start_times[operation.graph_id] = 0 # handle second set in precedence graph (first operations) for outport in prec_list[1]: operation = outport.operation - if operation.graph_id not in sched._start_times: - sched._start_times[operation.graph_id] = 0 + # if operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 # handle the remaining sets for outports in prec_list[2:]: @@ -1304,23 +1305,7 @@ class Scheduler: sched._start_times[operation.graph_id] = op_start_time - # handle output and remove delays - for output in sched._sfg.find_by_type_name(Output.type_name()): - output = cast(Output, output) - source_port = cast(OutputPort, output.inputs[0].signals[0].source) - if source_port.operation.graph_id in non_schedulable_ops: - sched._start_times[output.graph_id] = 0 - else: - if source_port.latency_offset is None: - raise ValueError( - f"Output port {source_port.index} of operation" - f" {source_port.operation.graph_id} has no" - " latency-offset." - ) - sched._start_times[output.graph_id] = sched._start_times[ - source_port.operation.graph_id - ] + cast(int, source_port.latency_offset) - sched._remove_delays() + self._handle_outputs_and_delays(non_schedulable_ops) def schedule_alap(self) -> None: """Schedule the operations using as-late-as-possible scheduling.""" @@ -1344,32 +1329,75 @@ class Scheduler: if not isinstance(outport.operation, Delay): sched.move_operation_alap(outport.operation.graph_id) - def schedule_earliest_deadline(self) -> None: + def schedule_earliest_deadline(self, process_elements: list[Operation]) -> None: """Schedule the operations using earliest deadline scheduling.""" - pass - # sched = self.schedule - # prec_list = sched.sfg.get_precedence_list() - # if len(prec_list) < 2: - # raise ValueError("Empty signal flow graph cannot be scheduled.") - - # # handle the first set in precedence graph (input and delays) - # non_schedulable_ops = set() - # for outport in prec_list[0]: - # operation = outport.operation - # if operation.type_name() == Delay.type_name(): - # non_schedulable_ops.add(operation.graph_id) - # elif operation.graph_id not in sched._start_times: - # sched._start_times[operation.graph_id] = 0 - - # # handle second set in precedence graph (first operations) - # for outport in prec_list[1]: - # operation = outport.operation - # if operation.graph_id not in sched._start_times: - # sched._start_times[operation.graph_id] = 0 - - # # handle the remaining sets - # for outports in prec_list[2:]: - # for outport in outports: - # operation = outport.operation - # if operation.graph_id not in sched._start_times: - # pass + + # ACT BASED ON THE NUMBER OF PEs! + + sched = self.schedule + prec_list = sched.sfg.get_precedence_list() + if len(prec_list) < 2: + raise ValueError("Empty signal flow graph cannot be scheduled.") + + # handle the first set in precedence graph (input and delays) + non_schedulable_ops = set() + for outport in prec_list[0]: + operation = outport.operation + if operation.type_name() == Delay.type_name(): + non_schedulable_ops.add(operation.graph_id) + elif operation.graph_id not in sched._start_times: + sched._start_times[operation.graph_id] = 0 + + # latencies = [outport.operation.latency for outport in prec_list[1]] + current_time = 0 + sorted_outports = sorted( + prec_list[1], key=lambda outport: outport.operation.latency + ) + for outport in sorted_outports: + op = outport.operation + sched._start_times[op.graph_id] = current_time + current_time += 1 + + for outports in prec_list[2:]: + # try all remaining operations for one time step + candidates = [] + while len(candidates) == 0: + for outport in outports: + remaining_op = outport.operation + op_is_ready_to_be_scheduled = True + for op_input in remaining_op.inputs: + source_op = op_input.signals[0].source.operation + source_op_time = sched.start_times[source_op.graph_id] + source_end_time = source_op_time + source_op.latency + if source_end_time > current_time: + op_is_ready_to_be_scheduled = False + if op_is_ready_to_be_scheduled: + candidates.append(remaining_op) + # sched._start_times[remaining_op.graph_id] = current_time + current_time += 1 + sorted_candidates = sorted( + candidates, key=lambda candidate: candidate.latency + ) + # schedule the best candidate to current time + sched._start_times[sorted_candidates[0].graph_id] = current_time + + self._handle_outputs_and_delays(non_schedulable_ops) + + def _handle_outputs_and_delays(self, non_schedulable_ops) -> None: + sched = self.schedule + for output in sched._sfg.find_by_type_name(Output.type_name()): + output = cast(Output, output) + source_port = cast(OutputPort, output.inputs[0].signals[0].source) + if source_port.operation.graph_id in non_schedulable_ops: + sched._start_times[output.graph_id] = 0 + else: + if source_port.latency_offset is None: + raise ValueError( + f"Output port {source_port.index} of operation" + f" {source_port.operation.graph_id} has no" + " latency-offset." + ) + sched._start_times[output.graph_id] = sched._start_times[ + source_port.operation.graph_id + ] + cast(int, source_port.latency_offset) + sched._remove_delays()