Skip to content

flow: delete find_sdc_file and the per-step .sdc rules; every .odb has its .sdc - #4751

Open
oharboe wants to merge 5 commits into
The-OpenROAD-Project:masterfrom
oharboe:sdc-pair
Open

oharboe wants to merge 5 commits into
The-OpenROAD-Project:masterfrom
oharboe:sdc-pair

Conversation

@oharboe

@oharboe oharboe commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Every step that writes N.odb now writes N.sdc beside it, and every step reads the .sdc beside the .odb it loads. The name of the .odb is the name of its constraints; nothing is inferred. Before synthesis the .sdc is the user's (1_2_yosys.sdc, a copy of SDC_FILE); from 1_synth on, every .odb has its own.

This follows the direction in The-OpenROAD-Project/OpenROAD#11260: carry the write_sdc output as text and read it back with read_sdc, the well-established path. No OpenROAD change is needed.

27 files, +88/−164.

Deleted

  • find_sdc_file: the glob of RESULTS_DIR, the reverse dictionary sort and "the greatest .sdc at or below this .odb's stem". open.tcl and generate_abstract.tcl read the .sdc with the same stem.
  • The second argument of load_design, at 21 call sites, each naming by hand which earlier step's .sdc it borrows.
  • 9 orfs_write_sdc calls in the stage scripts: orfs_write_db writes the pair, orfs_copy_db copies the pair.
  • flow_write_sdc and its 9 calls in flow.tcl: flow_write_db writes the pair.
  • In the Makefile, every .sdc but the user's: five empty-recipe X.sdc: X.odb rules, the three .sdc do-copy rules and do-copy's extension parameter, do-2_floorplan.sdc, do-5_route.sdc and do-6_1_fill.sdc in the stage recipes, and the .sdc prerequisites of the steps and stage targets. A step depends on its input .odb, and the .odb comes with its .sdc.

Fixed by construction

Which .sdc a step read was written down twice, once in its script and once in its Makefile prerequisites, and the two had drifted: 2_2_floorplan_macro declared 1_synth.sdc and macro_place.tcl read 2_1_floorplan.sdc. Now there is nothing to write down.

find_sdc_file picked by name order, so open_*/gui_* could pick a different stage's constraints:

opened .sdc read before .sdc read now
6_final.def 6_1_fill.sdc 6_final.sdc
5_1_grt-failed.odb 4_cts.sdc 5_1_grt-failed.sdc

The stem ends at the first dot of the file name, so 4_cts.odb, 4_cts.odb.def and 4_cts.odb.v all open with 4_cts.sdc.

load_design and open.tcl stop at read_sdc when the .sdc beside the input is missing; there is no fallback to search for. open.tcl reads the .sdc only with GUI_TIMING=1, as before.

Two reads change with it:

  • synth-report (synth_metrics.tcl) reads 1_2_yosys.sdc, the .sdc of the 1_2_yosys.v it loads, instead of 1_synth.sdc; synth_odb.tcl reads the same file.
  • make open_1_synth_lec.v GUI_TIMING=1 stops at read_sdc: 1_synth_lec.v has no .sdc of its own. Before, it found 1_synth.sdc.

In the Makefile, a step's .odb and .sdc are one grouped target (X.odb X.sdc &:), so a missing .sdc reruns the step that writes it. Every writer writes the .sdc first and the .odb last.

Every substep opens on its own

make open_*, gui_* and any script that loads 3_3_place_gp.odb, say, get exactly the constraints that step ran with. The Makefile Contract already says a step with nothing to do "copies its .odb and .sdc through"; now every step does.

Paves the way for FLOW_INPUT_VARIANT

