OR-1 dataflow CPU sketch
at main 447 lines 14 kB view raw
1""" 2Tests for ProcessingElement event firing with frame-based redesign. 3 4Verifies: 5- PE fires TokenReceived when a token is dequeued 6- PE fires Matched when dyadic match completes 7- PE fires Executed after ALU execution 8- PE fires Emitted for each output token 9""" 10 11import simpy 12 13from cm_inst import ( 14 ArithOp, FrameDest, Instruction, LogicOp, MemOp, 15 OutputStyle, Port, RoutingOp, TokenKind, 16) 17from emu.events import ( 18 Emitted, Executed, Matched, TokenReceived, 19) 20from emu.pe import ProcessingElement 21from emu.types import PEConfig 22from tokens import DyadToken, MonadToken 23 24 25def inject_and_run(env, pe, token): 26 """Helper: inject token and run simulation.""" 27 def _put(): 28 yield pe.input_store.put(token) 29 env.process(_put()) 30 env.run() 31 32 33def inject_two_and_run(env, pe, token1, token2): 34 """Helper: inject two tokens and run simulation.""" 35 def _put(): 36 yield pe.input_store.put(token1) 37 yield pe.input_store.put(token2) 38 env.process(_put()) 39 env.run() 40 41 42class TestTokenReceived: 43 """PE fires TokenReceived event when a token is dequeued.""" 44 45 def test_token_received_on_monad(self): 46 """TokenReceived event fires for MonadToken.""" 47 env = simpy.Environment() 48 events = [] 49 50 config = PEConfig(pe_id=0, on_event=events.append) 51 pe = ProcessingElement(env=env, pe_id=0, config=config) 52 53 token = MonadToken(target=0, offset=0, act_id=0, data=0x1234, inline=False) 54 inject_and_run(env, pe, token) 55 56 # Verify TokenReceived event was fired 57 token_received_events = [e for e in events if isinstance(e, TokenReceived)] 58 assert len(token_received_events) >= 1 59 assert token_received_events[0].token == token 60 assert token_received_events[0].component == "pe:0" 61 62 def test_token_received_on_dyad(self): 63 """TokenReceived event fires for DyadToken.""" 64 env = simpy.Environment() 65 events = [] 66 67 add_inst = Instruction( 68 opcode=ArithOp.ADD, 69 output=OutputStyle.INHERIT, 70 has_const=False, 71 dest_count=1, 72 wide=False, 73 fref=8, 74 ) 75 76 dest = FrameDest( 77 target_pe=1, offset=0, act_id=0, 78 port=Port.L, token_kind=TokenKind.DYADIC, 79 ) 80 81 config = PEConfig( 82 pe_id=0, 83 iram={0: add_inst}, 84 initial_frames={0: {8: dest}}, 85 initial_tag_store={0: (0, 0)}, 86 on_event=events.append, 87 ) 88 89 pe = ProcessingElement(env=env, pe_id=0, config=config) 90 output_store = simpy.Store(env) 91 pe.route_table[1] = output_store 92 93 token = DyadToken(target=0, offset=0, act_id=0, data=0x5678, port=Port.L) 94 inject_and_run(env, pe, token) 95 96 # Verify TokenReceived event was fired 97 token_received_events = [e for e in events if isinstance(e, TokenReceived)] 98 assert len(token_received_events) >= 1 99 assert token_received_events[0].token == token 100 101 102class TestMatched: 103 """PE fires Matched event when dyadic pair matches.""" 104 105 def test_matched_left_then_right(self): 106 """Matched event fires when dyadic tokens pair (L then R).""" 107 env = simpy.Environment() 108 events = [] 109 110 add_inst = Instruction( 111 opcode=ArithOp.ADD, 112 output=OutputStyle.INHERIT, 113 has_const=False, 114 dest_count=1, 115 wide=False, 116 fref=8, 117 ) 118 119 dest = FrameDest( 120 target_pe=1, offset=0, act_id=0, 121 port=Port.L, token_kind=TokenKind.DYADIC, 122 ) 123 124 config = PEConfig( 125 pe_id=0, 126 iram={0: add_inst}, 127 initial_frames={0: {8: dest}}, 128 initial_tag_store={0: (0, 0)}, 129 on_event=events.append, 130 ) 131 132 pe = ProcessingElement(env=env, pe_id=0, config=config) 133 output_store = simpy.Store(env) 134 pe.route_table[1] = output_store 135 136 token_l = DyadToken(target=0, offset=0, act_id=0, data=0x1111, port=Port.L) 137 token_r = DyadToken(target=0, offset=0, act_id=0, data=0x2222, port=Port.R) 138 139 inject_two_and_run(env, pe, token_l, token_r) 140 141 # Verify Matched event was fired 142 matched_events = [e for e in events if isinstance(e, Matched)] 143 assert len(matched_events) >= 1 144 matched = matched_events[0] 145 assert matched.left == 0x1111 146 assert matched.right == 0x2222 147 148 def test_matched_right_then_left(self): 149 """Matched event fires with correct operand ordering (R then L).""" 150 env = simpy.Environment() 151 events = [] 152 153 add_inst = Instruction( 154 opcode=ArithOp.ADD, 155 output=OutputStyle.INHERIT, 156 has_const=False, 157 dest_count=1, 158 wide=False, 159 fref=8, 160 ) 161 162 dest = FrameDest( 163 target_pe=1, offset=5, act_id=1, 164 port=Port.L, token_kind=TokenKind.DYADIC, 165 ) 166 167 config = PEConfig( 168 pe_id=0, 169 iram={5: add_inst}, 170 initial_frames={0: {8: dest}}, 171 initial_tag_store={1: (0, 0)}, 172 on_event=events.append, 173 ) 174 175 pe = ProcessingElement(env=env, pe_id=0, config=config) 176 output_store = simpy.Store(env) 177 pe.route_table[1] = output_store 178 179 token_r = DyadToken(target=0, offset=5, act_id=1, data=0x4444, port=Port.R) 180 token_l = DyadToken(target=0, offset=5, act_id=1, data=0x3333, port=Port.L) 181 182 inject_two_and_run(env, pe, token_r, token_l) 183 184 # Verify Matched event with correct operand ordering 185 matched_events = [e for e in events if isinstance(e, Matched)] 186 assert len(matched_events) >= 1 187 matched = matched_events[0] 188 assert matched.left == 0x3333 # L port data 189 assert matched.right == 0x4444 # R port data 190 191 192class TestExecuted: 193 """PE fires Executed event after ALU execution.""" 194 195 def test_executed_add_instruction(self): 196 """Executed event fires for ADD instruction.""" 197 env = simpy.Environment() 198 events = [] 199 200 add_inst = Instruction( 201 opcode=ArithOp.ADD, 202 output=OutputStyle.INHERIT, 203 has_const=False, 204 dest_count=1, 205 wide=False, 206 fref=8, 207 ) 208 209 dest = FrameDest( 210 target_pe=1, offset=0, act_id=0, 211 port=Port.L, token_kind=TokenKind.DYADIC, 212 ) 213 214 config = PEConfig( 215 pe_id=0, 216 iram={0: add_inst}, 217 initial_frames={0: {8: dest}}, 218 initial_tag_store={0: (0, 0)}, 219 on_event=events.append, 220 ) 221 222 pe = ProcessingElement(env=env, pe_id=0, config=config) 223 output_store = simpy.Store(env) 224 pe.route_table[1] = output_store 225 226 token_l = DyadToken(target=0, offset=0, act_id=0, data=5, port=Port.L) 227 token_r = DyadToken(target=0, offset=0, act_id=0, data=3, port=Port.R) 228 229 inject_two_and_run(env, pe, token_l, token_r) 230 231 # Verify Executed event was fired 232 executed_events = [e for e in events if isinstance(e, Executed)] 233 assert len(executed_events) >= 1 234 executed = executed_events[0] 235 assert executed.op == ArithOp.ADD 236 assert executed.result == 8 # 5 + 3 237 238 def test_executed_comparison_instruction(self): 239 """Executed event fires for comparison instruction.""" 240 env = simpy.Environment() 241 events = [] 242 243 eq_inst = Instruction( 244 opcode=LogicOp.EQ, 245 output=OutputStyle.INHERIT, 246 has_const=False, 247 dest_count=1, 248 wide=False, 249 fref=8, 250 ) 251 252 dest = FrameDest( 253 target_pe=1, offset=0, act_id=0, 254 port=Port.L, token_kind=TokenKind.DYADIC, 255 ) 256 257 config = PEConfig( 258 pe_id=0, 259 iram={0: eq_inst}, 260 initial_frames={0: {8: dest}}, 261 initial_tag_store={0: (0, 0)}, 262 on_event=events.append, 263 ) 264 265 pe = ProcessingElement(env=env, pe_id=0, config=config) 266 output_store = simpy.Store(env) 267 pe.route_table[1] = output_store 268 269 token_l = DyadToken(target=0, offset=0, act_id=0, data=5, port=Port.L) 270 token_r = DyadToken(target=0, offset=0, act_id=0, data=5, port=Port.R) 271 272 inject_two_and_run(env, pe, token_l, token_r) 273 274 # Verify Executed event was fired 275 executed_events = [e for e in events if isinstance(e, Executed)] 276 assert len(executed_events) >= 1 277 executed = executed_events[0] 278 assert executed.op == LogicOp.EQ 279 assert executed.bool_out is True # 5 == 5 280 281 282class TestEmitted: 283 """PE fires Emitted event for each output token.""" 284 285 def test_emitted_single_mode(self): 286 """Emitted event fires for SINGLE mode output.""" 287 env = simpy.Environment() 288 events = [] 289 290 add_inst = Instruction( 291 opcode=ArithOp.ADD, 292 output=OutputStyle.INHERIT, 293 has_const=False, 294 dest_count=1, 295 wide=False, 296 fref=8, 297 ) 298 299 dest = FrameDest( 300 target_pe=1, offset=0, act_id=0, 301 port=Port.L, token_kind=TokenKind.DYADIC, 302 ) 303 304 config = PEConfig( 305 pe_id=0, 306 iram={0: add_inst}, 307 initial_frames={0: {8: dest}}, 308 initial_tag_store={0: (0, 0)}, 309 on_event=events.append, 310 ) 311 312 pe = ProcessingElement(env=env, pe_id=0, config=config) 313 output_store = simpy.Store(env) 314 pe.route_table[1] = output_store 315 316 token_l = DyadToken(target=0, offset=0, act_id=0, data=10, port=Port.L) 317 token_r = DyadToken(target=0, offset=0, act_id=0, data=20, port=Port.R) 318 319 inject_two_and_run(env, pe, token_l, token_r) 320 321 # Verify Emitted events were fired 322 emitted_events = [e for e in events if isinstance(e, Emitted)] 323 assert len(emitted_events) >= 1 324 assert emitted_events[0].token.data == 30 325 326 def test_emitted_dual_mode(self): 327 """Emitted event fires for DUAL mode output (two tokens).""" 328 env = simpy.Environment() 329 events = [] 330 331 add_inst = Instruction( 332 opcode=ArithOp.ADD, 333 output=OutputStyle.INHERIT, 334 has_const=False, 335 dest_count=2, 336 wide=False, 337 fref=8, 338 ) 339 340 dest_l = FrameDest( 341 target_pe=1, offset=0, act_id=0, 342 port=Port.L, token_kind=TokenKind.DYADIC, 343 ) 344 dest_r = FrameDest( 345 target_pe=2, offset=1, act_id=0, 346 port=Port.L, token_kind=TokenKind.DYADIC, 347 ) 348 349 config = PEConfig( 350 pe_id=0, 351 iram={0: add_inst}, 352 initial_frames={0: {8: dest_l, 9: dest_r}}, 353 initial_tag_store={0: (0, 0)}, 354 on_event=events.append, 355 ) 356 357 pe = ProcessingElement(env=env, pe_id=0, config=config) 358 output_l = simpy.Store(env) 359 output_r = simpy.Store(env) 360 pe.route_table[1] = output_l 361 pe.route_table[2] = output_r 362 363 token_l = DyadToken(target=0, offset=0, act_id=0, data=10, port=Port.L) 364 token_r = DyadToken(target=0, offset=0, act_id=0, data=20, port=Port.R) 365 366 inject_two_and_run(env, pe, token_l, token_r) 367 368 # Verify Emitted events were fired (two for DUAL mode) 369 emitted_events = [e for e in events if isinstance(e, Emitted)] 370 assert len(emitted_events) >= 2 371 372 def test_emitted_switch_mode(self): 373 """Emitted event fires for SWITCH mode output (data + trigger).""" 374 env = simpy.Environment() 375 events = [] 376 377 sweq_inst = Instruction( 378 opcode=RoutingOp.SWEQ, 379 output=OutputStyle.INHERIT, 380 has_const=False, 381 dest_count=2, 382 wide=False, 383 fref=8, 384 ) 385 386 dest_l = FrameDest( 387 target_pe=1, offset=0, act_id=0, 388 port=Port.L, token_kind=TokenKind.MONADIC, 389 ) 390 dest_r = FrameDest( 391 target_pe=2, offset=1, act_id=0, 392 port=Port.L, token_kind=TokenKind.MONADIC, 393 ) 394 395 config = PEConfig( 396 pe_id=0, 397 iram={0: sweq_inst}, 398 initial_frames={0: {8: dest_l, 9: dest_r}}, 399 initial_tag_store={0: (0, 0)}, 400 on_event=events.append, 401 ) 402 403 pe = ProcessingElement(env=env, pe_id=0, config=config) 404 output_l = simpy.Store(env) 405 output_r = simpy.Store(env) 406 pe.route_table[1] = output_l 407 pe.route_table[2] = output_r 408 409 token_l = DyadToken(target=0, offset=0, act_id=0, data=0x1234, port=Port.L) 410 token_r = DyadToken(target=0, offset=0, act_id=0, data=0x1234, port=Port.R) 411 412 inject_two_and_run(env, pe, token_l, token_r) 413 414 # Verify Emitted events were fired (two for SWITCH) 415 emitted_events = [e for e in events if isinstance(e, Emitted)] 416 assert len(emitted_events) >= 2 417 418 def test_emitted_suppress_mode(self): 419 """Emitted event does not fire for SUPPRESS mode.""" 420 env = simpy.Environment() 421 events = [] 422 423 # FREE_FRAME (SUPPRESS mode) 424 free_inst = Instruction( 425 opcode=RoutingOp.FREE_FRAME, 426 output=OutputStyle.SINK, 427 has_const=False, 428 dest_count=0, 429 wide=False, 430 fref=8, 431 ) 432 433 config = PEConfig( 434 pe_id=0, 435 iram={0: free_inst}, 436 initial_tag_store={0: (0, 0)}, 437 on_event=events.append, 438 ) 439 440 pe = ProcessingElement(env=env, pe_id=0, config=config) 441 442 token = MonadToken(target=0, offset=0, act_id=0, data=100, inline=False) 443 inject_and_run(env, pe, token) 444 445 # Verify no Emitted events (SUPPRESS mode) 446 emitted_events = [e for e in events if isinstance(e, Emitted)] 447 assert len(emitted_events) == 0