The-OpenROAD-Project / The-OpenROAD-Project/OpenROAD

`diff_spef` cannot produce its documented output, and `calibrate_base_corner` is dropped

Open
#11,050 3 comments 0 reactions 1 assignee View on GitHub

@AcKoucher is already working on this.

Since Aug 2, 2026.

rcx
Dominant language
Verilog
Stars
3.1k
Forks
1k
Avg merge
2d 23h
Merged PRs (30d)
136

Description

Version: OpenROAD 26Q3-104-gb5624809f2 (b562480), Linux x86-64, +GPU -GUI +Python.

Summary

While correlating a third-party parasitic extractor against OpenRCX we tried to use diff_spef,
and found that it cannot write diff_spef.out — the output its own documentation describes. Two
smaller things fell out alongside it. All three are verifiable by reading, and I think they share
one cause: the command has no test that exercises it.

I'd be glad to send a PR for any of these if the direction is agreed — I didn't want to guess at
the intended behaviour of (2).

1. diff_spef.out is never written

src/rcx/README.md says of diff_spef: "The output of this command is diff_spef.out". It is
not produced, and I believe no input can produce it.

The file handle is opened in exactly one place, extSpef::setUseIdsFlag
(src/rcx/src/extSpef.cpp:228):

void extSpef::setUseIdsFlag(const bool diff, const bool calib)
{
  _diff = diff;
  if (diff && !calib) {
    _diffLogFP = fopen("diff_spef.log", "w");
    ...
    _diffOutFP = fopen("diff_spef.out", "w");

Across the tree there are two calls:

call site arguments
src/rcx/src/netRC.cpp:2116 setUseIdsFlag(false)
src/rcx/src/extSpefIn.cpp:2478 setUseIdsFlag(true /*diff*/, true /*calib*/)

The second is the only one that could open the handles, and it fails diff && !calib.
_diffOutFP is nullptr at declaration (include/rcx/extSpef.h:434) and is assigned nowhere
else, so every fprintf(_diffOutFP, …) in printDiff / printDiffCC is unreachable — which is
consistent with the command running to completion and writing nothing.

Observed: diff_spef -file ours.spef -r_res -r_cap -r_cc_cap exits 0, emits the usual
RCX-0463/RCX-0272/RCX-0292 messages, and produces no diff_spef.out or diff_spef.log in
the working directory.

Side observation, not reproduced: that same call sets _diff = true while leaving
_diffOutFP null. If any path reaches the printers with _diff set, it would write to a null
FILE*. I have not demonstrated such a path and am not claiming one exists — noting it only in
case it is relevant to the fix.

2. diff_spef drops opt.calibrate_base_corner

DiffOptions::calibrate_base_corner exists (include/rcx/ext_options.h:114) and is what
ultimately sets _db_calibbase_corner (src/extSpefIn.cpp:2379-2387), the flag guarding the
setUseIdsFlag(true, true) call above.

Ext::read_spef forwards it (src/ext.cpp:337):

                 opt.db_corner_name,
                 opt.calibrate_base_corner,
                 opt.spef_corner,

Ext::diff_spef passes nullptr in the same position (src/ext.cpp:387-389):

                 (char*) opt.db_corner_name,
                 nullptr,
                 opt.spef_corner,

So the option is defined, plumbed into DiffOptions, and discarded at the call site. I could not
tell from the code whether diff_spef is meant to accept it (the Tcl command doesn't expose it
either), which is why this is a report rather than a patch.

3. The rcx test helper for diff_spef has a bug, and nothing calls it

src/rcx/test/rcx_aux.py:120:

def diff_spef(
    design, *, filename="", r_conn=False, r_res=False, r_cap=False, r_cc_cap=False
):
    opts = rcx.DiffOptions()
    opts.file = file          # <-- `file`, not `filename`

file is not a parameter — on Python 3 this raises NameError on any call. Searching the tree,
nothing calls this helper, which is presumably why it has not surfaced. Fixing it to filename
and adding a regression that asserts diff_spef.out exists and has expected content would cover
(1) and (2) directly.

A crash we saw but cannot minimise

Separately, we hit a SIGSEGV in the same command on a large input:

rcx::NameTable::getDataId → rcx::extSpef::getDbInst → rcx::extSpef::getCapNodeId
  → rcx::extSpef::readDNet → rcx::Ext::diff_spef        SIGSEGV

The input was a ~18 MB SPEF our own tool wrote, and it was malformed — so this is a robustness
report, not a correctness one. I have not been able to reduce it, and to save anyone repeating
my dead ends, these do not reproduce it (each errors cleanly with RCX-0044/RCX-0052, which
is the desired behaviour):

  • a *CAP node label referencing a name-map id that is not defined (*0:clk_i);
  • a node label referencing a name-map id that resolves to a net rather than an instance;
  • a node label referencing an id absent from the map entirely.

Happy to send the 18 MB input privately or attach a link if that is useful.

Why we were in here

We maintain an open rule-based extractor — https://github.com/vyges-tools/extract (Apache-2.0)
— and correlate it against OpenRCX on routed sky130 blocks. OpenRCX is our reference,
and diff_spef looked like the right way to let the incumbent define what a difference is rather
than writing our own comparator. We ended up comparing SPEF files directly instead. The findings
above are a by-product, offered back.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.