Skip to content

Commit 8d95c46

Browse files
authored
Merge pull request #4649 from oharboe/synth-verilog-surgery
SYNTH_VERILOG_SURGERY: put RTL surgery on the books, so vendored RTL stays as upstream has it
2 parents 0b6c450 + 250b2f5 commit 8d95c46

6 files changed

Lines changed: 87 additions & 5 deletions

File tree

‎docs/user/FlowVariables.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ configuration file.
316316
| <a name="SYNTH_SKIP_KEEP"></a>SYNTH_SKIP_KEEP| Only meaningful together with SYNTH_CHECKPOINT. When set, signals that the supplied checkpoint is still canonical RTLIL (coarse synth and `keep_hierarchy` have not been run yet), so synth.tcl runs the full coarse+fine synthesis flattened. When unset and SYNTH_CHECKPOINT is used, synth.tcl assumes the checkpoint already has coarse synth + `keep_hierarchy` done and resumes from `coarse:fine`.| 0|
317317
| <a name="SYNTH_SLANG_ARGS"></a>SYNTH_SLANG_ARGS| Additional arguments passed to the slang frontend during synthesis.| |
318318
| <a name="SYNTH_USE_SYN"></a>SYNTH_USE_SYN| If set to 1, run synthesis using the "syn" tool built into OpenROAD (the synth_syn.tcl flow) instead of the default Yosys-based flow. Defaults to 0 (Yosys flow).| 0|
319+
| <a name="SYNTH_VERILOG_SURGERY"></a>SYNTH_VERILOG_SURGERY| RTL surgery as a step of the flow: a script that copies VERILOG_FILES and edits the copies before synthesis reads them, so vendored RTL stays byte-identical to upstream and the OpenROAD- and PDK-specific changes live in one script next to the design's config.mk. Typical surgery gives a memory or register file a module of its own that AUTO_MEMORIES can convert, maps a clock gate onto the platform's, rewrites a pragma for the frontend, or removes a simulation construct. A new upstream version is then a plain copy: the surgery reapplies, or stops where upstream moved. Best on generated or regular RTL (firtool, sv2v, netlists); a text edit cannot see the meaning of include files, macros, packages or `bind`. As the RTL is read (canonicalization, memory extraction, and the SYNTH_USE_SYN frontend), it runs with SYNTH_VERILOG_SURGERY_ARGS, then `--out-dir $(OBJECTS_DIR)/surgery --` and the VERILOG_FILES (by PYTHON_EXE when it ends in .py), and writes one copy per source under the same file name; synthesis reads the copies. Input file names must be unique. Diff a copy against its original to see exactly what the surgery changed. The script should fail when it does not recognise its input; a failing script, or a copy it did not write, stops synthesis.| |
320+
| <a name="SYNTH_VERILOG_SURGERY_ARGS"></a>SYNTH_VERILOG_SURGERY_ARGS| Arguments to SYNTH_VERILOG_SURGERY, before the ones the flow adds.| |
319321
| <a name="SYNTH_WRAPPED_ADDERS"></a>SYNTH_WRAPPED_ADDERS| Specify the adder modules that can be used for synthesis, separated by commas. The default adder module is determined by the first element of this variable.| |
320322
| <a name="SYNTH_WRAPPED_MULTIPLIERS"></a>SYNTH_WRAPPED_MULTIPLIERS| Specify the multiplier modules that can be used for synthesis, separated by commas. The default multiplier module is determined by the first element of this variable.| |
321323
| <a name="SYNTH_WRAPPED_OPERATORS"></a>SYNTH_WRAPPED_OPERATORS| Synthesize multiple architectural options for each arithmetic operator in the design. These options are available for switching among in later stages of the flow.| 0|
@@ -384,6 +386,8 @@ configuration file.
384386
- [SYNTH_SKIP_KEEP](#SYNTH_SKIP_KEEP)
385387
- [SYNTH_SLANG_ARGS](#SYNTH_SLANG_ARGS)
386388
- [SYNTH_USE_SYN](#SYNTH_USE_SYN)
389+
- [SYNTH_VERILOG_SURGERY](#SYNTH_VERILOG_SURGERY)
390+
- [SYNTH_VERILOG_SURGERY_ARGS](#SYNTH_VERILOG_SURGERY_ARGS)
387391
- [SYNTH_WRAPPED_ADDERS](#SYNTH_WRAPPED_ADDERS)
388392
- [SYNTH_WRAPPED_MULTIPLIERS](#SYNTH_WRAPPED_MULTIPLIERS)
389393
- [TIEHI_CELL_AND_PORT](#TIEHI_CELL_AND_PORT)

‎flow/scripts/synth_preamble.tcl‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ proc read_design_sources { } {
103103
{*}$vIdirsArgs {*}[env_var_or_empty VERILOG_DEFINES]]
104104

105105
# slang requires all files at once
106-
lappend slang_args {*}$::env(VERILOG_FILES)
106+
lappend slang_args {*}[synth_verilog_files]
107107

108108
# Add clock gate cell definition, if available
109109
if { [env_var_exists_and_non_empty CLKGATE_MAP_FILE] } {
@@ -155,7 +155,7 @@ proc read_design_sources { } {
155155
if { [env_var_exists_and_non_empty VERILOG_DEFINES] } {
156156
verific -vlog-define {*}$::env(VERILOG_DEFINES)
157157
}
158-
verific -sv2012 {*}$::env(VERILOG_FILES)
158+
verific -sv2012 {*}[synth_verilog_files]
159159
verific -import -no-split-complex-ports $::env(DESIGN_NAME)
160160

161161
dict for {key value} [env_var_or_empty VERILOG_TOP_PARAMS] {
@@ -174,7 +174,7 @@ proc read_design_sources { } {
174174
if { [env_var_exists_and_non_empty VERILOG_DEFINES] } {
175175
verilog_defaults -add {*}$::env(VERILOG_DEFINES)
176176
}
177-
foreach file $::env(VERILOG_FILES) {
177+
foreach file [synth_verilog_files] {
178178
read_verilog -defer -sv {*}$vIdirsArgs $file
179179
}
180180
# Read platform specific mapfile for OPENROAD_CLKGATE cells

‎flow/scripts/synth_syn.tcl‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ set elaborate_args [list \
3030
-D SYNTHESIS --compat=vcs --ignore-assertions --no-implicit-memories --top $::env(DESIGN_NAME) \
3131
{*}$vIdirsArgs {*}[env_var_or_empty VERILOG_DEFINES]]
3232

33-
lappend elaborate_args {*}$::env(VERILOG_FILES)
33+
set verilog_files [synth_verilog_files]
34+
lappend elaborate_args {*}$verilog_files
3435

3536
# Apply top-level parameters
3637
dict for {key value} [env_var_or_empty VERILOG_TOP_PARAMS] {
@@ -50,7 +51,7 @@ lappend elaborate_args {*}$::env(SYNTH_SLANG_ARGS)
5051

5152
# If the sources are solely .v files, enable Verilog compatibility
5253
set has_non_v_files false
53-
foreach fn $::env(VERILOG_FILES) {
54+
foreach fn $verilog_files {
5455
if { [file extension [string trim $fn]] != ".v" } {
5556
set has_non_v_files true
5657
}

‎flow/scripts/util.tcl‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,39 @@ proc source_step_tcl { hook_type step_name } {
468468
set env_var "${hook_type}_${step_name}_TCL"
469469
source_env_var_if_exists $env_var
470470
}
471+
472+
# The Verilog files synthesis reads: VERILOG_FILES or, with
473+
# SYNTH_VERILOG_SURGERY, the copies its script writes. The script runs
474+
# here, as the RTL is read, into $(OBJECTS_DIR)/surgery: one copy per
475+
# source under the same file name, kept so a diff against the original
476+
# shows what the surgery changed. They are no make target and no build
477+
# output.
478+
proc synth_verilog_files { } {
479+
if { ![env_var_exists_and_non_empty SYNTH_VERILOG_SURGERY] } {
480+
return $::env(VERILOG_FILES)
481+
}
482+
set names [lmap f $::env(VERILOG_FILES) { file tail $f }]
483+
if { [llength [lsort -unique $names]] != [llength $names] } {
484+
error "SYNTH_VERILOG_SURGERY: VERILOG_FILES has two files of one name;\
485+
the copies are named after the originals"
486+
}
487+
set dir [file join $::env(OBJECTS_DIR) surgery]
488+
file delete -force $dir
489+
file mkdir $dir
490+
set cmd {}
491+
if { [file extension [lindex $::env(SYNTH_VERILOG_SURGERY) 0]] eq ".py" } {
492+
lappend cmd $::env(PYTHON_EXE)
493+
}
494+
lappend cmd {*}$::env(SYNTH_VERILOG_SURGERY) \
495+
{*}[env_var_or_empty SYNTH_VERILOG_SURGERY_ARGS] \
496+
--out-dir $dir -- {*}$::env(VERILOG_FILES)
497+
puts "SYNTH_VERILOG_SURGERY: [join $cmd]"
498+
exec {*}$cmd >@ stdout 2>@ stderr
499+
set copies [lmap name $names { file join $dir $name }]
500+
foreach copy $copies {
501+
if { ![file exists $copy] } {
502+
error "SYNTH_VERILOG_SURGERY wrote no $copy"
503+
}
504+
}
505+
return $copies
506+
}

‎flow/scripts/variables.json‎

Lines changed: 12 additions & 0 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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,35 @@ ADDITIONAL_MEMORIES:
227227
docs/user/AutoMemories.md.
228228
stages:
229229
- synth
230+
SYNTH_VERILOG_SURGERY:
231+
description: >
232+
RTL surgery as a step of the flow: a script that copies VERILOG_FILES
233+
and edits the copies before synthesis reads them, so vendored RTL
234+
stays byte-identical to upstream and the OpenROAD- and PDK-specific
235+
changes live in one script next to the design's config.mk. Typical
236+
surgery gives a memory or register file a module of its own that
237+
AUTO_MEMORIES can convert, maps a clock gate onto the platform's,
238+
rewrites a pragma for the frontend, or removes a simulation
239+
construct. A new upstream version is then a plain copy: the surgery
240+
reapplies, or stops where upstream moved. Best on generated or
241+
regular RTL (firtool, sv2v, netlists); a text edit cannot see the
242+
meaning of include files, macros, packages or `bind`. As the RTL is
243+
read (canonicalization, memory extraction, and the SYNTH_USE_SYN
244+
frontend), it runs with SYNTH_VERILOG_SURGERY_ARGS, then
245+
`--out-dir $(OBJECTS_DIR)/surgery --` and the VERILOG_FILES (by
246+
PYTHON_EXE when it ends in .py), and writes one copy per source under
247+
the same file name; synthesis reads the copies. Input file names must
248+
be unique. Diff a copy against its original to see exactly what the
249+
surgery changed. The script should fail when it does not recognise
250+
its input; a failing script, or a copy it did not write, stops
251+
synthesis.
252+
stages:
253+
- synth
254+
SYNTH_VERILOG_SURGERY_ARGS:
255+
description: >
256+
Arguments to SYNTH_VERILOG_SURGERY, before the ones the flow adds.
257+
stages:
258+
- synth
230259
SYNTH_MEMORY_MAX_BITS:
231260
description: >
232261
Maximum number of bits for memory synthesis.

0 commit comments

Comments
 (0)