OR-1 dataflow CPU sketch

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