OR-1 dataflow CPU sketch

fix: address Phase 7 code review issues

- CRITICAL 1: Rewrite snapshot tests to use frame-based state (frames, tag_store, presence, port_store, free_frames) instead of removed matching_store and gen_counters
- test_capture_pe_matching_store_structure → test_capture_pe_frame_structure
- test_capture_pe_gen_counters → test_capture_pe_tag_store
- test_capture_multiple_pes_and_sms: updated to test frame state instead

- CRITICAL 2: Fix monitor/server.py DyadToken construction (inject and send commands)
- Change ctx= to act_id= parameter
- Remove old fields gen=0, wide=False
- Update JSON key from 'ctx' to 'act_id'

- CRITICAL 3: Fix test_backend.py MonadToken construction
- Change ctx= to act_id= in test_inject_token_appears_in_snapshot
- Change ctx= to act_id= in test_send_token_respects_backpressure

- IMPORTANT 1: Rewrite test_capture_sm_t0_store to use int values
- T0 store is now list[int], not list[Token]
- Changed from appending MonadToken objects to appending int values (777, 888)

- IMPORTANT 2: Fix formatting.py T0 store display
- Change 'tokens' to 'entries' in T0 store formatting line

Orual b17f212e 7842e5f1

+51 -51
+1 -1
monitor/formatting.py
··· 277 277 278 278 # T0 store 279 279 lines.append( 280 - f" T0 store: {colour(str(len(sm_snapshot.t0_store)), 'white')} tokens" 280 + f" T0 store: {colour(str(len(sm_snapshot.t0_store)), 'white')} entries" 281 281 ) 282 282 283 283 # Input queue
+6 -6
monitor/server.py
··· 148 148 # Construct a CMToken from command data 149 149 target = cmd_json.get("target", 0) 150 150 offset = cmd_json.get("offset", 0) 151 - ctx = cmd_json.get("ctx", 0) 151 + act_id = cmd_json.get("act_id", 0) 152 152 data = cmd_json.get("data", 0) 153 153 token = DyadToken( 154 - target=target, offset=offset, ctx=ctx, data=data, 155 - port=Port.L, gen=0, wide=False 154 + target=target, offset=offset, act_id=act_id, data=data, 155 + port=Port.L 156 156 ) 157 157 result = await loop.run_in_executor( 158 158 None, backend.send_command, ··· 163 163 # Same as inject but respects backpressure 164 164 target = cmd_json.get("target", 0) 165 165 offset = cmd_json.get("offset", 0) 166 - ctx = cmd_json.get("ctx", 0) 166 + act_id = cmd_json.get("act_id", 0) 167 167 data = cmd_json.get("data", 0) 168 168 token = DyadToken( 169 - target=target, offset=offset, ctx=ctx, data=data, 170 - port=Port.L, gen=0, wide=False 169 + target=target, offset=offset, act_id=act_id, data=data, 170 + port=Port.L 171 171 ) 172 172 result = await loop.run_in_executor( 173 173 None, backend.send_command,
+2 -2
tests/test_backend.py
··· 359 359 backend._handle_load(source) 360 360 361 361 # Inject a token 362 - token = MonadToken(target=0, offset=0, ctx=0, data=99, inline=True) 362 + token = MonadToken(target=0, offset=0, act_id=0, data=99, inline=True) 363 363 result = backend._handle_inject(token) 364 364 365 365 assert isinstance(result, StepResult) ··· 383 383 backend._handle_load(source) 384 384 385 385 # Send a token (should go through SimPy backpressure mechanism) 386 - token = MonadToken(target=0, offset=0, ctx=0, data=77, inline=True) 386 + token = MonadToken(target=0, offset=0, act_id=0, data=77, inline=True) 387 387 result = backend._handle_send(token) 388 388 389 389 assert isinstance(result, StepResult)
+42 -42
tests/test_snapshot.py
··· 103 103 # Since no cells were written, cells dict should be empty 104 104 assert sm_snap.cells == {} 105 105 106 - def test_capture_pe_matching_store_structure(self): 107 - """Test that PE matching store is captured with correct structure.""" 106 + def test_capture_pe_frame_structure(self): 107 + """Test that PE frame state is captured with correct structure.""" 108 108 env = simpy.Environment() 109 109 110 110 iram = {0: Instruction(opcode=ArithOp.ADD, output=OutputStyle.INHERIT, has_const=False, dest_count=2, wide=False, fref=0)} 111 - pe_configs = [PEConfig(pe_id=0, iram=iram, frame_count=4, offsets=64)] 111 + pe_configs = [PEConfig(pe_id=0, iram=iram, frame_count=4, frame_slots=64)] 112 112 sm_configs = [] 113 113 114 114 system = build_topology(env, pe_configs, sm_configs) ··· 116 116 snapshot = capture(system) 117 117 118 118 pe_snap = snapshot.pes[0] 119 - # matching_store should be a tuple of tuples (frozen dataclass) 120 - assert isinstance(pe_snap.matching_store, tuple) 121 - assert len(pe_snap.matching_store) == 4 # 4 context slots 122 - for ctx_row in pe_snap.matching_store: 123 - assert isinstance(ctx_row, tuple) 124 - assert len(ctx_row) == 64 # 64 offsets per context 119 + # frames should be a tuple of tuples (frozen dataclass) 120 + assert isinstance(pe_snap.frames, tuple) 121 + assert len(pe_snap.frames) == 4 # 4 frames 122 + for frame in pe_snap.frames: 123 + assert isinstance(frame, tuple) 124 + # Each frame has slots 125 + 126 + # tag_store should be dict mapping act_id to frame_id 127 + assert isinstance(pe_snap.tag_store, dict) 128 + 129 + # presence should be a tuple of tuples (frame_count x matchable_offsets) 130 + assert isinstance(pe_snap.presence, tuple) 131 + assert len(pe_snap.presence) == 4 # 4 frames 132 + 133 + # port_store should be a tuple of tuples 134 + assert isinstance(pe_snap.port_store, tuple) 135 + assert len(pe_snap.port_store) == 4 # 4 frames 125 136 126 - # Each entry should have occupied, data, port 127 - for ctx_row in pe_snap.matching_store: 128 - for entry in ctx_row: 129 - assert "occupied" in entry 130 - assert "data" in entry 131 - assert "port" in entry 132 - # Unoccupied entries should have port=None 133 - assert entry["occupied"] is False 134 - assert entry["data"] is None 135 - assert entry["port"] is None 137 + # free_frames should be a tuple of frame IDs 138 + assert isinstance(pe_snap.free_frames, tuple) 136 139 137 140 def test_capture_pe_output_log(self): 138 141 """Test that PE output_log is captured.""" ··· 168 171 with pytest.raises(AttributeError): 169 172 snapshot.pes = {} 170 173 171 - def test_capture_pe_gen_counters(self): 172 - """Test that PE generation counters are captured.""" 174 + def test_capture_pe_tag_store(self): 175 + """Test that PE tag_store is captured.""" 173 176 env = simpy.Environment() 174 177 175 178 iram = {0: Instruction(opcode=RoutingOp.CONST, output=OutputStyle.SINK, has_const=True, dest_count=0, wide=False, fref=0)} 176 - pe_configs = [PEConfig(pe_id=0, iram=iram)] 179 + pe_configs = [PEConfig(pe_id=0, iram=iram, frame_count=8)] 177 180 system = build_topology(env, pe_configs, []) 178 181 179 - # Simulate generation counter increment 180 - system.pes[0].gen_counters[0] = 5 181 - system.pes[0].gen_counters[1] = 10 182 - 182 + # Verify tag_store is captured 183 183 snapshot = capture(system) 184 184 185 185 pe_snap = snapshot.pes[0] 186 - assert pe_snap.gen_counters[0] == 5 187 - assert pe_snap.gen_counters[1] == 10 186 + # tag_store should be a dict mapping act_id to frame_id 187 + assert isinstance(pe_snap.tag_store, dict) 188 + assert hasattr(pe_snap, 'frames') 189 + assert hasattr(pe_snap, 'free_frames') 188 190 189 191 def test_capture_sm_deferred_read(self): 190 192 """Test that SM deferred_read state is captured.""" ··· 214 216 system = build_topology(env, [], sm_configs) 215 217 216 218 sm = system.sms[0] 217 - # Add some tokens to T0 store 218 - token1 = MonadToken(target=0, offset=5, act_id=0, data=777, inline=True) 219 - token2 = MonadToken(target=0, offset=6, act_id=0, data=888, inline=True) 220 - sm.t0_store.append(token1) 221 - sm.t0_store.append(token2) 219 + # Add some int values to T0 store 220 + sm.t0_store.append(777) 221 + sm.t0_store.append(888) 222 222 223 223 snapshot = capture(system) 224 224 225 225 sm_snap = snapshot.sms[0] 226 226 assert len(sm_snap.t0_store) == 2 227 - assert sm_snap.t0_store[0] == token1 228 - assert sm_snap.t0_store[1] == token2 227 + assert sm_snap.t0_store[0] == 777 228 + assert sm_snap.t0_store[1] == 888 229 229 230 230 def test_capture_multiple_pes_and_sms(self): 231 231 """Test capturing state of a system with multiple PEs and SMs.""" 232 232 env = simpy.Environment() 233 233 234 234 pe_configs = [ 235 - PEConfig(pe_id=0, iram={0: Instruction(opcode=RoutingOp.CONST, output=OutputStyle.SINK, has_const=True, dest_count=0, wide=False, fref=0)}), 236 - PEConfig(pe_id=1, iram={0: Instruction(opcode=RoutingOp.CONST, output=OutputStyle.SINK, has_const=True, dest_count=0, wide=False, fref=0)}), 235 + PEConfig(pe_id=0, iram={0: Instruction(opcode=RoutingOp.CONST, output=OutputStyle.SINK, has_const=True, dest_count=0, wide=False, fref=0)}, frame_count=4), 236 + PEConfig(pe_id=1, iram={0: Instruction(opcode=RoutingOp.CONST, output=OutputStyle.SINK, has_const=True, dest_count=0, wide=False, fref=0)}, frame_count=4), 237 237 ] 238 238 sm_configs = [ 239 239 SMConfig(sm_id=0, cell_count=256), ··· 243 243 system = build_topology(env, pe_configs, sm_configs) 244 244 245 245 # Set up some state 246 - system.pes[0].gen_counters[0] = 7 247 - system.pes[1].gen_counters[0] = 13 248 246 system.sms[0].cells[5].pres = Presence.FULL 249 247 system.sms[0].cells[5].data_l = 50 250 248 system.sms[1].cells[10].pres = Presence.FULL ··· 256 254 assert len(snapshot.pes) == 2 257 255 assert len(snapshot.sms) == 2 258 256 259 - # Verify PE state 260 - assert snapshot.pes[0].gen_counters[0] == 7 261 - assert snapshot.pes[1].gen_counters[0] == 13 257 + # Verify PE frame state is captured 258 + assert len(snapshot.pes[0].frames) == 4 259 + assert len(snapshot.pes[1].frames) == 4 260 + assert isinstance(snapshot.pes[0].tag_store, dict) 261 + assert isinstance(snapshot.pes[1].tag_store, dict) 262 262 263 263 # Verify SM state 264 264 assert snapshot.sms[0].cells[5].pres == Presence.FULL