Skip to content

Commit b1782cb

Browse files
authored
Merge pull request #4333 from oharboe/synth-single-writer-1synth-sdc
synth: single writer for 1_synth.sdc + single-process flow test that enforces it
2 parents a5ff7ef + adaa6a9 commit b1782cb

23 files changed

Lines changed: 617 additions & 155 deletions

.bazelrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ common --registry=https://raw.githubusercontent.com/oharboe/bazel-central-regist
1111
build --incompatible_strict_action_env
1212
build --cxxopt "-std=c++20" --host_cxxopt "-std=c++20"
1313

14+
# Refuse to autodetect a local C++ toolchain (/usr/bin/gcc); the hermetic
15+
# toolchain must win resolution. Mirrors tools/OpenROAD/.bazelrc.
16+
common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
17+
18+
# Launch py_binary via a shell stub instead of rules_python's legacy python
19+
# stub, whose '#!/usr/bin/env python3' shebang needs a host python3 before
20+
# the hermetic interpreter takes over. Mirrors tools/OpenROAD/.bazelrc.
21+
common --@rules_python//python/config_settings:bootstrap_impl=script
22+
1423
# Don't track MODULE.bazel.lock. Resolved versions ride along with the
1524
# pinned BCR + git_override(commit=…) coordinates in MODULE.bazel and
1625
# downstream cache hits are what we actually care about. Tracking the

MODULE.bazel

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,64 @@ bazel_dep(name = "rules_shell", version = "0.6.1")
1919
# our MODULE.bazel doesn't need to be patched at non-root consumption
2020
# time.
2121

