OR-1 dataflow CPU sketch
at pe-frame-redesign 336 lines 11 kB view raw
1"""Parser tests for the dataflow graph assembly grammar.""" 2 3from textwrap import dedent 4 5import pytest 6from lark import exceptions, LarkError 7 8 9class TestInstDefs: 10 def test_basic_instructions(self, parser): 11 tree = parser.parse(dedent("""\ 12 &my_add <| add 13 &my_sub <| sub 14 &my_const <| const, 10 15 &my_shift <| shl 16 &my_not <| not 17 """)) 18 assert tree.data == "start" 19 assert len(tree.children) == 5 20 assert all(child.data == "inst_def" for child in tree.children) 21 22 def test_hex_const(self, parser): 23 tree = parser.parse(dedent("""\ 24 &mask <| const, 0xFF 25 """)) 26 assert tree.data == "start" 27 assert len(tree.children) == 1 28 assert tree.children[0].data == "inst_def" 29 30 def test_named_args(self, parser): 31 tree = parser.parse(dedent("""\ 32 &serial <| ior, dest=0x45, addr=0x91, data=0x43 33 """)) 34 assert tree.data == "start" 35 assert len(tree.children) == 1 36 assert tree.children[0].data == "inst_def" 37 38 def test_system_config(self, parser): 39 tree = parser.parse(dedent("""\ 40 &loader <| load_inst, dest=0x01, addr=0x00, data_l=0xABCD, data_h=0x1234 41 """)) 42 assert tree.data == "start" 43 assert len(tree.children) == 1 44 assert tree.children[0].data == "inst_def" 45 46 47class TestEdges: 48 def test_plain_edges(self, parser): 49 tree = parser.parse(dedent("""\ 50 &a |> &b:L 51 &a |> &b:R 52 &c |> &d, &e 53 """)) 54 assert tree.data == "start" 55 assert len(tree.children) == 3 56 assert all(child.data == "plain_edge" for child in tree.children) 57 58 def test_strong_inline_edge(self, parser): 59 tree = parser.parse(dedent("""\ 60 add &a, &b |> &c, &d 61 """)) 62 assert tree.data == "start" 63 assert len(tree.children) == 1 64 assert tree.children[0].data == "strong_edge" 65 66 def test_weak_inline_edge(self, parser): 67 tree = parser.parse(dedent("""\ 68 &c, &d sub <| &a, &b 69 """)) 70 assert tree.data == "start" 71 assert len(tree.children) == 1 72 assert tree.children[0].data == "weak_edge" 73 74 def test_fanout(self, parser): 75 tree = parser.parse(dedent("""\ 76 &splitter <| pass 77 &input |> &splitter:L 78 &splitter |> &consumer_a:L, &consumer_b:R 79 """)) 80 assert tree.data == "start" 81 assert len(tree.children) == 3 82 assert tree.children[0].data == "inst_def" 83 assert tree.children[1].data == "plain_edge" 84 assert tree.children[2].data == "plain_edge" 85 86 87class TestFunctions: 88 def test_fib_function(self, parser): 89 tree = parser.parse(dedent("""\ 90 $fib |> { 91 &const_n <| const, 10 92 &sub1 <| sub 93 &sub2 <| sub 94 &branch <| sweq 95 96 &const_n |> &branch:L 97 &const_n |> &sub1:L 98 &const_n |> &sub1:R 99 &const_n |> &sub2:R 100 &sub1 |> &recurse_a:L 101 } 102 """)) 103 assert tree.data == "start" 104 assert len(tree.children) == 1 105 assert tree.children[0].data == "func_def" 106 107 108class TestPlacement: 109 def test_pe_qualifiers(self, parser): 110 tree = parser.parse(dedent("""\ 111 &my_add|pe0 <| add 112 &result|pe1 <| pass 113 &my_add|pe0 |> &result|pe1:L 114 """)) 115 assert tree.data == "start" 116 assert len(tree.children) == 3 117 assert tree.children[0].data == "inst_def" 118 assert tree.children[1].data == "inst_def" 119 assert tree.children[2].data == "plain_edge" 120 121 def test_location_directive(self, parser): 122 tree = parser.parse(dedent("""\ 123 @data_section|sm0: 124 """)) 125 assert tree.data == "start" 126 assert len(tree.children) == 1 127 assert tree.children[0].data == "location_dir" 128 129 def test_location_directive_with_placement_and_colon(self, parser): 130 """AC6.1: Location directive with placement and trailing colon.""" 131 tree = parser.parse(dedent("""\ 132 @region|pe0: 133 """)) 134 assert tree.data == "start" 135 assert len(tree.children) == 1 136 assert tree.children[0].data == "location_dir" 137 138 def test_node_ref_in_edge_context_no_colon(self, parser): 139 """AC6.2: @node without colon in edge context parses as node_ref.""" 140 tree = parser.parse(dedent("""\ 141 @src |> @dest:L 142 """)) 143 assert tree.data == "start" 144 assert len(tree.children) == 1 145 assert tree.children[0].data == "plain_edge" 146 # Verify the edge has two node refs, not a location_dir 147 148 def test_location_directive_no_colon_produces_parse_error(self, parser): 149 """AC6.3: Location directive without trailing colon produces PARSE error.""" 150 # The grammar now requires trailing colon for location_dir. 151 # A bare @ref without colon should produce a parse error. 152 with pytest.raises(LarkError): 153 parser.parse(dedent("""\ 154 @data_section 155 &a <| add 156 """)) 157 158 159class TestDataDefs: 160 def test_hex_data(self, parser): 161 tree = parser.parse(dedent("""\ 162 @hello|sm0:0 = 0x05 163 @hello|sm0:1 = 'h', 'e' 164 @hello|sm0:2 = 'l', 'l' 165 """)) 166 assert tree.data == "start" 167 assert len(tree.children) == 3 168 assert all(child.data == "data_def" for child in tree.children) 169 170 def test_macro_invocation(self, parser): 171 tree = parser.parse(dedent("""\ 172 @hello = #str "hello" 173 """)) 174 assert tree.data == "start" 175 assert len(tree.children) == 1 176 assert tree.children[0].data == "data_def" 177 178 def test_multi_line_string(self, parser): 179 tree = parser.parse(dedent('''\ 180 @msg = "hello 181world" 182 ''')) 183 assert tree.data == "start" 184 assert len(tree.children) == 1 185 assert tree.children[0].data == "data_def" 186 187 def test_raw_string(self, parser): 188 tree = parser.parse(dedent("""\ 189 @path = r"no\\escapes\\here" 190 """)) 191 assert tree.data == "start" 192 assert len(tree.children) == 1 193 assert tree.children[0].data == "data_def" 194 195 def test_byte_string(self, parser): 196 tree = parser.parse(dedent("""\ 197 @raw_data = b"\\x01\\x02\\x03" 198 """)) 199 assert tree.data == "start" 200 assert len(tree.children) == 1 201 assert tree.children[0].data == "data_def" 202 203 204class TestComments: 205 def test_inline_comments(self, parser): 206 tree = parser.parse(dedent("""\ 207 &my_add <| add ; this is a comment 208 &a |> &b:L ; wire a to b left port 209 """)) 210 assert tree.data == "start" 211 assert len(tree.children) == 2 212 assert tree.children[0].data == "inst_def" 213 assert tree.children[1].data == "plain_edge" 214 215 216class TestMixedPrograms: 217 def test_mixed_program(self, parser): 218 tree = parser.parse(dedent("""\ 219 @counter|sm0:0 = 0x00 220 221 $main |> { 222 &init <| const, 0 223 &loop_add <| add 224 &cmp <| lte 225 &branch <| breq 226 &output <| iow, dest=0x01 227 228 &init |> &loop_add:L 229 &loop_add |> &cmp:L 230 &loop_add |> &output:L 231 } 232 """)) 233 assert tree.data == "start" 234 assert len(tree.children) == 2 235 assert tree.children[0].data == "data_def" 236 assert tree.children[1].data == "func_def" 237 238 239class TestSMOps: 240 def test_read_op(self, parser): 241 tree = parser.parse(dedent("""\ 242 &cell <| read 243 """)) 244 assert tree.data == "start" 245 assert len(tree.children) == 1 246 assert tree.children[0].data == "inst_def" 247 248 def test_write_op(self, parser): 249 tree = parser.parse(dedent("""\ 250 &cell <| write 251 """)) 252 assert tree.data == "start" 253 assert len(tree.children) == 1 254 assert tree.children[0].data == "inst_def" 255 256 def test_clear_op(self, parser): 257 tree = parser.parse(dedent("""\ 258 &cell <| clear 259 """)) 260 assert tree.data == "start" 261 assert len(tree.children) == 1 262 assert tree.children[0].data == "inst_def" 263 264 def test_alloc_op(self, parser): 265 tree = parser.parse(dedent("""\ 266 &cell <| alloc 267 """)) 268 assert tree.data == "start" 269 assert len(tree.children) == 1 270 assert tree.children[0].data == "inst_def" 271 272 def test_free_op(self, parser): 273 tree = parser.parse(dedent("""\ 274 &cell <| free 275 """)) 276 assert tree.data == "start" 277 assert len(tree.children) == 1 278 assert tree.children[0].data == "inst_def" 279 280 def test_rd_inc_op(self, parser): 281 tree = parser.parse(dedent("""\ 282 &cell <| rd_inc 283 """)) 284 assert tree.data == "start" 285 assert len(tree.children) == 1 286 assert tree.children[0].data == "inst_def" 287 288 def test_rd_dec_op(self, parser): 289 tree = parser.parse(dedent("""\ 290 &cell <| rd_dec 291 """)) 292 assert tree.data == "start" 293 assert len(tree.children) == 1 294 assert tree.children[0].data == "inst_def" 295 296 def test_cmp_sw_op(self, parser): 297 tree = parser.parse(dedent("""\ 298 &cell <| cmp_sw 299 """)) 300 assert tree.data == "start" 301 assert len(tree.children) == 1 302 assert tree.children[0].data == "inst_def" 303 304 305class TestSystemPragma: 306 def test_system_pragma_minimal(self, parser): 307 tree = parser.parse(dedent("""\ 308 @system pe=4, sm=1 309 """)) 310 assert tree.data == "start" 311 assert len(tree.children) == 1 312 assert tree.children[0].data == "system_pragma" 313 314 def test_system_pragma_full(self, parser): 315 tree = parser.parse(dedent("""\ 316 @system pe=2, sm=1, iram=128, ctx=2 317 """)) 318 assert tree.data == "start" 319 assert len(tree.children) == 1 320 assert tree.children[0].data == "system_pragma" 321 322 def test_system_pragma_with_hex(self, parser): 323 tree = parser.parse(dedent("""\ 324 @system pe=0x04, sm=0x01 325 """)) 326 assert tree.data == "start" 327 assert len(tree.children) == 1 328 assert tree.children[0].data == "system_pragma" 329 330 331class TestUnknownOpcodes: 332 def test_unknown_opcode_fails(self, parser): 333 with pytest.raises((exceptions.UnexpectedToken, exceptions.UnexpectedCharacters)): 334 parser.parse(dedent("""\ 335 &unknown <| foobar 336 """))