Skip to content

Commit 5d5949a

Browse files
committed
bazel: user_sources= for design-private SOURCE_VARS, drop SDC_FILE_EXTRA from variables.yaml
SDC_FILE_EXTRA is set by flow/designs/asap7/mock-cpu/config.mk and source'd from that design's io.tcl. No ORFS script reads it — it is a per-design Tcl hook, not part of the flow's shared schema. Earlier in this branch I declared it in flow/scripts/variables.yaml because bazel-orfs's config_mk_parser puts SDC_FILE_EXTRA in SOURCE_VARS (so the path is staged into the sandbox as a label) and orfs_flow then validates sources.keys() against variables.yaml, which fails the load phase for an undeclared name. user_arguments= didn't help: it pops from arguments, not sources. This commit replaces that workaround with the symmetric knob: - bazel/0001-orfs_design-add-user_sources.patch carries a bazel-orfs change adding user_sources=[VAR,...] to orfs_design() and user_sources={VAR: [label,...]} to orfs_flow(). The named vars are popped from sources before check_variables() runs, then merged back in before _orfs_pass so files are still staged. Shadow guard against known ORFS vars mirrors user_arguments. - bazel/BUILD exports the patch file, MODULE.bazel applies it to the pinned bazel-orfs commit (ce6efd9). Drops once upstream lands an equivalent API. - flow/designs/design.bzl forwards user_sources= from design() to orfs_design(). - flow/designs/asap7/mock-cpu/BUILD declares user_sources = ["SDC_FILE_EXTRA"]. - flow/scripts/variables.yaml, flow/scripts/variables.json, and docs/user/FlowVariables.md revert the SDC_FILE_EXTRA entry added earlier in this branch. Verified: bazelisk query //flow/designs/asap7/mock-cpu:all lists all stages, and bazelisk cquery --output=build on mock_cpu_synth shows arguments["SDC_FILE_EXTRA"] = "\$(locations //flow/designs/src/mock-array:util.tcl)" with util.tcl in data — i.e. the file is still staged. Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
1 parent cd5f4c7 commit 5d5949a

8 files changed

Lines changed: 182 additions & 16 deletions

File tree

MODULE.bazel

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ bazel_dep(name = "rules_shell", version = "0.6.1")
2020
# time.
2121

