OR-1 dataflow CPU sketch
1# Dataflow Graph Renderer (dfgraph/)
2
3Last verified: 2026-02-26
4
5## Purpose
6
7Interactive visualisation tool for dfasm dataflow programs. Renders the assembler's IR as a live-updating graph in the browser, enabling developers to see node placement, edge routing, and pipeline errors as they edit source files.
8
9## Contracts
10
11- **Exposes**: `python -m dfgraph path/to/file.dfasm [--port 8420]` launches a web server with WebSocket-driven graph updates
12- **Guarantees**: Progressive pipeline captures the deepest successful IRGraph even when later passes fail, so partial graphs are always shown. Parse errors are fatal (no graph); resolve/place/allocate errors accumulate in `IRGraph.errors` and are displayed alongside the partial graph. File changes trigger reassembly within 300ms debounce window.
13- **Expects**: Valid path to a `.dfasm` file. The `dfasm.lark` grammar file at project root. Node modules installed in `dfgraph/frontend/` for the TypeScript build.
14
15## Dependencies
16
17- **Uses**: `cm_inst` (ArithOp, LogicOp, RoutingOp, MemOp, Addr), `asm/` (ir, lower, resolve, place, allocate, errors, opcodes), `lark` (parser), `fastapi`/`uvicorn` (server), `watchdog` (file watcher), `cytoscape`/`cytoscape-elk` (frontend)
18- **Used by**: Developer tooling only. `monitor/graph_json.py` imports `dfgraph/categories.py` for opcode categorisation.
19- **Boundary**: `cm_inst`, `asm/`, `emu/`, and root-level modules must NEVER import from `dfgraph/`
20
21## Key Decisions
22
23- **isinstance dispatch for categories** (`categories.py`): ALUOp subclasses (ArithOp, LogicOp, RoutingOp) share IntEnum values across types, so dict/set lookup would collide. isinstance checks on the type hierarchy avoid this.
24- **LogicOp split**: EQ/LT/LTE/GT/GTE map to COMPARISON; AND/OR/XOR/NOT map to LOGIC. This gives visually distinct colours for boolean-producing vs bitwise ops.
25- **CONST and FREE_CTX as CONFIG**: RoutingOp.CONST and FREE_CTX are categorised as CONFIG (not ROUTING) since they are infrastructure ops, not data-movement ops.
26- **ELK layout engine**: Uses Eclipse Layout Kernel (via cytoscape-elk) for layered DAG layout with orthogonal edge routing. Replaced dagre for better edge routing.
27- **Progressive pipeline**: Unlike `asm._run_pipeline()` which raises on error, `run_progressive()` runs each pass independently so the frontend can show partial graphs with error annotations.
28
29## Invariants
30
31- `dfgraph/` never imports from `emu/` -- it only needs the assembler IR, not the simulator
32- `categorise()` covers all ALUOp/MemOp values; raises `ValueError` on unknown types
33- `graph_to_json()` always returns a dict with `type: "graph_update"` -- even on parse failure (with empty node/edge lists)
34- WebSocket protocol: server sends `GraphUpdate` JSON on connect and on every file change; client sends nothing meaningful (receive loop is just for keepalive)
35
36## Key Files
37
38- `pipeline.py` -- `PipelineStage` enum, `PipelineResult` dataclass, `run_progressive()` function
39- `categories.py` -- `OpcodeCategory` enum, `CATEGORY_COLOURS` dict, `categorise()` function
40- `graph_json.py` -- `graph_to_json(PipelineResult) -> dict` serialisation
41- `server.py` -- `create_app(source_path) -> FastAPI`, `ConnectionManager`, `DebouncedFileHandler`
42- `frontend/src/types.ts` -- TypeScript interfaces matching `graph_to_json` output shape
43- `frontend/src/main.ts` -- Cytoscape graph rendering, logical/physical view toggle
44- `frontend/src/layout.ts` -- ELK layout configurations for both views
45
46## Gotchas
47
48- The Lark parser is lazily initialised and cached globally in `pipeline.py` (`_parser` module var) -- safe for single-process use but not for parallel test runners that fork
49- `DebouncedFileHandler` runs its callback on a `threading.Timer` thread, so `_on_file_change` uses `asyncio.run_coroutine_threadsafe` to bridge into the async event loop
50- Frontend `dist/` directory must contain the compiled TypeScript bundle; the server mounts it as static files at `/dist`
51- Shared TypeScript modules (layout, style, export, types) have been extracted to `frontend-common/`; `dfgraph/frontend/` now imports from `frontend-common` as a workspace dependency
52
53<!-- freshness: 2026-02-26 -->