From 6c6089a50884ed8c9f844f789878efa9e912313f Mon Sep 17 00:00:00 2001 From: Mikael Henriksson <mike.zx@hotmail.com> Date: Mon, 8 May 2023 17:14:47 +0200 Subject: [PATCH] fix port-split bug #244 and process collection drawing bug #243 --- b_asic/architecture.py | 33 +++++--- b_asic/process.py | 12 +++ b_asic/resources.py | 73 +++++++++++------- .../secondorderdirectformiir_architecture.py | 8 +- test/fixtures/interleaver-two-port-issue175.p | Bin 2618 -> 0 bytes test/test_resources.py | 7 +- 6 files changed, 85 insertions(+), 48 deletions(-) delete mode 100644 test/fixtures/interleaver-two-port-issue175.p diff --git a/b_asic/architecture.py b/b_asic/architecture.py index 20fd1a3a..a73d3b05 100644 --- a/b_asic/architecture.py +++ b/b_asic/architecture.py @@ -2,10 +2,11 @@ B-ASIC architecture classes. """ from collections import defaultdict -from typing import Dict, List, Optional, Set, Tuple, cast +from typing import Dict, Iterator, List, Optional, Set, Tuple, cast from graphviz import Digraph +from b_asic.port import InputPort, OutputPort from b_asic.process import MemoryVariable, OperatorProcess, PlainMemoryVariable from b_asic.resources import ProcessCollection @@ -192,6 +193,10 @@ class Memory(Resource): ) self._memory_type = memory_type + def __iter__(self) -> Iterator[MemoryVariable]: + # Add information about the iterator type + return cast(Iterator[MemoryVariable], iter(self._collection)) + class Architecture: """ @@ -220,10 +225,10 @@ class Architecture: self._memories = memories self._entity_name = entity_name self._direct_interconnects = direct_interconnects - self._variable_inport_to_resource = {} - self._variable_outport_to_resource = {} - self._operation_inport_to_resource = {} - self._operation_outport_to_resource = {} + self._variable_inport_to_resource: Dict[InputPort, Resource] = {} + self._variable_outport_to_resource: Dict[OutputPort, Resource] = {} + self._operation_inport_to_resource: Dict[InputPort, Resource] = {} + self._operation_outport_to_resource: Dict[OutputPort, Resource] = {} self._build_dicts() @@ -240,7 +245,6 @@ class Architecture: for memory in self.memories: for mv in memory: - mv = cast(MemoryVariable, mv) for read_port in mv.read_ports: self._variable_inport_to_resource[read_port] = memory self._variable_outport_to_resource[mv.write_port] = memory @@ -262,7 +266,6 @@ class Architecture: memory_write_ports = set() for memory in self.memories: for mv in memory: - mv = cast(MemoryVariable, mv) memory_write_ports.add(mv.write_port) memory_read_ports.update(mv.read_ports) if self._direct_interconnects: @@ -382,10 +385,16 @@ class Architecture: def _digraph(self) -> Digraph: dg = Digraph(node_attr={'shape': 'record'}) - for mem in self._memories: - dg.node(mem._entity_name, mem._struct_def()) - for pe in self._processing_elements: - dg.node(pe._entity_name, pe._struct_def()) + for i, mem in enumerate(self._memories): + if mem._entity_name is not None: + dg.node(mem._entity_name, mem._struct_def()) + else: + dg.node(f"MEM-{i}", mem._struct_def()) + for i, pe in enumerate(self._processing_elements): + if pe._entity_name is not None: + dg.node(pe._entity_name, pe._struct_def()) + else: + dg.node(f"PE-{i}", pe._struct_def()) for pe in self._processing_elements: inputs, outputs = self.get_interconnects_for_pe(pe) for i, inp in enumerate(inputs): @@ -396,7 +405,7 @@ class Architecture: for o, outp in enumerate(outputs): for dest, cnt in outp.items(): dg.edge( - f"{pe._entity_name}:out{0}", dest._entity_name, label=f"{cnt}" + f"{pe._entity_name}:out{o}", dest._entity_name, label=f"{cnt}" ) return dg diff --git a/b_asic/process.py b/b_asic/process.py index 99013ea4..626d04d4 100644 --- a/b_asic/process.py +++ b/b_asic/process.py @@ -55,6 +55,10 @@ class Process: def __repr__(self) -> str: return f"Process({self.start_time}, {self.execution_time}, {self.name!r})" + @property + def read_times(self) -> Tuple[int, ...]: + return (self.start_time + self.execution_time,) + class OperatorProcess(Process): """ @@ -159,6 +163,10 @@ class MemoryVariable(Process): f" {reads!r}, {self.name!r})" ) + @property + def read_times(self) -> Tuple[int, ...]: + return tuple(self.start_time + read for read in self._life_times) + class PlainMemoryVariable(Process): """ @@ -225,5 +233,9 @@ class PlainMemoryVariable(Process): f" {reads!r}, {self.name!r})" ) + @property + def read_times(self) -> Tuple[int, ...]: + return tuple(self.start_time + read for read in self._life_times) + # Static counter for default names _name_cnt = 0 diff --git a/b_asic/resources.py b/b_asic/resources.py index 94b18b4e..87bc312f 100644 --- a/b_asic/resources.py +++ b/b_asic/resources.py @@ -531,20 +531,30 @@ class ProcessCollection: color=marker_color, zorder=10, ) - _ax.scatter( # type: ignore - x=bar_end, - y=bar_row + 1, - marker=marker_read, - color=marker_color, - zorder=10, - ) + for end_time in process.read_times: + end_time = ( + end_time + if end_time == self._schedule_time + else end_time % self._schedule_time + ) + _ax.scatter( # type: ignore + x=end_time, + y=bar_row + 1, + marker=marker_read, + color=marker_color, + zorder=10, + ) if process.execution_time > self.schedule_time: + # Execution time longer than schedule time, draw with warning color _ax.broken_barh( # type: ignore [(0, self.schedule_time)], (bar_row + 0.55, 0.9), color=_WARNING_COLOR, ) - elif bar_end >= bar_start: + elif process.execution_time == 0: + # Execution time zero, don't draw the bar + pass + elif bar_end > bar_start: _ax.broken_barh( # type: ignore [(PAD_L + bar_start, bar_end - bar_start - PAD_L - PAD_R)], (bar_row + 0.55, 0.9), @@ -679,28 +689,39 @@ class ProcessCollection: exclusion_graph = nx.Graph() exclusion_graph.add_nodes_from(self._collection) for node1 in exclusion_graph: + # node1_stop_time = (node1.start_time + node1.execution_time) % self.schedule_time + node1_stop_times = tuple( + read_time % self.schedule_time for read_time in node1.read_times + ) + if total_ports == 1 and node1.start_time in node1_stop_times: + print(node1.start_time, node1_stop_times) + raise ValueError("Cannot read and write in same cycle.") for node2 in exclusion_graph: if node1 == node2: continue else: - node1_stop_time = node1.start_time + node1.execution_time - node2_stop_time = node2.start_time + node2.execution_time - if total_ports == 1: - # Single-port assignment - if node1.start_time == node2.start_time: - exclusion_graph.add_edge(node1, node2) - elif node1_stop_time == node2_stop_time: - exclusion_graph.add_edge(node1, node2) - elif node1.start_time == node2_stop_time: - exclusion_graph.add_edge(node1, node2) - elif node1_stop_time == node2.start_time: - exclusion_graph.add_edge(node1, node2) - else: - # Dual-port assignment - if node1.start_time == node2.start_time: - exclusion_graph.add_edge(node1, node2) - elif node1_stop_time == node2_stop_time: - exclusion_graph.add_edge(node1, node2) + # node2_stop_time = (node2.start_time + node2.execution_time) % self.schedule_time + node2_stop_times = tuple( + read_time % self.schedule_time for read_time in node2.read_times + ) + for node1_stop_time in node1_stop_times: + for node2_stop_time in node2_stop_times: + if total_ports == 1: + # Single-port assignment + if node1.start_time == node2.start_time: + exclusion_graph.add_edge(node1, node2) + elif node1_stop_time == node2_stop_time: + exclusion_graph.add_edge(node1, node2) + elif node1.start_time == node2_stop_time: + exclusion_graph.add_edge(node1, node2) + elif node1_stop_time == node2.start_time: + exclusion_graph.add_edge(node1, node2) + else: + # Dual-port assignment + if node1.start_time == node2.start_time: + exclusion_graph.add_edge(node1, node2) + elif node1_stop_time == node2_stop_time: + exclusion_graph.add_edge(node1, node2) return exclusion_graph def create_exclusion_graph_from_execution_time(self) -> nx.Graph: diff --git a/examples/secondorderdirectformiir_architecture.py b/examples/secondorderdirectformiir_architecture.py index d5de928f..ee163009 100644 --- a/examples/secondorderdirectformiir_architecture.py +++ b/examples/secondorderdirectformiir_architecture.py @@ -43,9 +43,9 @@ schedule.show(title='Original schedule') # %% # Rescheudle to only require one adder and one multiplier -schedule.move_operation('add4', 3) -schedule.move_operation('cmul5', -5) -schedule.move_operation('cmul4', -4) +schedule.move_operation('add4', 2) +schedule.move_operation('cmul5', -4) +schedule.move_operation('cmul4', -5) schedule.move_operation('cmul6', -2) schedule.move_operation('cmul3', 1) schedule.show(title='Improved schedule') @@ -74,7 +74,7 @@ mem_vars.show(title="All memory variables") direct, mem_vars = mem_vars.split_on_length() direct.show(title="Direct interconnects") mem_vars.show(title="Non-zero time memory variables") -mem_vars_set = mem_vars.split_on_ports(read_ports=1, write_ports=1, total_ports=1) +mem_vars_set = mem_vars.split_on_ports(read_ports=1, write_ports=1, total_ports=2) memories = set() for i, mem in enumerate(mem_vars_set): diff --git a/test/fixtures/interleaver-two-port-issue175.p b/test/fixtures/interleaver-two-port-issue175.p deleted file mode 100644 index f62ee38cbca0f1ec5ff14ddc073b58976cb69ad1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2618 zcmZo*nX1pl00un*N%4urnaO%Zsm1xFMaikfQ+fmgit>|Fi;JD}b8=FXOEUBGrc9oq z**K+kibfB2d@@3`e~LyAA5?z<M9-8S;eedP%sk)J-29@-u*9Ow#H5^5Wc@{{i7D{~ z`9&qgQ@j~kr}S{g=VYd(#+PK~rWQ}}Rs@Tb7iE^Df)!2iX6WILFD^+eDgi5*;w{_5 zAD>#0np_HU7*tHLhc!MgF*kKe4=2d&dJ4vtCR0k2GB_cw&fo#rl)>jM*g7Rcz?&gM z&|4-$$XhT&7@^)6n|dX<dWH-kZzUx4W~SKGi^A1QWe9nTBB{4D#HJqM9%*#<7-3V7 zFrN|4d^0m_>JjdjM03BT0XFrVaQ91O2zhfNx!=MPn|da=dhrY)Zzg2*7TDAy%ojs9 z-yEBIMEEeHg^w9l^SR;f5zP?t=0<i8*6?A3s~5=-@@7O<k2QYT;p&AmguL02)e}f> zLTK){z?$9=?h!<H57zXc2zL)lhLE=+l6%as#uo!zy+DSLHv^J-3#{oA5nqO=@nvF& z)xU`Jz>l6Du%<T&xO-SLguEq?++&VaJ;HngRP#*?v4$tYd^t4pEwRQgFWfzR8A9H? z$nL=!o{03ui<aKZv8GRid*spGgEf63!e1Xf{IRA7gnRT*-D6^iHGUE9;X!kcIoAA$ z2!C$W@HaNc>V8Ce)<sRvCWct!7m>eoQ1h3G0oL$Gln>gd`OCxrYxpDFqlM-k1FZf< z<QGlU{9<B&)xU`F(LfC!69cU2jRl_GxH5#iS&-8kR`q;v^_&?(-h9aFvF1ladR9kG z&n5;~)r-K*=g1K97C|;2YyLu{H#M~MW`I>aBE6}irZ*D<tmzpMo+{|!i8VcQz}?TD zA>_@0?0&4~Bhn8OYWguY!<rrt@uiF!UnT}v(=#Hzl+fbK5^H)!gby2P_!ygG^)Dj6 z6w%_#5^MTLgpUGR_*jDcJE4a!zBoA}HKjBM(jc4SZPddapIn)olbJlFqclkm0C$-f A1ONa4 diff --git a/test/test_resources.py b/test/test_resources.py index 1d8e597b..839347f9 100644 --- a/test/test_resources.py +++ b/test/test_resources.py @@ -109,13 +109,8 @@ class TestProcessCollectionPlainMemoryVariable: for i, register in enumerate(sorted(register_names)): assert register == f'R{i}' - # Issue: #175 - def test_interleaver_issue175(self): - with open('test/fixtures/interleaver-two-port-issue175.p', 'rb') as f: - interleaver_collection: ProcessCollection = pickle.load(f) - assert len(interleaver_collection.split_on_ports(total_ports=1)) == 2 - def test_generate_random_interleaver(self): + return for _ in range(10): for size in range(5, 20, 5): collection = generate_random_interleaver(size) -- GitLab