OpenROAD: clean-room public-interface study with REA¶
Date: 2026-10-23. This is a bounded interface study, a small external placement oracle, and a RustROAD design proposal. It is not a reproduction of OpenROAD internals or evidence of RTL-to-GDS correctness. No OpenROAD, Yosys, ABC or OpenSTA implementation source, disassembly or decompiler output was inspected. REA was used only for its documented process-capture workflow.
Observed executable and capture boundary¶
The host has no openroad command on PATH. Filename inventory found OpenROAD in
an existing local Podman image; no download, build, installation or registry pull
was needed. REA 6.7.0 was already available in npm's runner cache and was invoked
by its absolute CLI path. Its version was checked before use.
The selected image has local content ID
f152917ebc458bd015042e44d9dac5af69bcae9fb5657d5121cb4d7226ed0c67.
Its OCI digests and the selected OpenROAD executable's complete SHA-256 are in
identities.json. The executable's -version
prints unknown. That is the observed response, not a usable semantic version;
reproduction must pin the captured image and binary identities. The container
path is /OpenROAD-flow-scripts/tools/install/OpenROAD/bin/openroad.
The outer process was /usr/bin/podman. Therefore REA's executable digest binds
Podman, not OpenROAD; the separate binary digest and image ID provide the latter's
identity. Neither file digest is a proof of an atomic operating-system image
binding. Commands inherit an unrecorded host environment in REA, while the target
container receives only its image defaults and selected arguments.
Three scenarios used the existing image with --network=none --read-only, a
64 MiB /tmp tmpfs, --memory=2g --memory-swap=2g --cpus=2 --pids-limit=128 and
--rm. The parent process had an 8 GiB virtual-address ceiling and a 60-second
wall limit. Each REA scenario had a 30-second deadline, 256 KiB output cap and
128-sample process bound. The preflight estimate is in
resource-estimate.md.
| Probe | Observable outcome |
|---|---|
-version |
unknown, exit 0 |
-help |
Batch/GUI/web flags, exit 0 |
Public Tcl help |
21 requested commands listed, exit 0 |
The time receipts kept with the captures include REA/container startup and
measure no physical-design algorithm. No placement/routing speedup is
claimed. All captures were untruncated.
REA reported verified owned-process-group cleanup and removal of its temporary
root, but the help and Tcl captures also contain unverified_processes from
EACCES reading /proc/.../environ. Full descendant observation is therefore
not established. Network activity is not measured by REA; network isolation is a
selected container setting. No filesystem observation paths were selected.
The complete scenarios, original capture envelopes, progress logs, derived stdout
and time receipts are under evidence/rea.
summary.json indexes the evidence IDs and limits.
Public contracts that were actually observed¶
Runtime -help supports a command file, -exit, -no_init, -no_splash,
-threads, JSON -metrics, and GUI/web options. RustROAD reference invocations
should be batch-only with a finite command file, -no_init, explicit thread count
and an outer timeout. The presence of GUI/server flags is not a reason to adopt a
resident service in RustROAD.
The self-authored public-contract.tcl
only invokes help on selected public commands. No command definitions were
read and no design was loaded. Observed contracts include:
- Data interchange:
read_lef,read_def,read_verilog,read_liberty,read_sdc,write_def,write_verilog,write_spef,read_db,write_db. - Physical stages:
initialize_floorplan,global_placement,detailed_placement,check_placement,clock_tree_synthesis,global_route,detailed_route,extract_parasitics. - Reports:
report_worst_slack,report_tns,report_design_area.
The public API documentation
separately describes LEF/DEF/netlist interchange and saved database files.
RustROAD should use the standard interchange formats at reference boundaries;
it should own its disk-workspace representation rather than depend on .odb.
Matching command names alone proves neither data compatibility nor correctness.
Flow lessons and our independent design¶
The published flow overview separates floorplanning, placement, clock synthesis, routing and final extraction/ verification. This motivates independently checkpointed RustROAD stages: a failed route should preserve the last valid placement and its inputs rather than make all earlier work opaque. This stage contract is our proposal, not an observation of OpenROAD's internal implementation.
| RustROAD boundary | Owned data and output | Acceptance evidence |
|---|---|---|
| Import / floorplan | Bounded LEF/DEF reader, integer DBU geometry, rows and sites | Explicit unsupported-feature diagnostics; independently checked units and bounds |
| Global placement | Separate net-pin CSR and placement SoA, bounded optimizer state | Density, wirelength and timing quality; deterministic reference mode |
| Legalization | Generational instance IDs, legal row/site positions | Independent geometry checker plus OpenROAD check_placement on exported DEF |
| CTS / repair | Versioned inserted-cell/netlist edits and clock constraints | Equivalence where applicable; clock/skew and setup/hold reports |
| Routing | Bounded routing grid/search frontier and wire/via shapes | Connectivity, route DRC, routed length and via counts |
| Extraction / finish | Corner-tagged SPEF, DEF/GDS exports and signoff manifests | Extracted timing plus independent DRC/LVS; missing checks remain unverified |
The global-placement user guide exposes timing and routability tradeoffs. RustROAD can define these as explicit objectives and budgets without copying the reference's numerical method. Start with a deterministic pure-Rust CPU reference, then measure SoA/CSR lowering, iterations and synchronization before adding CUDA or Metal/Accelerate.
The detailed-placement contract describes site/row legality, displacement and incremental changes. A small independent legalizer is a useful first vertical slice; it is not a complete analytical placer. Our checker must not share the placement algorithm's geometry assumptions as its sole oracle.
The extraction contract requires routed geometry and process/corner rules and can export SPEF. Consequently pre-route estimated RC cannot establish post-route timing closure. RustSTA needs a validated SPEF ingestion path before it can independently close that boundary.
Performance, workspace and renderer implications¶
The following are RustROAD requirements derived from the project constitution, not measured properties of OpenROAD:
- A mutating command locks
workspace.lock, checks--base-rev, and atomically publishes immutable revision tables. Analysis uses read snapshots viamemmap2. - Keep mutable physical IR, timing IR and compute views distinct. Share immutable technology libraries, and bound every search frontier, iteration count and retained report. Never materialize all paths on reconvergent nets.
- A GUI reads immutable render snapshots without holding a workspace write lock. Tile/viewport filtering, level of detail and batched GPU instances should scale with visible geometry rather than total shapes. Native UI-framework selection needs an actual macOS/Linux workload benchmark; this REA study proves nothing about GPUI versus another renderer.
- Benchmark the whole command: lock/snapshot read, mmap faults, lowering, compute, host/device synchronization, revision publication and report generation. Compare wall time, peak RSS and QoR at the same corner, constraints, seed, thread count and input identities. Report p95 interaction latency separately from placement/routing throughput.
ORFS flow variables
include explicit thread controls, timing/report costs and configurable formal
checks. RustROAD should make those choices visible in its run manifest. A process
exit code is insufficient for a successful stage: require the stage's expected
completion marker, valid artifacts and checked metrics. Publish unchecked or
unverified for missing DRC/LVS/formal/timing evidence, never numeric zero.
Executed external placement oracle¶
The exported RustROAD fixture .work/tiny.def was checked using the pinned
OpenROAD's public read_lef, read_def and check_placement commands. The
self-authored placement-oracle.lef has one
0.5 by 1.0 micron site and three pinless cell shapes, with 1000 DBU per micron.
It is a geometry fixture, not a fabrication PDK. Inputs contain 24 placed
instances and six rows. The driver is
openroad_placement_oracle.sh.
| Case | External observation |
|---|---|
| Exported placement | Read 24 components; accepted; exit 0 |
| Negative control | Second instance moved onto the first; one overlap reported; rejected; exit 42 |
The negative control reported u1 (CELL2) overlaps u0 (CELL4) and diagnostic
DPL-0033. The harness requires this overlap-specific observation and a rejected
marker, not merely an arbitrary command failure. Both captures were untruncated.
The negative capture retains the same /proc/.../environ permission limitation
for some descendant observations; it does not invalidate the captured placement
diagnostic but prevents a complete process-tree claim.
Full input hashes, immutable input copies, Tcl scenarios, REA captures and time
receipts are in placement-oracle.
result.json reports the bounded
geometry result as pass, with DRC/LVS/timing explicitly unchecked. A positive
placement check produces no violation report file; its successful exit and
completion marker are retained. The negative violation report is preserved.
This supplies an external oracle for the first placement slice; it does not establish logical connectivity, routed DRC, timing or a complete flow. Both sky130 and gf180mcu still need their own content-pinned full-library/constraint campaigns. This investigation ran no PDK corpus, synthesis, simulation, CEC, routing, extraction or GDS/DRC/LVS.
To rerun the external geometry oracle after exporting a fresh DEF:
scripts/openroad_placement_oracle.sh .work/tiny.def .work/openroad-placement-oracle
The driver refuses a missing pinned image or a non-6.7.0 REA runtime as
unverified; it never downloads a replacement. Inputs are limited to 16 MiB and
5000 instances. The preflight estimate accounts conservatively for possible
quadratic overlap-pair storage; both runs are sequential and use the resource
caps described above.
To reproduce a captured scenario with the observed cached REA runtime:
ulimit -v 8388608
/usr/bin/time -v timeout 60s \
node /home/cgao/.npm/_npx/a1722018f28f9df2/node_modules/rea-agents/scripts/rea.mjs \
capture-process evidence/rea/openroad-public-contract.scenario.json --json
That absolute npm-cache path is host-specific. Other hosts should use an explicitly
pinned rea-agents@6.7.0 runtime and an image with the recorded digest, after a
memory preflight. Do not substitute latest and call the result reproduced.