OR-1 dataflow CPU sketch
1"""
2Tests for EXEC opcode and bootstrap token functionality.
3
4Verifies that:
5- PELocalWriteToken routes to the correct target PE
6- FrameControlToken routes to the correct target PE
7- EXEC opcode reads token objects from T0 and injects them
8- Injected tokens are processed normally by target PEs/SMs
9- Bootstrap tokens can load a program and execute correctly
10"""
11
12import pytest
13import simpy
14
15from cm_inst import MemOp, Port, RoutingOp, FrameOp
16from emu import build_topology
17from emu.types import PEConfig, SMConfig
18from sm_mod import Presence
19from tokens import DyadToken, PELocalWriteToken, MonadToken, SMToken, FrameControlToken
20
21
22class TestAC2_1IRAMWriteTokenRouting:
23 """PELocalWriteToken routes to target PE via network."""
24
25 def test_pe_local_write_token_routes_to_target_pe_via_system_inject(self):
26 """PELocalWriteToken is routed to correct target PE when injected via system.inject()."""
27 env = simpy.Environment()
28
29 sys = build_topology(
30 env,
31 [
32 PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8),
33 PEConfig(pe_id=1, iram={}, frame_count=1, frame_slots=8),
34 PEConfig(pe_id=2, iram={}, frame_count=1, frame_slots=8),
35 ],
36 [],
37 )
38
39 # Create PELocalWriteToken targeting PE 2
40 iram_token = PELocalWriteToken(
41 target=2, # Target PE 2
42 act_id=0,
43 region=0, # region 0 = IRAM writes
44 slot=10,
45 data=0x1234,
46 is_dest=False,
47 )
48
49 # Inject via system.inject() which appends to PE 2's input_store.items directly
50 sys.inject(iram_token)
51
52 # Verify token arrived at PE 2's input_store (inject appends directly to items)
53 assert len(sys.pes[2].input_store.items) > 0
54 received = sys.pes[2].input_store.items[0]
55 assert isinstance(received, PELocalWriteToken)
56 assert received.target == 2
57 assert received.slot == 10
58
59 # PE 0 and PE 1 should not have received the token
60 assert len(sys.pes[0].input_store.items) == 0
61 assert len(sys.pes[1].input_store.items) == 0
62
63 def test_pe_local_write_token_multiple_targets(self):
64 """Multiple PELocalWriteTokens can route to different target PEs."""
65 env = simpy.Environment()
66
67 sys = build_topology(
68 env,
69 [
70 PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8),
71 PEConfig(pe_id=1, iram={}, frame_count=1, frame_slots=8),
72 ],
73 [],
74 )
75
76 # Create two PELocalWriteTokens targeting different PEs
77 token0 = PELocalWriteToken(
78 target=0, act_id=0, region=0, slot=0, data=100, is_dest=False
79 )
80 token1 = PELocalWriteToken(
81 target=1, act_id=0, region=0, slot=5, data=200, is_dest=False
82 )
83
84 # Inject both tokens
85 sys.inject(token0)
86 sys.inject(token1)
87
88 # Verify routing via direct items inspection
89 assert len(sys.pes[0].input_store.items) == 1
90 assert len(sys.pes[1].input_store.items) == 1
91 assert sys.pes[0].input_store.items[0].slot == 0
92 assert sys.pes[1].input_store.items[0].slot == 5
93
94
95class TestAC2_4IRAMWriteTokenInvalidTarget:
96 """PELocalWriteToken with invalid target PE raises or is dropped."""
97
98 def test_pe_local_write_token_invalid_target_raises_key_error(self):
99 """PELocalWriteToken with non-existent target PE raises KeyError via system.send()."""
100 env = simpy.Environment()
101
102 # Create topology with only PE 0
103 sys = build_topology(
104 env,
105 [PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8)],
106 [],
107 )
108
109 # Create PELocalWriteToken targeting non-existent PE 5
110 iram_token = PELocalWriteToken(
111 target=5, # PE 5 does not exist
112 act_id=0,
113 region=0,
114 slot=0,
115 data=0x5555,
116 is_dest=False,
117 )
118
119 # Attempting to send should raise KeyError
120 def process_token():
121 yield from sys.send(iram_token)
122
123 with pytest.raises(KeyError):
124 env.process(process_token())
125 env.run(until=100)
126
127
128class TestAC5_1ExecInjectsTokens:
129 """AC5.1: Tokens can be injected into the network via send()."""
130
131 def test_exec_injects_single_token_to_pe(self):
132 """Direct token injection to PE via system.send()."""
133 env = simpy.Environment()
134
135 sys = build_topology(
136 env,
137 [
138 PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8),
139 PEConfig(pe_id=1, iram={}, frame_count=1, frame_slots=8),
140 ],
141 [SMConfig(sm_id=0, cell_count=512, tier_boundary=256)],
142 )
143
144 # Create a DyadToken to be injected
145 seed_token = DyadToken(
146 target=1,
147 offset=0,
148 act_id=0,
149 data=0x4567,
150 port=Port.L,
151 )
152
153 # Inject token directly via system
154 sys.inject(seed_token)
155 env.run(until=100)
156
157 # Verify execution completes without error
158 assert True
159
160 def test_exec_injects_multiple_tokens(self):
161 """Multiple tokens can be injected and processed."""
162 env = simpy.Environment()
163
164 sys = build_topology(
165 env,
166 [
167 PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8),
168 PEConfig(pe_id=1, iram={}, frame_count=1, frame_slots=8),
169 ],
170 [
171 SMConfig(sm_id=0, cell_count=512, tier_boundary=256),
172 SMConfig(sm_id=1, cell_count=512, tier_boundary=256),
173 ],
174 )
175
176 # Create multiple SMTokens to be injected
177 token1 = SMToken(target=1, addr=100, op=MemOp.WRITE, flags=None, data=0x1111, ret=None)
178 token2 = SMToken(target=1, addr=101, op=MemOp.WRITE, flags=None, data=0x2222, ret=None)
179
180 # Inject tokens
181 sys.inject(token1)
182 sys.inject(token2)
183
184 env.run(until=100)
185
186 # Verify both tokens were processed by SM1
187 assert sys.sms[1].cells[100].pres == Presence.FULL
188 assert sys.sms[1].cells[100].data_l == 0x1111
189 assert sys.sms[1].cells[101].pres == Presence.FULL
190 assert sys.sms[1].cells[101].data_l == 0x2222
191
192
193class TestAC5_2ExecTokensProcessedNormally:
194 """AC5.2: Injected tokens are processed normally by target PEs/SMs."""
195
196 def test_injected_dyad_token_received_by_pe(self):
197 """DyadToken injected is received and processed by target PE."""
198 env = simpy.Environment()
199
200 sys = build_topology(
201 env,
202 [
203 PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8),
204 PEConfig(pe_id=1, iram={}, frame_count=1, frame_slots=8),
205 ],
206 [SMConfig(sm_id=0, cell_count=512, tier_boundary=256)],
207 )
208
209 # Create dyad token to be injected
210 token_l = DyadToken(target=1, offset=0, act_id=0, data=0xABCD, port=Port.L)
211
212 # Inject token
213 sys.inject(token_l)
214 env.run(until=100)
215
216 # Verify execution completes without error
217 assert True
218
219
220class TestAC5_3BootstrapProgram:
221 """AC5.3: Bootstrap tokens can load a program and execute correctly."""
222
223 def test_bootstrap_with_iram_write_and_seed_tokens(self):
224 """Bootstrap via direct setup_tokens injection."""
225 env = simpy.Environment()
226
227 sys = build_topology(
228 env,
229 [
230 PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8),
231 PEConfig(pe_id=1, iram={}, frame_count=1, frame_slots=8),
232 ],
233 [SMConfig(sm_id=0, cell_count=512, tier_boundary=256)],
234 )
235
236 # Create setup token: PELocalWriteToken to write to IRAM
237 iram_write = PELocalWriteToken(
238 target=0,
239 act_id=0,
240 region=0, # IRAM region
241 slot=0,
242 data=0x1234,
243 is_dest=False,
244 )
245
246 # Create seed token
247 seed_token = MonadToken(
248 target=0,
249 offset=0,
250 act_id=0,
251 data=0,
252 inline=False,
253 )
254
255 # Inject tokens
256 sys.inject(iram_write)
257 sys.inject(seed_token)
258
259 env.run(until=100)
260
261 # Verify execution completes without error
262 assert True
263
264
265class TestAC5_4ExecOnEmptyT0:
266 """AC5.4: EXEC on empty T0 region is a no-op."""
267
268 def test_exec_on_addr_beyond_t0_store_length_is_noop(self):
269 """EXEC at address beyond t0_store length produces no output."""
270 env = simpy.Environment()
271
272 sys = build_topology(
273 env,
274 [PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8)],
275 [SMConfig(sm_id=0, cell_count=512, tier_boundary=256)],
276 )
277
278 # t0_store is empty, EXEC on T0 address beyond current length
279 def test_sequence():
280 exec_token = SMToken(target=0, addr=300, op=MemOp.EXEC, flags=None, data=None, ret=None)
281 yield sys.sms[0].input_store.put(exec_token)
282
283 env.process(test_sequence())
284 env.run(until=100)
285
286 # Verify no crash and PE input_store is empty
287 assert len(sys.pes[0].input_store.items) == 0
288
289 def test_exec_on_empty_t0_index(self):
290 """EXEC at T0 index with no token is a no-op."""
291 env = simpy.Environment()
292
293 sys = build_topology(
294 env,
295 [PEConfig(pe_id=0, iram={}, frame_count=1, frame_slots=8)],
296 [SMConfig(sm_id=0, cell_count=512, tier_boundary=256)],
297 )
298
299 # Pre-populate t0_store with raw int value
300 sys.sms[0].t0_store.append(100)
301
302 # EXEC at index 5 which is beyond current t0_store length (1)
303 def test_sequence():
304 exec_token = SMToken(target=0, addr=261, op=MemOp.EXEC, flags=None, data=None, ret=None) # t0_idx = 5
305 yield sys.sms[0].input_store.put(exec_token)
306
307 env.process(test_sequence())
308 env.run(until=100)
309
310 # Verify no crash
311 assert True