2222
bazel_dep(name = "toolchains_llvm", version = "1.5.0", dev_dependency = True)
23-
2423
bazel_dep(name = "openroad", dev_dependency = True)
2524
local_path_override(
2625
module_name = "openroad",
@@ -45,6 +44,15 @@ BAZEL_ORFS_REMOTE = "https://github.com/The-OpenROAD-Project/bazel-orfs.git"
4544
git_override(
4645
module_name = "bazel-orfs",
4746
commit = BAZEL_ORFS_COMMIT,
47+
patch_strip = 1,
48+
patches = [
49+
# Adds orfs_design(user_sources=[VAR,...]) / orfs_flow(user_sources={...})
50+
# so design-private SOURCE_VARS path hooks (e.g. SDC_FILE_EXTRA in
51+
# flow/designs/asap7/mock-cpu) bypass the variables.yaml validator
52+
# without polluting the ORFS-wide variable schema. Drop once upstream
53+
# bazel-orfs lands the user_sources= API.
54+
"//bazel:0001-orfs_design-add-user_sources.patch",
55+
],
4856
remote = BAZEL_ORFS_REMOTE,
4957
)
5058

@@ -79,7 +87,10 @@ llvm.toolchain(
7987
)
8088
use_repo(llvm, "llvm_toolchain")
8189

82-
register_toolchains("@llvm_toolchain//:all", dev_dependency = True)
90+
register_toolchains(
91+
"@llvm_toolchain//:all",
92+
dev_dependency = True,
93+
)
8394

8495
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
8596
python.toolchain(
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
From 58df356479d6d9f04aa0bc3ac3676d3b89d26813 Mon Sep 17 00:00:00 2001
2+
From: =?UTF-8?q?=C3=98yvind=20Harboe?= <oyvind@ascenium.com>
3+
Date: Thu, 14 May 2026 18:48:12 +0200
4+
Subject: [PATCH] orfs_design/orfs_flow: add user_sources= for design-private
5+
path hooks
6+
MIME-Version: 1.0
7+
Content-Type: text/plain; charset=UTF-8
8+
Content-Transfer-Encoding: 8bit
9+
10+
Symmetric to user_arguments=, but for source-typed (path-label) vars:
11+
a variable that the config_mk_parser classifies as a SOURCE_VAR (path
12+
staged into the sandbox via _map_source_file) but that is read only
13+
by user-supplied .tcl/.mk and has no entry in ORFS variables.yaml.
14+
15+
Today such a variable bumps into check_variables(sources.keys()) in
16+
orfs_flow and fails the load phase, even though the file itself is
17+
staged correctly. user_arguments= doesn't help because it pops from
18+
arguments, not sources.
19+
20+
The motivating case is SDC_FILE_EXTRA — set by
21+
flow/designs/asap7/mock-cpu/config.mk and source'd from that design's
22+
io.tcl, with no ORFS-side consumer. SDC_FILE_EXTRA is in this file's
23+
SOURCE_VARS list, so its value is path-staged; making it work without
24+
declaring it as a global ORFS variable is what user_sources= is for.
25+
26+
API:
27+
- orfs_design() gains user_sources=[VAR, ...]. Each named var is
28+
popped from sources and forwarded as orfs_flow(user_sources={...}).
29+
- orfs_flow() gains user_sources={VAR: [label, ...]}. Merged into
30+
sources before _orfs_pass so files are still staged, but the key
31+
set skips check_variables. Same shadowing guard as user_arguments=:
32+
collision with a known ORFS variable fails fast.
33+
34+
Mirrors user_arguments= in both surface and intent.
35+
36+
Signed-off-by: Øyvind Harboe <oyvind@ascenium.com>
37+
---
38+
private/flow.bzl | 19 +++++++++++++++++--
39+
private/orfs_design.bzl | 19 ++++++++++++++++++-
40+
2 files changed, 35 insertions(+), 3 deletions(-)
41+
42+
diff --git a/private/flow.bzl b/private/flow.bzl
43+
index fd9001f..b59d059 100644
44+
--- a/private/flow.bzl
45+
+++ b/private/flow.bzl
46+
@@ -171,6 +171,7 @@ def orfs_flow(
47+
verilog_files = [],
48+
macros = [],
49+
sources = {},
50+
+ user_sources = {},
51+
stage_sources = {},
52+
stage_arguments = {},
53+
renamed_inputs = {},
54+
@@ -213,6 +214,12 @@ def orfs_flow(
55+
validating against ORFS variables.yaml. Use for vars read only by user-supplied .tcl/.mk
56+
(e.g. ARRAY_COLS in a project's MACRO_PLACEMENT_TCL). Keys that collide with known ORFS
57+
variables are rejected — route those through 'arguments' instead.
58+
+ user_sources: dictionary of project-specific source-typed (path-label) env vars to expose
59+
+ to every stage without validating against ORFS variables.yaml. The path is still staged
60+
+ into the sandbox like a normal source — only the variable name skips the validator.
61+
+ Use for path hooks read only by user-supplied .tcl/.mk (e.g. an extra-SDC hook
62+
+ source'd from the design's own io.tcl). Keys that collide with known ORFS variables
63+
+ are rejected — route those through 'sources' instead.
64+
extra_arguments: dictionary keyed by ORFS stages with lists of .json argument file labels.
65+
These .json files are merged into the stage config, providing computed arguments
66+
that flow through OrfsInfo to subsequent stages.
67+
@@ -269,6 +276,14 @@ def orfs_flow(
68+
) +
69+
"Use arguments= for ORFS variables; reserve user_arguments= for project-specific env vars.",
70+
)
71+
+ shadowed_srcs = sorted([k for k in user_sources if k in ALL_VARIABLE_TO_STAGES])
72+
+ if shadowed_srcs:
73+
+ fail(
74+
+ "user_sources contains known ORFS variable(s): {shadowed}. ".format(
75+
+ shadowed = ", ".join(shadowed_srcs),
76+
+ ) +
77+
+ "Use sources= for ORFS variables; reserve user_sources= for project-specific path hooks.",
78+
+ )
79+
if abstract_stage and last_stage:
80+
fail("abstract_stage and last_stage are mutually exclusive")
81+
if variant == "base":
82+
@@ -281,7 +296,7 @@ def orfs_flow(
83+
top = top,
84+
verilog_files = verilog_files,
85+
macros = macros,
86+
- sources = sources,
87+
+ sources = sources | user_sources,
88+
stage_sources = stage_sources,
89+
stage_arguments = stage_arguments,
90+
renamed_inputs = renamed_inputs,
91+
@@ -319,7 +334,7 @@ def orfs_flow(
92+
top = top,
93+
verilog_files = verilog_files,
94+
macros = macros,
95+
- sources = sources,
96+
+ sources = sources | user_sources,
97+
stage_sources = stage_sources,
98+
stage_arguments = stage_arguments,
99+
renamed_inputs = {},
100+
diff --git a/private/orfs_design.bzl b/private/orfs_design.bzl
101+
index 554da44..d53d4bd 100644
102+
--- a/private/orfs_design.bzl
103+
+++ b/private/orfs_design.bzl
104+
@@ -47,7 +47,7 @@ def _convert_sources(sources, pkg):
105+
result[var] = converted
106+
return result
107+
108+
-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
109+
+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
110+
"""Create orfs_flow() targets for a design based on its parsed config.mk.
111+
112+
Usage:
113+
@@ -79,6 +79,13 @@ def orfs_design(name = None, config = "config.mk", platform = None, design = Non
114+
Routed through orfs_flow(user_arguments=...) to bypass the
115+
variables.yaml validator instead of being checked as known
116+
ORFS arguments.
117+
+ user_sources: List of source-typed variable names (vars in
118+
+ SOURCE_VARS) that are project-specific path hooks read only
119+
+ by user-supplied .tcl/.mk, not by ORFS itself (e.g. a
120+
+ per-design extra-SDC hook source'd from the design's own
121+
+ io.tcl). Routed through orfs_flow(user_sources=...) so the
122+
+ file is still staged into the sandbox, but the variable
123+
+ name bypasses the variables.yaml validator.
124+
local_arguments: List of variable names that are only used for
125+
$(VAR) expansion within the same config.mk and are not read
126+
by ORFS or by any user .tcl/.mk (e.g. VERILOG_FILES_BLACKBOX,
127+
@@ -195,6 +202,15 @@ def orfs_design(name = None, config = "config.mk", platform = None, design = Non
128+
if var in arguments:
129+
user_args[var] = arguments.pop(var)
130+
131+
+ # Same idea for source-typed (path-label) project-specific knobs:
132+
+ # variables that are in SOURCE_VARS (so the parser staged the path
133+
+ # as a label) but are read only by user .tcl/.mk and have no
134+
+ # variables.yaml entry.
135+
+ user_srcs = {}
136+
+ for var in user_sources:
137+
+ if var in sources:
138+
+ user_srcs[var] = sources.pop(var)
139+
+
140+
# Default SYNTH_NUM_PARTITIONS to a static value so that the action graph
141+
# is identical across machines and remote cache hits are possible. Users
142+
# who prefer local parallelism over caching can pass NUM_CPUS explicitly.
143+
@@ -213,6 +229,7 @@ def orfs_design(name = None, config = "config.mk", platform = None, design = Non
144+
arguments = arguments,
145+
user_arguments = user_args,
146+
sources = sources,
147+
+ user_sources = user_srcs,
148+
macros = macros if macros else [],
149+
stage_data = {"synth": extra_data} if extra_data else {},
150+
tags = tags,
151+
--
152+
2.51.0
153+

bazel/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exports_files(glob(["*.patch"]))

docs/user/FlowVariables.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,6 @@ configuration file.
268268
| <a name="RUN_SCRIPT"></a>RUN_SCRIPT| Path to script to run from `make run`, python or tcl script detected by .py or .tcl extension.| |
269269
| <a name="SC_LEF"></a>SC_LEF| Path to technology standard cell LEF file.| |
270270
| <a name="SDC_FILE"></a>SDC_FILE| The path to design constraint (SDC) file.| |
271-
| <a name="SDC_FILE_EXTRA"></a>SDC_FILE_EXTRA| Path to an extra Tcl file the design's own SDC / io.tcl can `source` for per-design hooks (constraints that don't fit cleanly in the shared SDC_FILE). bazel-orfs classifies this as a source-typed variable (path label), so the value is staged into the sandbox. Used by flow/designs/asap7/mock-cpu.| |
272271
| <a name="SDC_GUT"></a>SDC_GUT| Load design and remove all internal logic before doing synthesis. This is useful when creating a mock .lef abstract that has a smaller area than the amount of logic would allow. bazel-orfs uses this to mock SRAMs, for instance.| |
273272
| <a name="SEAL_GDS"></a>SEAL_GDS| Seal macro to place around the design.| |
274273
| <a name="SETUP_MOVE_SEQUENCE"></a>SETUP_MOVE_SEQUENCE| Passed as -sequence to repair_timing. This should be a string of move keywords separated by commas.| |
@@ -659,7 +658,6 @@ configuration file.
659658
- [RUN_LOG_NAME_STEM](#RUN_LOG_NAME_STEM)
660659
- [RUN_SCRIPT](#RUN_SCRIPT)
661660
- [SC_LEF](#SC_LEF)
662-
- [SDC_FILE_EXTRA](#SDC_FILE_EXTRA)
663661
- [SEAL_GDS](#SEAL_GDS)
664662
- [SET_RC_TCL](#SET_RC_TCL)
665663
- [SLEW_MARGIN](#SLEW_MARGIN)

flow/designs/asap7/mock-cpu/BUILD

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
load("//flow/designs:design.bzl", "design")
22

3-
design(config = "config.mk")
3+
# SDC_FILE_EXTRA is a design-private path hook: set in this design's
4+
# config.mk, source'd from this design's io.tcl, never read by ORFS.
5+
# bazel-orfs's config_mk_parser SOURCE_VARS still stages the file into
6+
# the sandbox; user_sources= tells the validator to leave the name
7+
# alone (no variables.yaml entry required).
8+
design(
9+
config = "config.mk",
10+
user_sources = ["SDC_FILE_EXTRA"],
11+
)

flow/designs/design.bzl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,18 @@ def _export_design_files():
5151
visibility = ["//visibility:private"],
5252
)
5353

54-
def design(config = "config.mk", user_arguments = [], local_arguments = []):
54+
def design(config = "config.mk", user_arguments = [], user_sources = [], local_arguments = []):
5555
"""Standard BUILD body for flow/designs/<platform>/<design>/.
5656
5757
Args:
5858
config: The config.mk file that drives this design.
5959
user_arguments: see orfs_design — list of config.mk var names that
6060
are project-specific (read by the design's own .tcl/.mk, not by
6161
ORFS) and should bypass the variables.yaml validator.
62+
user_sources: see orfs_design — list of config.mk var names that
63+
are project-specific source-typed (path-label) hooks read only
64+
by the design's own .tcl/.mk; the file is still staged into the
65+
sandbox but the var name skips variables.yaml validation.
6266
local_arguments: see orfs_design — list of config.mk var names that
6367
are pure make-only helpers (used only via $(VAR) expansion
6468
within the same config.mk, never read by ORFS or by user
@@ -68,6 +72,7 @@ def design(config = "config.mk", user_arguments = [], local_arguments = []):
6872
orfs_design(
6973
config = config,
7074
user_arguments = user_arguments,
75+
user_sources = user_sources,
7176
local_arguments = local_arguments,
7277
)
7378

flow/scripts/variables.json

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flow/scripts/variables.yaml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -930,13 +930,6 @@ SDC_FILE:
930930
The path to design constraint (SDC) file.
931931
stages:
932932
- synth
933-
SDC_FILE_EXTRA:
934-
description: >
935-
Path to an extra Tcl file the design's own SDC / io.tcl can `source`
936-
for per-design hooks (constraints that don't fit cleanly in the shared
937-
SDC_FILE). bazel-orfs classifies this as a source-typed variable
938-
(path label), so the value is staged into the sandbox. Used by
939-
flow/designs/asap7/mock-cpu.
940933
SDC_GUT:
941934
description: >
942935
Load design and remove all internal logic before doing synthesis. This is

0 commit comments

Comments
 (0)