diff --git a/private/orfs_design.bzl b/private/orfs_design.bzl index 3a0f3c2b..cb607fcd 100644 --- a/private/orfs_design.bzl +++ b/private/orfs_design.bzl @@ -44,7 +44,7 @@ def _convert_sources(sources, pkg): result[var] = converted return result -def orfs_design(name = None, platform = None, design = None, designs = None, mock_openroad = None, mock_yosys = None): # buildifier: disable=unused-variable +def orfs_design(name = None, platform = None, design = None, designs = None, mock_openroad = None, mock_yosys = None, user_arguments = []): # buildifier: disable=unused-variable """Create orfs_flow() targets for a design based on its parsed config.mk. Call this from a design's BUILD.bazel: @@ -66,6 +66,12 @@ def orfs_design(name = None, platform = None, design = None, designs = None, moc mock_yosys: Label for mock-yosys binary. When set, lint flow uses this instead of real Yosys for synthesis. Example: "//mock/yosys/src/bin:yosys". + user_arguments: List of variable names from config.mk that are + project-specific (read by user-supplied .tcl/.mk, not by + ORFS itself, e.g. FP_PDN_RAIL_OFFSET in a custom pdn.cfg). + Routed through orfs_flow(user_arguments=...) to bypass the + variables.yaml validator instead of being checked as known + ORFS arguments. """ if designs == None: fail("orfs_design() requires designs argument: pass DESIGNS from @orfs_designs//:designs.bzl") @@ -158,6 +164,13 @@ def orfs_design(name = None, platform = None, design = None, designs = None, moc # Real flow — uses Docker image with real OpenROAD/Yosys arguments = dict(config["arguments"]) + # Move caller-flagged design-specific knobs out of arguments and into + # user_arguments so they bypass the variables.yaml validator. + user_args = {} + for var in user_arguments: + if var in arguments: + user_args[var] = arguments.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. @@ -174,6 +187,7 @@ def orfs_design(name = None, platform = None, design = None, designs = None, moc verilog_files = verilog_files, pdk = "//flow:" + platform, arguments = arguments, + user_arguments = user_args, sources = sources, macros = macros if macros else [], stage_data = {"synth": extra_data} if extra_data else {}, diff --git a/private/stages.bzl b/private/stages.bzl index 8c1391a8..28e8afa7 100644 --- a/private/stages.bzl +++ b/private/stages.bzl @@ -6,9 +6,31 @@ load("//private:utils.bzl", "flatten", "set", "union") # A stage argument is used in one or more stages. This is metainformation # about the ORFS code that there is no known nice way for ORFS to # provide. -BAZEL_VARIABLE_TO_STAGES = {} +# +# Variables auto-injected by bazel-orfs (not present in ORFS variables.yaml) +# are registered here so that check_variables() does not flag them as +# unknown when downstream callers see them in arguments. +BAZEL_VARIABLE_TO_STAGES = { + # Set in orfs_design.bzl when SYNTH_HIERARCHICAL=1. + "SYNTH_NUM_PARTITIONS": ["synth"], +} -BAZEL_STAGE_TO_VARIABLES = {} +BAZEL_STAGE_TO_VARIABLES = { + stage: [v for v, stages in BAZEL_VARIABLE_TO_STAGES.items() if stage in stages] + for stage in [ + "synth", + "floorplan", + "place", + "cts", + "grt", + "route", + "final", + "generate_abstract", + "generate_metadata", + "test", + "update_rules", + ] +} ALL_STAGES = [ "synth", @@ -135,7 +157,8 @@ ORFS_STAGE_TO_VARIABLES = { } ALL_STAGE_TO_VARIABLES = { - stage: ORFS_STAGE_TO_VARIABLES.get(stage, []) + stage: ORFS_STAGE_TO_VARIABLES.get(stage, []) + + BAZEL_STAGE_TO_VARIABLES.get(stage, []) for stage in ALL_STAGES } diff --git a/synth_partition.sh b/synth_partition.sh index bf3deb51..f37ca48f 100755 --- a/synth_partition.sh +++ b/synth_partition.sh @@ -17,13 +17,18 @@ NUM_PARTITIONS=${SYNTH_NUM_PARTITIONS:?} KEPT_JSON="$RESULTS_DIR/kept_modules.json" OUTPUT="$RESULTS_DIR/partition_${PARTITION_ID}.v" -# Parse module list from JSON using sed (avoids python wrapper issues) -# JSON format: {"modules": ["mod1", "mod2", ...]} -ALL_MODULES=$(sed 's/.*\[//;s/\].*//;s/"//g;s/,/\n/g;s/ //g' "$KEPT_JSON") +# Parse module list from JSON. Module names can contain '[' and ']' +# (slang elaborates parameterized instances to names like +# 'foo$bar.gen_tiles[0].i_tile.gen_banks[3]'), so a greedy sed regex is +# unsafe. Module names cannot contain '"', so extracting all quoted +# strings and skipping the first ("modules" key) is correct. +ALL_MODULES=$(grep -oE '"[^"]+"' "$KEPT_JSON" | tail -n +2 | sed 's/"//g') -# When SYNTH_SKIP_KEEP is set, the keep-hierarchy discovery was skipped. +# When SYNTH_SKIP_KEEP=1, the keep-hierarchy discovery was skipped. # Partitions read from canonical RTLIL and run full synthesis (coarse+fine). -if [ -n "${SYNTH_SKIP_KEEP:-}" ]; then +# ORFS variables.yaml defaults SYNTH_SKIP_KEEP to "0", so test for "1" +# explicitly rather than -n (which is true for any non-empty string). +if [ "${SYNTH_SKIP_KEEP:-0}" = "1" ]; then CHECKPOINT="$RESULTS_DIR/1_1_yosys_canonicalize.rtlil" # Validate that every SYNTH_KEEP_MODULES entry exists in the canonical RTLIL RTLIL_MODULES_FILE=$(mktemp)