| name | byo-openroad |
| description | Iterate on local OpenROAD source changes against a bazel-orfs flow by building the OpenROAD checkout binary and injecting it via OPENROAD_EXE (a bring-your-own / BYO loop), instead of encoding the diff as a base64 patch swapped into the archive_override. Use when developing or debugging an OpenROAD C++/TCL change that you need to exercise through an ORFS stage (cts, place, grt, route), when a long edit-build-run loop is taxing turnaround, or when a change "isn't taking effect" and you suspect a silently-rejected patch. |
Goal
When you are changing OpenROAD source and need to see the effect through a
bazel-orfs flow stage, run the flow against a binary you built directly from
your OpenROAD checkout — bring your own OpenROAD — rather than routing the
change through the module-graph patch mechanism.
The two mechanisms, and why BYO wins for a debug loop
There are two ways to get a local OpenROAD change into a flow run:
- Patch the archive_override — encode your
git diff (often base64) and
splice it into the OpenROAD archive_override / git_override in
MODULE.bazel so bazel applies it at fetch time with patch -p1.
- BYO — build openroad straight from the checkout and hand the flow that
binary via an
OPENROAD_EXE= override.
The patch mechanism is the right thing for the final, carried change (it is
reproducible and hermetic). It is the wrong thing for an iteration loop:
patch -p1 silently rejects hunks on context drift. The build keeps
going, and you end up debugging a binary that does not contain your change —
the single most expensive failure mode, because nothing tells you.
- The regenerate-diff → re-encode → swap → re-fetch → rebuild cycle is applied
on every fetch and taxes every iteration.
BYO removes both: the working tree is the source, so there is nothing to
re-encode and nothing to silently reject.
Prove the swap before you trust it
BYO removes the silent-reject failure, it does not remove the silently wrong
binary failure — a stale build, a wrong path, or a rebuild that did not happen
all leave you measuring one binary twice. Before any long run, execute a cheap
in-tree regression whose goldens your change moves, once per binary, and diff
the two outputs:
cd src/<tool>/test
for arm in a b; do "$ARM_A_OR_B/openroad" -no_init -exit <test>.tcl > /tmp/$arm.log; done
diff /tmp/a.log /tmp/b.log
Seconds of work, and it converts the most expensive failure mode into an
immediate one.
A .odb freezes the cell masters — a library change cannot be a runtime override
load_design on a .odb calls read_db, not read_lef: the master set
was fixed when the design was floorplanned. So handing a later stage a different
library selection does not give the tools new cells to use.
The failure is completely silent. Setting ASAP7_USE_VT="RVT LVT SLVT" on a run
over a pre-built ODB reads all fifteen liberty files without complaint and adds
zero masters — measured as RVT 202 liberty cells / 212 db masters, LVT 202 /
0, SLVT 202 / 0. The resizer cannot instantiate a cell with no master,
so hasVtSwapCells() stays false, VtSwapMove never enters the sequence, and
the run finishes with bit-identical results and no diagnostic of any kind.
Count masters, not liberty cells, when checking whether a library actually
arrived:
foreach lib [[ord::get_db] getLibs] { foreach m [$lib getMasters] { ... } }
A library variant is a whole flow from synthesis onward — in ORFS, a sibling
design directory.
The BYO loop
cd /path/to/OpenROAD
bazelisk build //:openroad
export OPENROAD_EXE="$(readlink -f bazel-bin/openroad)"
Building with CMake is VERBOTEN, and the guard blocks it. Not a style
preference: a cmake build uses whatever compiler, flags and system libraries
the machine happens to have, while the flow's binary comes from the pinned
hermetic bazel toolchain. Swap one for the other and the binary under test is
no longer the binary the flow builds, so every number measured with it is
measuring the toolchain as much as the change -- silently, because it still
runs and still produces plausible output. bazelisk build //:openroad in the
checkout, or bazelisk build @openroad//:openroad here.
A raw git clone of OpenROAD will NOT build: the GitHub tarball and a
blobless clone both arrive without src/sta and third-party/abc. bazel-orfs
vendors those in the @openroad archive_override's patch_cmds, which is
the other reason to build through this repository rather than beside it.
The ODB is compatible across the two binaries as long as your checkout and the
flow's pinned OpenROAD share a base revision.
Three-tier test loop — climb only as far as you must
Pick the cheapest tier that can reproduce what you are chasing; escalate only
when it cannot:
- Pure unit gtest (sub-second). A dependency-free kernel test on the
algorithm/math you changed. No STA, no ODB, no relink of the tool library.
This catches most logic errors in milliseconds.
- In-checkout regression test (seconds). OpenROAD ships
//src/<tool>/test:*
targets — real STA on a tiny design. These are maintained and need no
OPENROAD_EXE plumbing, so for anything they cover, prefer them over BYO.
- Flow-level BYO run (minutes+). The full ORFS stage on a real design with
your BYO binary — the final confirm. BYO earns its setup cost only here, for
loops the wired tests in tiers 1–2 cannot reach (e.g. a specific placed ODB).
It is a judgement call, not a rule
Weigh BYO against just using the already-wired tests. Tiers 1–2 are maintained
and plumbing-free; use them whenever they cover the change. BYO is for the
long / flow-level loops they can't reach.
The real fix: make a rejected patch LOUD
The root hazard is that patch -p1 returns nonzero on a reject but the build
swallows it. Whatever your carry mechanism, make a mis-applied patch fail the
build hard rather than masquerade as a working binary:
- check the
patch exit status and abort the build on nonzero, or
- add a post-patch sentinel: grep the patched file for a known unique line from
your patch and fail if it is absent.
A loud patch failure turns the worst BYO-motivating failure mode (a silent
no-op binary) into an immediate, obvious build error — at which point the patch
path is safe for iteration too.