Skip to content

AUTO_MEMORIES_REGFILES: register files of placed standard cells - #4651

Closed
oharboe wants to merge 10 commits into
The-OpenROAD-Project:masterfrom
oharboe:auto-memories-regfile-generator
Closed

oharboe wants to merge 10 commits into
The-OpenROAD-Project:masterfrom
oharboe:auto-memories-regfile-generator

Conversation

@oharboe

@oharboe oharboe commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

AUTO_MEMORIES_REGFILES: register files built by OpenROAD's generate_regfile (The-OpenROAD-Project/OpenROAD#11669) in place of the flip-flops synthesis would make.

Draft until that PR merges and ORFS's OpenROAD submodule is bumped.

Depends on #4650, and is built on its branch: a register file's line in blackboxes.txt is <module> <area>, the format #4650 introduces, so SYNTH_MINIMUM_KEEP_SIZE can cost a register-file macro like a FakeRAM one. Until #4650 merges, this PR's commit list starts with #4650's commits; review only the three below.

On asap7/riscv32i at global route (asap7/riscv32i-regfile in this PR), against the same design with flip-flops: minimum period 923 → 922 ps, standard-cell area −24 %, power −24 %. Results on more designs are in the integration test, The-OpenROAD-Project/bazel-orfs#1187, which runs these changes with the OpenROAD PR on ORFS's own designs.

