diff --git a/b_asic/architecture.py b/b_asic/architecture.py index 2edcd3525d483b3e67b356a9498db04734d1fdf6..f33300ae5f0f1b51614d969116797b4707e11acb 100644 --- a/b_asic/architecture.py +++ b/b_asic/architecture.py @@ -189,8 +189,8 @@ class Resource(HardwareBlock): def _struct_def(self) -> str: # Create GraphViz struct - inputs = [f"in{i}" for i in range(self._input_count)] - outputs = [f"out{i}" for i in range(self._output_count)] + inputs = [f"in{i}" for i in range(self.input_count)] + outputs = [f"out{i}" for i in range(self.output_count)] ret = "" if inputs: in_strs = [f"<{in_str}> {in_str}" for in_str in inputs] diff --git a/b_asic/resources.py b/b_asic/resources.py index ef1204ab6032a3819b772e09bcda052df8996f36..15a3b0da8009a096326495ed82e1202cf2c3f630 100644 --- a/b_asic/resources.py +++ b/b_asic/resources.py @@ -243,8 +243,8 @@ class _ForwardBackwardTable: # Insert all processes (one per time-slot) to the table input # TODO: "Input each variable at the time step corresponding to the beginning of # its lifetime. If multiple variables are input in a given cycle, these - # are allocated to multple registers such that the variable with the - # longest lifetime is allocated to the inital register and the other + # are allocated to multiple registers such that the variable with the + # longest lifetime is allocated to the initial register and the other # variables are allocated to consecutive registers in decreasing order # of lifetime." -- K. Parhi for mv in collection: @@ -262,7 +262,7 @@ class _ForwardBackwardTable: self._do_forward_allocation() else: self._do_single_backward_allocation() - forward = not (forward) + forward = not forward def _forward_backward_is_complete(self) -> bool: s = {proc for e in self.table for proc in e.outputs} @@ -355,7 +355,7 @@ class _ForwardBackwardTable: return len(self.table) def __str__(self): - # ANSI escape codes for coloring in the forward-backward table stirng + # ANSI escape codes for coloring in the forward-backward table string GREEN_BACKGROUND_ANSI = "\u001b[42m" BROWN_BACKGROUND_ANSI = "\u001b[43m" RESET_BACKGROUND_ANSI = "\033[0m" @@ -391,8 +391,8 @@ class _ForwardBackwardTable: # Input column inputs_str = '' - for input in entry.inputs: - inputs_str += input.name + ',' + for input_ in entry.inputs: + inputs_str += input_.name + ',' if inputs_str: inputs_str = inputs_str[:-1] res += f'{inputs_str:^{input_col_w-1}}|' @@ -440,7 +440,7 @@ class ProcessCollection: The scheduling time associated with this :class:`~b_asic.resources.ProcessCollection`. cyclic : bool, default: False - Whether the processes operates cyclically, i.e., if time + Whether the processes operate cyclically, i.e., if time .. math:: t = t \bmod T_{\textrm{schedule}}. """ @@ -520,9 +520,9 @@ class ProcessCollection: If the ``ax`` parameter is not specified, a new Matplotlib figure is created. Raises :class:`KeyError` if any :class:`~b_asic.process.Process` lifetime - excedes this :class:`~b_asic.resources.ProcessCollection` schedule time, + exceedes this :class:`~b_asic.resources.ProcessCollection` schedule time, unless ``allow_excessive_lifetimes`` is specified. In that case, - :class:`~b_asic.process.Process` objects whose lifetime exceed the scheudle + :class:`~b_asic.process.Process` objects whose lifetime exceed the schedule time are drawn using the B-ASIC warning color. Parameters @@ -548,8 +548,8 @@ class ProcessCollection: axes object. Defaults to None, which renders all processes on separate rows. This option is useful when drawing cell assignments. allow_excessive_lifetimes : bool, default False - If set to true, the plot method allows ploting collections of variables with - a greater lifetime than the schedule time. + If set to true, the plot method allows plotting collections of variables + with a longer lifetime than the schedule time. Returns ------- @@ -568,8 +568,8 @@ class ProcessCollection: if not allow_excessive_lifetimes and max_execution_time > self._schedule_time: # Schedule time needs to be greater than or equal to the maximum process # lifetime - raise KeyError( - f'Error: Schedule time: {self._schedule_time} < Max execution' + raise ValueError( + f'Schedule time: {self._schedule_time} < Max execution' f' time: {max_execution_time}' ) @@ -1033,7 +1033,7 @@ class ProcessCollection: Perform assignment of the processes in this collection using graph coloring. Two or more processes can share a single resource if, and only if, they have no - overlaping execution time. + overlapping execution time. Parameters ---------- @@ -1042,7 +1042,7 @@ class ProcessCollection: :func:`networkx.algorithms.coloring.greedy_color`. coloring : dict, optional An optional graph coloring, dictionary with Process and its associated color - (int). If a graph coloring is not provided throught this parameter, one will + (int). If a graph coloring is not provided through this parameter, one will be created when calling this method. Returns @@ -1406,26 +1406,52 @@ class ProcessCollection: ------- int """ + return max(self.read_port_accesses().values()) + + def read_port_accesses(self) -> Dict[int, int]: reads = [] for process in self._collection: reads.extend( set(read_time % self.schedule_time for read_time in process.read_times) ) - count = Counter(reads) - return max(count.values()) + return dict(sorted(Counter(reads).items())) def write_ports_bound(self) -> int: """ - Get the write port lower-bound (maximum number of concurrent writes) of this + Get the total port lower-bound (maximum number of concurrent writes) of this :class:`~b_asic.resources.ProcessCollection`. Returns ------- int """ - writes = [process.start_time for process in self._collection] - count = Counter(writes) - return max(count.values()) + return max(self.write_port_accesses().values()) + + def write_port_accesses(self) -> Dict[int, int]: + writes = [ + process.start_time % self.schedule_time for process in self._collection + ] + return dict(sorted(Counter(writes).items())) + + def total_ports_bound(self) -> int: + """ + Get the total port lower-bound (maximum number of concurrent reads and writes). + + Returns + ------- + int + """ + return max(self.total_port_accesses().values()) + + def total_port_accesses(self) -> Dict[int, int]: + accesses = [ + process.start_time % self.schedule_time for process in self._collection + ] + for process in self._collection: + accesses.extend( + set(read_time % self.schedule_time for read_time in process.read_times) + ) + return dict(sorted(Counter(accesses).items())) def from_name(self, name: str): """ @@ -1440,7 +1466,7 @@ class ProcessCollection: The name of the process to retrieve. """ name_to_proc = {p.name: p for p in self.collection} - if name not in name_to_proc: - raise KeyError(f'{name} not in {self}') - else: + if name in name_to_proc: return name_to_proc[name] + else: + raise KeyError(f'{name} not in {self}') diff --git a/examples/fivepointwinograddft.py b/examples/fivepointwinograddft.py index 2d26c4de2acef020683d28dc8304a03a8cab05ef..798d3bc34b8a4af34335516df8be0217e374fe1d 100644 --- a/examples/fivepointwinograddft.py +++ b/examples/fivepointwinograddft.py @@ -6,6 +6,9 @@ Five-point Winograd DFT from math import cos, pi, sin +import matplotlib.pyplot as plt +import networkx as nx + from b_asic.architecture import Architecture, Memory, ProcessingElement from b_asic.core_operations import AddSub, Butterfly, ConstantMultiplication from b_asic.schedule import Schedule @@ -73,98 +76,58 @@ sfg.set_execution_time_of_type(Butterfly.type_name(), 1) schedule = Schedule(sfg, cyclic=True) schedule.show() +# %% # Reschedule to only use one AddSub and one multiplier -schedule.move_operation('out2', 4) -schedule.move_operation('out3', 4) -schedule.move_operation('out4', 3) -schedule.move_operation('out5', 6) -schedule.set_schedule_time(15) -schedule.move_operation('out5', 3) -schedule.move_operation('out4', 5) -schedule.move_operation('out3', 3) -schedule.move_operation('out2', 2) -schedule.move_operation('out1', 2) -schedule.move_operation('bfly4', 16) -schedule.move_operation('bfly3', 14) -schedule.move_operation('bfly2', 14) -schedule.move_operation('addsub3', 17) -schedule.move_operation('addsub5', 15) -schedule.move_operation('addsub2', 14) -schedule.move_operation('cmul5', 15) -schedule.move_operation('cmul3', 15) -schedule.move_operation('cmul1', 14) -schedule.move_operation('addsub1', 2) -schedule.move_operation('cmul2', 16) -schedule.move_operation('addsub4', 15) -schedule.move_operation('out1', 15) -schedule.move_operation('addsub1', 13) -schedule.move_operation('cmul4', 18) -schedule.move_operation('bfly1', 14) -schedule.move_operation('bfly6', 14) -schedule.move_operation('bfly5', 14) -schedule.move_operation('in5', 1) -schedule.move_operation('in3', 2) -schedule.move_operation('in2', 3) -schedule.move_operation('in4', 4) -schedule.move_operation('bfly6', -5) -schedule.move_operation('bfly5', -6) -schedule.move_operation('addsub1', -1) -schedule.move_operation('bfly1', -1) -schedule.move_operation('bfly1', -4) -schedule.move_operation('addsub1', -5) -schedule.move_operation('addsub4', -6) -schedule.move_operation('cmul4', -10) -schedule.move_operation('cmul2', -7) -schedule.move_operation('cmul1', -2) -schedule.move_operation('cmul3', -6) -schedule.move_operation('cmul5', -5) -schedule.move_operation('cmul1', -3) -schedule.move_operation('cmul5', -1) -schedule.set_schedule_time(13) -schedule.move_operation('bfly5', -6) -schedule.move_operation('bfly6', -1) -schedule.move_operation('cmul4', -6) -schedule.move_operation('addsub1', 4) -schedule.move_operation('cmul3', 4) -schedule.move_operation('cmul1', 3) -schedule.move_operation('bfly1', 3) -schedule.move_operation('cmul2', 5) +schedule.set_schedule_time(10) +schedule.move_operation('out5', 12) +schedule.move_operation('out4', 11) +schedule.move_operation('out3', 10) +schedule.move_operation('out2', 9) +schedule.move_operation('out1', 12) +schedule.move_operation('bfly4', 10) +schedule.move_operation('bfly3', 9) +schedule.move_operation('bfly2', 7) +schedule.move_operation('addsub5', 5) +schedule.move_operation('addsub3', 5) +schedule.move_operation('addsub2', 5) schedule.move_operation('cmul5', 4) +schedule.move_operation('cmul3', 4) +schedule.move_operation('cmul1', 5) +schedule.move_operation('addsub1', 6) +schedule.move_operation('cmul2', 6) schedule.move_operation('addsub4', 4) -schedule.set_schedule_time(10) +schedule.move_operation('bfly1', 4) +schedule.move_operation('cmul4', 6) +schedule.move_operation('bfly6', 4) +schedule.move_operation('bfly5', 4) +schedule.move_operation('in2', 1) +schedule.move_operation('in3', 2) +schedule.move_operation('in4', 3) +schedule.move_operation('in5', 4) +schedule.move_operation('bfly6', -1) +schedule.move_operation('bfly4', 1) +schedule.move_operation('cmul3', 1) +schedule.move_operation('cmul5', 1) +schedule.move_operation('bfly1', 1) schedule.move_operation('addsub1', -1) -schedule.move_operation('cmul4', 1) -schedule.move_operation('addsub4', -1) +schedule.move_operation('cmul2', -3) +schedule.move_operation('cmul4', -2) schedule.move_operation('cmul5', -1) -schedule.move_operation('cmul2', -2) -schedule.move_operation('bfly6', -4) +schedule.move_operation('addsub5', 1) +schedule.move_operation('addsub2', 2) +schedule.move_operation('cmul1', 1) schedule.move_operation('bfly1', -1) schedule.move_operation('addsub1', -1) -schedule.move_operation('cmul1', -1) -schedule.move_operation('cmul2', -3) +schedule.move_operation('bfly3', -1) +schedule.move_operation('cmul3', -1) +schedule.move_operation('cmul5', 1) +schedule.move_operation('addsub3', -1) +schedule.move_operation('addsub5', -1) schedule.move_operation('addsub2', -1) schedule.move_operation('bfly2', -1) -schedule.move_operation('bfly1', -1) -schedule.move_operation('cmul1', -1) -schedule.move_operation('addsub2', -1) -schedule.move_operation('addsub4', -1) -schedule.move_operation('addsub4', -3) -schedule.move_operation('cmul4', -1) -schedule.move_operation('bfly1', -2) -schedule.move_operation('cmul2', -1) -schedule.move_operation('cmul1', -2) -schedule.move_operation('cmul5', -4) -schedule.move_operation('cmul1', 1) -schedule.move_operation('cmul3', -5) -schedule.move_operation('cmul5', 2) -schedule.move_operation('addsub3', -3) -schedule.move_operation('addsub1', -3) -schedule.move_operation('addsub2', -1) -schedule.move_operation('addsub3', -4) -schedule.move_operation('bfly2', -2) -schedule.move_operation('addsub5', -3) schedule.move_operation('bfly3', -2) +schedule.move_operation('bfly4', -1) schedule.show() # Extract memory variables and operation executions @@ -201,9 +164,12 @@ for i, mem in enumerate(mem_vars_set): memory.assign("left_edge") memory.show_content(title=f"Assigned {memory.entity_name}") +fig, ax = plt.subplots() +fig.suptitle('Exclusion graph based on ports') +nx.draw(mem_vars.create_exclusion_graph_from_ports(1, 1, 2), ax=ax) arch = Architecture( - {addsub, butterfly, multiplier, pe_in, pe_out}, + [addsub, butterfly, multiplier, pe_in, pe_out], memories, direct_interconnects=direct, )