Skip to content

SYNTH_VERILOG_SURGERY: put RTL surgery on the books, so vendored RTL stays as upstream has it - #4649

Open
oharboe wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
oharboe:synth-verilog-surgery
Open

oharboe wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
oharboe:synth-verilog-surgery

Conversation

@oharboe

@oharboe oharboe commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Getting third-party RTL through OpenROAD on a given PDK takes surgery: a memory becomes a macro, a clock gate becomes the platform's, a synopsys pragma becomes an attribute the frontend reads, a simulation construct goes. ORFS already does this on three in five of its designs, 19 of the 32 vendored RTL trees in flow/designs/src (survey below), by editing the vendored copy.

The flow already does surgery itself, in yosys: AUTO_MEMORIES' pre-synthesis pass recognises a memory in the RTL, an array with read and write ports, and a memory that is a module of its own becomes a generated macro. That works where the RTL already looks like a memory. Where it does not, the step that recognises the pattern and introduces the module has been an edit to the vendored copy.

SYNTH_VERILOG_SURGERY puts that step on the books, as a concern of its own. A script next to the design's config.mk edits copies of VERILOG_FILES, and synthesis reads the copies. The vendored tree stays byte-identical to upstream, so:

  • a new upstream version is a plain copy: the surgery reapplies, or stops where upstream moved;
  • what was done is visible: the OpenROAD and PDK concerns sit in one script under the design's flow configuration, not spread through the RTL's git history. The copies are in $(OBJECTS_DIR)/surgery/ under the original file names, so a diff against the original shows exactly what changed;
  • someone who copies a design sees that the step exists and what it does.

A module whose implementation varies by PDK, like the clock gate, is the tool where upstream already has that boundary; surgery is for where it does not yet.

Make and Bazel. make needs this hook. In Bazel, design-specific surgery is a genrule. This PR starts with design-specific surgery; once a few designs use it, a second survey can show whether a surgery recurs often enough to belong in a library both builds use.

Scope. Best on generated or regular RTL (firtool, sv2v, netlists). A text edit cannot see the meaning of include files, macros, packages or bind.

How it runs. As the RTL is read (canonicalization, memory extraction, and the SYNTH_USE_SYN frontend), the script runs with SYNTH_VERILOG_SURGERY_ARGS, then --out-dir $(OBJECTS_DIR)/surgery -- and the VERILOG_FILES (through PYTHON_EXE when it ends in .py). It writes one copy per source under the same file name, and synthesis reads the copies. The copies are no make target and no build output: the Makefile and variables.mk are unchanged. A failing script, or a copy it did not write, stops synthesis.

Example: #4653, coralnpu, with the RTL snapshot unchanged. Its integer register file is register-file inference at home: firtool flattened the Vec of registers into 31 scalars, regfile_1 to regfile_31; each of the 8 read ports is a generated and-or select over them, and the 6 write ports write them in one always block. There is no array, so yosys sees no memory. The surgery recognises that generated pattern, moves the words into Regfile_rf and rewrites the bypass as a write-through read on its ports, and AUTO_MEMORIES_REGFILES replaces Regfile_rf with a generated register file. The TCM SRAMs are the same move one step earlier: yosys sees their arrays, but inside a wrapper, so surgery gives each a memory module of firtool's convention, write mask included, for AUTO_MEMORIES to convert. A third surgery leaves out firtool's verification layers. The integration test, with simulation of each module before and after surgery, is The-OpenROAD-Project/bazel-orfs#1187.

Survey: the edits to vendored RTL in flow/designs/src, and the surgery that does the same

