OR-1 dataflow CPU sketch
at main 411 lines 14 kB view raw
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