OR-1 dataflow CPU sketch
1"""
2Tests for StructureMemory event firing (observability hooks).
3
4Verifies acceptance criteria:
5- or1-monitor.AC2.6: SM fires TokenReceived when a token is dequeued
6- or1-monitor.AC2.7: SM fires CellWritten on any cell state change
7- or1-monitor.AC2.8: SM fires DeferredRead when a read blocks
8- or1-monitor.AC2.9: SM fires DeferredSatisfied when deferred read is satisfied
9- or1-monitor.AC2.10: SM fires ResultSent when a result token is routed back
10"""
11
12import simpy
13
14from cm_inst import MemOp
15from emu.events import (
16 TokenReceived, CellWritten, DeferredRead as DeferredReadEvent,
17 DeferredSatisfied, ResultSent,
18)
19from emu.sm import StructureMemory
20from sm_mod import Presence
21from tokens import CMToken, SMToken
22
23
24def inject_token(env: simpy.Environment, store: simpy.Store, token):
25 """Helper to inject token into store via a process."""
26 def _injector():
27 yield store.put(token)
28
29 env.process(_injector())
30
31
32class TestAC2_6TokenReceived:
33 """AC2.6: SM fires TokenReceived when a token is dequeued."""
34
35 def test_token_received_write(self):
36 """TokenReceived event fires for WRITE operation."""
37 env = simpy.Environment()
38 events = []
39
40 def on_event(event):
41 events.append(event)
42
43 sm = StructureMemory(env, 0, on_event=on_event)
44
45 # Inject WRITE token
46 write_token = SMToken(target=0, addr=10, op=MemOp.WRITE, flags=None, data=0x1234, ret=None)
47 inject_token(env, sm.input_store, write_token)
48
49 env.run(until=100)
50
51 # Verify TokenReceived event was fired
52 token_received_events = [e for e in events if isinstance(e, TokenReceived)]
53 assert len(token_received_events) >= 1
54 assert token_received_events[0].token == write_token
55 assert token_received_events[0].component == "sm:0"
56
57 def test_token_received_read(self):
58 """TokenReceived event fires for READ operation."""
59 env = simpy.Environment()
60 events = []
61
62 def on_event(event):
63 events.append(event)
64
65 sm = StructureMemory(env, 0, on_event=on_event)
66 collector = simpy.Store(env)
67 sm.route_table[0] = collector
68
69 # Pre-populate cell
70 sm.cells[5].pres = Presence.FULL
71 sm.cells[5].data_l = 0x5555
72
73 # Inject READ token
74 ret_route = CMToken(target=0, offset=0, act_id=0, data=0)
75 read_token = SMToken(target=0, addr=5, op=MemOp.READ, flags=None, data=None, ret=ret_route)
76 inject_token(env, sm.input_store, read_token)
77
78 env.run(until=100)
79
80 # Verify TokenReceived event was fired
81 token_received_events = [e for e in events if isinstance(e, TokenReceived)]
82 assert len(token_received_events) >= 1
83 assert token_received_events[0].token == read_token
84
85
86class TestAC2_7CellWritten:
87 """AC2.7: SM fires CellWritten on any cell state change."""
88
89 def test_cell_written_empty_to_full(self):
90 """CellWritten fires when EMPTY cell becomes FULL."""
91 env = simpy.Environment()
92 events = []
93
94 def on_event(event):
95 events.append(event)
96
97 sm = StructureMemory(env, 0, on_event=on_event)
98
99 # Cell starts EMPTY
100 assert sm.cells[20].pres == Presence.EMPTY
101
102 # Inject WRITE to EMPTY cell
103 write_token = SMToken(target=0, addr=20, op=MemOp.WRITE, flags=None, data=0xABCD, ret=None)
104 inject_token(env, sm.input_store, write_token)
105
106 env.run(until=100)
107
108 # Verify CellWritten event was fired
109 cell_written_events = [e for e in events if isinstance(e, CellWritten)]
110 assert len(cell_written_events) >= 1
111 cw = cell_written_events[0]
112 assert cw.addr == 20
113 assert cw.old_pres == Presence.EMPTY
114 assert cw.new_pres == Presence.FULL
115
116 def test_cell_written_clear(self):
117 """CellWritten fires when cell is cleared to EMPTY."""
118 env = simpy.Environment()
119 events = []
120
121 def on_event(event):
122 events.append(event)
123
124 sm = StructureMemory(env, 0, on_event=on_event)
125
126 # Set cell to FULL
127 sm.cells[30].pres = Presence.FULL
128 sm.cells[30].data_l = 0x9999
129
130 # Inject CLEAR token
131 clear_token = SMToken(target=0, addr=30, op=MemOp.CLEAR, flags=None, data=None, ret=None)
132 inject_token(env, sm.input_store, clear_token)
133
134 env.run(until=100)
135
136 # Verify CellWritten event was fired
137 cell_written_events = [e for e in events if isinstance(e, CellWritten)]
138 assert len(cell_written_events) >= 1
139 cw = cell_written_events[0]
140 assert cw.addr == 30
141 assert cw.old_pres == Presence.FULL
142 assert cw.new_pres == Presence.EMPTY
143
144 def test_cell_written_alloc(self):
145 """CellWritten fires when EMPTY cell is allocated to RESERVED."""
146 env = simpy.Environment()
147 events = []
148
149 def on_event(event):
150 events.append(event)
151
152 sm = StructureMemory(env, 0, on_event=on_event)
153
154 # Cell starts EMPTY
155 assert sm.cells[40].pres == Presence.EMPTY
156
157 # Inject ALLOC token
158 alloc_token = SMToken(target=0, addr=40, op=MemOp.ALLOC, flags=None, data=None, ret=None)
159 inject_token(env, sm.input_store, alloc_token)
160
161 env.run(until=100)
162
163 # Verify CellWritten event was fired
164 cell_written_events = [e for e in events if isinstance(e, CellWritten)]
165 assert len(cell_written_events) >= 1
166 cw = cell_written_events[0]
167 assert cw.addr == 40
168 assert cw.old_pres == Presence.EMPTY
169 assert cw.new_pres == Presence.RESERVED
170
171 def test_cell_written_deferred_satisfaction(self):
172 """CellWritten fires when deferred read is satisfied (WAITING -> FULL)."""
173 env = simpy.Environment()
174 events = []
175
176 def on_event(event):
177 events.append(event)
178
179 sm = StructureMemory(env, 0, on_event=on_event)
180 collector = simpy.Store(env)
181 sm.route_table[0] = collector
182
183 # Set up deferred read on cell 50
184 ret_route = CMToken(target=0, offset=0, act_id=0, data=0)
185 read_token = SMToken(target=0, addr=50, op=MemOp.READ, flags=None, data=None, ret=ret_route)
186 inject_token(env, sm.input_store, read_token)
187
188 env.run(until=10)
189
190 # Clear events to focus on satisfaction
191 events.clear()
192
193 # Inject WRITE to satisfy deferred read
194 write_token = SMToken(target=0, addr=50, op=MemOp.WRITE, flags=None, data=0xDEAD, ret=None)
195 inject_token(env, sm.input_store, write_token)
196
197 env.run(until=100)
198
199 # Verify CellWritten event with WAITING -> FULL transition
200 cell_written_events = [e for e in events if isinstance(e, CellWritten)]
201 assert len(cell_written_events) >= 1
202 cw = cell_written_events[0]
203 assert cw.addr == 50
204 assert cw.old_pres == Presence.WAITING
205 assert cw.new_pres == Presence.FULL
206
207 def test_cell_written_atomic_full_to_full(self):
208 """CellWritten fires for atomic ops with FULL -> FULL transition."""
209 env = simpy.Environment()
210 events = []
211
212 def on_event(event):
213 events.append(event)
214
215 sm = StructureMemory(env, 0, on_event=on_event)
216 collector = simpy.Store(env)
217 sm.route_table[0] = collector
218
219 # Set cell to FULL
220 sm.cells[100].pres = Presence.FULL
221 sm.cells[100].data_l = 42
222
223 # Inject RD_INC token
224 ret_route = CMToken(target=0, offset=0, act_id=0, data=0)
225 inc_token = SMToken(target=0, addr=100, op=MemOp.RD_INC, flags=None, data=None, ret=ret_route)
226 inject_token(env, sm.input_store, inc_token)
227
228 env.run(until=100)
229
230 # Verify CellWritten event with FULL -> FULL transition
231 cell_written_events = [e for e in events if isinstance(e, CellWritten)]
232 assert len(cell_written_events) >= 1
233 cw = cell_written_events[0]
234 assert cw.addr == 100
235 assert cw.old_pres == Presence.FULL
236 assert cw.new_pres == Presence.FULL
237
238
239class TestAC2_8DeferredRead:
240 """AC2.8: SM fires DeferredRead when a read blocks on a non-FULL cell."""
241
242 def test_deferred_read_on_empty(self):
243 """DeferredRead event fires when READ blocks on EMPTY cell."""
244 env = simpy.Environment()
245 events = []
246
247 def on_event(event):
248 events.append(event)
249
250 sm = StructureMemory(env, 0, on_event=on_event)
251 collector = simpy.Store(env)
252 sm.route_table[0] = collector
253
254 # Cell starts EMPTY
255 assert sm.cells[60].pres == Presence.EMPTY
256
257 # Inject READ token
258 ret_route = CMToken(target=0, offset=0, act_id=0, data=0)
259 read_token = SMToken(target=0, addr=60, op=MemOp.READ, flags=None, data=None, ret=ret_route)
260 inject_token(env, sm.input_store, read_token)
261
262 env.run(until=100)
263
264 # Verify DeferredRead event was fired
265 deferred_read_events = [e for e in events if isinstance(e, DeferredReadEvent)]
266 assert len(deferred_read_events) >= 1
267 dr = deferred_read_events[0]
268 assert dr.addr == 60
269 assert dr.component == "sm:0"
270
271
272class TestAC2_9DeferredSatisfied:
273 """AC2.9: SM fires DeferredSatisfied when a subsequent write satisfies a deferred read."""
274
275 def test_deferred_satisfied(self):
276 """DeferredSatisfied event fires when deferred read is satisfied."""
277 env = simpy.Environment()
278 events = []
279
280 def on_event(event):
281 events.append(event)
282
283 sm = StructureMemory(env, 0, on_event=on_event)
284 collector = simpy.Store(env)
285 sm.route_table[0] = collector
286
287 # Set up deferred read on cell 70
288 ret_route = CMToken(target=0, offset=0, act_id=0, data=0)
289 read_token = SMToken(target=0, addr=70, op=MemOp.READ, flags=None, data=None, ret=ret_route)
290 inject_token(env, sm.input_store, read_token)
291
292 env.run(until=10)
293
294 # Clear events to focus on satisfaction
295 events.clear()
296
297 # Inject WRITE to satisfy deferred read
298 write_token = SMToken(target=0, addr=70, op=MemOp.WRITE, flags=None, data=0x7777, ret=None)
299 inject_token(env, sm.input_store, write_token)
300
301 env.run(until=100)
302
303 # Verify DeferredSatisfied event was fired
304 deferred_satisfied_events = [e for e in events if isinstance(e, DeferredSatisfied)]
305 assert len(deferred_satisfied_events) >= 1
306 ds = deferred_satisfied_events[0]
307 assert ds.addr == 70
308 assert ds.data == 0x7777
309 assert ds.component == "sm:0"
310
311
312class TestAC2_10ResultSent:
313 """AC2.10: SM fires ResultSent when a result token is routed back to a PE."""
314
315 def test_result_sent_on_read_full(self):
316 """ResultSent event fires when READ on FULL cell returns result."""
317 env = simpy.Environment()
318 events = []
319
320 def on_event(event):
321 events.append(event)
322
323 sm = StructureMemory(env, 0, on_event=on_event)
324 collector = simpy.Store(env)
325 sm.route_table[0] = collector
326
327 # Pre-populate cell with data
328 sm.cells[80].pres = Presence.FULL
329 sm.cells[80].data_l = 0xCAFE
330
331 # Inject READ token
332 ret_route = CMToken(target=0, offset=5, act_id=2, data=0)
333 read_token = SMToken(target=0, addr=80, op=MemOp.READ, flags=None, data=None, ret=ret_route)
334 inject_token(env, sm.input_store, read_token)
335
336 env.run(until=100)
337
338 # Verify ResultSent event was fired
339 result_sent_events = [e for e in events if isinstance(e, ResultSent)]
340 assert len(result_sent_events) >= 1
341 rs = result_sent_events[0]
342 assert rs.token.data == 0xCAFE
343 assert rs.token.offset == 5
344 assert rs.token.act_id == 2
345 assert rs.component == "sm:0"
346
347 def test_result_sent_on_atomic(self):
348 """ResultSent event fires when atomic op returns old value."""
349 env = simpy.Environment()
350 events = []
351
352 def on_event(event):
353 events.append(event)
354
355 sm = StructureMemory(env, 0, on_event=on_event)
356 collector = simpy.Store(env)
357 sm.route_table[0] = collector
358
359 # Pre-populate cell
360 sm.cells[110].pres = Presence.FULL
361 sm.cells[110].data_l = 100
362
363 # Inject RD_INC token
364 ret_route = CMToken(target=0, offset=12, act_id=1, data=0)
365 inc_token = SMToken(target=0, addr=110, op=MemOp.RD_INC, flags=None, data=None, ret=ret_route)
366 inject_token(env, sm.input_store, inc_token)
367
368 env.run(until=100)
369
370 # Verify ResultSent event was fired with old value
371 result_sent_events = [e for e in events if isinstance(e, ResultSent)]
372 assert len(result_sent_events) >= 1
373 rs = result_sent_events[0]
374 assert rs.token.data == 100 # Old value
375 assert rs.component == "sm:0"
376
377 def test_result_sent_on_deferred_satisfaction(self):
378 """ResultSent event fires when deferred read is satisfied."""
379 env = simpy.Environment()
380 events = []
381
382 def on_event(event):
383 events.append(event)
384
385 sm = StructureMemory(env, 0, on_event=on_event)
386 collector = simpy.Store(env)
387 sm.route_table[0] = collector
388
389 # Set up deferred read on cell 120
390 ret_route = CMToken(target=0, offset=15, act_id=3, data=0)
391 read_token = SMToken(target=0, addr=120, op=MemOp.READ, flags=None, data=None, ret=ret_route)
392 inject_token(env, sm.input_store, read_token)
393
394 env.run(until=10)
395
396 # Clear events
397 events.clear()
398
399 # Inject WRITE to satisfy deferred read
400 write_token = SMToken(target=0, addr=120, op=MemOp.WRITE, flags=None, data=0x8888, ret=None)
401 inject_token(env, sm.input_store, write_token)
402
403 env.run(until=100)
404
405 # Verify ResultSent event was fired with written data
406 result_sent_events = [e for e in events if isinstance(e, ResultSent)]
407 assert len(result_sent_events) >= 1
408 rs = result_sent_events[0]
409 assert rs.token.data == 0x8888
410 assert rs.token.offset == 15
411 assert rs.token.act_id == 3