OR-1 dataflow CPU sketch
at main 467 lines 17 kB view raw
1""" 2Tests for SM T0/T1 memory tier split. 3 4Verifies acceptance criteria: 5- token-migration.AC4.1: SM operations on T1 (< tier_boundary) use I-structure semantics 6- token-migration.AC4.2: SM WRITE to T0 (>= tier_boundary) stores data without presence checking 7- token-migration.AC4.3: SM READ on T0 address returns immediately (no deferral) 8- token-migration.AC4.4: T0 storage is shared across all SMs 9- token-migration.AC4.5: I-structure ops (CLEAR, ALLOC, FREE, atomics) on T0 produce error/warning 10- token-migration.AC4.6: Tier boundary is configurable via SMConfig; default is 256 11- token-migration.AC6.2: Existing I-structure behaviour unchanged (is_wide=False path) 12""" 13 14import simpy 15 16from cm_inst import MemOp 17from emu import build_topology 18from emu.sm import StructureMemory 19from emu.types import SMConfig 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 TestAC4_1T1IStructureSemantics: 33 """AC4.1: SM READ on T1 (< tier_boundary) uses I-structure semantics.""" 34 35 def test_t1_read_on_full_returns_immediately(self): 36 """READ on T1 full cell returns data immediately.""" 37 env = simpy.Environment() 38 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 39 40 # Pre-populate T1 cell (addr < 256) to FULL 41 t1_addr = 100 42 sm.cells[t1_addr].pres = Presence.FULL 43 sm.cells[t1_addr].data_l = 0x1111 44 45 collector = simpy.Store(env) 46 sm.route_table[0] = collector 47 48 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 49 read_token = SMToken(target=0, addr=t1_addr, op=MemOp.READ, flags=None, data=None, ret=ret_route) 50 inject_token(env, sm.input_store, read_token) 51 52 env.run(until=100) 53 54 assert len(collector.items) == 1 55 assert collector.items[0].data == 0x1111 56 57 def test_t1_read_on_empty_defers_and_sets_waiting(self): 58 """READ on T1 empty cell sets WAITING and stashes return route.""" 59 env = simpy.Environment() 60 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 61 62 t1_addr = 150 63 assert sm.cells[t1_addr].pres == Presence.EMPTY 64 65 collector = simpy.Store(env) 66 sm.route_table[0] = collector 67 68 ret_route = CMToken(target=0, offset=5, act_id=1, data=0) 69 read_token = SMToken(target=0, addr=t1_addr, op=MemOp.READ, flags=None, data=None, ret=ret_route) 70 inject_token(env, sm.input_store, read_token) 71 72 env.run(until=100) 73 74 # Verify deferred read is set 75 assert sm.deferred_read is not None 76 assert sm.deferred_read.cell_addr == t1_addr 77 assert sm.cells[t1_addr].pres == Presence.WAITING 78 # No result token yet 79 assert len(collector.items) == 0 80 81 def test_t1_deferred_read_satisfied_by_write(self): 82 """WRITE on T1 WAITING cell satisfies deferred read.""" 83 env = simpy.Environment() 84 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 85 86 t1_addr = 120 87 collector = simpy.Store(env) 88 sm.route_table[0] = collector 89 90 def test_sequence(): 91 # First: READ on empty T1 cell to defer 92 ret_route = CMToken(target=0, offset=10, act_id=0, data=0) 93 read_token = SMToken(target=0, addr=t1_addr, op=MemOp.READ, flags=None, data=None, ret=ret_route) 94 yield sm.input_store.put(read_token) 95 96 # Second: WRITE to satisfy the deferred read 97 yield env.timeout(10) 98 write_token = SMToken(target=0, addr=t1_addr, op=MemOp.WRITE, flags=None, data=0x2222, ret=None) 99 yield sm.input_store.put(write_token) 100 101 env.process(test_sequence()) 102 env.run(until=100) 103 104 # Verify result token was emitted with correct data 105 assert len(collector.items) == 1 106 assert collector.items[0].data == 0x2222 107 108 109class TestAC4_2T0WriteDirect: 110 """AC4.2: SM WRITE to T0 stores data without presence checking.""" 111 112 def test_t0_write_stores_directly(self): 113 """WRITE to T0 (addr >= tier_boundary) stores in t0_store directly.""" 114 env = simpy.Environment() 115 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 116 117 t0_addr = 256 118 write_token = SMToken(target=0, addr=t0_addr, op=MemOp.WRITE, flags=None, data=0x3333, ret=None) 119 inject_token(env, sm.input_store, write_token) 120 121 env.run(until=100) 122 123 # Verify data in t0_store at correct index 124 t0_idx = t0_addr - 256 125 assert len(sm.t0_store) > t0_idx 126 assert sm.t0_store[t0_idx] == 0x3333 127 128 def test_t0_write_overwrites_previous_value(self): 129 """WRITE to T0 can overwrite previous value.""" 130 env = simpy.Environment() 131 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 132 133 # Pre-populate 134 sm.t0_store.append(0x1111) 135 136 # Overwrite at same index 137 t0_addr = 256 138 write_token = SMToken(target=0, addr=t0_addr, op=MemOp.WRITE, flags=None, data=0x4444, ret=None) 139 inject_token(env, sm.input_store, write_token) 140 141 env.run(until=100) 142 143 # Verify overwrite 144 assert sm.t0_store[0] == 0x4444 145 146 147class TestAC4_3T0ReadImmediate: 148 """AC4.3: SM READ on T0 address returns immediately (no deferral).""" 149 150 def test_t0_read_immediate_no_deferral(self): 151 """READ on T0 address returns immediately without I-structure blocking.""" 152 env = simpy.Environment() 153 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 154 155 # Pre-populate t0_store 156 sm.t0_store.append(0x5555) 157 158 collector = simpy.Store(env) 159 sm.route_table[0] = collector 160 161 t0_addr = 256 162 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 163 read_token = SMToken(target=0, addr=t0_addr, op=MemOp.READ, flags=None, data=None, ret=ret_route) 164 inject_token(env, sm.input_store, read_token) 165 166 env.run(until=100) 167 168 # Verify immediate result 169 assert len(collector.items) == 1 170 assert collector.items[0].data == 0x5555 171 # No deferred read 172 assert sm.deferred_read is None 173 174 def test_t0_read_on_empty_returns_zero(self): 175 """READ on empty T0 address returns 0.""" 176 env = simpy.Environment() 177 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 178 179 # t0_store is empty 180 collector = simpy.Store(env) 181 sm.route_table[0] = collector 182 183 t0_addr = 256 184 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 185 read_token = SMToken(target=0, addr=t0_addr, op=MemOp.READ, flags=None, data=None, ret=ret_route) 186 inject_token(env, sm.input_store, read_token) 187 188 env.run(until=100) 189 190 # Verify 0 returned 191 assert len(collector.items) == 1 192 assert collector.items[0].data == 0 193 194 195class TestAC4_5T0IStructureOpsRejected: 196 """AC4.5: I-structure ops (CLEAR, ALLOC, FREE, RD_INC, RD_DEC, CMP_SW) on T0 are rejected.""" 197 198 def test_t0_clear_rejected(self): 199 """CLEAR on T0 address is rejected (no-op).""" 200 env = simpy.Environment() 201 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 202 203 # Pre-populate t0_store 204 sm.t0_store.append(0xDEAD) 205 206 t0_addr = 256 207 clear_token = SMToken(target=0, addr=t0_addr, op=MemOp.CLEAR, flags=None, data=None, ret=None) 208 inject_token(env, sm.input_store, clear_token) 209 210 env.run(until=100) 211 212 # Verify t0_store unchanged 213 assert sm.t0_store[0] == 0xDEAD 214 215 def test_t0_alloc_rejected(self): 216 """ALLOC on T0 address is rejected.""" 217 env = simpy.Environment() 218 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 219 220 t0_addr = 256 221 alloc_token = SMToken(target=0, addr=t0_addr, op=MemOp.ALLOC, flags=None, data=None, ret=None) 222 inject_token(env, sm.input_store, alloc_token) 223 224 env.run(until=100) 225 226 # t0_store should remain empty 227 assert len(sm.t0_store) == 0 228 229 def test_t0_free_rejected(self): 230 """FREE on T0 address is rejected.""" 231 env = simpy.Environment() 232 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 233 234 # Pre-populate 235 sm.t0_store.append(0xBEEF) 236 237 t0_addr = 256 238 free_token = SMToken(target=0, addr=t0_addr, op=MemOp.FREE, flags=None, data=None, ret=None) 239 inject_token(env, sm.input_store, free_token) 240 241 env.run(until=100) 242 243 # t0_store unchanged 244 assert sm.t0_store[0] == 0xBEEF 245 246 def test_t0_rd_inc_rejected(self): 247 """RD_INC on T0 address is rejected.""" 248 env = simpy.Environment() 249 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 250 251 collector = simpy.Store(env) 252 sm.route_table[0] = collector 253 254 t0_addr = 256 255 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 256 inc_token = SMToken(target=0, addr=t0_addr, op=MemOp.RD_INC, flags=None, data=None, ret=ret_route) 257 inject_token(env, sm.input_store, inc_token) 258 259 env.run(until=100) 260 261 # No result token (operation rejected) 262 assert len(collector.items) == 0 263 264 def test_t0_rd_dec_rejected(self): 265 """RD_DEC on T0 address is rejected.""" 266 env = simpy.Environment() 267 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 268 269 collector = simpy.Store(env) 270 sm.route_table[0] = collector 271 272 t0_addr = 256 273 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 274 dec_token = SMToken(target=0, addr=t0_addr, op=MemOp.RD_DEC, flags=None, data=None, ret=ret_route) 275 inject_token(env, sm.input_store, dec_token) 276 277 env.run(until=100) 278 279 # No result token 280 assert len(collector.items) == 0 281 282 def test_t0_cmp_sw_rejected(self): 283 """CMP_SW on T0 address is rejected.""" 284 env = simpy.Environment() 285 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=256) 286 287 # Pre-populate t0_store 288 sm.t0_store.append(0x100) 289 290 collector = simpy.Store(env) 291 sm.route_table[0] = collector 292 293 t0_addr = 256 294 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 295 cmp_token = SMToken(target=0, addr=t0_addr, op=MemOp.CMP_SW, flags=0x100, data=0x200, ret=ret_route) 296 inject_token(env, sm.input_store, cmp_token) 297 298 env.run(until=100) 299 300 # No result token and t0_store unchanged 301 assert len(collector.items) == 0 302 assert sm.t0_store[0] == 0x100 303 304 305class TestAC4_6ConfigurableTierBoundary: 306 """AC4.6: Tier boundary is configurable via SMConfig; default is 256.""" 307 308 def test_default_tier_boundary_is_256(self): 309 """SMConfig creates SM with default tier_boundary=256.""" 310 env = simpy.Environment() 311 sm = StructureMemory(env, 0, cell_count=512) 312 assert sm.tier_boundary == 256 313 314 def test_custom_tier_boundary(self): 315 """SMConfig allows custom tier_boundary.""" 316 env = simpy.Environment() 317 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=128) 318 assert sm.tier_boundary == 128 319 320 def test_addr_below_custom_tier_boundary_is_t1(self): 321 """Addresses below custom tier_boundary use I-structure semantics.""" 322 env = simpy.Environment() 323 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=128) 324 325 # Addr 127 should be T1 (< 128) 326 t1_addr = 127 327 sm.cells[t1_addr].pres = Presence.FULL 328 sm.cells[t1_addr].data_l = 0x7777 329 330 collector = simpy.Store(env) 331 sm.route_table[0] = collector 332 333 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 334 read_token = SMToken(target=0, addr=t1_addr, op=MemOp.READ, flags=None, data=None, ret=ret_route) 335 inject_token(env, sm.input_store, read_token) 336 337 env.run(until=100) 338 339 # Verify I-structure read worked 340 assert len(collector.items) == 1 341 assert collector.items[0].data == 0x7777 342 343 def test_addr_at_custom_tier_boundary_is_t0(self): 344 """Addresses at or above custom tier_boundary use T0 semantics.""" 345 env = simpy.Environment() 346 sm = StructureMemory(env, 0, cell_count=512, tier_boundary=128) 347 348 # Addr 128 should be T0 (>= 128) 349 t0_addr = 128 350 351 collector = simpy.Store(env) 352 sm.route_table[0] = collector 353 354 # T0 read on empty should return 0, not defer 355 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 356 read_token = SMToken(target=0, addr=t0_addr, op=MemOp.READ, flags=None, data=None, ret=ret_route) 357 inject_token(env, sm.input_store, read_token) 358 359 env.run(until=100) 360 361 # Should return 0 immediately 362 assert len(collector.items) == 1 363 assert collector.items[0].data == 0 364 365 366class TestAC4_4T0SharedAcrossTopology: 367 """AC4.4: T0 storage is shared — all SMs reference the same T0 store.""" 368 369 def test_all_sms_share_same_t0_store(self): 370 """All SMs in topology reference identical t0_store object.""" 371 env = simpy.Environment() 372 373 sys = build_topology( 374 env, 375 [], 376 [ 377 SMConfig(sm_id=0, cell_count=512, tier_boundary=256), 378 SMConfig(sm_id=1, cell_count=512, tier_boundary=256), 379 SMConfig(sm_id=2, cell_count=512, tier_boundary=256), 380 ], 381 ) 382 383 # Verify all SMs share same t0_store object 384 assert sys.sms[0].t0_store is sys.sms[1].t0_store 385 assert sys.sms[1].t0_store is sys.sms[2].t0_store 386 387 def test_t0_write_by_sm0_visible_to_sm1(self): 388 """Data written to T0 by SM0 is visible to SM1 READ.""" 389 env = simpy.Environment() 390 391 sys = build_topology( 392 env, 393 [], 394 [ 395 SMConfig(sm_id=0, cell_count=512, tier_boundary=256), 396 SMConfig(sm_id=1, cell_count=512, tier_boundary=256), 397 ], 398 ) 399 400 # Collector for SM1 results 401 collector = simpy.Store(env) 402 sys.sms[1].route_table[0] = collector 403 404 def test_sequence(): 405 # SM0 writes to T0 406 write_token = SMToken(target=0, addr=256, op=MemOp.WRITE, flags=None, data=0x9999, ret=None) 407 yield sys.sms[0].input_store.put(write_token) 408 409 # SM1 reads from same T0 410 yield env.timeout(10) 411 ret_route = CMToken(target=0, offset=0, act_id=0, data=0) 412 read_token = SMToken(target=1, addr=256, op=MemOp.READ, flags=None, data=None, ret=ret_route) 413 yield sys.sms[1].input_store.put(read_token) 414 415 env.process(test_sequence()) 416 env.run(until=100) 417 418 # Verify SM1 read returned SM0's written data 419 assert len(collector.items) == 1 420 assert collector.items[0].data == 0x9999 421 422 423class TestAC6_1SMCellIsWideField: 424 """AC6.1: SMCell has is_wide field (default False).""" 425 426 def test_is_wide_defaults_to_false(self): 427 from sm_mod import Presence, SMCell 428 cell = SMCell(Presence.EMPTY, None, None) 429 assert cell.is_wide is False 430 431 def test_is_wide_can_be_set_true(self): 432 from sm_mod import Presence, SMCell 433 cell = SMCell(Presence.EMPTY, None, None, is_wide=True) 434 assert cell.is_wide is True 435 436 437class TestAC6_2IsWideMetadata: 438 """AC6.2: Existing I-structure behaviour unchanged (is_wide=False path).""" 439 440 def test_is_wide_false_preserves_existing_behavior(self): 441 """Frame-based PE can be created with proper configuration. 442 443 Note: This test has been updated for frame-based architecture. 444 The detailed token matching behavior is tested elsewhere (test_integration.py). 445 This test just verifies PE can be initialized with frame config. 446 """ 447 env = simpy.Environment() 448 449 from emu.types import PEConfig 450 from emu.pe import ProcessingElement 451 452 # Create PE with frame-based config 453 config = PEConfig( 454 pe_id=0, 455 frame_count=8, 456 frame_slots=64, 457 matchable_offsets=8, 458 ) 459 pe = ProcessingElement(env, 0, config) 460 461 # Verify PE was initialized correctly 462 assert pe.pe_id == 0 463 assert pe.frame_count == 8 464 assert len(pe.frames) == 8 465 assert all(len(f) == 64 for f in pe.frames) 466 assert len(pe.free_frames) == 8 # All frames initially free 467 assert len(pe.tag_store) == 0 # No activations allocated yet