diff --git a/b_asic/architecture.py b/b_asic/architecture.py index 559cb48564468e0bbc4d5ebd3aa4d0bf14867b0f..444ea6c3c3b47548c216e12803c9b73b4c566652 100644 --- a/b_asic/architecture.py +++ b/b_asic/architecture.py @@ -170,7 +170,7 @@ class Resource(HardwareBlock): return iter(self._collection) def _digraph(self) -> Digraph: - dg = Digraph(node_attr={'shape': 'record'}) + dg = Digraph(node_attr={'shape': 'box'}) dg.node( self.entity_name, self._struct_def(), style='filled', fillcolor=self._color ) @@ -190,15 +190,16 @@ class Resource(HardwareBlock): # 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)] - ret = "" + ret = '<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">' + table_width = max(len(inputs), len(outputs), 1) if inputs: - in_strs = [f"<{in_str}> {in_str}" for in_str in inputs] - ret += f"{{{'|'.join(in_strs)}}}|" - ret += f"<{self.entity_name}> {self.entity_name}{self._info()}" + in_strs = [f'<TD COLSPAN="{int(table_width/len(inputs))}" PORT="{in_str}">{in_str}</TD>' for in_str in inputs] + ret += f"<TR>{''.join(in_strs)}</TR>" + ret += f'<TR><TD COLSPAN="{table_width}">{self.entity_name}{self._info()}</TD></TR>' if outputs: - out_strs = [f"<{out_str}> {out_str}" for out_str in outputs] - ret += f"|{{{'|'.join(out_strs)}}}" - return "{" + ret + "}" + out_strs = [f'<TD COLSPAN="{int(table_width/len(outputs))}" PORT="{out_str}">{out_str}</TD>' for out_str in outputs] + ret += f"<TR>{''.join(out_strs)}</TR>" + return ret + "</TABLE>>" def _info(self): return "" @@ -825,7 +826,7 @@ of :class:`~b_asic.architecture.ProcessingElement` colored : bool, default: True Whether to color the nodes. """ - dg = Digraph(node_attr={'shape': 'record'}) + dg = Digraph(node_attr={'shape': 'box'}) dg.attr(splines=splines) # Setup colors pe_color = ( @@ -869,6 +870,7 @@ of :class:`~b_asic.architecture.ProcessingElement` mem._struct_def(), style='filled', fillcolor=memory_color, + fontname='Times New Roman', ) label = "Memory" if len(self._memories) <= 1 else "Memories" c.attr(label=label, bgcolor=memory_cluster_color) @@ -880,6 +882,7 @@ of :class:`~b_asic.architecture.ProcessingElement` pe._struct_def(), style='filled', fillcolor=pe_color, + fontname='Times New Roman', ) label = ( "Processing element" @@ -896,6 +899,7 @@ of :class:`~b_asic.architecture.ProcessingElement` pe._struct_def(), style='filled', fillcolor=io_color, + fontname='Times New Roman', ) c.attr(label="IO", bgcolor=io_cluster_color) else: @@ -906,6 +910,7 @@ of :class:`~b_asic.architecture.ProcessingElement` pe._struct_def(), style='filled', fillcolor=io_color, + fontname='Times New Roman', ) else: for i, mem in enumerate(self._memories): @@ -914,6 +919,7 @@ of :class:`~b_asic.architecture.ProcessingElement` mem._struct_def(), style='filled', fillcolor=memory_color, + fontname='Times New Roman', ) for i, pe in enumerate(self._processing_elements): dg.node( @@ -954,13 +960,13 @@ of :class:`~b_asic.architecture.ProcessingElement` if len(source_list) > 1: # Create GraphViz struct for multiplexer inputs = [f"in{i}" for i in range(len(source_list))] - ret = "" - in_strs = [f"<{in_str}> {in_str}" for in_str in inputs] - ret += f"{{{'|'.join(in_strs)}}}|" + ret = '<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">' + in_strs = [f'<TD COLSPAN="1" PORT="{in_str}">{in_str}</TD>' for in_str in inputs] + ret += f"<TR>{''.join(in_strs)}</TR>" name = f"{destination.replace(':', '_')}_mux" - ret += f"<{name}> {name}" - ret += "|<out0> out0" - dg.node(name, "{" + ret + "}", style='filled', fillcolor=mux_color) + ret += f'<TR><TD COLSPAN="{len(inputs)}" PORT="{name}">{name}</TD></TR>' + ret += f'<TR><TD COLSPAN="{len(inputs)}" PORT="out0">out0</TD></TR>' + dg.node(name, ret + "</TABLE>>", style='filled', fillcolor=mux_color, fontname='Times New Roman') # Add edge from mux output to resource input dg.edge(f"{name}:out0", destination) diff --git a/test/test_architecture.py b/test/test_architecture.py index d2917d08bde5b0952a94a6c9108e097d2d0c9c03..9083a2a0202e3d42191d7cb23f399a4d3c9089d3 100644 --- a/test/test_architecture.py +++ b/test/test_architecture.py @@ -95,11 +95,14 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule): output_pe, ] s = ( - 'digraph {\n\tnode [shape=record]\n\t' - + "adder" - + ' [label="{{<in0> in0|<in1> in1}|' - + '<adder> adder' - + '|{<out0> out0}}" fillcolor="#00B9E7" style=filled]\n}' + 'digraph {\n\tnode [shape=box]\n\t' + + 'adder' + + ' [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">' + + '<TR><TD COLSPAN="1" PORT="in0">in0</TD>' + + '<TD COLSPAN="1" PORT="in1">in1</TD></TR>' + + '<TR><TD COLSPAN="2">adder</TD></TR>' + + '<TR><TD COLSPAN="2" PORT="out0">out0</TD></TR>' + + '</TABLE>> fillcolor="#00B9E7" style=filled]\n}' ) assert adder._digraph().source in (s, s + '\n') @@ -115,8 +118,13 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule): for i, memory in enumerate(memories): memory.set_entity_name(f"MEM{i}") s = ( - 'digraph {\n\tnode [shape=record]\n\tMEM0 [label="{{<in0> in0}|<MEM0>' - ' MEM0|{<out0> out0}}" fillcolor="#00CFB5" style=filled]\n}' + 'digraph {\n\tnode [shape=box]\n\tMEM0' + + ' [label=<<TABLE BORDER="0" CELLBORDER="1"' + + ' CELLSPACING="0" CELLPADDING="4">' + + '<TR><TD COLSPAN="1" PORT="in0">in0</TD></TR>' + + '<TR><TD COLSPAN="1">MEM0</TD></TR>' + + '<TR><TD COLSPAN="1" PORT="out0">out0</TD></TR>' + + '</TABLE>> fillcolor="#00CFB5" style=filled]\n}' ) assert memory._digraph().source in (s, s + '\n') assert memory.schedule_time == 18 @@ -149,11 +157,11 @@ def test_architecture(schedule_direct_form_iir_lp_filter: Schedule): # Graph representation # Parts are non-deterministic, but this first part seems OK s = ( - 'digraph {\n\tnode [shape=record]\n\tsplines=spline\n\tsubgraph' + 'digraph {\n\tnode [shape=box]\n\tsplines=spline\n\tsubgraph' ' cluster_memories' ) assert architecture._digraph().source.startswith(s) - s = 'digraph {\n\tnode [shape=record]\n\tsplines=spline\n\tMEM0' + s = 'digraph {\n\tnode [shape=box]\n\tsplines=spline\n\tMEM0' assert architecture._digraph(cluster=False).source.startswith(s) assert architecture.schedule_time == 18