Every edit to upstream text below can be a surgery step. Files ORFS adds next to the RTL (a new top, macros.v, stubs) and the generation that produced a netlist (sv2v, firtool, pickling) are not surgery and stay as they are. Unedited: uart, ibex_sv, picorv32 and tpu are byte-identical to upstream; microwatt, tinyRocket and the four black_parrot variants are generated and taken as given; chi_async_bridge (rewritten from XiangShan's Chisel) and mock-alu are written in ORFS; mempool_group differs from its source only by deleted comment lines.

Kind Design Edit Commit
Memory seam cva6 SRAM wrappers instantiate fakeram7_* 186eed1, 5917742, cb73ed6
riscv32i dmem.v as four fakeram7_256x32 with bank select 4bd6f01
coralnpu ifdef USE_ASAP7 fakeram branch in Sram_*x128, in place of the TSMC/GF22 branches 3dd5892 (at import)
ariane133, ariane136 SRAM modules matched to macro data widths 06ea877, 542d452 (at import)
ariane, swerv SRAM modules left undefined in the netlist, supplied by macros.v 1e840e3 (ariane), aebb487 (swerv, at import)
coyote tsmc65lp_* SRAM instances renamed mem_* f2e61e9, bfcf906
chameleon (* blackbox *), parameter as localparam, for hardened blocks 5392cb7 (at import)
Frontend aes, dynamic_node, jpeg synopsys full_case/parallel_case comments as attributes 3819038
ethmac synopsys parallel_case comments commented out 05fdea7 (at import)
riscv32i reg declarations for slang e02b879
cva6 drivers for optional outputs 837beb5
swerv port lists for the SYN frontend ac9ff54
Simulation ethmac $stop commented out 05fdea7 (at import)
jpeg translate_off as ifdef SIMULATION 3819038
Clock gate swerv latch as OPENROAD_CLKGATE f4c7052
coralnpu ClockGate ICG branches left out 3dd5892 (at import)
chameleon_hier commented-out prim_clock_gating latch as ifdef CG_LATCH 5e4e598
Hierarchy fifo (* keep_hierarchy *) f0a31fd
Top and shape gcd, spi top renamed 6cb6af9, 7209991 (at import)
bp_quad unused DDR ports removed c36155d
uart-no-param parameter removed, as a copy of uart 9a13548 (at import)
Fixes bp_quad, chameleon, chameleon_hier, jpeg, riscv32i RTL fixes carried in the copy b86ea5f, ce1a4f2; 843726a, a2ffb3b; d2f1f41; ee431af; 3829d1a, 035008c

🤖 Generated with Claude Code

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the SYNTH_VERILOG_SURGERY feature, which allows a custom script to copy and edit Verilog source files prior to synthesis. The build system and synthesis scripts have been updated to read from the resulting SYNTH_VERILOG_FILES instead of the original VERILOG_FILES. Feedback on the changes highlights two potential Makefile issues: a dependency resolution failure if SYNTH_VERILOG_SURGERY starts with a system command like python3, and a shell syntax error in the validation loop if SYNTH_VERILOG_FILES is empty.

Comment thread flow/Makefile Outdated
Comment thread flow/Makefile Outdated
Surgery at home: SYNTH_VERILOG_SURGERY names a script that copies
VERILOG_FILES and edits the copies before synthesis reads them, so the
source tree stays as upstream has it. Two reasons to edit at synthesis
time rather than in the source:

- Bringing in upstream stays easy. A local edit to the source has to be
  carried across every bump of the upstream RTL and every regeneration
  of a generated core. The script applies it as a patch, as a procedure
  (find this construct, rewrite it), or both; a procedure can survive
  RTL changes that make a patch fail to apply.
- Some seams are PDK-specific. Whether a register file becomes a module
  of its own that a generated register file replaces, or stays flops
  for synthesis to map, depends on which gives the best QoR on that PDK,
  not on the source code. The same RTL can want the seam on one platform
  and not another, so it belongs in the platform's flow configuration.

A hack, documented as one: nothing checks what it changed. Good for
simple RTL, especially generated RTL (firtool, sv2v), whose text is
regular enough to edit reliably. Unsuitable for idiomatic SystemVerilog
code bases with include files, macros, packages, `bind` and other
advanced language features, where an edit to the text cannot see what
the code means.

It runs where the RTL is read (synth_verilog_files in util.tcl, called by
read_design_sources and by the SYNTH_USE_SYN frontend), with
SYNTH_VERILOG_SURGERY_ARGS, then `--out-dir $(OBJECTS_DIR)/surgery --`
and the VERILOG_FILES (by PYTHON_EXE when it ends in .py). It writes one
copy per source under the same file name, and synthesis reads the
copies. They are kept for inspection but are no make target and no build
output: the Makefile and variables.mk are unchanged. A failing script,
or a copy it did not write, stops synthesis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe
oharboe force-pushed the synth-verilog-surgery branch from 5434a5e to c3a7c6c Compare October 8, 2026 07:00
@oharboe oharboe changed the title SYNTH_VERILOG_SURGERY: a script edits copies of the sources before synthesis SYNTH_VERILOG_SURGERY: edit copies of the sources before synthesis, leaving the source tree as upstream has it Oct 8, 2026
@openroad-ci

openroad-ci commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

🔍 QoR check

Metrics reflect the PR merge build — i.e. what will land on the target branch.

Commit b345751 · Jenkins build #7 · Baseline: build · View build on dashboard

62 design(s) checked — 0 with regression(s), 0 without a comparable baseline.

@maliberty

Copy link
Copy Markdown
Member

A hack as stated. PDK specific seams are generally handled by having module whose implementation varies by PDK (eg clock gates). It doesn't feel generally applicable enough to be worth the complexity.

@maliberty maliberty closed this Oct 8, 2026
The variable's documentation and the util.tcl comment now say what the
step is for: 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. A diff of a copy against its original shows what
the surgery changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe oharboe changed the title SYNTH_VERILOG_SURGERY: edit copies of the sources before synthesis, leaving the source tree as upstream has it SYNTH_VERILOG_SURGERY: put RTL surgery on the books, so vendored RTL stays as upstream has it Oct 9, 2026
@oharboe oharboe reopened this Oct 9, 2026
@oharboe

oharboe commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Reopening with a reframe, after a survey of flow/designs/src: ORFS already does RTL surgery on three in five of its designs, 19 of 32. Memory seams (cva6, riscv32i, ariane133/136, coyote, chameleon), pragmas for the frontend (aes, dynamic_node, ethmac, jpeg), clock gates (swerv), tops and parameters (bp_quad, uart-no-param). Each of those can be a surgery step, which keeps the vendored RTL identical to upstream and puts the OpenROAD and PDK concerns in one visible place per design: on the books, as a step of the flow.

The flow already does a form of this in yosys: AUTO_MEMORIES recognises a memory and makes a module of it. Surgery extends that to patterns yosys cannot see, such as coralnpu's register file, which firtool generated as 31 separate registers behind and-or selects.

The description has the survey table, with the commit for each edit; #4653 (coralnpu) is the first user.

oharboe added a commit to The-OpenROAD-Project/bazel-orfs that referenced this pull request Oct 9, 2026
Carries The-OpenROAD-Project/OpenROAD-flow-scripts#4649 as ORFS patch
0094: SYNTH_VERILOG_SURGERY names a script that copies VERILOG_FILES
and edits the copies; synthesis reads the copies, so vendored RTL stays
as upstream has it. SYNTH_VERILOG_SURGERY_ARGS passes it arguments.

A design's config.mk can name the script: config_mk_parser stages it as
a source, and the design DSL exports .py files so the label resolves.

test/verilog_surgery synthesizes a fixture whose script inverts an
assignment the vendored RTL leaves plain; the netlist has the inverter
only if synthesis read the copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
oharboe added a commit to The-OpenROAD-Project/bazel-orfs that referenced this pull request Oct 9, 2026
Carries The-OpenROAD-Project/OpenROAD-flow-scripts#4649 as ORFS patch
0094: SYNTH_VERILOG_SURGERY names a script that copies VERILOG_FILES
and edits the copies; synthesis reads the copies, so vendored RTL stays
as upstream has it. SYNTH_VERILOG_SURGERY_ARGS passes it arguments.

A design's config.mk can name the script: config_mk_parser stages it as
a source, and the design DSL exports .py files so the label resolves.

test/verilog_surgery synthesizes a fixture whose script inverts an
assignment the vendored RTL leaves plain; the netlist has the inverter
only if synthesis read the copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@oharboe

oharboe commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator Author

@maliberty Another surgery use-case... a stop-gap fix for sv-elab povik/sv-elab#395

@oharboe

oharboe commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator Author

@maliberty TL;DR: editing the vendored copy in git is not the same as surgery. With surgery, the vendored RTL stays as upstream has it, and each PDK's edit lives in that platform's script. In git, the edit becomes code in the vendored tree that only one PDK uses: coralnpu's ifdef USE_ASAP7 SRAM branch, ariane133/136 as two copies, uart-no-param as a third. A module that varies by PDK only helps where upstream already has that boundary, and the survey shows most of these edits fall where it doesn't. Surgery also stays visible after import, applies again to a new upstream version or stops with an error, survives regenerated RTL, and can be checked and deleted. Git edits are still right for a fix that belongs upstream.

pr-merge is just an outage: the OpenROAD build timed out downloading bliss from users.aalto.fi, and pr-head is green. Approve with auto-merge on and the re-run will merge it.

@oharboe

oharboe commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator Author

@maliberty 🥹 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants