diff --git a/MODULE.bazel b/MODULE.bazel index 1d21ea2f79..90161b23f2 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -83,6 +83,20 @@ register_toolchains( dev_dependency = True, ) +# sv-lang (via tools/OpenROAD) pulls in rules_pycross, whose toolchain +# extension fails on Python 3.8 (transitively registered via or-tools -> +# pybind11_abseil, dropped from rules_python 2.0.0's MINOR_MAPPING). +# pycross.configure_environments is root-honored only, so the workaround +# must be mirrored here from tools/OpenROAD/MODULE.bazel. +bazel_dep(name = "rules_pycross", version = "0.8.1", dev_dependency = True) + +pycross = use_extension( + "@rules_pycross//pycross/extensions:pycross.bzl", + "pycross", + dev_dependency = True, +) +pycross.configure_environments(python_versions = ["3.13"]) + python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain( ignore_root_user_error = True, diff --git a/flow/designs/asap7/gcd/BUILD b/flow/designs/asap7/gcd/BUILD index 527d6542e1..9674a4de33 100644 --- a/flow/designs/asap7/gcd/BUILD +++ b/flow/designs/asap7/gcd/BUILD @@ -1,3 +1,143 @@ +load("@bazel-orfs//:openroad.bzl", "orfs_run") +load("@orfs_designs//:designs.bzl", "DESIGNS") +load("@rules_shell//shell:sh_test.bzl", "sh_test") load("//flow/designs:design.bzl", "design") -design(config = "config.mk") +# SYNTH_USE_SYN is make-only for now: bazel-orfs's synth stage always +# runs the yosys flow and neither stages the Verilog sources nor sets +# VERILOG_FILES for the OpenROAD synthesis step, so the built-in +# synthesizer opt-in must not reach the bazel arguments. +design( + config = "config.mk", + local_arguments = ["SYNTH_USE_SYN"], +) + +# Stage-boundary .odb/.sdc stems shared by the normal per-stage flow +# targets and the single-process flow below; gcd_single_flow_test +# compares each of these files pairwise between the two flows. +SINGLE_FLOW_STAGES = { + "floorplan": "2_floorplan", + "place": "3_place", + "cts": "4_cts", + "grt": "5_1_grt", + "route": "5_route", + "final": "6_final", +} + +# Substep files the single-process flow also writes, declared so the +# action keeps them and a human can inspect them on failure. +SINGLE_FLOW_EXTRA_OUTS = [ + "2_1_floorplan.odb", + "2_1_floorplan.sdc", + "2_2_floorplan_macro.odb", + "2_3_floorplan_tapcell.odb", + "2_4_floorplan_pdn.odb", + "3_1_place_gp_skip_io.odb", + "3_2_place_iop.odb", + "3_3_place_gp.odb", + "3_4_place_resized.odb", + "3_5_place_dp.odb", + "4_1_cts.odb", + "5_2_route.odb", + "5_3_fillcell.odb", + "6_1_fill.odb", + "6_1_fill.sdc", +] + +# The whole flow, floorplan through finish, as one Tcl script in a +# single OpenROAD process (scripts/flow.tcl). The synth stage config +# only carries synth-scoped variables, so the design's full argument +# set rides along; FLOW_VARIANT=single keeps the output paths clear of +# the regular stage targets' declared outputs in this package. +orfs_run( + name = "gcd_single_flow", + src = ":gcd_synth", + outs = [ + "results/asap7/gcd/single/{}.{}".format(stem, ext) + for stem in SINGLE_FLOW_STAGES.values() + for ext in [ + "odb", + "sdc", + ] + ] + [ + "results/asap7/gcd/single/" + name + for name in SINGLE_FLOW_EXTRA_OUTS + ], + arguments = { + k: v + for k, v in DESIGNS["asap7/gcd"]["arguments"].items() + if k != "SYNTH_USE_SYN" + } | { + "FLOW_VARIANT": "single", + }, + script = ":single_flow.tcl", + variant = "single", +) + +# Normal-flow stage outputs, one filegroup per compared file. +[ + filegroup( + name = "gcd_base_{}_{}".format(stage, ext), + srcs = [":gcd_" + stage], + output_group = "{}.{}".format(stem, ext), + ) + for stage, stem in SINGLE_FLOW_STAGES.items() + for ext in [ + "odb", + "sdc", + ] +] + +# Byte-compare each stage boundary's .odb/.sdc between the per-stage +# flow and the single-process flow: equal files prove the +# single-process mode is equivalent to the per-stage flow, not merely +# that it runs to completion. check_same.sh takes file pairs: +# file_a file_b ... (.sdc pair first: check_same.sh stops at a binary +# diff, and the .sdc verdict should be reported before that). +# +# Known divergence, deliberately left failing so it gets root-caused +# rather than papered over: from the grt stage on, the grt-stage +# repair_timing inserts one extra buffer (8 vs 7 on asap7/gcd) when the +# process continues from the in-memory CTS state instead of reloading +# 4_cts.odb, even though the 4_cts.odb/.sdc it starts from are +# byte-identical (all .sdc match through the whole flow). +[ + sh_test( + name = "gcd_single_flow_{}_test".format(stage), + srcs = ["@openroad//test/orfs:check_same.sh"], + args = [ + arg + for ext in [ + "sdc", + "odb", + ] + for arg in [ + "$(location :gcd_base_{}_{})".format(stage, ext), + "$(location :results/asap7/gcd/single/{}.{})".format(stem, ext), + ] + ], + data = [ + ":gcd_base_{}_{}".format(stage, ext) + for ext in [ + "sdc", + "odb", + ] + ] + [ + ":results/asap7/gcd/single/{}.{}".format(stem, ext) + for ext in [ + "sdc", + "odb", + ] + ], + tags = ["orfs"], + ) + for stage, stem in SINGLE_FLOW_STAGES.items() +] + +test_suite( + name = "gcd_single_flow_test", + tests = [ + ":gcd_single_flow_{}_test".format(stage) + for stage in SINGLE_FLOW_STAGES + ], +) diff --git a/flow/designs/asap7/gcd/single_flow.tcl b/flow/designs/asap7/gcd/single_flow.tcl new file mode 100644 index 0000000000..64fe2466b5 --- /dev/null +++ b/flow/designs/asap7/gcd/single_flow.tcl @@ -0,0 +1,10 @@ +# Seed this variant's results dir with the synthesis outputs, then run +# the whole flow in this single OpenROAD process. Under bazel-orfs the +# synthesis results are staged in the src stage's variant folder and +# exposed via ODB_FILE, while RESULTS_DIR points at this run's own +# variant folder. +file mkdir $::env(RESULTS_DIR) +file copy -force $::env(ODB_FILE) $::env(RESULTS_DIR)/1_synth.odb +file copy -force [file rootname $::env(ODB_FILE)].sdc $::env(RESULTS_DIR)/1_synth.sdc + +source $::env(SCRIPTS_DIR)/flow.tcl diff --git a/flow/scripts/flow.tcl b/flow/scripts/flow.tcl new file mode 100644 index 0000000000..400b172981 --- /dev/null +++ b/flow/scripts/flow.tcl @@ -0,0 +1,41 @@ +# Runs all OpenROAD stages of the flow, from 1_synth.odb/.sdc through +# the final report, in a single OpenROAD process. Produces the same +# .odb/.sdc files in $RESULTS_DIR as the stage-per-process make flow: +# the file copy -force lines mirror the Makefile do-copy recipes. + +# Enable keeping variables between stages +set ::env(KEEP_VARS) 1 + +# Floorplan +source $::env(SCRIPTS_DIR)/floorplan.tcl +source $::env(SCRIPTS_DIR)/macro_place.tcl +source $::env(SCRIPTS_DIR)/tapcell.tcl +source $::env(SCRIPTS_DIR)/pdn.tcl +file copy -force $::env(RESULTS_DIR)/2_4_floorplan_pdn.odb $::env(RESULTS_DIR)/2_floorplan.odb +file copy -force $::env(RESULTS_DIR)/2_1_floorplan.sdc $::env(RESULTS_DIR)/2_floorplan.sdc + +# Place +source $::env(SCRIPTS_DIR)/global_place_skip_io.tcl +source $::env(SCRIPTS_DIR)/io_placement.tcl +source $::env(SCRIPTS_DIR)/global_place.tcl +source $::env(SCRIPTS_DIR)/resize.tcl +source $::env(SCRIPTS_DIR)/detail_place.tcl +file copy -force $::env(RESULTS_DIR)/3_5_place_dp.odb $::env(RESULTS_DIR)/3_place.odb +file copy -force $::env(RESULTS_DIR)/2_floorplan.sdc $::env(RESULTS_DIR)/3_place.sdc + +# CTS (cts.tcl itself writes 4_cts.sdc) +source $::env(SCRIPTS_DIR)/cts.tcl +file copy -force $::env(RESULTS_DIR)/4_1_cts.odb $::env(RESULTS_DIR)/4_cts.odb + +# Route +source $::env(SCRIPTS_DIR)/global_route.tcl +source $::env(SCRIPTS_DIR)/detail_route.tcl +source $::env(SCRIPTS_DIR)/fillcell.tcl +file copy -force $::env(RESULTS_DIR)/5_3_fillcell.odb $::env(RESULTS_DIR)/5_route.odb +file copy -force $::env(RESULTS_DIR)/5_1_grt.sdc $::env(RESULTS_DIR)/5_route.sdc + +# Finish +source $::env(SCRIPTS_DIR)/density_fill.tcl +file copy -force $::env(RESULTS_DIR)/5_route.sdc $::env(RESULTS_DIR)/6_1_fill.sdc +file copy -force $::env(RESULTS_DIR)/5_route.sdc $::env(RESULTS_DIR)/6_final.sdc +source $::env(SCRIPTS_DIR)/final_report.tcl