From 8e388b95813da4cc8dabce0aa2518590abc6b712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Harboe?= Date: Fri, 22 May 2026 10:02:36 +0200 Subject: [PATCH 1/3] flow: add OpenROAD built-in synth as _syn variant of every flat design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds OpenROAD's integrated synthesizer (sv_elaborate + synthesize, OpenROAD #10473) as a universal "_syn" variant of every flat design driven by design() in flow/designs/design.bzl. Relocates and generalises the throwaway smoke target from OpenROAD #10479, which explicitly said the proper home for this was bazel-orfs / ORFS, not OpenROAD's test tree. Purely additive: no existing Makefile recipe, bazel rule, design, or target's behaviour is altered. Pieces: - flow/scripts/synth_syn.tcl (new) -- povik's driver. Reads LEF/Liberty/SDC via the ORFS env vars and writes 1_synth.odb + 1_synth.sdc directly, bypassing the Yosys 1_2_yosys.v -> synth_odb.tcl chain. - flow/Makefile gains one new phony target do-syn-synth that invokes openroad on synth_syn.tcl. No existing recipe touched. - patches/bazel-orfs/0002-...patch adds a parallel orfs_openroad_synth_rule + orfs_openroad_synth macro in bazel-orfs. OrfsInfo shape matches orfs_synth_rule, so orfs_flow(previous_stage = {"floorplan": ":_syn_synth"}) chains downstream stages unchanged. Vendored here until the integrated syn tool stabilises and this lands upstream. - flow/designs/design.bzl -- design() now also emits the _syn chain for every flat design via a DESIGNS lookup + helper. Hierarchical designs (blocks non-empty) are skipped -- the built-in synthesizer doesn't handle BLOCKS yet. All _syn targets are tagged manual so bazel build //... is unaffected. End state for every flat design: bazelisk build //flow/designs/asap7/gcd:gcd_syn_synth # OpenROAD synth bazelisk build //flow/designs/asap7/gcd:gcd_synth # Yosys (unchanged) Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Øyvind Harboe --- MODULE.bazel | 1 + flow/Makefile | 12 + flow/designs/design.bzl | 61 +++- flow/scripts/synth_syn.tcl | 74 +++++ ...d-orfs_openroad_synth-rule-and-macro.patch | 265 ++++++++++++++++++ 5 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 flow/scripts/synth_syn.tcl create mode 100644 patches/bazel-orfs/0002-add-orfs_openroad_synth-rule-and-macro.patch diff --git a/MODULE.bazel b/MODULE.bazel index 126de82d6f..fea29ce83f 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -52,6 +52,7 @@ git_override( patch_strip = 1, patches = [ "//patches/bazel-orfs:0001-render_gds-monkey-patch-PDK_CONFIGS-not-gdsii_use_custom_config.patch", + "//patches/bazel-orfs:0002-add-orfs_openroad_synth-rule-and-macro.patch", ], remote = BAZEL_ORFS_REMOTE, ) diff --git a/flow/Makefile b/flow/Makefile index 1bb45047a4..f06faa51ab 100644 --- a/flow/Makefile +++ b/flow/Makefile @@ -283,6 +283,18 @@ clean_synth: rm -f $(SYNTH_STATS) rm -f $(SDC_FILE_CLOCK_PERIOD) +# Run Synthesis using OpenROAD's integrated synthesizer (sv_elaborate + +# synthesize, OpenROAD #10473). Drives `synth_syn.tcl`, which reads +# LEF/Liberty/SDC straight from the ORFS env vars and writes 1_synth.odb +# + 1_synth.sdc directly -- bypassing the Yosys + synth_odb chain. Used +# by the bazel-orfs `orfs_openroad_synth_rule` for the `_syn` flow +# variant. Independent of the Yosys recipes above; either path can run. +.PHONY: do-syn-synth +do-syn-synth: + @mkdir -p $(RESULTS_DIR) $(LOG_DIR) $(REPORTS_DIR) + $(RUN_CMD) --log $(abspath $(LOG_DIR)/1_synth.log) --tee -- \ + $(OPENROAD_CMD) $(SCRIPTS_DIR)/synth_syn.tcl + # ============================================================================== # _____ _ ___ ___ ____ ____ _ _ _ _ # | ___| | / _ \ / _ \| _ \| _ \| | / \ | \ | | diff --git a/flow/designs/design.bzl b/flow/designs/design.bzl index b4efc0b1d0..cc1fa5abd0 100644 --- a/flow/designs/design.bzl +++ b/flow/designs/design.bzl @@ -1,6 +1,7 @@ """BUILD boilerplate for flow/designs/.""" -load("@orfs_designs//:designs.bzl", "orfs_design") +load("@bazel-orfs//:openroad.bzl", "orfs_flow", "orfs_openroad_synth") +load("@orfs_designs//:designs.bzl", "DESIGNS", "orfs_design") # Per filegroup target: extensions included in the filegroup. # bazel-orfs's config_mk_parser produces these target names from @@ -76,6 +77,7 @@ def design(config = "config.mk", user_arguments = [], user_sources = [], local_a local_arguments = local_arguments, blender = True, ) + _emit_syn_variant(DESIGNS.get(_design_key())) def files(group, extra_srcs = None): """Named filegroup over conventional extensions. @@ -94,3 +96,60 @@ def files(group, extra_srcs = None): visibility = ["//visibility:public"], ) _export_design_files() + +def _design_key(): + """Map this BUILD package to its DESIGNS key. + + `native.package_name()` looks like `flow/designs//`; + the DESIGNS dict is keyed by `/` (bazel-orfs's + config_mk_parser also indexes by directory name when it differs + from the nickname). Returns "" for packages outside that layout + so the lookup misses cleanly. + """ + parts = native.package_name().split("/") + if len(parts) < 2: + return "" + return "{}/{}".format(parts[-2], parts[-1]) + +def _emit_syn_variant(entry): + """Emit the `_syn` variant chain for `entry` from DESIGNS. + + Adds `_syn_synth` (built-in OpenROAD synth, driven by + flow/scripts/synth_syn.tcl) plus the downstream `_syn_*` chain + via `orfs_flow(variant = "syn", previous_stage = {...})`. + + No-op for hierarchical designs (`entry["blocks"]` non-empty); the + built-in synthesizer doesn't handle BLOCKS yet and those would + just fail. Flat designs pick the variant up for free. + + All emitted targets are tagged `manual` so `bazel build //...` + does not pick them up while the OpenROAD synth tool (PR #10473) + matures. + """ + if entry == None or entry.get("blocks"): + return + name = entry["name"] + pdk = "//flow:" + entry["platform"] + syn_args = dict(entry["arguments"]) + syn_synth = name + "_syn_synth" + orfs_openroad_synth( + name = syn_synth, + module_top = name, + verilog_files = entry["verilog_files"], + sources = entry["sources"], + arguments = syn_args, + pdk = pdk, + variant = "syn", + tags = ["manual"], + ) + orfs_flow( + name = name, + variant = "syn", + verilog_files = entry["verilog_files"], + sources = entry["sources"], + arguments = syn_args, + pdk = pdk, + previous_stage = {"floorplan": ":" + syn_synth}, + tags = ["manual"], + test_kwargs = {"tags": ["orfs", "manual"]}, + ) diff --git a/flow/scripts/synth_syn.tcl b/flow/scripts/synth_syn.tcl new file mode 100644 index 0000000000..a7516024c0 --- /dev/null +++ b/flow/scripts/synth_syn.tcl @@ -0,0 +1,74 @@ +utl::set_metrics_stage "synthesis__{}" +source $::env(SCRIPTS_DIR)/load.tcl +erase_non_stage_variables synth + +source_env_var_if_exists PLATFORM_TCL +source $::env(SCRIPTS_DIR)/read_liberty.tcl + +read_lef $::env(TECH_LEF) +read_lef $::env(SC_LEF) +if { [env_var_exists_and_non_empty ADDITIONAL_LEFS] } { + foreach lef $::env(ADDITIONAL_LEFS) { + read_lef $lef + } +} +set_dont_use $::env(DONT_USE_CELLS) + +# Setup verilog include directories +set vIdirsArgs "" +if { [env_var_exists_and_non_empty VERILOG_INCLUDE_DIRS] } { + foreach dir $::env(VERILOG_INCLUDE_DIRS) { + lappend vIdirsArgs "-I$dir" + } + set vIdirsArgs [join $vIdirsArgs] +} + +set elaborate_args [list \ + -D SYNTHESIS --compat=vcs --ignore-assertions --no-implicit-memories --top $::env(DESIGN_NAME) \ + {*}$vIdirsArgs {*}[env_var_or_empty VERILOG_DEFINES]] + +lappend elaborate_args {*}$::env(VERILOG_FILES) + +# Apply top-level parameters +dict for {key value} [env_var_or_empty VERILOG_TOP_PARAMS] { + lappend elaborate_args -G "$key=$value" +} + +# Apply module blackboxing based on module names as they appear +# in the input, that is before any module name mangling done +# by elaboration and synthesis +if { [env_var_exists_and_non_empty SYNTH_BLACKBOXES] } { + foreach m $::env(SYNTH_BLACKBOXES) { + lappend elaborate_args --blackboxed-module "$m" + } +} + +lappend elaborate_args {*}$::env(SYNTH_SLANG_ARGS) + +# If the sources are solely .v files, enable Verilog compatibility +set has_non_v_files false +foreach fn $::env(VERILOG_FILES) { + if { [file extension [string trim $fn]] != ".v" } { + set has_non_v_files true + } +} +if { !$has_non_v_files } { + lappend elaborate_args --std=1364-2005 +} + +sv_elaborate {*}$elaborate_args +syn::stats + +if {$::env(DESIGN_NAME) == "cva6"} { + syn::remove_ports rvfi_probes_o +} +synthesize + +read_sdc $::env(SDC_FILE) + +orfs_write_db $::env(RESULTS_DIR)/1_synth.odb +# Canonicalize 1_synth.sdc. The original SDC_FILE provided by +# the user could have dependencies, such as sourcing util.tcl, +# which are read in here and a canonicalized version is written +# out by OpenSTA that has no dependencies. +orfs_write_sdc $::env(RESULTS_DIR)/1_synth.sdc diff --git a/patches/bazel-orfs/0002-add-orfs_openroad_synth-rule-and-macro.patch b/patches/bazel-orfs/0002-add-orfs_openroad_synth-rule-and-macro.patch new file mode 100644 index 0000000000..dfbfe6530c --- /dev/null +++ b/patches/bazel-orfs/0002-add-orfs_openroad_synth-rule-and-macro.patch @@ -0,0 +1,265 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=98yvind=20Harboe?= +Date: Fri, 22 May 2026 00:00:00 +0200 +Subject: [PATCH] flow: orfs_openroad_synth_rule + macro for OpenROAD-driven + synthesis + +Adds a parallel synth rule that drives OpenROAD's integrated +synthesizer (sv_elaborate + synthesize, OpenROAD #10473) via the ORFS +Makefile target `do-syn-synth`. The new `flow/scripts/synth_syn.tcl` +script reads LEF/Liberty/SDC straight from the ORFS env vars and +writes `1_synth.odb` + `1_synth.sdc` directly -- bypassing the Yosys ++ synth_odb chain entirely. + +OrfsInfo shape matches `orfs_synth_rule`, so +`orfs_flow(previous_stage = {"floorplan": ":_syn_synth"})` +chains downstream stages unchanged. Used by ORFS's `design()` wrapper +to emit a `_syn` variant alongside every flat design. + +`orfs_synth`, `orfs_synth_rule` and every other existing symbol are +untouched. Vendored here until the integrated syn tool stabilises and +this lands upstream in bazel-orfs. +--- + openroad.bzl | 2 + + private/flow.bzl | 14 ++++ + private/rules.bzl | 178 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 194 insertions(+) + +diff --git a/openroad.bzl b/openroad.bzl +--- a/openroad.bzl ++++ b/openroad.bzl +@@ -19,6 +19,7 @@ + load( + "//private:flow.bzl", + _orfs_flow = "orfs_flow", ++ _orfs_openroad_synth = "orfs_openroad_synth", + _orfs_synth = "orfs_synth", + _orfs_update = "orfs_update", + ) +@@ -133,6 +134,7 @@ + orfs_blender = _orfs_blender + orfs_flow = _orfs_flow + orfs_synth = _orfs_synth ++orfs_openroad_synth = _orfs_openroad_synth + orfs_update = _orfs_update + + # Design config macros +diff --git a/private/flow.bzl b/private/flow.bzl +--- a/private/flow.bzl ++++ b/private/flow.bzl +@@ -18,6 +18,7 @@ + "orfs_run", + "orfs_squashed", + "orfs_synth_rule", ++ "orfs_openroad_synth_rule", + ) + load( + "//private:stages.bzl", +@@ -165,6 +166,19 @@ + orfs_synth_rule(**_filter_stage_args("synth", **kwargs)) + _create_deps_tar(kwargs.get("name"), **kwargs) + ++def orfs_openroad_synth(**kwargs): ++ """Parallel to `orfs_synth`, but drives OpenROAD's integrated ++ synthesizer (`sv_elaborate` + `synthesize`, OpenROAD #10473) via ++ the ORFS Makefile target `do-syn-synth`. The script ++ `flow/scripts/synth_syn.tcl` writes `1_synth.odb` and ++ `1_synth.sdc` directly; no Yosys, no `synth_odb.tcl`. ++ ++ The rule's OrfsInfo shape is identical to `orfs_synth_rule`'s, so ++ `orfs_flow(previous_stage = {"floorplan": ":_syn_synth"})` ++ chains downstream stages unchanged. ++ """ ++ orfs_openroad_synth_rule(**_filter_stage_args("synth", **kwargs)) ++ + def _step_name(name, variant, stage): + if variant: + name += "_" + variant +diff --git a/private/rules.bzl b/private/rules.bzl +--- a/private/rules.bzl ++++ b/private/rules.bzl +@@ -1480,6 +1480,184 @@ + executable = True, + ) + ++# --- OpenROAD integrated-synth implementation --- ++# ++# Mirrors _yosys_impl but drives OpenROAD's built-in synthesizer ++# (sv_elaborate + synthesize, OpenROAD #10473) via the ORFS Makefile ++# target `do-syn-synth`. The script `flow/scripts/synth_syn.tcl` ++# writes `1_synth.odb` and `1_synth.sdc` directly, so this rule does ++# not run Yosys, does not declare `1_2_yosys.v`, and does not invoke ++# `synth_odb.tcl` -- it is a single make-shell action. ++# ++# Provider shape matches `orfs_synth_rule` so that ++# `orfs_flow(previous_stage = {"floorplan": ":_syn_synth"})` ++# chains downstream stages unchanged. ++ ++def _openroad_synth_impl(ctx): ++ all_arguments = merge_arguments( ++ data_arguments(ctx) | required_arguments(ctx), ++ orfs_additional_arguments( ++ [dep[OrfsInfo] for dep in ctx.attr.deps], ++ use_pre_layout = True, ++ ), ++ ) ++ config = declare_artifact(ctx, "results", "1_synth.mk") ++ ctx.actions.write( ++ output = config, ++ content = config_content( ++ ctx, ++ all_arguments, ++ [file.path for file in ctx.files.extra_configs], ++ ), ++ ) ++ ++ synth_logs = declare_artifacts(ctx, "logs", ["1_synth.log"]) ++ synth_odb = declare_artifact(ctx, "results", "1_synth.odb") ++ synth_sdc = declare_artifact(ctx, "results", "1_synth.sdc") ++ synth_outputs = [synth_odb, synth_sdc] ++ ++ commands = [_make_cmd(ctx)] + generation_commands(synth_logs + synth_outputs) ++ ++ ctx.actions.run_shell( ++ arguments = [ ++ "--file", ++ ctx.file._makefile.path, ++ "do-syn-synth", ++ ], ++ command = EXPAND_VERILOG_DIRS + " && ".join(commands), ++ env = config_overrides( ++ ctx, ++ verilog_arguments(ctx.files.verilog_files) | ++ flow_environment(ctx) | ++ config_environment(config), ++ ), ++ inputs = depset( ++ [config] + ctx.files.verilog_files + ctx.files.extra_configs, ++ transitive = [ ++ data_inputs(ctx), ++ pdk_inputs(ctx), ++ deps_inputs(ctx), ++ ], ++ ), ++ outputs = synth_outputs + synth_logs, ++ tools = flow_inputs(ctx), ++ ) ++ ++ # data_arguments → JSON for downstream OrfsInfo.arguments propagation ++ # (same role the Yosys path uses 1_synth.args.json for). ++ synth_args_json = declare_artifact(ctx, "results", "1_synth.args.json") ++ ctx.actions.write( ++ output = synth_args_json, ++ content = json.encode( ++ data_arguments(ctx) | verilog_arguments(ctx.files.verilog_files), ++ ), ++ ) ++ ++ config_short = declare_artifact(ctx, "results", "1_synth.short.mk") ++ ctx.actions.write( ++ output = config_short, ++ content = config_content( ++ ctx, ++ arguments = hack_away_prefix( ++ arguments = merge_arguments( ++ data_arguments(ctx) | required_arguments(ctx), ++ orfs_additional_arguments([dep[OrfsInfo] for dep in ctx.attr.deps]), ++ ) | verilog_arguments(ctx.files.verilog_files), ++ prefix = config_short.root.path, ++ ), ++ paths = [file.short_path for file in ctx.files.extra_configs], ++ ), ++ ) ++ ++ outputs = synth_outputs ++ ++ runfiles = ctx.runfiles( ++ [config_short] + outputs + synth_logs + ctx.files.extra_configs, ++ transitive_files = depset( ++ transitive = [ ++ flow_inputs(ctx), ++ deps_inputs(ctx), ++ pdk_inputs(ctx), ++ ], ++ ), ++ ) ++ ++ return [ ++ DefaultInfo( ++ files = depset(outputs), ++ runfiles = runfiles, ++ ), ++ OutputGroupInfo( ++ logs = depset(synth_logs), ++ reports = depset([]), ++ **{f.basename: depset([f]) for f in [config] + outputs} ++ ), ++ OrfsDepInfo( ++ make = config_short, ++ config = config_short, ++ renames = [], ++ files = depset( ++ [config_short] + ctx.files.verilog_files + ctx.files.extra_configs, ++ ), ++ runfiles = runfiles, ++ ), ++ OrfsInfo( ++ stage = "1_synth", ++ config = config, ++ variant = ctx.attr.variant, ++ odb = synth_odb, ++ gds = None, ++ lef = None, ++ lib = None, ++ lib_pre_layout = None, ++ additional_gds = depset( ++ [dep[OrfsInfo].gds for dep in ctx.attr.deps if dep[OrfsInfo].gds], ++ ), ++ additional_lefs = depset( ++ [dep[OrfsInfo].lef for dep in ctx.attr.deps if dep[OrfsInfo].lef], ++ ), ++ additional_libs = depset( ++ [dep[OrfsInfo].lib for dep in ctx.attr.deps if dep[OrfsInfo].lib], ++ ), ++ additional_libs_pre_layout = depset( ++ [ ++ (dep[OrfsInfo].lib_pre_layout or dep[OrfsInfo].lib) ++ for dep in ctx.attr.deps ++ if (dep[OrfsInfo].lib_pre_layout or dep[OrfsInfo].lib) ++ ], ++ ), ++ arguments = depset([synth_args_json]), ++ ), ++ ctx.attr.pdk[PdkInfo], ++ TopInfo( ++ module_top = ctx.attr.module_top, ++ ), ++ LoggingInfo( ++ logs = depset(synth_logs), ++ reports = depset([]), ++ drcs = depset([]), ++ jsons = depset([]), ++ ), ++ ] ++ ++orfs_openroad_synth_rule = rule( ++ implementation = _openroad_synth_impl, ++ attrs = flow_attrs() | synth_attrs() | { ++ "_stage": attr.string( ++ default = "synth", ++ ), ++ }, ++ provides = [ ++ DefaultInfo, ++ OutputGroupInfo, ++ OrfsDepInfo, ++ OrfsInfo, ++ PdkInfo, ++ TopInfo, ++ LoggingInfo, ++ ], ++) ++ + # --- Make-based stage implementation --- + + _PRE_LAYOUT_STAGES = ("2_floorplan", "3_place") From c4a8cea1378b10ebabee55ad089b530588951185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Harboe?= Date: Fri, 22 May 2026 14:24:04 +0200 Subject: [PATCH 2/3] flow: apply tclfmt to synth_syn.tcl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reformat with tclfmt 0.7.0 — normalize line endings, replace tab indentation with two spaces, and add spaces inside the `if { ... }` brace expression. Pure formatting; no behavior change. Fixes the Tclint CI job on PR #4253. Signed-off-by: Øyvind Harboe --- flow/scripts/synth_syn.tcl | 148 ++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/flow/scripts/synth_syn.tcl b/flow/scripts/synth_syn.tcl index a7516024c0..e6fc590c15 100644 --- a/flow/scripts/synth_syn.tcl +++ b/flow/scripts/synth_syn.tcl @@ -1,74 +1,74 @@ -utl::set_metrics_stage "synthesis__{}" -source $::env(SCRIPTS_DIR)/load.tcl -erase_non_stage_variables synth - -source_env_var_if_exists PLATFORM_TCL -source $::env(SCRIPTS_DIR)/read_liberty.tcl - -read_lef $::env(TECH_LEF) -read_lef $::env(SC_LEF) -if { [env_var_exists_and_non_empty ADDITIONAL_LEFS] } { - foreach lef $::env(ADDITIONAL_LEFS) { - read_lef $lef - } -} -set_dont_use $::env(DONT_USE_CELLS) - -# Setup verilog include directories -set vIdirsArgs "" -if { [env_var_exists_and_non_empty VERILOG_INCLUDE_DIRS] } { - foreach dir $::env(VERILOG_INCLUDE_DIRS) { - lappend vIdirsArgs "-I$dir" - } - set vIdirsArgs [join $vIdirsArgs] -} - -set elaborate_args [list \ - -D SYNTHESIS --compat=vcs --ignore-assertions --no-implicit-memories --top $::env(DESIGN_NAME) \ - {*}$vIdirsArgs {*}[env_var_or_empty VERILOG_DEFINES]] - -lappend elaborate_args {*}$::env(VERILOG_FILES) - -# Apply top-level parameters -dict for {key value} [env_var_or_empty VERILOG_TOP_PARAMS] { - lappend elaborate_args -G "$key=$value" -} - -# Apply module blackboxing based on module names as they appear -# in the input, that is before any module name mangling done -# by elaboration and synthesis -if { [env_var_exists_and_non_empty SYNTH_BLACKBOXES] } { - foreach m $::env(SYNTH_BLACKBOXES) { - lappend elaborate_args --blackboxed-module "$m" - } -} - -lappend elaborate_args {*}$::env(SYNTH_SLANG_ARGS) - -# If the sources are solely .v files, enable Verilog compatibility -set has_non_v_files false -foreach fn $::env(VERILOG_FILES) { - if { [file extension [string trim $fn]] != ".v" } { - set has_non_v_files true - } -} -if { !$has_non_v_files } { - lappend elaborate_args --std=1364-2005 -} - -sv_elaborate {*}$elaborate_args -syn::stats - -if {$::env(DESIGN_NAME) == "cva6"} { - syn::remove_ports rvfi_probes_o -} -synthesize - -read_sdc $::env(SDC_FILE) - -orfs_write_db $::env(RESULTS_DIR)/1_synth.odb -# Canonicalize 1_synth.sdc. The original SDC_FILE provided by -# the user could have dependencies, such as sourcing util.tcl, -# which are read in here and a canonicalized version is written -# out by OpenSTA that has no dependencies. -orfs_write_sdc $::env(RESULTS_DIR)/1_synth.sdc +utl::set_metrics_stage "synthesis__{}" +source $::env(SCRIPTS_DIR)/load.tcl +erase_non_stage_variables synth + +source_env_var_if_exists PLATFORM_TCL +source $::env(SCRIPTS_DIR)/read_liberty.tcl + +read_lef $::env(TECH_LEF) +read_lef $::env(SC_LEF) +if { [env_var_exists_and_non_empty ADDITIONAL_LEFS] } { + foreach lef $::env(ADDITIONAL_LEFS) { + read_lef $lef + } +} +set_dont_use $::env(DONT_USE_CELLS) + +# Setup verilog include directories +set vIdirsArgs "" +if { [env_var_exists_and_non_empty VERILOG_INCLUDE_DIRS] } { + foreach dir $::env(VERILOG_INCLUDE_DIRS) { + lappend vIdirsArgs "-I$dir" + } + set vIdirsArgs [join $vIdirsArgs] +} + +set elaborate_args [list \ + -D SYNTHESIS --compat=vcs --ignore-assertions --no-implicit-memories --top $::env(DESIGN_NAME) \ + {*}$vIdirsArgs {*}[env_var_or_empty VERILOG_DEFINES]] + +lappend elaborate_args {*}$::env(VERILOG_FILES) + +# Apply top-level parameters +dict for {key value} [env_var_or_empty VERILOG_TOP_PARAMS] { + lappend elaborate_args -G "$key=$value" +} + +# Apply module blackboxing based on module names as they appear +# in the input, that is before any module name mangling done +# by elaboration and synthesis +if { [env_var_exists_and_non_empty SYNTH_BLACKBOXES] } { + foreach m $::env(SYNTH_BLACKBOXES) { + lappend elaborate_args --blackboxed-module "$m" + } +} + +lappend elaborate_args {*}$::env(SYNTH_SLANG_ARGS) + +# If the sources are solely .v files, enable Verilog compatibility +set has_non_v_files false +foreach fn $::env(VERILOG_FILES) { + if { [file extension [string trim $fn]] != ".v" } { + set has_non_v_files true + } +} +if { !$has_non_v_files } { + lappend elaborate_args --std=1364-2005 +} + +sv_elaborate {*}$elaborate_args +syn::stats + +if { $::env(DESIGN_NAME) == "cva6" } { + syn::remove_ports rvfi_probes_o +} +synthesize + +read_sdc $::env(SDC_FILE) + +orfs_write_db $::env(RESULTS_DIR)/1_synth.odb +# Canonicalize 1_synth.sdc. The original SDC_FILE provided by +# the user could have dependencies, such as sourcing util.tcl, +# which are read in here and a canonicalized version is written +# out by OpenSTA that has no dependencies. +orfs_write_sdc $::env(RESULTS_DIR)/1_synth.sdc From 658c2b2173d73407e3aa76284a515061ee8b72d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Harboe?= Date: Fri, 22 May 2026 14:25:31 +0200 Subject: [PATCH 3/3] flow: keep vIdirsArgs as a list in synth_syn.tcl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build vIdirsArgs with `[list]` + `lappend` and let `{*}` expand it directly into elaborate_args. The previous code joined the list into a space-separated string and re-expanded it, which would corrupt any include directory path containing whitespace. Signed-off-by: Øyvind Harboe --- flow/scripts/synth_syn.tcl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/flow/scripts/synth_syn.tcl b/flow/scripts/synth_syn.tcl index e6fc590c15..0e582585a0 100644 --- a/flow/scripts/synth_syn.tcl +++ b/flow/scripts/synth_syn.tcl @@ -15,12 +15,11 @@ if { [env_var_exists_and_non_empty ADDITIONAL_LEFS] } { set_dont_use $::env(DONT_USE_CELLS) # Setup verilog include directories -set vIdirsArgs "" +set vIdirsArgs [list] if { [env_var_exists_and_non_empty VERILOG_INCLUDE_DIRS] } { foreach dir $::env(VERILOG_INCLUDE_DIRS) { lappend vIdirsArgs "-I$dir" } - set vIdirsArgs [join $vIdirsArgs] } set elaborate_args [list \