# OR1 Monitor (monitor/) Last verified: 2026-03-07 ## Purpose Interactive 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. ## Contracts - **Exposes**: `SimulationBackend` (threaded controller), command types (`LoadCmd`, `StepTickCmd`, `StepEventCmd`, `RunUntilCmd`, `InjectCmd`, `SendCmd`, `ResetCmd`, `StopCmd`), result types (`GraphLoaded`, `StepResult`, `ErrorResult`), `StateSnapshot`, `capture(system)` - **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. - **Expects**: Valid dfasm source for `LoadCmd`. Running backend thread (`start()` called before `send_command()`). ## Command/Result Protocol Commands are frozen dataclasses sent to the backend thread via queue: 1. `LoadCmd(source)` -> `GraphLoaded(ir_graph, snapshot)` or `ErrorResult` 2. `StepTickCmd()` -> `StepResult` (all events at current sim time) 3. `StepEventCmd()` -> `StepResult` (exactly one `env.step()`) 4. `RunUntilCmd(until)` -> `StepResult` (batched events up to target time) 5. `InjectCmd(token)` -> `StepResult` (direct inject, no backpressure) 6. `SendCmd(token)` -> `StepResult` (via `store.put()`, respects backpressure) 7. `ResetCmd(reload)` -> `StepResult` or `GraphLoaded` (if `reload=True`) 8. `StopCmd()` -> terminates backend thread ## Dependencies - **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) - **Used by**: CLI entry point (`python -m monitor`), test suite - **Boundary**: `cm_inst`, `tokens`, `sm_mod`, `emu/`, and `asm/` must NEVER import from `monitor/` ## Key Decisions - **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()`. - **Frozen command/result types**: All protocol types are frozen dataclasses for thread safety and predictability. - **Event callback wiring**: Backend uses `dataclasses.replace()` to inject `on_event` into PEConfig/SMConfig during `LoadCmd`, avoiding mutation of user-provided configs. - **StateSnapshot as frozen copy**: `capture()` reads live PE/SM state and produces frozen dataclasses suitable for serialization, ensuring no aliasing with mutable simulation state. - **REPL uses `cmd.Cmd`**: Standard library module, no external deps for CLI mode. ## Invariants - Backend thread processes exactly one command at a time (sequential dispatch) - `StepResult.finished == True` when `env.peek() == inf` (no more events) - `StepResult.events` contains only events from the most recent command execution - `StateSnapshot` is always captured after the step completes - `GraphLoaded.snapshot` reflects state after seed token injection - `ResetCmd` always clears `_env`, `_system`, `_events`, `_ir_graph`; preserves `_last_source` ## Key Files - `__init__.py` -- Public API exports - `backend.py` -- `SimulationBackend` class with thread lifecycle and command dispatch - `commands.py` -- All command and result frozen dataclasses, `SimCommand` union type - `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()` - `graph_json.py` -- JSON serialization with execution overlay (extends dfgraph patterns) - `server.py` -- `create_app(backend)` FastAPI factory, `ConnectionManager`, WebSocket handler - `repl.py` -- `MonitorREPL(cmd.Cmd)` interactive CLI - `formatting.py` -- ANSI colour formatting with `NO_COLOUR` flag for testing - `frontend/` -- TypeScript browser UI (Cytoscape.js, WebSocket client, event log, state inspector) ## Gotchas - Backend must be `start()`ed before any `send_command()` call; otherwise commands sit in queue forever - `SendCmd` internally creates a one-shot SimPy process and steps once; this means it may process other pending events too - `RunUntilCmd` batches events per tick internally but returns all events in a single `StepResult` - The `formatting.NO_COLOUR` global flag must be set before calling format functions (used by tests to get predictable output) - Frontend requires `npm install && node build.mjs` in `monitor/frontend/` to produce `dist/bundle.js`