OR-1 dataflow CPU sketch
1# OR1 Monitor (monitor/)
2
3Last verified: 2026-03-07
4
5## Purpose
6
7Interactive simulation monitor for OR1 dataflow CPU programs. Provides a threaded simulation backend with a command/result protocol, exposed through both a CLI REPL and a web UI with real-time graph visualization and execution overlay.
8
9## Contracts
10
11- **Exposes**: `SimulationBackend` (threaded controller), command types (`LoadCmd`, `StepTickCmd`, `StepEventCmd`, `RunUntilCmd`, `InjectCmd`, `SendCmd`, `ResetCmd`, `StopCmd`), result types (`GraphLoaded`, `StepResult`, `ErrorResult`), `StateSnapshot`, `capture(system)`
12- **Guarantees**: Backend owns SimPy environment in a dedicated daemon thread. Commands are sent via `queue.Queue` and processed sequentially. Results include full state snapshots and event lists. Invalid programs return `ErrorResult` without crashing the backend. Reset tears down state cleanly.
13- **Expects**: Valid dfasm source for `LoadCmd`. Running backend thread (`start()` called before `send_command()`).
14
15## Command/Result Protocol
16
17Commands are frozen dataclasses sent to the backend thread via queue:
181. `LoadCmd(source)` -> `GraphLoaded(ir_graph, snapshot)` or `ErrorResult`
192. `StepTickCmd()` -> `StepResult` (all events at current sim time)
203. `StepEventCmd()` -> `StepResult` (exactly one `env.step()`)
214. `RunUntilCmd(until)` -> `StepResult` (batched events up to target time)
225. `InjectCmd(token)` -> `StepResult` (direct inject, no backpressure)
236. `SendCmd(token)` -> `StepResult` (via `store.put()`, respects backpressure)
247. `ResetCmd(reload)` -> `StepResult` or `GraphLoaded` (if `reload=True`)
258. `StopCmd()` -> terminates backend thread
26
27## Dependencies
28
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- **Used by**: CLI entry point (`python -m monitor`), test suite
31- **Boundary**: `cm_inst`, `tokens`, `sm_mod`, `emu/`, and `asm/` must NEVER import from `monitor/`
32
33## Key Decisions
34
35- **Threaded backend**: SimPy is not async-safe, so the simulation runs in a dedicated thread with queue-based IPC. The FastAPI server bridges async/sync via `asyncio.to_thread()`.
36- **Frozen command/result types**: All protocol types are frozen dataclasses for thread safety and predictability.
37- **Event callback wiring**: Backend uses `dataclasses.replace()` to inject `on_event` into PEConfig/SMConfig during `LoadCmd`, avoiding mutation of user-provided configs.
38- **StateSnapshot as frozen copy**: `capture()` reads live PE/SM state and produces frozen dataclasses suitable for serialization, ensuring no aliasing with mutable simulation state.
39- **REPL uses `cmd.Cmd`**: Standard library module, no external deps for CLI mode.
40
41## Invariants
42
43- Backend thread processes exactly one command at a time (sequential dispatch)
44- `StepResult.finished == True` when `env.peek() == inf` (no more events)
45- `StepResult.events` contains only events from the most recent command execution
46- `StateSnapshot` is always captured after the step completes
47- `GraphLoaded.snapshot` reflects state after seed token injection
48- `ResetCmd` always clears `_env`, `_system`, `_events`, `_ir_graph`; preserves `_last_source`
49
50## Key Files
51
52- `__init__.py` -- Public API exports
53- `backend.py` -- `SimulationBackend` class with thread lifecycle and command dispatch
54- `commands.py` -- All command and result frozen dataclasses, `SimCommand` union type
55- `snapshot.py` -- `StateSnapshot`, `PESnapshot` (frame-based: frames, tag_store mapping act_id → (frame_id, lane) tuples, presence, port_store, match_data all 3D [frame_id][match_slot][lane], lane_count, free_frames), `SMSnapshot`, `SMCellSnapshot`, `capture()`
56- `graph_json.py` -- JSON serialization with execution overlay (extends dfgraph patterns)
57- `server.py` -- `create_app(backend)` FastAPI factory, `ConnectionManager`, WebSocket handler
58- `repl.py` -- `MonitorREPL(cmd.Cmd)` interactive CLI
59- `formatting.py` -- ANSI colour formatting with `NO_COLOUR` flag for testing
60- `frontend/` -- TypeScript browser UI (Cytoscape.js, WebSocket client, event log, state inspector)
61
62## Gotchas
63
64- Backend must be `start()`ed before any `send_command()` call; otherwise commands sit in queue forever
65- `SendCmd` internally creates a one-shot SimPy process and steps once; this means it may process other pending events too
66- `RunUntilCmd` batches events per tick internally but returns all events in a single `StepResult`
67- The `formatting.NO_COLOUR` global flag must be set before calling format functions (used by tests to get predictable output)
68- Frontend requires `npm install && node build.mjs` in `monitor/frontend/` to produce `dist/bundle.js`
69
70<!-- freshness: 2026-03-07 -->