diff --git a/.bazelrc b/.bazelrc index b5d97b6598..5fd082d8fb 100644 --- a/.bazelrc +++ b/.bazelrc @@ -9,4 +9,12 @@ build --cxxopt "-std=c++20" --host_cxxopt "-std=c++20" # bazel-orfs itself does (its own .bazelrc:7). common --lockfile_mode=off +# Test yosys 0.65 via the BCR PR (bazelbuild/bazel-central-registry#8863). +# Upstream BCR doesn't have 0.65 yet, so point at oharboe's fork branch +# as an additional registry; combined with single_version_override(yosys, +# "0.65") in MODULE.bazel this resolves yosys to 0.65 with the +# WRAPCELL/RTLIL-identifier corruption fix that was missing in 0.63. +common --registry=https://raw.githubusercontent.com/oharboe/bazel-central-registry/yosys-0.65/ +common --registry=https://bcr.bazel.build/ + try-import %workspace%/user.bazelrc diff --git a/MODULE.bazel b/MODULE.bazel index 8441a582f1..25f2382310 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -18,6 +18,13 @@ bazel_dep(name = "rules_shell", version = "0.6.1") # dev_dependency = True makes those declarations no-ops in that case, so # our MODULE.bazel doesn't need to be patched at non-root consumption # time. +# +# Exception: bazel_dep(name = "bazel-orfs") is intentionally NOT dev-only, +# because flow/BUILD has `load("@bazel-orfs//:openroad.bzl", "orfs_pdk")` +# and load-visibility requires the bazel_dep to be honoured at non-root +# consumption time too. The companion git_override below is silently +# ignored by Bazel when @orfs is non-root, so the downstream module's +# own bazel-orfs pin still wins. bazel_dep(name = "toolchains_llvm", version = "1.5.0", dev_dependency = True) bazel_dep(name = "openroad", dev_dependency = True) @@ -33,25 +40,26 @@ git_override( remote = "https://github.com/The-OpenROAD-Project/qt_bazel_prebuilts", ) -bazel_dep(name = "bazel-orfs", dev_dependency = True) +bazel_dep(name = "bazel-orfs") + bazel_dep(name = "bazel-orfs-verilog", dev_dependency = True) -BAZEL_ORFS_COMMIT = "ce6efd96dfe39a9c4ef244f8712386f071545d77" +BAZEL_ORFS_COMMIT = "3a5ddd7eb48c363717e65903ae4573d528327fd3" BAZEL_ORFS_REMOTE = "https://github.com/The-OpenROAD-Project/bazel-orfs.git" # To bump version, run: bazelisk run @bazel-orfs//:bump +# +# `patches =` keeps small bazel-orfs fixes vendored in this repo +# while we iterate, instead of round-tripping every change through a +# bazel-orfs PR + pin bump. When a patch lands upstream, drop the +# entry here and bump BAZEL_ORFS_COMMIT. git_override( module_name = "bazel-orfs", commit = BAZEL_ORFS_COMMIT, patch_strip = 1, patches = [ - # Adds orfs_design(user_sources=[VAR,...]) / orfs_flow(user_sources={...}) - # so design-private SOURCE_VARS path hooks (e.g. SDC_FILE_EXTRA in - # flow/designs/asap7/mock-cpu) bypass the variables.yaml validator - # without polluting the ORFS-wide variable schema. Drop once upstream - # bazel-orfs lands the user_sources= API. - "//bazel:0001-orfs_design-add-user_sources.patch", + "//patches/bazel-orfs:0001-render_gds-monkey-patch-PDK_CONFIGS-not-gdsii_use_custom_config.patch", ], remote = BAZEL_ORFS_REMOTE, ) @@ -63,6 +71,16 @@ git_override( strip_prefix = "verilog", ) +# Force yosys to 0.65 (from BCR PR #8863, picked up via the custom +# registry in .bazelrc). 0.65 fixes the WRAPCELL/RTLIL-identifier +# memory corruption that produces garbage like `\_KOGGE_STONE` +# strings in cva6 / ibex synthesis on 0.63. bazel-orfs pins yosys=0.63 +# in its MODULE.bazel; this root override wins. +single_version_override( + module_name = "yosys", + version = "0.65", +) + # yosys-slang is not on BCR. Pin to a commit on povik/yosys-slang master # that has the upstream Bazel build (povik/yosys-slang#310) and the # slang.so visibility fix (povik/yosys-slang#311). Submodules pull in diff --git a/bazel/0001-orfs_design-add-user_sources.patch b/bazel/0001-orfs_design-add-user_sources.patch deleted file mode 100644 index d22eaef7cf..0000000000 --- a/bazel/0001-orfs_design-add-user_sources.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 58df356479d6d9f04aa0bc3ac3676d3b89d26813 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?=C3=98yvind=20Harboe?= -Date: Thu, 14 May 2026 18:48:12 +0200 -Subject: [PATCH] orfs_design/orfs_flow: add user_sources= for design-private - path hooks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Symmetric to user_arguments=, but for source-typed (path-label) vars: -a variable that the config_mk_parser classifies as a SOURCE_VAR (path -staged into the sandbox via _map_source_file) but that is read only -by user-supplied .tcl/.mk and has no entry in ORFS variables.yaml. - -Today such a variable bumps into check_variables(sources.keys()) in -orfs_flow and fails the load phase, even though the file itself is -staged correctly. user_arguments= doesn't help because it pops from -arguments, not sources. - -The motivating case is SDC_FILE_EXTRA — set by -flow/designs/asap7/mock-cpu/config.mk and source'd from that design's -io.tcl, with no ORFS-side consumer. SDC_FILE_EXTRA is in this file's -SOURCE_VARS list, so its value is path-staged; making it work without -declaring it as a global ORFS variable is what user_sources= is for. - -API: -- orfs_design() gains user_sources=[VAR, ...]. Each named var is - popped from sources and forwarded as orfs_flow(user_sources={...}). -- orfs_flow() gains user_sources={VAR: [label, ...]}. Merged into - sources before _orfs_pass so files are still staged, but the key - set skips check_variables. Same shadowing guard as user_arguments=: - collision with a known ORFS variable fails fast. - -Mirrors user_arguments= in both surface and intent. - -Signed-off-by: Øyvind Harboe ---- - private/flow.bzl | 19 +++++++++++++++++-- - private/orfs_design.bzl | 19 ++++++++++++++++++- - 2 files changed, 35 insertions(+), 3 deletions(-) - -diff --git a/private/flow.bzl b/private/flow.bzl -index fd9001f..b59d059 100644 ---- a/private/flow.bzl -+++ b/private/flow.bzl -@@ -171,6 +171,7 @@ def orfs_flow( - verilog_files = [], - macros = [], - sources = {}, -+ user_sources = {}, - stage_sources = {}, - stage_arguments = {}, - renamed_inputs = {}, -@@ -213,6 +214,12 @@ def orfs_flow( - validating against ORFS variables.yaml. Use for vars read only by user-supplied .tcl/.mk - (e.g. ARRAY_COLS in a project's MACRO_PLACEMENT_TCL). Keys that collide with known ORFS - variables are rejected — route those through 'arguments' instead. -+ user_sources: dictionary of project-specific source-typed (path-label) env vars to expose -+ to every stage without validating against ORFS variables.yaml. The path is still staged -+ into the sandbox like a normal source — only the variable name skips the validator. -+ Use for path hooks read only by user-supplied .tcl/.mk (e.g. an extra-SDC hook -+ source'd from the design's own io.tcl). Keys that collide with known ORFS variables -+ are rejected — route those through 'sources' instead. - extra_arguments: dictionary keyed by ORFS stages with lists of .json argument file labels. - These .json files are merged into the stage config, providing computed arguments - that flow through OrfsInfo to subsequent stages. -@@ -269,6 +276,14 @@ def orfs_flow( - ) + - "Use arguments= for ORFS variables; reserve user_arguments= for project-specific env vars.", - ) -+ shadowed_srcs = sorted([k for k in user_sources if k in ALL_VARIABLE_TO_STAGES]) -+ if shadowed_srcs: -+ fail( -+ "user_sources contains known ORFS variable(s): {shadowed}. ".format( -+ shadowed = ", ".join(shadowed_srcs), -+ ) + -+ "Use sources= for ORFS variables; reserve user_sources= for project-specific path hooks.", -+ ) - if abstract_stage and last_stage: - fail("abstract_stage and last_stage are mutually exclusive") - if variant == "base": -@@ -281,7 +296,7 @@ def orfs_flow( - top = top, - verilog_files = verilog_files, - macros = macros, -- sources = sources, -+ sources = sources | user_sources, - stage_sources = stage_sources, - stage_arguments = stage_arguments, - renamed_inputs = renamed_inputs, -@@ -319,7 +334,7 @@ def orfs_flow( - top = top, - verilog_files = verilog_files, - macros = macros, -- sources = sources, -+ sources = sources | user_sources, - stage_sources = stage_sources, - stage_arguments = stage_arguments, - renamed_inputs = {}, -diff --git a/private/orfs_design.bzl b/private/orfs_design.bzl -index 554da44..d53d4bd 100644 ---- a/private/orfs_design.bzl -+++ b/private/orfs_design.bzl -@@ -47,7 +47,7 @@ def _convert_sources(sources, pkg): - result[var] = converted - return result - --def orfs_design(name = None, config = "config.mk", platform = None, design = None, designs = None, mock_openroad = None, mock_yosys = None, user_arguments = [], local_arguments = []): # buildifier: disable=unused-variable -+def orfs_design(name = None, config = "config.mk", platform = None, design = None, designs = None, mock_openroad = None, mock_yosys = None, user_arguments = [], user_sources = [], local_arguments = []): # buildifier: disable=unused-variable - """Create orfs_flow() targets for a design based on its parsed config.mk. - - Usage: -@@ -79,6 +79,13 @@ def orfs_design(name = None, config = "config.mk", platform = None, design = Non - Routed through orfs_flow(user_arguments=...) to bypass the - variables.yaml validator instead of being checked as known - ORFS arguments. -+ user_sources: List of source-typed variable names (vars in -+ SOURCE_VARS) that are project-specific path hooks read only -+ by user-supplied .tcl/.mk, not by ORFS itself (e.g. a -+ per-design extra-SDC hook source'd from the design's own -+ io.tcl). Routed through orfs_flow(user_sources=...) so the -+ file is still staged into the sandbox, but the variable -+ name bypasses the variables.yaml validator. - local_arguments: List of variable names that are only used for - $(VAR) expansion within the same config.mk and are not read - by ORFS or by any user .tcl/.mk (e.g. VERILOG_FILES_BLACKBOX, -@@ -195,6 +202,15 @@ def orfs_design(name = None, config = "config.mk", platform = None, design = Non - if var in arguments: - user_args[var] = arguments.pop(var) - -+ # Same idea for source-typed (path-label) project-specific knobs: -+ # variables that are in SOURCE_VARS (so the parser staged the path -+ # as a label) but are read only by user .tcl/.mk and have no -+ # variables.yaml entry. -+ user_srcs = {} -+ for var in user_sources: -+ if var in sources: -+ user_srcs[var] = sources.pop(var) -+ - # Default SYNTH_NUM_PARTITIONS to a static value so that the action graph - # is identical across machines and remote cache hits are possible. Users - # who prefer local parallelism over caching can pass NUM_CPUS explicitly. -@@ -213,6 +229,7 @@ def orfs_design(name = None, config = "config.mk", platform = None, design = Non - arguments = arguments, - user_arguments = user_args, - sources = sources, -+ user_sources = user_srcs, - macros = macros if macros else [], - stage_data = {"synth": extra_data} if extra_data else {}, - tags = tags, --- -2.51.0 - diff --git a/flow/README.md b/flow/README.md index 9c4f2ff678..1b62058383 100644 --- a/flow/README.md +++ b/flow/README.md @@ -56,3 +56,91 @@ the generated targets get `tags = ["manual"]`. Each OpenROAD invocation takes `-threads `. A wildcard `bazelisk test` runs designs in parallel and overcommits the host. Cap with `--jobs=N`. + +## Running a full sweep without overburdening the host + +To verify every design under one or more platforms (e.g. before +bumping `bazel-orfs` or `tools/yosys`), drive `bazelisk test` per +platform with `--keep_going` so one failure doesn't mask the rest, +and split big designs into their own invocations so routing/synth +don't overcommit RAM. + +```bash +# 1. Install the make-flow toolchain too, so failures can be +# cross-checked against `make DESIGN_CONFIG=...`. +(cd tools/OpenROAD && bazelisk run //:install) + +# 2. List actual test targets per platform (capture before running +# so the sweep is reproducible). +bazelisk query 'tests(//flow/designs/asap7/...)' +bazelisk query 'tests(//flow/designs/sky130hd/...)' + +# 3. Small/fast designs in one parallel invocation; each big design +# in its own sequential invocation. 16-core / 30 GiB box: +bazelisk test --keep_going \ + //flow/designs/asap7/aes/... //flow/designs/asap7/gcd/... \ + //flow/designs/asap7/uart/... //flow/designs/sky130hd/gcd/... # etc. + +for big in cva6 ibex jpeg riscv32i swerv_wrapper; do + bazelisk test --keep_going //flow/designs/asap7/${big}/... +done +``` + +Heuristic: hierarchical / RISC-V cores / image-processing designs +run solo; everything else batches. Use `--jobs=N` to cap parallel +OpenROAD invocations if the host still thrashes. + +## Triaging a failing `_test` + +For each failing `_test`, decide which bucket it falls into: + +1. **Trivial config fix.** Missing variable in + `flow/scripts/variables.yaml`, a typo in `config.mk`, a missing + `exports_files()` for a cross-design SDC/Verilog reference, etc. + Fix in place; `variables.yaml` is the single source of truth + bazel-orfs validates against. + +2. **Yosys-environment false positive (OpenROAD is idempotent given + the same netlist).** `rules-base.json` carries three SHA-1 + fingerprints of yosys' internal state at warning level so a + bazel-yosys vs make-yosys drift surfaces in `checkMetadata.py` + as three buckets: + + [WARN] synth__canonical_netlist__hash differs test: == # front-end + opt_clean + [WARN] synth__preabc_netlist__hash differs test: == # mid-level synth, immediately pre-ABC + [WARN] synth__netlist__hash differs test: == # post-ABC `1_2_yosys.v` + + The bucket(s) that DIFFER bisect where drift entered: front-end + already differing means iteration-order non-determinism is firing + in `read_design_sources`/`hierarchy`/`opt_clean`; pre-ABC matching + but post-ABC diverging means the bazel-pinned vs make-pulled ABC + are different revisions. A QoR threshold breaking together with + any of these is almost always yosys-env drift. Confirm with the per-stage `.odb` SHA + matrix from `@bazel-orfs//:make-yosys-netlist` (feeds make's + netlist into bazel) or its reverse-direction sibling + `@bazel-orfs//:yosys-check` (bazel's netlist into make): + + ```bash + bazelisk run @bazel-orfs//:make-yosys-netlist \ + //flow/designs/asap7/:_test + ``` + + All stages MATCH → yosys-only drift, relax the threshold; any + DIFFER → real bazel-vs-make OpenROAD divergence, file the matrix + as an issue. Full workflow lives in + [`@bazel-orfs//:TESTING.md`](https://github.com/The-OpenROAD-Project/bazel-orfs/blob/main/TESTING.md) + under "Debugging OpenROAD determinism (bazel vs make)". + +3. **Real bug (both flows fail at the same stage).** `.odb` SHAs may + still differ (yosys version) but the failure mode matches. + Generate an issue tarball from the make flow: + + ```bash + cd flow + make _issue DESIGN_CONFIG=designs///config.mk \ + ISSUE_TAG=-- + ``` + + `flow/util/utils.mk` auto-generates `