22-
bazel_dep(name = "toolchains_llvm", version = "1.5.0", dev_dependency = True)
22+
# hermetic-llvm (BCR module "llvm") provides statically linked LLVM
23+
# binaries and a zero-sysroot cc_toolchain: no host compiler, linker or
24+
# /usr/include involved, so host glibc changes cannot break the build.
25+
# Mirrors tools/OpenROAD/MODULE.bazel (its llvm dep is dev-only, so it
26+
# does not propagate here).
27+
bazel_dep(name = "llvm", version = "0.8.11", dev_dependency = True)
28+
29+
# The gnulib-based GNU tools (sed, gawk, m4, bison) vendor wrapper
30+
# headers that must shadow libc headers, which breaks against
31+
# hermetic-llvm's explicit libc -isystem entries (BCR #7642). Mirror
32+
# tools/OpenROAD's fixed-version overrides (single_version_override is
33+
# root-honored only); drop each when tools/OpenROAD drops its copy.
34+
#
35+
# sed is transitive (bazel-orfs -> abc -> readline -> ncurses -> sed);
36+
# 4.9.bcr.5 carries the upstream fix (BCR #7915).
37+
single_version_override(
38+
module_name = "sed",
39+
version = "4.9.bcr.5",
40+
)
41+
42+
# gawk is transitive (abc -> ncurses -> gawk; yosys -> gawk);
43+
# 5.3.2.bcr.7 carries the upstream fix (BCR #7989).
44+
single_version_override(
45+
module_name = "gawk",
46+
version = "5.3.2.bcr.7",
47+
)
48+
49+
# m4 is transitive (rules_bison/rules_flex -> m4).
50+
single_version_override(
51+
module_name = "m4",
52+
version = "1.4.21.bcr.4",
53+
)
54+
55+
# bison is transitive (rules_bison -> bison). 3.8.2.bcr.7 fixes lib/
56+
# but src/ still picks up raw libc headers; the patch restores gnulib
57+
# wrapper-header precedence via -I (to be proposed upstream as
58+
# 3.8.2.bcr.8, then drop).
59+
single_version_override(
60+
module_name = "bison",
61+
patch_strip = 1,
62+
patches = [
63+
"//patches/bison:0001-gnulib-wrapper-headers-use-I-not-isystem.patch",
64+
],
65+
version = "3.8.2.bcr.7",
66+
)
67+
68+
# tclZipfs.c's '#include "crypt.h"' picks up glibc's crypt.h instead of
69+
# the vendored minizip one when glibc headers are explicit -isystem
70+
# directories (hermetic-llvm zero-sysroot toolchain). Drop when fixed
71+
# in a tcl_lang BCR release.
72+
single_version_override(
73+
module_name = "tcl_lang",
74+
patch_strip = 1,
75+
patches = [
76+
"//patches/tcl_lang:0001-tclZipfs-include-vendored-minizip-crypt.h-by-path.patch",
77+
],
78+
)
79+
2380
bazel_dep(name = "openroad", dev_dependency = True)
2481
local_path_override(
2582
module_name = "openroad",
@@ -76,20 +133,24 @@ git_override(
76133

77134
# --- Extensions ---
78135

79-
llvm = use_extension(
80-
"@toolchains_llvm//toolchain/extensions:llvm.bzl",
81-
"llvm",
136+
register_toolchains(
137+
"@llvm//toolchain:all",
82138
dev_dependency = True,
83139
)
84-
llvm.toolchain(
85-
llvm_version = "20.1.8",
86-
)
87-
use_repo(llvm, "llvm_toolchain")
88140

89-
register_toolchains(
90-
"@llvm_toolchain//:all",
141+
# sv-lang (via tools/OpenROAD) pulls in rules_pycross, whose toolchain
142+
# extension fails on Python 3.8 (transitively registered via or-tools ->
143+
# pybind11_abseil, dropped from rules_python 2.0.0's MINOR_MAPPING).
144+
# pycross.configure_environments is root-honored only, so the workaround
145+
# must be mirrored here from tools/OpenROAD/MODULE.bazel.
146+
bazel_dep(name = "rules_pycross", version = "0.8.1", dev_dependency = True)
147+
148+
pycross = use_extension(
149+
"@rules_pycross//pycross/extensions:pycross.bzl",
150+
"pycross",
91151
dev_dependency = True,
92152
)
153+
pycross.configure_environments(python_versions = ["3.13"])
93154

94155
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
95156
python.toolchain(

bazel/slang-compat/README

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Placeholder directory for the @slang alias build file to internally
2+
manifest. tools/OpenROAD's MODULE.bazel declares
3+
new_local_repository(name="slang", path="bazel/slang-compat"); the
4+
relative path resolves against the root workspace, so when ORFS is the
5+
root module consuming openroad via git_override the directory must
6+
exist here too. Mirrors tools/OpenROAD/bazel/slang-compat.

flow/designs/asap7/gcd/BUILD

Lines changed: 166 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,168 @@
1+
load("@bazel-orfs//:openroad.bzl", "orfs_run")
2+
load("@orfs_designs//:designs.bzl", "DESIGNS")
3+
load("@rules_shell//shell:sh_test.bzl", "sh_test")
14
load("//flow/designs:design.bzl", "design")
25

3-
design(config = "config.mk")
6+
# SYNTH_USE_SYN is make-only for now: bazel-orfs's synth stage always
7+
# runs the yosys flow and neither stages the Verilog sources nor sets
8+
# VERILOG_FILES for the OpenROAD synthesis step, so the built-in
9+
# synthesizer opt-in must not reach the bazel arguments.
10+
design(
11+
config = "config.mk",
12+
local_arguments = ["SYNTH_USE_SYN"],
13+
)
14+
15+
# Stage-boundary .odb/.sdc stems shared by the normal per-stage flow
16+
# targets and the single-process flow below; the gcd_single_flow_test
17+
# suite compares each of these files pairwise between the two flows.
18+
SINGLE_FLOW_STAGES = {
19+
"synth": "1_synth",
20+
"floorplan": "2_floorplan",
21+
"place": "3_place",
22+
"cts": "4_cts",
23+
"grt": "5_1_grt",
24+
"route": "5_route",
25+
"final": "6_final",
26+
}
27+
28+
# Substep files the single-process flow also writes, declared so the
29+
# action keeps them and a human can inspect them on failure.
30+
SINGLE_FLOW_EXTRA_OUTS = [
31+
"2_1_floorplan.odb",
32+
"2_1_floorplan.sdc",
33+
"2_2_floorplan_macro.odb",
34+
"2_3_floorplan_tapcell.odb",
35+
"2_4_floorplan_pdn.odb",
36+
"3_1_place_gp_skip_io.odb",
37+
"3_2_place_iop.odb",
38+
"3_3_place_gp.odb",
39+
"3_4_place_resized.odb",
40+
"3_5_place_dp.odb",
41+
"4_1_cts.odb",
42+
"5_2_route.odb",
43+
"5_3_fillcell.odb",
44+
"6_1_fill.odb",
45+
"6_1_fill.sdc",
46+
]
47+
48+
# The whole flow, yosys netlist through finish, as one Tcl script in a
49+
# single OpenROAD process (scripts/flow.tcl) with
50+
# WRITE_ODB_AND_SDC_EACH_STAGE=0: the stage scripts write no .odb/.sdc
51+
# files (flow.tcl errors if one does), the top level writes every file
52+
# the per-stage flow produces via explicit write_db/write_sdc. The
53+
# synth stage config only carries synth-scoped variables, so the
54+
# design's full argument set rides along; FLOW_VARIANT=single keeps the
55+
# output paths clear of the regular stage targets' declared outputs in
56+
# this package.
57+
orfs_run(
58+
name = "gcd_single_flow",
59+
src = ":gcd_synth",
60+
outs = [
61+
"results/asap7/gcd/single/{}.{}".format(stem, ext)
62+
for stem in SINGLE_FLOW_STAGES.values()
63+
for ext in [
64+
"odb",
65+
"sdc",
66+
]
67+
] + [
68+
"results/asap7/gcd/single/" + name
69+
for name in SINGLE_FLOW_EXTRA_OUTS
70+
],
71+
arguments = {
72+
k: v
73+
for k, v in DESIGNS["asap7/gcd"]["arguments"].items()
74+
if k != "SYNTH_USE_SYN"
75+
} | {
76+
"FLOW_VARIANT": "single",
77+
},
78+
script = ":single_flow.tcl",
79+
variant = "single",
80+
)
81+
82+
# Normal-flow stage outputs, one filegroup per compared file.
83+
[
84+
filegroup(
85+
name = "gcd_base_{}_{}".format(stage, ext),
86+
srcs = [":gcd_" + stage],
87+
output_group = "{}.{}".format(stem, ext),
88+
)
89+
for stage, stem in SINGLE_FLOW_STAGES.items()
90+
for ext in [
91+
"odb",
92+
"sdc",
93+
]
94+
]
95+
96+
# Byte-compare each stage boundary's .odb/.sdc between the per-stage
97+
# flow and the single-process flow: equal files prove the
98+
# single-process mode is equivalent to the per-stage flow, not merely
99+
# that it runs to completion. check_same.sh takes file pairs:
100+
# file_a file_b ... (.sdc pair first: check_same.sh stops at a binary
101+
# diff, and the .sdc verdict should be reported before that).
102+
#
103+
# The synth through grt boundaries are byte-identical. The grt stage
104+
# used to diverge (an extra repair_timing buffer): STA endpoint state
105+
# for CTS dummy-load pins differed between a process that inserted them
106+
# and a process that read the same design from disk, and
107+
# resistance-aware global routing consumed the resulting bogus net
108+
# slacks — fixed by patches/openroad/0001-sta-register-new-instance-
109+
# pin-vertices-as-endpoints.patch.
110+
#
111+
# The route stage used to route differently in-process (net/instance
112+
# iteration order and via emission order in drt reflected the process
113+
# history) — fixed by patches/openroad/0002-drt-make-incremental-
114+
# design-updates-match-a-fresh-read.patch; the routed layout is now
115+
# identical (equal DEF).
116+
#
117+
# The route .odb byte difference was NOT an odb id/freelist issue
118+
# (free lists serialize and reload): the per-stage flow accumulated
119+
# stale block-terminal access points on every pin access rerun — fixed
120+
# by patches/openroad/0003-drt-do-not-duplicate-bterm-access-points.
121+
# patch; route is byte-identical now.
122+
#
123+
# The final stage used to diverge because the per-stage flow writes
124+
# 6_final.odb mid-final_report (before the RCX extraction reorders
125+
# every dbWire via odb orderWires) while this flow wrote at end of
126+
# script — resolved by splitting final_report.tcl around its write so
127+
# flow.tcl writes at the same point. All seven stage boundaries are
128+
# byte-identical between the two flows.
129+
[
130+
sh_test(
131+
name = "gcd_single_flow_{}_test".format(stage),
132+
srcs = ["@openroad//test/orfs:check_same.sh"],
133+
args = [
134+
arg
135+
for ext in [
136+
"sdc",
137+
"odb",
138+
]
139+
for arg in [
140+
"$(location :gcd_base_{}_{})".format(stage, ext),
141+
"$(location :results/asap7/gcd/single/{}.{})".format(stem, ext),
142+
]
143+
],
144+
data = [
145+
":gcd_base_{}_{}".format(stage, ext)
146+
for ext in [
147+
"sdc",
148+
"odb",
149+
]
150+
] + [
151+
":results/asap7/gcd/single/{}.{}".format(stem, ext)
152+
for ext in [
153+
"sdc",
154+
"odb",
155+
]
156+
],
157+
tags = ["orfs"],
158+
)
159+
for stage, stem in SINGLE_FLOW_STAGES.items()
160+
]
161+
162+
test_suite(
163+
name = "gcd_single_flow_test",
164+
tests = [
165+
":gcd_single_flow_{}_test".format(stage)
166+
for stage in SINGLE_FLOW_STAGES
167+
],
168+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Seed this variant's results dir with the yosys synthesis outputs,
2+
# then run the whole flow in this single OpenROAD process. Under
3+
# bazel-orfs the synthesis results are staged in the src stage's
4+
# variant folder and its odb is exposed via ODB_FILE, while RESULTS_DIR
5+
# points at this run's own variant folder.
6+
file mkdir $::env(RESULTS_DIR)
7+
set src_results [file dirname $::env(ODB_FILE)]
8+
file copy -force $src_results/1_2_yosys.v $::env(RESULTS_DIR)/1_2_yosys.v
9+
file copy -force $src_results/1_2_yosys.sdc $::env(RESULTS_DIR)/1_2_yosys.sdc
10+
11+
source $::env(SCRIPTS_DIR)/flow.tcl

flow/scripts/density_fill.tcl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ if { $::env(USE_FILL) } {
1111
write_verilog $::env(RESULTS_DIR)/6_1_fill.v
1212
orfs_write_db $::env(RESULTS_DIR)/6_1_fill.odb
1313
} else {
14-
log_cmd exec cp $::env(RESULTS_DIR)/5_route.odb $::env(RESULTS_DIR)/6_1_fill.odb
14+
orfs_copy_db $::env(RESULTS_DIR)/5_route.odb $::env(RESULTS_DIR)/6_1_fill.odb
1515
# There is no 5_route.v file to copy
1616
}
1717

0 commit comments

Comments
 (0)