FLOW_INPUT_VARIANT (#4475) names the variant a stage reads its inputs from, so stages are shared between variants without copying results: one synthesis and floorplan feeding many experiments. #4475 had to teach find_sdc_file to rank candidates across two directories, and each read site had to remember to search them. With the pair, a stage's input is one stem: load_design is the single read of both files, so reading another variant's results is one path in one place.

Disk space

11 more .sdc files per flow, each the size of the design's constraints. Measured on sky130hd/aes:

size
20 .odb files 319.6 MB
of which byte-identical copies the flow already keeps (2_floorplan, 3_place, 4_cts, 5_route, 6_1_fill) 98.2 MB
one .sdc 36 KB
11 new .sdc files 0.40 MB

ORFS and OpenROAD have chosen maintainability over disk space repeatedly, and the same holds here:

For callers outside ORFS

load_design takes one argument. A script that passes two stops at the call with Tcl's wrong # args.

Testing

  • tclfmt/tclint 0.7.0 clean.
  • Integration test: orfs: every .odb has its .sdc (ORFS #4751), reference only bazel-orfs#1418 (closed, reference only), this change on top of the ORFS patches bazel-orfs carries, with the bazel-orfs side of the bump.
  • asap7/gcd through final, generate_abstract, generate_metadata and the floorplan estimate, built by bazel-orfs on top of the patches it carries, with and without this change: every stage's .odb and .sdc (1_synth to 6_final), 6_final.v, gcd.lef and gcd_typ.lib are byte-identical. 6_final.spef differs only in its *DATE line.
  • The same comparison shows the new substep .sdc files carry the constraints they replace: 2_floorplan.sdc, 3_place.sdc and 5_route.sdc now come from the last substep of their stage and are byte-identical to before.
  • The single-process flow, flow.tcl from the yosys netlist to 6_final with WRITE_ODB_AND_SDC_EACH_STAGE=0: all 20 .odb files get their .sdc; within a stage every .sdc is identical, and the constraints change once, at CTS.
  • make do-cts, then open_4_1_cts.odb, open_4_cts.odb.def and open_4_cts.odb.v with GUI_TIMING=1: each reads 4_1_cts.sdc or 4_cts.sdc.

🤖 Generated with Claude Code

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request simplifies the handling of SDC (Synopsys Design Constraints) files throughout the physical design flow. It refactors the build system and TCL scripts to automatically associate each ODB design file with its corresponding SDC file using a new sdc_file_of utility. This change removes the need to explicitly pass, track, and copy SDC files as separate dependencies in the Makefile and as separate arguments in load_design and other helper procedures. I have no feedback to provide as there are no review comments to assess.

Every step that writes N.odb writes N.sdc beside it, and every step
reads the .sdc beside the .odb it loads. Before synthesis the .sdc is
the user's (1_2_yosys.sdc); from 1_synth on, every .odb has its own.

This deletes find_sdc_file, which picked an .sdc by name order and
gave open_*/gui_* another stage's constraints for a .def or a failed
.odb; the second argument of load_design, which named by hand which
earlier step's .sdc a script borrowed; the separate orfs_write_sdc and
flow_write_sdc calls, now part of orfs_write_db, orfs_copy_db and
flow_write_db; and every .sdc rule and prerequisite in the Makefile
except the user's, since a step's input .odb comes with its .sdc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe oharboe changed the title flow: every .odb has its .sdc flow: delete find_sdc_file and the per-step .sdc rules; every .odb has its .sdc Oct 29, 2026
@openroad-ci

openroad-ci commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

🔍 QoR check

Metrics reflect the PR merge build — i.e. what will land on the target branch.

Commit 3180214 · Jenkins build #8 · Baseline: build · View build on dashboard

62 design(s) checked — 0 with regression(s), 0 without a comparable baseline.

@oharboe
oharboe marked this pull request as ready for review November 2, 2026 17:19
@oharboe
oharboe requested a review from maliberty November 2, 2026 17:20
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>

@maliberty maliberty left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice simplification; the description holds up against the diff. All in-repo load_design callers are updated, find_sdc_file is fully gone, and every skip path goes through orfs_copy_db, which now carries the .sdc. Checked against docs/contrib/MakefileContract.md and docs/contrib/TclContract.md:

1. The .sdc a step reads is not declared to make (MakefileContract: "a step never reads a file it does not declare"). After this PR the only .sdc with a rule is 1_2_yosys.sdc; every step reads X.sdc but depends only on X.odb. Consequences:

  • An existing results tree (built before this PR) has no 3_3_place_gp.sdc etc. Rerunning a single step there (config change, make do-3_4_place_resized) fails at read_sdc, and make will not rebuild the missing .sdc because the .odb is up to date. Users need a clean.
  • A deleted .sdc is never regenerated, and make $(RESULTS_DIR)/3_place.sdc has no rule.

Suggestion: make the pair a grouped target in do-step and do-copy, e.g. $(RESULTS_DIR)/$(1).odb $(RESULTS_DIR)/$(1).sdc &: $(2). The Makefile already uses &: in GENERATE_ABSTRACT_RULE, so no new make version requirement.

2. Write order. orfs_write_db, orfs_copy_db and the do-copy recipe write the .odb first, then the .sdc, and there is no .DELETE_ON_ERROR. If the .sdc write/copy fails, the .odb is fresh, make considers the step done, and the failure surfaces one step later as a read_sdc error. Writing the .sdc first makes the declared target the last file written.

3. Unmentioned behavior change. synth_metrics.tcl (make synth-report) now reads 1_2_yosys.sdc (the user's raw copy) instead of the canonicalized 1_synth.sdc. Probably equivalent, since synth_odb.tcl reads the same file, but per the TclContract ("a refactor never changes behavior") it belongs in the description next to the open.tcl changes.

4. open.tcl without the SDC_FILE fallback. Stated in the description, but one case not listed: make open_1_synth_lec.v GUI_TIMING=1 now looks for 1_synth_lec.sdc, which is never written (before, it found 1_synth.sdc). Fine under "no fallback" if intended.

5. Nit: docs/contrib/TclContract.md:30 still says a result is written "with orfs_write_db and orfs_write_sdc". orfs_write_db now writes the pair and orfs_write_sdc is only used inside it and by write_ref_sdc.tcl; worth updating in this PR.

6. Nit: failure paths differ. global_route.tcl writes 5_1_grt-failed.odb/.sdc with raw write_db/write_sdc, while pdn.tcl writes 2_4_floorplan_pdn-failed.odb via orfs_write_db (endpoint slack, gated by WRITE_ODB_AND_SDC_EACH_STAGE). Pre-existing, but the line is touched here.

7. Merge order. bazel-orfs must declare the new per-step .sdc outputs; the PR only references the closed bazel-orfs#1418. The bazel-orfs side should be ready to land together with this.

do-step (for .odb results) and do-copy declare the pair as one grouped
target, X.odb X.sdc &: deps, and 6_final.odb/6_final.sdc depend on
6_report.log again. A missing .sdc now reruns the step that writes it;
a do- target still runs on what is in the tree and stops at read_sdc.

orfs_write_db, orfs_copy_db, flow_write_db and do-copy write the .sdc
before the .odb, so the target make tracks is the last file written.

TclContract: orfs_write_db writes the .odb and its .sdc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe

oharboe commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed in f8fac4c:

  1. do-step (for .odb results) and do-copy now declare the pair as a grouped target, X.odb X.sdc &: deps, and 6_final.odb 6_final.sdc: 6_report.log is back. A missing .sdc reruns the step that writes it: on asap7/gcd, deleting 3_3_place_gp.sdc and running make place reruns 3_3_place_gp to 3_5_place_dp, then everything is up to date. A do- target is unchanged: it runs on what is in the tree and stops at read_sdc (STA-0340) when the .sdc is missing. That is intended, since dependency checking for do- lives outside the Makefile (MakefileContract, Targets).
  2. orfs_write_db, orfs_copy_db, flow_write_db and do-copy write the .sdc first and the .odb last.
  3. Added to the description.
  4. Intended: no fallback. Added to the description.
  5. Updated.
  6. Left as is.
  7. bazel-orfs adapts as part of its bump onto this, as with every ORFS bump.

@oharboe
oharboe requested a review from maliberty November 6, 2026 06:21
oharboe and others added 2 commits November 6, 2026 20:39
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>

# Conflicts:
#	flow/scripts/global_route.tcl
@oharboe

oharboe commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

@maliberty Good to go? merge w master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants