OR-1 dataflow CPU sketch

docs: fix CLAUDE.md inaccuracies from PE frame-based redesign

Orual 5bbf02f7 f8ad84fc

+47 -27
+43 -23
CLAUDE.md
··· 50 50 51 51 ## Project Structure 52 52 53 - - `cm_inst.py` — Instruction set definitions (Port, MemOp, ALUOp hierarchy, Instruction, FrameDest, OutputStyle, TokenKind) 54 - - `tokens.py` — Token type hierarchy (Token -> CMToken -> DyadToken/MonadToken/PELocalWriteToken/FrameControlToken; SMToken). Imports ISA enums from cm_inst. 53 + - `cm_inst.py` — Instruction set definitions (Port, MemOp, ALUOp hierarchy, Instruction, FrameDest, OutputStyle, TokenKind, FrameOp, FrameSlotValue) 54 + - `tokens.py` — Token type hierarchy (Token -> PEToken -> CMToken -> DyadToken/MonadToken; PEToken -> PELocalWriteToken/FrameControlToken; Token -> SMToken). Imports ISA enums from cm_inst. 55 + - `encoding.py` — Pack/unpack boundary between semantic types and 16-bit hardware words (instruction encoding, flit1 routing words) 55 56 - `sm_mod.py` — Structure Memory cell model (Presence enum, SMCell dataclass with `is_wide` metadata flag) 56 57 - `dfasm.lark` — Lark grammar for dfasm graph assembly language 57 58 - `emu/` — Behavioural emulator package (SimPy-based discrete event simulation) 58 59 - `emu/events.py` — Simulation event types: `SimEvent` union (TokenReceived, Matched, Executed, Emitted, IRAMWritten, FrameSlotWritten, FrameAllocated, FrameFreed, TokenRejected, CellWritten, DeferredRead, DeferredSatisfied, ResultSent) and `EventCallback` type alias 59 - - `emu/types.py` — Config and internal types (PEConfig, SMConfig, DeferredRead, FrameSlotValue). PEConfig/SMConfig accept `on_event: EventCallback | None` for simulation observability. 60 + - `emu/types.py` — Config and internal types (PEConfig, SMConfig, DeferredRead). PEConfig/SMConfig accept `on_event: EventCallback | None` for simulation observability. 60 61 - `emu/alu.py` — Pure-function ALU: `execute(op, left, right, const) -> (result, bool_out)` 61 62 - `emu/pe.py` — ProcessingElement: matching store, IRAM fetch, output routing, event emission via `on_event` callback 62 63 - `emu/sm.py` — StructureMemory: I-structure semantics with deferred reads, event emission via `on_event` callback ··· 103 104 - `tests/test_sm_tiers.py` — T0/T1 memory tier and EXEC bootstrap tests 104 105 - `tests/test_exec_bootstrap.py` — EXEC opcode acceptance criteria tests 105 106 - `tests/test_migration_cleanup.py` — Verifies removed types (SysToken, CfgOp, etc.) are absent from codebase 106 - - `tests/test_pe_events.py` — PE event emission tests (TokenReceived, Matched, Executed, Emitted, IRAMWritten) 107 + - `tests/test_pe_events.py` — PE event emission tests (TokenReceived, Matched, Executed, Emitted, IRAMWritten, FrameAllocated, FrameFreed, FrameSlotWritten, TokenRejected) 108 + - `tests/test_pe_frames.py` — Frame-based PE matching, routing, and lifecycle tests 107 109 - `tests/test_sm_events.py` — SM event emission tests (CellWritten, DeferredRead, DeferredSatisfied, ResultSent) 108 - - `tests/test_cycle_timing.py` — Cycle-accurate timing verification tests (PE pipeline stages, SM operation timing, network delivery latency, parallel execution) 110 + - `tests/test_cycle_timing.py` — Cycle-accurate timing verification tests 109 111 - `tests/test_network_events.py` — Network-level event propagation tests 112 + - `tests/test_network_routing.py` — Network routing and connectivity tests 113 + - `tests/test_foundation_types.py` — Foundation type (Instruction, FrameDest, encoding) tests 114 + - `tests/test_encoding.py` — Pack/unpack encoding tests for instructions and flit1 words 115 + - `tests/test_ir_frame_types.py` — IR frame-related type tests 116 + - `tests/test_allocate_frames.py` — Frame layout allocation tests 117 + - `tests/test_codegen_frames.py` — Frame-based code generation tests 118 + - `tests/test_sm_t0_raw.py` — SM T0 raw storage tests 110 119 - `tests/test_backend.py` — SimulationBackend command/result protocol tests 111 120 - `tests/test_snapshot.py` — StateSnapshot capture tests 112 121 - `tests/test_repl.py` — MonitorREPL command tests ··· 137 146 138 147 All tokens inherit from `Token(target: int)`. The hierarchy: 139 148 140 - - `CMToken(Token)` -- adds `act_id`, `data` (frozen dataclass, base for data-carrying tokens) 141 - - `DyadToken(CMToken)` -- adds `port: Port`, `gen: int` (dyadic operand requiring match) 142 - - `MonadToken(CMToken)` -- monadic operand (no match required) 143 - - `PELocalWriteToken(CMToken)` -- adds `offset: int`, `values: list[int]` (writes IRAM directly) 144 - - `FrameControlToken(CMToken)` -- frame lifecycle control (no data) 149 + - `PEToken(Token)` -- base for all PE-targeted tokens (used for routing in network.py) 150 + - `CMToken(PEToken)` -- adds `offset: int`, `act_id: int`, `data: int` (frozen dataclass, base for data-carrying tokens) 151 + - `DyadToken(CMToken)` -- adds `port: Port` (dyadic operand requiring match) 152 + - `MonadToken(CMToken)` -- adds `inline: bool` (monadic operand, no match required) 153 + - `PELocalWriteToken(PEToken)` -- adds `act_id`, `region`, `slot`, `data`, `is_dest` (writes frame slots directly) 154 + - `FrameControlToken(PEToken)` -- adds `act_id`, `op: FrameOp`, `payload: int` (frame alloc/free) 145 155 - `SMToken(Token)` -- `addr: int`, `op: MemOp`, `flags`, `data`, `ret: Optional[CMToken]` 146 156 147 157 ### Instruction Set (cm_inst.py) 148 158 149 159 - `ALUOp(IntEnum)` base with subclasses: `ArithOp`, `LogicOp`, `RoutingOp` 150 - - `MemOp(IntEnum)` -- read/write/atomic ops; includes WRITE, READ, FREE, CLEAR, EXEC, SET_PAGE, WRITE_IMM for T0 tier control 151 - - `Instruction(op, has_const: bool, fref: int)` -- unified instruction type for both ALU and memory ops, stored in PE IRAM 160 + - `MemOp(IntEnum)` -- read/write/atomic ops (READ, WRITE, EXEC, ALLOC, FREE, EXT, CLEAR, RD_INC, RD_DEC, CMP_SW, RAW_READ, SET_PAGE, WRITE_IMM) 161 + - `Instruction(opcode, output: OutputStyle, has_const, dest_count, wide, fref)` -- unified instruction type for both ALU and memory ops, stored in PE IRAM 152 162 - `FrameDest(target_pe: int, offset: int, act_id: int, port: Port, token_kind: TokenKind)` -- destination address resolved from frame slot 163 + - `FrameSlotValue = int | FrameDest | None` -- type alias for frame slot contents 153 164 - `OutputStyle` enum -- INHERIT, CHANGE_TAG, SINK for output routing decisions 154 - - `TokenKind` enum -- DYADIC, MONADIC for token kind classification 165 + - `TokenKind` enum -- DYADIC, MONADIC, INLINE for token kind classification 166 + - `FrameOp(IntEnum)` -- ALLOC, FREE for frame lifecycle control tokens 155 167 - `is_monadic_alu(op: ALUOp) -> bool` -- canonical source of truth for monadic ALU op classification (used by `emu/pe.py` and `asm/opcodes.py`) 156 168 157 169 ### ALU (emu/alu.py) ··· 178 190 179 191 **Token Processing Pipeline:** 180 192 - Side paths (FrameControlToken, PELocalWriteToken): 1 cycle 181 - - Dyadic CMToken: 5 cycles (dequeue + MATCH + IFETCH + EXECUTE + EMIT) 193 + - Dyadic CMToken: 5 cycles (dequeue + IFETCH + MATCH + EXECUTE + EMIT) 182 194 - Monadic CMToken: 4 cycles (dequeue + IFETCH + EXECUTE + EMIT) 183 195 184 196 **Matching Logic:** ··· 187 199 - If slot occupied: retrieve partner data and port, clear presence bit, fire instruction with both operands 188 200 - Port ordering: partner with Port.L goes to left operand; Port.R to right operand 189 201 190 - **Output Routing** (determined by `Instruction.mode`): 202 + **Output Routing** (determined by `Instruction.output`): 191 203 - `OutputStyle.INHERIT` -- routes to destinations specified in frame slots 192 204 - `OutputStyle.CHANGE_TAG` -- routes with different act_id tag (context switch) 193 205 - `OutputStyle.SINK` -- writes result to frame slot, emits no token 194 206 195 207 **Frame Initialization:** 196 - - PE constructor loads initial_frames from PEConfig (dict or list format) 197 - - Dict format: {frame_id: {slot_idx: int_value}} where int_value is unpacked via unpack_flit1() to FrameDest 208 + - PE constructor loads initial_frames from PEConfig: `dict[int, dict[int, FrameSlotValue] | list[FrameSlotValue]]` 209 + - Dict value format: `{slot_idx: value}` where int values are unpacked via `unpack_flit1()` to FrameDest 210 + - List value format: `[slot0, slot1, ...]` raw values assigned by index 198 211 - Handles both codegen-produced packed integers and test-produced FrameDest objects 199 212 200 213 **Output logging:** 201 214 - `PE.output_log: list` records every token emitted (for testing and tracing) 202 215 203 216 **PELocalWriteToken handling:** 204 - - Writes values directly to IRAM at specified offset (1 cycle) 217 + - Writes data to frame slot at specified region/slot within the act_id's frame (1 cycle) 205 218 206 219 ### Structure Memory (emu/sm.py) 207 220 ··· 244 257 - If `PEConfig.allowed_pe_routes` or `allowed_sm_routes` is set, `build_topology` restricts routes at construction time 245 258 246 259 **System API:** 247 - - `System.inject(token: Token)` -- route token by type: SMToken → target SM, CMToken → target PE (IRAMWriteToken routes to PE automatically as CMToken subclass) (direct append, bypasses FIFO) 260 + - `System.inject(token: Token)` -- route token by type: SMToken → target SM, PEToken → target PE (direct append, bypasses FIFO) 248 261 - `System.send(token: Token)` -- same routing as inject() but yields `env.timeout(1)` for 1-cycle delivery latency then `store.put()` (SimPy generator, respects FIFO backpressure) 249 262 - `System.load(tokens: list[Token])` -- spawns SimPy process that calls send() for each token in order 250 263 251 - **PEConfig extensions (emu/types.py):** 264 + **PEConfig (emu/types.py):** 265 + - `pe_id: int`, `iram: dict[int, Instruction] | None`, `frame_count: int = 8`, `frame_slots: int = 64`, `matchable_offsets: int = 8` 266 + - `initial_frames: Optional[dict[int, list[FrameSlotValue]]]` -- pre-loaded frame data 267 + - `initial_tag_store: Optional[dict[int, int]]` -- pre-loaded act_id -> frame_id mappings 252 268 - `allowed_pe_routes: Optional[set[int]]` -- if set, restrict PE route_table to these PE IDs 253 269 - `allowed_sm_routes: Optional[set[int]]` -- if set, restrict PE sm_routes to these SM IDs 254 - - `on_event: EventCallback | None` -- if set, PE fires `SimEvent` for every token receive, match, execute, emit, and IRAM write 270 + - `on_event: EventCallback | None` -- if set, PE fires `SimEvent` for every token receive, match, execute, emit, frame alloc/free, slot write, and rejection 255 271 256 272 **SMConfig (emu/types.py):** 257 273 - `sm_id: int`, `cell_count: int = 512`, `initial_cells: Optional[dict]`, `tier_boundary: int = 256` ··· 265 281 266 282 **Event types:** 267 283 - `TokenReceived(time, component, token)` -- PE/SM received a token 268 - - `Matched(time, component, left, right, ctx, offset)` -- PE matched a dyadic pair 284 + - `Matched(time, component, left, right, act_id, offset, frame_id)` -- PE matched a dyadic pair 269 285 - `Executed(time, component, op, result, bool_out)` -- PE executed an ALU instruction 270 286 - `Emitted(time, component, token)` -- PE emitted an output token 271 287 - `IRAMWritten(time, component, offset, count)` -- PE wrote instructions to IRAM 288 + - `FrameAllocated(time, component, act_id, frame_id)` -- PE allocated a frame 289 + - `FrameFreed(time, component, act_id, frame_id)` -- PE freed a frame 290 + - `FrameSlotWritten(time, component, frame_id, slot, value)` -- PE wrote to a frame slot 291 + - `TokenRejected(time, component, token, reason)` -- PE rejected a token (e.g., act_id not in tag store) 272 292 - `CellWritten(time, component, addr, old_pres, new_pres)` -- SM cell presence changed 273 293 - `DeferredRead(time, component, addr)` -- SM registered a deferred read 274 294 - `DeferredSatisfied(time, component, addr, data)` -- SM satisfied a deferred read 275 295 - `ResultSent(time, component, token)` -- SM sent a result token back 276 296 277 - **Union type:** `SimEvent = TokenReceived | Matched | Executed | Emitted | IRAMWritten | CellWritten | DeferredRead | DeferredSatisfied | ResultSent` 297 + **Union type:** `SimEvent = TokenReceived | Matched | Executed | Emitted | IRAMWritten | FrameAllocated | FrameFreed | FrameSlotWritten | TokenRejected | CellWritten | DeferredRead | DeferredSatisfied | ResultSent` 278 298 279 299 **Callback type:** `EventCallback = Callable[[SimEvent], None]` 280 300
+4 -4
monitor/CLAUDE.md
··· 1 1 # OR1 Monitor (monitor/) 2 2 3 - Last verified: 2026-02-26 3 + Last verified: 2026-03-07 4 4 5 5 ## Purpose 6 6 ··· 26 26 27 27 ## Dependencies 28 28 29 - - **Uses**: `cm_inst` (MemOp, Port), `tokens` (Token types), `sm_mod` (Presence), `emu/` (events, types, network), `asm/` (run_pipeline, codegen, ir), `dfgraph/categories` (opcode categorisation for graph JSON) 29 + - **Uses**: `cm_inst` (MemOp, Port, Instruction, FrameSlotValue), `tokens` (Token types), `sm_mod` (Presence), `emu/` (events, types, network), `asm/` (run_pipeline, codegen, ir), `dfgraph/categories` (opcode categorisation for graph JSON) 30 30 - **Used by**: CLI entry point (`python -m monitor`), test suite 31 31 - **Boundary**: `cm_inst`, `tokens`, `sm_mod`, `emu/`, and `asm/` must NEVER import from `monitor/` 32 32 ··· 52 52 - `__init__.py` -- Public API exports 53 53 - `backend.py` -- `SimulationBackend` class with thread lifecycle and command dispatch 54 54 - `commands.py` -- All command and result frozen dataclasses, `SimCommand` union type 55 - - `snapshot.py` -- `StateSnapshot`, `PESnapshot`, `SMSnapshot`, `SMCellSnapshot`, `capture()` 55 + - `snapshot.py` -- `StateSnapshot`, `PESnapshot` (frame-based: frames, tag_store, presence, port_store, free_frames), `SMSnapshot`, `SMCellSnapshot`, `capture()` 56 56 - `graph_json.py` -- JSON serialization with execution overlay (extends dfgraph patterns) 57 57 - `server.py` -- `create_app(backend)` FastAPI factory, `ConnectionManager`, WebSocket handler 58 58 - `repl.py` -- `MonitorREPL(cmd.Cmd)` interactive CLI ··· 67 67 - The `formatting.NO_COLOUR` global flag must be set before calling format functions (used by tests to get predictable output) 68 68 - Frontend requires `npm install && node build.mjs` in `monitor/frontend/` to produce `dist/bundle.js` 69 69 70 - <!-- freshness: 2026-02-26 --> 70 + <!-- freshness: 2026-03-07 -->