OR-1 dataflow CPU sketch
at main 338 lines 10 kB view raw
1"""Tests for Enhancement 2: Parameterized placement and port qualifiers (macro-enh.E2.*). 2 3Tests verify: 4- macro-enh.E2.1: Grammar accepts param_ref in placement position 5- macro-enh.E2.2: Grammar accepts param_ref in port position 6- macro-enh.E2.3: Context slot bracket syntax parses 7- macro-enh.E2.4: Expand pass resolves placement ParamRef 8- macro-enh.E2.5: Expand pass resolves port ParamRef 9- macro-enh.E2.6: Expand pass resolves context slot ParamRef 10- macro-enh.E2.7: Full pipeline with placement/port params 11 12Note: Placement qualifiers attach to the LHS qualified_ref in inst_def, 13not to the opcode. So `&n|${pe} <| add` is the correct syntax, not 14`&n <| add|${pe}`. 15""" 16 17from pathlib import Path 18 19from lark import Lark 20 21from asm import assemble, run_pipeline 22from asm.expand import expand 23from asm.lower import lower 24from asm.errors import ErrorCategory 25from asm.ir import ( 26 IRNode, ParamRef, PlacementRef, PortRef, ActSlotRef, ActSlotRange, 27) 28from cm_inst import ArithOp, Port 29 30 31def _get_parser(): 32 grammar_path = Path(__file__).parent.parent / "dfasm.lark" 33 return Lark( 34 grammar_path.read_text(), 35 parser="earley", 36 propagate_positions=True, 37 ) 38 39 40def parse_and_lower(source: str): 41 parser = _get_parser() 42 tree = parser.parse(source) 43 return lower(tree) 44 45 46def parse_lower_expand(source: str): 47 graph = parse_and_lower(source) 48 return expand(graph) 49 50 51class TestE21_PlacementParamRef: 52 """E2.1: Grammar accepts param_ref in placement position.""" 53 54 def test_placement_param_ref_in_macro_body(self): 55 """&n|${pe} <| add parses and lowers to PlacementRef.""" 56 source = """ 57 @system pe=1, sm=1 58 #place pe |> { 59 &n|${pe} <| add 60 } 61 """ 62 graph = parse_and_lower(source) 63 assert not graph.errors 64 body_nodes = graph.macro_defs[0].body.nodes 65 node = list(body_nodes.values())[0] 66 assert isinstance(node.pe, PlacementRef) 67 assert node.pe.param.param == "pe" 68 69 70class TestE22_PortParamRef: 71 """E2.2: Grammar accepts param_ref in port position.""" 72 73 def test_port_param_ref_in_edge(self): 74 """&src |> &dst:${port} parses and lowers to PortRef.""" 75 source = """ 76 @system pe=1, sm=1 77 #wire port |> { 78 &src <| pass 79 &dst <| add 80 &src |> &dst:${port} 81 } 82 """ 83 graph = parse_and_lower(source) 84 assert not graph.errors 85 body_edges = graph.macro_defs[0].body.edges 86 port_ref_edges = [e for e in body_edges if isinstance(e.port, PortRef)] 87 assert len(port_ref_edges) == 1 88 assert port_ref_edges[0].port.param.param == "port" 89 90 91class TestE23_CtxSlotSyntax: 92 """E2.3: Context slot bracket syntax parses.""" 93 94 def test_literal_ctx_slot(self): 95 """&node[2] parses — literal context slot in bracket syntax.""" 96 source = """ 97 @system pe=1, sm=1 98 #slot_macro |> { 99 &n[2] <| add 100 } 101 """ 102 graph = parse_and_lower(source) 103 assert not graph.errors 104 body_nodes = graph.macro_defs[0].body.nodes 105 node = list(body_nodes.values())[0] 106 assert isinstance(node.act_slot, ActSlotRange) 107 assert node.act_slot.start == 2 108 assert node.act_slot.end == 2 109 110 def test_full_qualifier_chain(self): 111 """&node|pe0[2]:L parses — placement + ctx_slot + port.""" 112 source = """ 113 @system pe=1, sm=1 114 &n|pe0[2]:L <| add 115 """ 116 graph = parse_and_lower(source) 117 assert not graph.errors 118 node = list(graph.nodes.values())[0] 119 assert node.pe == 0 120 assert isinstance(node.act_slot, ActSlotRange) 121 assert node.act_slot.start == 2 122 assert node.act_slot.end == 2 123 124 def test_parameterized_ctx_slot(self): 125 """&node[${ctx}] parses — parameterized context slot.""" 126 source = """ 127 @system pe=1, sm=1 128 #ctx_macro ctx |> { 129 &n[${ctx}] <| add 130 } 131 """ 132 graph = parse_and_lower(source) 133 assert not graph.errors 134 body_nodes = graph.macro_defs[0].body.nodes 135 node = list(body_nodes.values())[0] 136 assert isinstance(node.act_slot, ActSlotRef) 137 assert node.act_slot.param.param == "ctx" 138 139 def test_ctx_slot_range(self): 140 """&node[0..4] parses — range reservation.""" 141 source = """ 142 @system pe=1, sm=1 143 &n[0..4] <| add 144 """ 145 graph = parse_and_lower(source) 146 assert not graph.errors 147 node = list(graph.nodes.values())[0] 148 assert isinstance(node.act_slot, ActSlotRange) 149 assert node.act_slot.start == 0 150 assert node.act_slot.end == 4 151 152 153class TestE24_ExpandResolvesPlacement: 154 """E2.4: Expand pass resolves placement ParamRef.""" 155 156 def test_resolve_pe0(self): 157 """Placement param 'pe0' resolves to PE 0.""" 158 source = """ 159 @system pe=2, sm=1 160 #place pe |> { 161 &n|${pe} <| add 162 } 163 #place pe0 164 """ 165 graph = parse_lower_expand(source) 166 assert not graph.errors 167 node = list(graph.nodes.values())[0] 168 assert node.pe == 0 169 170 def test_resolve_pe1(self): 171 """Placement param 'pe1' resolves to PE 1.""" 172 source = """ 173 @system pe=2, sm=1 174 #place pe |> { 175 &n|${pe} <| add 176 } 177 #place pe1 178 """ 179 graph = parse_lower_expand(source) 180 assert not graph.errors 181 node = list(graph.nodes.values())[0] 182 assert node.pe == 1 183 184 def test_invalid_placement_error(self): 185 """Invalid placement value produces MACRO error.""" 186 source = """ 187 @system pe=1, sm=1 188 #place pe |> { 189 &n|${pe} <| add 190 } 191 #place &banana 192 """ 193 graph = parse_lower_expand(source) 194 macro_errors = [e for e in graph.errors if e.category == ErrorCategory.MACRO] 195 assert len(macro_errors) >= 1 196 assert "placement" in macro_errors[0].message.lower() 197 198 199class TestE25_ExpandResolvesPort: 200 """E2.5: Expand pass resolves port ParamRef.""" 201 202 def test_resolve_port_L(self): 203 """Port param 'L' resolves to Port.L.""" 204 source = """ 205 @system pe=1, sm=1 206 #wire port |> { 207 &src <| pass 208 &dst <| add 209 &src |> &dst:${port} 210 } 211 #wire L 212 """ 213 graph = parse_lower_expand(source) 214 assert not graph.errors 215 edges = [e for e in graph.edges if e.dest.endswith("&dst")] 216 assert len(edges) >= 1 217 assert edges[0].port == Port.L 218 219 def test_resolve_port_R(self): 220 """Port param 'R' resolves to Port.R.""" 221 source = """ 222 @system pe=1, sm=1 223 #wire port |> { 224 &src <| pass 225 &dst <| add 226 &src |> &dst:${port} 227 } 228 #wire R 229 """ 230 graph = parse_lower_expand(source) 231 assert not graph.errors 232 edges = [e for e in graph.edges if e.dest.endswith("&dst")] 233 assert len(edges) >= 1 234 assert edges[0].port == Port.R 235 236 def test_invalid_port_value_produces_error(self): 237 """E2.5c: Invalid port value produces MACRO error.""" 238 source = """ 239 @system pe=1, sm=1 240 #wire port |> { 241 &src <| pass 242 &dst <| add 243 &src |> &dst:${port} 244 } 245 #wire X 246 """ 247 graph = parse_lower_expand(source) 248 macro_errors = [e for e in graph.errors if e.category == ErrorCategory.MACRO] 249 assert len(macro_errors) >= 1 250 assert "port" in macro_errors[0].message.lower() 251 252 def test_invalid_source_port_value_produces_error(self): 253 """Invalid source port value produces MACRO error.""" 254 source = """ 255 @system pe=1, sm=1 256 #wire port |> { 257 &src <| pass 258 &dst <| add 259 &src:${port} |> &dst 260 } 261 #wire Z 262 """ 263 graph = parse_lower_expand(source) 264 macro_errors = [e for e in graph.errors if e.category == ErrorCategory.MACRO] 265 assert len(macro_errors) >= 1 266 assert "port" in macro_errors[0].message.lower() 267 268 269class TestE26_ExpandResolvesCtxSlot: 270 """E2.6: Expand pass resolves context slot ParamRef.""" 271 272 def test_resolve_ctx_slot_int(self): 273 """Ctx slot param resolves to CtxSlotRange.""" 274 source = """ 275 @system pe=1, sm=1 276 #ctx_macro ctx |> { 277 &n[${ctx}] <| add 278 } 279 #ctx_macro 2 280 """ 281 graph = parse_lower_expand(source) 282 assert not graph.errors 283 node = list(graph.nodes.values())[0] 284 assert isinstance(node.act_slot, ActSlotRange) 285 assert node.act_slot.start == 2 286 assert node.act_slot.end == 2 287 288 def test_non_numeric_ctx_slot_error(self): 289 """Non-numeric ctx slot value produces MACRO error.""" 290 source = """ 291 @system pe=1, sm=1 292 #ctx_macro ctx |> { 293 &n[${ctx}] <| add 294 } 295 #ctx_macro &banana 296 """ 297 graph = parse_lower_expand(source) 298 macro_errors = [e for e in graph.errors if e.category == ErrorCategory.MACRO] 299 assert len(macro_errors) >= 1 300 assert "act_slot" in macro_errors[0].message.lower() or "ctx_slot" in macro_errors[0].message.lower() 301 302 303class TestE27_FullPipeline: 304 """E2.7: Full pipeline with placement/port params.""" 305 306 def test_full_pipeline_placement_param(self): 307 """Placement-parameterized macro assembles; node on correct PE.""" 308 source = """ 309 @system pe=2, sm=1 310 #place pe |> { 311 &n|${pe} <| add 312 } 313 &seed <| const, 5 314 #place pe1 315 &seed |> #place_0.&n:L 316 &seed |> #place_0.&n:R 317 """ 318 graph = run_pipeline(source) 319 assert not graph.errors 320 placed_node = graph.nodes["#place_0.&n"] 321 assert placed_node.pe == 1 322 323 def test_full_pipeline_port_param(self): 324 """Port-parameterized macro assembles; edge targets correct port.""" 325 source = """ 326 @system pe=1, sm=1 327 #wire port |> { 328 &src <| pass 329 &dst <| add 330 &src |> &dst:${port} 331 } 332 &seed <| const, 5 333 #wire L 334 &seed |> #wire_0.&src 335 &seed |> #wire_0.&dst:R 336 """ 337 result = assemble(source) 338 assert result is not None