One commit per concern:

  1. AUTO_MEMORIES_REGFILES: spec files, one per register file. gen_memories.py checks each against its RTL module and generates it. The register file is a macro (abstract and timing model, and its .lib area in blackboxes.txt) or, in mode netlist, dissolves into its cells after macro placement. designs/asap7/regfile is the smallest example; designs/asap7/riscv32i-regfile is riscv32i with its register file generated.
  2. AUTO_MEMORIES_MACRO_PLACE: register files not listed are inlined. Synthesis reads the generated netlist in the module's place, and its cells are placed, sized and buffered with the rest of the design, which is the better shape for a small design.
  3. Inlining in place of a parameterized module (ibex's ibex_register_file_ff): the instances' parameters are dropped before the netlist is read.
    Tests: gen_memories_test.py checks the contract files each commit adds (regfiles.txt, inline.txt), that a register file's area is the cell area its .lib states, and that a .lib stating none stops the build.

🤖 Generated with Claude Code

With SYNTH_MINIMUM_KEEP_SIZE, synthesis decides which modules to keep
with keep_hierarchy -min_cost, which needs a cost for every instanced
blackbox. convert_liberty_areas gives Liberty cells one, as a gate
equivalent of their area, but an AUTO_MEMORIES macro is a blackbox made
from the RTL's own module and has none: any AUTO_MEMORIES design with a
keep threshold stopped with "Missing cost information on instanced
blackbox". asap7/riscv32i is such a design once its register file is a
macro (AUTO_MEMORIES_REGFILES).

Each AUTO_MEMORIES blackbox now gets the gate equivalent of the area its
generated .lib states. The view is not read in the module's place: its
pins need not be the module's (FakeRAM names its own).

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

@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 support for generating and placing register files as standard cells or macros using OpenROAD's generate_regfile tool, adding new configuration variables, documentation, and a Tcl script to dissolve macro-placed register files back into standard cells. The review feedback focuses on improving code robustness and performance, including preventing potential IndexError and "None" string conversions in Python scripts, extracting side-effect functions from f-strings for better readability, and caching processed nets in Tcl to avoid redundant computations.

Comment thread flow/scripts/memories/gen_memories.py
Comment thread flow/scripts/memories/gen_memories.py
Comment thread flow/scripts/memories/schema.py
Comment thread flow/scripts/regfile_dissolve.tcl
@oharboe
oharboe force-pushed the auto-memories-regfile-generator branch from c5dd9be to b8ff330 Compare October 8, 2026 05:49
oharboe and others added 2 commits October 8, 2026 07:52
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
Only `$` was escaped; yosys and slang module names also carry `\`,
`[` and `.`, each a regular-expression metacharacter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
@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 e289421 · Jenkins build #4 · Baseline: build · View build on dashboard

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

oharboe and others added 7 commits October 9, 2026 07:56
…cost

Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
The cost loop caught every set_attr failure as "not in the design", so a
listed module whose name did not match its definition went unnoticed.
slang's --keep-hierarchy names each elaborated module
`<definition>$<instance path>` and --blackboxed-module matches only the
definition, so such a name is in the design with its body elaborated:
set_attr succeeded on it and the memory became flip-flops.

A listed module absent from the design is skipped (slang imports a
blackboxed module only where it is instanced); one present but not a
blackbox stops synthesis by name; set_attr is no longer caught.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
The cost loop read a macro's area out of its .lib with a regular
expression that matched only because FakeRAM writes `area :` first in
the cell group. FakeRAM knows the area when it writes the view, so it now
writes it beside the module name, `<module> <area>` in um^2 per line, and
synthesis reads it from there. A line of any other shape is an error.

gen_memories_test.py checks the area against the one the .lib states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
The cost loop looked each blackboxes.txt name up in the module list as
a Tcl list. yosys lists an escaped module with its leading backslash,
`\1d_ram`, which list parsing reads as an escape, and blackboxes.txt
names it as read_slang takes it, `1d_ram`; the lookup missed, the
module was taken for absent and skipped, and keep_hierarchy -min_cost
stopped with "Missing cost information on instanced blackbox \1d_ram".
The list is now read one name per line, without the backslash.

Checked with read_slang on a design with `ram` and `\1d_ram`
blackboxed: before, the error above; now both get their cost and
keep_hierarchy -min_cost runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
A register file, many ports over few words, is not an SRAM: FakeRAM's
single-port model does not describe it, and synthesised to flops its
mux trees are placed by a placer that does not know they are an array.

AUTO_MEMORIES_REGFILES lists spec files, one per RTL module.
gen_memories.py runs OpenROAD's generate_regfile on each: it checks the
spec against the module's ports and writes the cells (<m>.v), an
abstract whose pins sit where their connections land in the array
(<m>.lef) and a timing model (<m>.lib, <m>_pre_layout.lib) beside the
FakeRAM views. The module joins blackboxes.txt with the area its .lib
states, so synthesis and macro placement see a macro that
SYNTH_MINIMUM_KEEP_SIZE can cost, and memories.json records it with kind
`regfile` (the others get `fakeram` or `flops`).

In `mode netlist` the macro dissolves into its cells at the end of
macro placement (regfile_dissolve.tcl): the core FIRM where the placer
put the macro, flipped as it was, row parity kept; the address decode
left to global placement and the resizer; the core's own cells and
nets dont_touch except the flops (CTS rewires them) and the cells on
the core's boundary (repair_design buffers their nets); then
eliminate_dead_logic, as at synthesis. `mode macro` keeps the macro.

designs/asap7/regfile is the smallest example: one 16x8 2R2W file in a
parent of flops, seconds to global route.

Moving parts into OpenROAD, the dissolve in particular, is left for
later: faster, unit-testable, maintained with the database it edits.

Needs OpenROAD's generate_regfile:
The-OpenROAD-Project/OpenROAD#11669

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
AUTO_MEMORIES_REGFILES built every register file as a macro: blackboxed
in synthesis, placed by the macro placer and, in `mode netlist`,
dissolved into its cells after macro placement. On a small design the
macro's outline and channels cost more core than the array saves:
riscv32i's register file as a macro does not fit riscv32i's own 62 %
utilisation beside its four FakeRAMs (MPL-0003).

AUTO_MEMORIES_MACRO_PLACE lists the register files to place as macros.
Any other is inlined: gen_memories.py writes only its generated netlist
(no abstract, model liberty or taps) and inline.txt, and synthesis reads
the RTL module as a blackbox and the netlist in its place. Its cells are
then placed, sized and buffered with the rest of the design. A name no
spec builds stops the build.

asap7/regfile lists its RegFile, which keeps the dissolve its tests
exercise. asap7/riscv32i-regfile lists nothing, takes riscv32i's own
utilisation, and its spec writes through a clock gate per word
(`write_style clock_gate`, OpenROAD#11669): 955 ps at global route
against the flip-flops' 954, on a core 12 % smaller.

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

An inlined register file's generated netlist has no parameters; a
parameterized RTL module (ibex_register_file_ff #(RV32E, DataWidth,
DummyInstructions)) is instantiated with them, and hierarchy refused:
"does not have a parameter named ...". The netlist is the module at
the widths generate_regfile's port check confirmed, so the parameters
are dropped from its instances before it is read.

Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@oharboe
oharboe force-pushed the auto-memories-regfile-generator branch from 6affa8a to 468db41 Compare October 9, 2026 10:30
oharboe added a commit to The-OpenROAD-Project/bazel-orfs that referenced this pull request Oct 9, 2026
…ells

AUTO_MEMORIES_REGFILES lists register-file specs; OpenROAD's
generate_regfile builds each as placed standard cells during
canonicalization. In mode netlist a file is a macro through synthesis
and macro placement and then dissolves into its cells.
AUTO_MEMORIES_MACRO_PLACE names the files placed as macros; the others
are inlined in synthesis.

Carried:
- OpenROAD 0010: The-OpenROAD-Project/OpenROAD#11669, its net diff at
  its head, as one patch.
- ORFS 0089, 0091, 0099: the three commits of
  The-OpenROAD-Project/OpenROAD-flow-scripts#4651 as rebuilt on #4650
  (blackboxes.txt lines carry each register file's area), without its
  example designs (asap7/regfile, asap7/riscv32i-regfile, src/regfile).
  They apply after 0090 (#4650, #1205).

0090 loses the hunks it carried for the retired 0071 (its register
files' DEF die area); the rest of it is unchanged.

STRUCTURED_MEMORIES and STRUCTURED_PLACEMENT (ORFS patches 0071, 0078,
0082, 0084) are retired. A flow that still sets either one fails and
the error names the replacement: at load time in arguments or sources,
and at analysis in user_arguments or on a stage rule.

test/structured_netlist, test/planned_parent, the XiangShan flows,
tools/structured_gen/test and tools/macro_select move to the new
variables. test/regfile is the flow's own fixture: the dissolve, every
register clocked (macro and inlined), global route, simulation
against the RTL, and synthesis with SYNTH_MINIMUM_KEEP_SIZE=1.

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

oharboe commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Closing for now: the register-file support moved onto OpenROAD's generate_ram (The-OpenROAD-Project/OpenROAD#11723), and the ORFS side is carried in The-OpenROAD-Project/bazel-orfs#1234 until OpenROAD has it. This can be reopened or reworded then.

@oharboe oharboe closed this Oct 13, 2026
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.

2 participants