OR-1 dataflow CPU sketch
at ba08ffded3d3b2badb2a7e22816feafaacea5ded 266 lines 9.5 kB view raw
1"""Tests for IRGraph serialization to dfasm source. 2 3Tests verify the serialize() function converts IRGraphs back to valid dfasm source 4and preserves structural information through round-trip parsing and lowering. 5""" 6 7from tests.pipeline import parse_and_lower, parse_lower_resolve 8 9from asm.ir import ( 10 IRGraph, IRNode, IREdge, IRRegion, RegionKind, IRDataDef 11) 12from asm.serialize import serialize 13from asm.opcodes import OP_TO_MNEMONIC 14from cm_inst import ArithOp, LogicOp, MemOp, Port, RoutingOp 15 16 17class TestRoundTrip: 18 """AC11.1, AC11.2: Round-trip parse → lower → serialize → parse → lower""" 19 20 def test_simple_two_node_graph(self, parser): 21 """AC11.1: Simple graph with two nodes and one edge round-trips.""" 22 source = """ 23 &add|pe0 <| add 24 &const|pe0 <| const, 42 25 &add |> &const:L 26 """ 27 # Parse and lower to get baseline IRGraph 28 graph1 = parse_and_lower(parser, source) 29 30 # Serialize and re-parse, lower 31 serialized = serialize(graph1) 32 graph2 = parse_and_lower(parser, serialized) 33 34 # Check structural equivalence 35 assert set(graph1.nodes.keys()) == set(graph2.nodes.keys()) 36 assert len(graph1.edges) == len(graph2.edges) 37 38 def test_single_node_roundtrip(self, parser): 39 """AC11.1: Single node without edges round-trips.""" 40 source = "&foo|pe0 <| add" 41 42 graph1 = parse_and_lower(parser, source) 43 serialized = serialize(graph1) 44 graph2 = parse_and_lower(parser, serialized) 45 46 assert set(graph1.nodes.keys()) == set(graph2.nodes.keys()) 47 assert len(graph1.edges) == len(graph2.edges) 48 49 def test_graph_with_multiple_edges(self, parser): 50 """AC11.2: Graph with multiple edges preserves edge count.""" 51 source = """ 52 &a|pe0 <| add 53 &b|pe0 <| sub 54 &c|pe1 <| add 55 &a |> &b:L 56 &a |> &c:R 57 &b |> &c:L 58 """ 59 graph1 = parse_and_lower(parser, source) 60 serialized = serialize(graph1) 61 graph2 = parse_and_lower(parser, serialized) 62 63 assert len(graph1.edges) == len(graph2.edges) 64 assert set(graph1.nodes.keys()) == set(graph2.nodes.keys()) 65 66 67class TestPlacementQualifiers: 68 """AC11.3: All inst_def lines include |pe{N} qualifiers.""" 69 70 def test_all_nodes_have_pe_qualifier(self): 71 """AC11.3: Serialized output contains |pe{N} on every inst_def.""" 72 # Create a graph with nodes on different PEs 73 nodes = { 74 "&a": IRNode(name="&a", opcode=ArithOp.ADD, pe=0), 75 "&b": IRNode(name="&b", opcode=ArithOp.SUB, pe=1), 76 "&c": IRNode(name="&c", opcode=RoutingOp.CONST, const=42, pe=0), 77 } 78 graph = IRGraph(nodes=nodes) 79 80 serialized = serialize(graph) 81 lines = serialized.strip().split('\n') 82 83 # Filter to inst_def lines (contain '<|') 84 inst_lines = [l for l in lines if '<|' in l] 85 86 # Each should contain |pe{N} 87 for line in inst_lines: 88 assert '|pe' in line, f"Missing PE qualifier in: {line}" 89 90 91class TestFunctionScoping: 92 """AC11.4, AC11.7: FUNCTION regions serialize with scoping blocks.""" 93 94 def test_function_region_structure(self): 95 """AC11.4, AC11.7: FUNCTION regions emit $name |> { ... } blocks.""" 96 # Create a function region with unqualified node names inside 97 func_nodes = { 98 "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0), 99 "&const": IRNode(name="&const", opcode=RoutingOp.CONST, const=10, pe=0), 100 } 101 func_body = IRGraph(nodes=func_nodes) 102 func_region = IRRegion(tag="$main", kind=RegionKind.FUNCTION, body=func_body) 103 104 graph = IRGraph(regions=[func_region]) 105 serialized = serialize(graph) 106 107 # Check for function block syntax 108 assert "$main |>" in serialized 109 assert "{" in serialized 110 assert "}" in serialized 111 112 # Check that node names inside are unqualified (no $main. prefix) 113 assert "&add|pe0 <| add" in serialized or "&add|pe0 <| add" in serialized.replace('\n', ' ') 114 115 def test_unqualified_names_in_function(self): 116 """AC11.7: Names inside FUNCTION regions are unqualified.""" 117 func_nodes = { 118 "&label": IRNode(name="&label", opcode=ArithOp.ADD, pe=0), 119 } 120 func_body = IRGraph(nodes=func_nodes) 121 func_region = IRRegion(tag="$main", kind=RegionKind.FUNCTION, body=func_body) 122 123 graph = IRGraph(regions=[func_region]) 124 serialized = serialize(graph) 125 126 # Should not contain the qualified name $main.&label 127 assert "$main.&label" not in serialized 128 # Should contain unqualified &label 129 assert "&label" in serialized 130 131 132class TestDataDefs: 133 """AC11.5: data_def entries serialize with SM placement and cell addresses.""" 134 135 def test_data_def_serialization(self): 136 """AC11.5: Data definitions serialize as @name|sm{id}:{cell} = {value}.""" 137 data_defs = [ 138 IRDataDef(name="@hello", sm_id=0, cell_addr=5, value=0x2a), 139 IRDataDef(name="@world", sm_id=1, cell_addr=10, value=0x3f), 140 ] 141 graph = IRGraph(data_defs=data_defs) 142 143 serialized = serialize(graph) 144 145 # Check for data def entries 146 assert "@hello|sm0:5 =" in serialized 147 assert "@world|sm1:10 =" in serialized 148 # Check hex values are present 149 assert "0x2a" in serialized or "42" in serialized 150 assert "0x3f" in serialized or "63" in serialized 151 152 153class TestAnonymousNodes: 154 """AC11.6: Anonymous nodes serialize as inst_def + plain_edge, not inline.""" 155 156 def test_anonymous_node_not_inline(self): 157 """AC11.6: Nodes with __anon_ in name use inst_def + plain_edge form.""" 158 nodes = { 159 "&source": IRNode(name="&source", opcode=ArithOp.ADD, pe=0), 160 "__anon_1": IRNode(name="__anon_1", opcode=ArithOp.SUB, pe=0), 161 "&dest": IRNode(name="&dest", opcode=ArithOp.ADD, pe=1), 162 } 163 edges = [ 164 IREdge(source="&source", dest="__anon_1", port=Port.L), 165 IREdge(source="__anon_1", dest="&dest", port=Port.R), 166 ] 167 graph = IRGraph(nodes=nodes, edges=edges) 168 169 serialized = serialize(graph) 170 171 # Anonymous node should be defined as a separate inst_def, not inline 172 assert "__anon_1|pe0 <|" in serialized 173 # Should have explicit edges, not inline syntax 174 assert "&source |> __anon_1:L" in serialized 175 assert "__anon_1 |> &dest:R" in serialized 176 177 178class TestLocationRegions: 179 """AC11.8: LOCATION regions serialize as bare directive + body.""" 180 181 def test_location_region_structure(self): 182 """AC11.8: LOCATION regions emit bare directive tag followed by body.""" 183 # Create a location region with data definitions inside 184 loc_data = [ 185 IRDataDef(name="@data1", sm_id=0, cell_addr=5, value=100), 186 ] 187 loc_body = IRGraph(data_defs=loc_data) 188 loc_region = IRRegion(tag="@data_section", kind=RegionKind.LOCATION, body=loc_body) 189 190 graph = IRGraph(regions=[loc_region]) 191 serialized = serialize(graph) 192 193 # Location directive should appear as bare tag with trailing colon (not in $func |> form) 194 assert "@data_section:" in serialized 195 # Body content should follow 196 assert "@data1|sm0:5" in serialized 197 198 199class TestEdgeSerialization: 200 """Edges serialize as plain_edge form.""" 201 202 def test_edge_port_notation(self): 203 """Edges serialize with :L and :R port notation.""" 204 nodes = { 205 "&a": IRNode(name="&a", opcode=ArithOp.ADD, pe=0), 206 "&b": IRNode(name="&b", opcode=ArithOp.ADD, pe=1), 207 } 208 edges = [ 209 IREdge(source="&a", dest="&b", port=Port.L), 210 ] 211 graph = IRGraph(nodes=nodes, edges=edges) 212 213 serialized = serialize(graph) 214 215 # Should contain edge with port notation 216 assert "|> &b:L" in serialized 217 218 219class TestMnemonicUsage: 220 """Opcodes serialize using OP_TO_MNEMONIC.""" 221 222 def test_various_opcodes(self): 223 """Different opcodes serialize with correct mnemonics.""" 224 nodes = { 225 "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0), 226 "&sub": IRNode(name="&sub", opcode=ArithOp.SUB, pe=0), 227 "&and": IRNode(name="&and", opcode=LogicOp.AND, pe=0), 228 "&read": IRNode(name="&read", opcode=MemOp.READ, pe=0), 229 } 230 graph = IRGraph(nodes=nodes) 231 232 serialized = serialize(graph) 233 234 # Check that mnemonics appear 235 assert "add" in serialized 236 assert "sub" in serialized 237 assert "and" in serialized 238 assert "read" in serialized 239 240 241class TestConstValues: 242 """Const values serialize in inst_def format.""" 243 244 def test_const_node_with_value(self): 245 """Nodes with const values serialize with const argument.""" 246 nodes = { 247 "&c": IRNode(name="&c", opcode=RoutingOp.CONST, const=255, pe=0), 248 } 249 graph = IRGraph(nodes=nodes) 250 251 serialized = serialize(graph) 252 253 # Should include const value 254 assert "const, 255" in serialized or "const, 0xff" in serialized 255 256 257class TestEmptyGraph: 258 """Edge case: empty graph.""" 259 260 def test_empty_graph_serializes(self): 261 """AC11.1: Empty graph serializes to empty or whitespace string.""" 262 graph = IRGraph() 263 serialized = serialize(graph) 264 265 # Should not raise, should be empty or whitespace 266 assert serialized.strip() == ""