feat: rewrite ProcessingElement with frame-based matching and output routing master
Task 2: Complete rewrite of emu/pe.py implementing frame-based architecture Acceptance Criteria Verified: - AC3.1: PE constructor accepts frame_count, frame_slots, matchable_offsets - AC3.2: Pipeline order IFETCH → act_id resolution → MATCH/FRAME → EXECUTE → EMIT - AC3.3: Dyadic matching uses tag_store + presence bits + frame SRAM - AC3.4: INHERIT output reads FrameDest from frame and routes token - AC3.5: CHANGE_TAG unpacks left operand (flit 1) to get destination - AC3.6: SINK writes result to frame slot, emits no token - AC3.7: EXTRACT_TAG produces packed flit 1 with PE/offset/act_id/port/kind - AC3.8: ALLOC/FREE frame control, FREE_FRAME opcode, ALLOC_REMOTE remote allocation - AC3.9: PELocalWriteToken with is_dest=True decodes FrameDest - AC3.10: Pipeline timing preserved (5 cycles dyadic, 4 monadic, 2 side paths) - AC1.6: Invalid act_id emits TokenRejected, doesn't crash Changes: - Complete ProcessingElement rewrite with frame-based architecture - Replace matching_store/gen_counters with frames/tag_store/presence/port_store - Implement frame allocation/deallocation (ALLOC, FREE via FrameControlToken) - Implement frame-based dyadic matching via presence bits - Implement mode-driven output routing (INHERIT, CHANGE_TAG, SINK) - Implement PE-level EXTRACT_TAG handling (pack current PE/offset/act_id) - Implement PE-level ALLOC_REMOTE handling (deliver FrameControlToken to target PE) - Implement side path handling for PELocalWriteToken (IRAM/frame writes) - Update PEConfig to accept optional pe_id and iram parameters - Add comprehensive frame-based PE tests (14 tests, all passing) Verification: - python -m pytest tests/test_pe_frames.py -v: All 14 tests pass - python -m pytest tests/test_alu.py tests/test_pe_frames.py -v: 54 total tests pass