OR-1 dataflow CPU sketch
1"""Tests for the Placement validation pass.
2
3Tests verify:
4- or1-asm.AC5.1: Valid placements are accepted
5- or1-asm.AC5.2: Placement on nonexistent PE produces error
6- or1-asm.AC5.3: Unplaced node produces error
7"""
8
9from asm.place import place
10from asm.ir import IRGraph, IRNode, SystemConfig, SourceLoc
11from asm.errors import ErrorCategory
12from cm_inst import ArithOp
13
14
15class TestValidPlacement:
16 """AC5.1: All nodes with explicit PE placements are accepted when PE exists."""
17
18 def test_single_node_pe0(self):
19 """Single node on PE0."""
20 node = IRNode(
21 name="&add",
22 opcode=ArithOp.ADD,
23 pe=0,
24 loc=SourceLoc(1, 1),
25 )
26 graph = IRGraph({"&add": node})
27 result = place(graph)
28
29 assert len(result.errors) == 0
30 assert result.system is not None
31 assert result.system.pe_count >= 1
32
33 def test_multiple_nodes_different_pes(self):
34 """Multiple nodes on different PEs."""
35 nodes = {
36 "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0, loc=SourceLoc(1, 1)),
37 "&sub": IRNode(name="&sub", opcode=ArithOp.SUB, pe=1, loc=SourceLoc(2, 1)),
38 "&inc": IRNode(name="&inc", opcode=ArithOp.INC, pe=2, loc=SourceLoc(3, 1)),
39 "&dec": IRNode(name="&dec", opcode=ArithOp.DEC, pe=3, loc=SourceLoc(4, 1)),
40 }
41 system = SystemConfig(pe_count=4, sm_count=1, iram_capacity=64, frame_count=4)
42 graph = IRGraph(nodes, system=system)
43 result = place(graph)
44
45 assert len(result.errors) == 0
46 assert result.system.pe_count == 4
47
48 def test_system_config_inferred(self):
49 """System config is inferred from max PE ID."""
50 node = IRNode(
51 name="&add",
52 opcode=ArithOp.ADD,
53 pe=3,
54 loc=SourceLoc(1, 1),
55 )
56 graph = IRGraph({"&add": node}, system=None)
57 result = place(graph)
58
59 assert len(result.errors) == 0
60 assert result.system is not None
61 assert result.system.pe_count >= 4 # At least 4 PEs (0-3)
62
63
64class TestNonexistentPE:
65 """AC5.2: Node placed on nonexistent PE produces error."""
66
67 def test_node_on_pe9_with_4_pes(self):
68 """Node on PE9 when system only has 4 PEs."""
69 node = IRNode(
70 name="&add",
71 opcode=ArithOp.ADD,
72 pe=9,
73 loc=SourceLoc(5, 10),
74 )
75 system = SystemConfig(pe_count=4, sm_count=1, iram_capacity=64, frame_count=4)
76 graph = IRGraph({"&add": node}, system=system)
77 result = place(graph)
78
79 assert len(result.errors) == 1
80 error = result.errors[0]
81 assert error.category == ErrorCategory.PLACEMENT
82 assert "PE9" in error.message
83 assert "4 PEs" in error.message
84 assert "0-3" in error.message
85
86 def test_node_on_pe1_with_1_pe(self):
87 """Node on PE1 when system only has 1 PE."""
88 node = IRNode(
89 name="&add",
90 opcode=ArithOp.ADD,
91 pe=1,
92 loc=SourceLoc(1, 1),
93 )
94 system = SystemConfig(pe_count=1, sm_count=1, iram_capacity=64, frame_count=4)
95 graph = IRGraph({"&add": node}, system=system)
96 result = place(graph)
97
98 assert len(result.errors) == 1
99 error = result.errors[0]
100 assert error.category == ErrorCategory.PLACEMENT
101 assert "PE1" in error.message
102 assert "0-0" in error.message
103
104 def test_multiple_nodes_one_invalid_pe(self):
105 """Multiple nodes, one on invalid PE."""
106 nodes = {
107 "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0, loc=SourceLoc(1, 1)),
108 "&sub": IRNode(name="&sub", opcode=ArithOp.SUB, pe=5, loc=SourceLoc(2, 1)),
109 "&inc": IRNode(name="&inc", opcode=ArithOp.INC, pe=1, loc=SourceLoc(3, 1)),
110 }
111 system = SystemConfig(pe_count=4, sm_count=1, iram_capacity=64, frame_count=4)
112 graph = IRGraph(nodes, system=system)
113 result = place(graph)
114
115 assert len(result.errors) == 1
116 error = result.errors[0]
117 assert error.category == ErrorCategory.PLACEMENT
118 assert "&sub" in error.message
119 assert "PE5" in error.message
120
121
122class TestUnplacedNode:
123 """AC10.1: Unplaced nodes are auto-placed without explicit annotations (Phase 7)."""
124
125 def test_single_unplaced_node(self):
126 """Single unplaced node gets auto-placed."""
127 node = IRNode(
128 name="&add",
129 opcode=ArithOp.ADD,
130 pe=None,
131 loc=SourceLoc(5, 3),
132 )
133 graph = IRGraph({"&add": node})
134 result = place(graph)
135
136 # Phase 7: auto-placement should succeed
137 assert len(result.errors) == 0
138 assert result.nodes["&add"].pe is not None
139
140 def test_multiple_nodes_some_unplaced(self):
141 """Unplaced nodes get auto-placed (Phase 7)."""
142 nodes = {
143 "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0, loc=SourceLoc(1, 1)),
144 "&sub": IRNode(name="&sub", opcode=ArithOp.SUB, pe=None, loc=SourceLoc(2, 1)),
145 "&inc": IRNode(name="&inc", opcode=ArithOp.INC, pe=None, loc=SourceLoc(3, 1)),
146 }
147 system = SystemConfig(pe_count=2, sm_count=1, iram_capacity=64, frame_count=4)
148 graph = IRGraph(nodes, system=system)
149 result = place(graph)
150
151 # Phase 7: auto-placement should succeed
152 assert len(result.errors) == 0
153 assert result.nodes["&sub"].pe is not None
154 assert result.nodes["&inc"].pe is not None
155
156 def test_all_nodes_unplaced(self):
157 """All unplaced nodes get auto-placed (Phase 7)."""
158 nodes = {
159 "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=None, loc=SourceLoc(1, 1)),
160 "&sub": IRNode(name="&sub", opcode=ArithOp.SUB, pe=None, loc=SourceLoc(2, 1)),
161 }
162 graph = IRGraph(nodes)
163 result = place(graph)
164
165 # Phase 7: auto-placement should succeed
166 assert len(result.errors) == 0
167 for node in result.nodes.values():
168 assert node.pe is not None
169
170
171class TestMixedErrors:
172 """Combined placement errors."""
173
174 def test_unplaced_and_invalid_pe(self):
175 """Invalid PE placement produces error; unplaced nodes are auto-placed."""
176 nodes = {
177 "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0, loc=SourceLoc(1, 1)),
178 "&sub": IRNode(name="&sub", opcode=ArithOp.SUB, pe=None, loc=SourceLoc(2, 1)),
179 "&inc": IRNode(name="&inc", opcode=ArithOp.INC, pe=9, loc=SourceLoc(3, 1)),
180 }
181 system = SystemConfig(pe_count=4, sm_count=1, iram_capacity=64, frame_count=4)
182 graph = IRGraph(nodes, system=system)
183 result = place(graph)
184
185 # Should have 1 error for invalid PE placement on &inc; &sub is auto-placed
186 assert len(result.errors) == 1
187 error = result.errors[0]
188 assert error.category == ErrorCategory.PLACEMENT
189 assert "&inc" in error.message
190 assert "PE9" in error.message
191 # &sub should be auto-placed
192 assert result.nodes["&sub"].pe is not None