vyges-stt — rectilinear Steiner minimal trees
Part of the Vyges Loom suite. Install once with
vyges install loom, then runvyges loom stt. It’s also a standalonevyges-sttbinary on your PATH (the integration contract for flow authors).
vyges-stt answers: given where a net’s pins are, what shape should the wire be? A net with
more than two pins has no single answer — the pins can be joined many ways, and the extra corner
points that shorten the total wire (Steiner points) have to be chosen. Every routing and timing
stage downstream reads that shape: global routing routes it, extraction charges capacitance
against it, and timing sees the delay it implies. Getting a different tree is getting a different
design.
Two builders sit behind one entry point, and which one runs is a property of the net:
| builder | when | optimises |
|---|---|---|
| FLUTE | alpha = 0 (the default) | total wirelength, from a lookup table of optimal topologies |
| Prim-Dijkstra | alpha > 0 | a blend of wirelength and depth — the distance from the driver to the furthest pin |
A clock or a long timing-critical net wants a shallow tree even at some cost in wire; alpha
is that dial. ⚠️ A Prim-Dijkstra tree is used only if it passes a self-overlap check;
otherwise the net silently falls back to FLUTE. --json reports which builder actually answered,
which is the one thing the reference does not tell you.
Run it
vyges install loom # one-time
vyges loom stt run net.json # -> the tree, as the reference prints it
vyges loom stt run net.json --json # -> machine-readable
vyges loom stt batch nets.json -o trees.txt # many nets, one table load
vyges loom stt check tree.json # is this tree self-overlapping?
A job names the pins and, optionally, the driver and the trade-off:
{ "x": [0, 100, 200, 300], "y": [0, 10, 20, 30], "drvr": 0, "alpha": 0.4 }
⚠️ drvr is a pin INDEX into x/y, never a coordinate. Two pins can share a location; the
index decides which one the tree is rooted at.
{ "status": "ok", "builder": "prim-dijkstra", "deg": 4, "length": 340, "branches": 6 }
Without --json the output is reportSteinerTree’s own format — the wire length, the path depth,
and one line per branch — so it diffs directly against the reference.
See the full CLI reference (generated from --help).
Exit status
| code | status | meaning |
|---|---|---|
| 0 | ok | a tree was built, or the check passed |
| 1 | failed | the tree failed the self-overlap check — a finding, not an error |
| 2 | vacuous | nothing was built: no pins, or a batch of none. Not a pass. |
| 2 | error | usage, unreadable input, or a driver index that is not a pin |
| 3 | refused | the engine declined; stderr says which path declined |
⛔ vacuous is not success. The declared assertion passes only on ok, so a run that built
nothing fails it rather than signing off a net nothing was computed for.
Correlation
Scored against OpenROAD at the pin this binary reports in --describe, over every
tree-producing case in its src/stt/test plus a 41-net gate of ours: 341 of 341 trees
byte-identical, comparing the wire length, the path depth and every branch line.
Every net is scored through makeSteinerTree, the public entry point — not through the two
builders directly, which would score the builders and leave the branch between them unscored.
⚠️ A number here means nothing without the build. The reference’s own answer moves between
OpenROAD commits, so the pin is part of the claim; --describe publishes it.
⚠️ Two transcription details are unwitnessed and --describe says so: the f32 weight
arithmetic in the Prim-Dijkstra search, and one tie-break in the heap’s child selection. Neither
changes any of the 341 trees, so no input distinguishes them — believed right, not verified.
Where it sits
stt is a computational kernel, not a database pass: it reads pin coordinates and returns a tree,
touching no .odb. Global routing, clock-tree synthesis and timing-driven resizing are its
consumers.
Licence
Apache-2.0. The FLUTE lookup tables are OpenROAD’s own POWV9/POST9 data, BSD-3-Clause, fetched
from the pinned commit at build time rather than vendored.