You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e82ccee
Browse filesBrowse the repository at this point in the historyBrowse files
AUTO_MEMORIES_REGFILES: register files of placed standard cells, built by generate_ram
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.
memories/regfile_ram.py translates each to OpenROAD's
generate_ram_netlist options (ports, banks, folds, the zero word,
reset, read latency, write style and priority, cells), checks it
against the module's ports from synthesis (a stray, missing or
mis-sized port stops the build), and writes a wrapper in the module's
place around the RAM, <m>_ram: an active-high reset inverted for an
active-low flop, addresses narrowed or widened.
A register file is inlined unless AUTO_MEMORIES_MACRO_PLACE names it:
its cells are then placed, sized and buffered with the rest of the
design. A named one is a macro to synthesis and macro placement (an
abstract LEF and a liberty of its area beside the FakeRAM views, in
blackboxes.txt; a parameterized module's instances lose parameters the
netlist does not declare), and 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 periphery left
to global placement and the resizer; then eliminate_dead_logic.
Where a row holds several words they sit side by side, and a word whose
bits would run more than 150 um is folded into more rows: XiangShan's
RobEntryFile and RenameBufferFile, dissolved into a parent, did not
legalize otherwise (the parent buffers the word's selects inside the
full array).
`mode netlist`, the default, is the only mode. A spec with `mode macro`
stops: a timing model written from generate_ram's netlist before any
clock tree is built has the whole unbuffered clock net as its clock
pin, 8.5 pF for 64 words of 256 bits on asap7, 32 pF for XiangShan's
954-bit Ftq queue, whose clock then arrived 7.7 ns late in its parent.
A register file wanted as a macro is a design of its own: run it
through the flow, where CTS builds its clock tree, and give the parent
its abstract as a macro.
A module named in a spec whose memory has an initial value stops the
build: generate_ram's storage has none, and the value would be lost
without a word. The spec's read_latency is generate_ram's
-register_read_address.
The RAMs the wrappers instantiate as macros are listed in
regfile_cores.txt: they are in blackboxes.txt but no module of the
sources, so the check that every AUTO_MEMORIES module is a black box
after read_slang skips them.
Uses generate_ram's register-file options (OpenROAD#11723), in
tools/OpenROAD.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
Copy file name to clipboardExpand all lines: docs/user/AutoMemories.md
+130Lines changed: 130 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -122,6 +122,134 @@ the left edge, alternating horizontal M4 `VDD`/`VSS` straps across the
122
122
macro (which the platform's PDN macro grid connects to M5), and an `OBS`
123
123
covering M1 to M4.
124
124
125
+
## Register files
126
+
127
+
A register file, many read and write ports over a few words, is no
128
+
SRAM: FakeRAM's single-port model does not describe it, and synthesised
129
+
to flops its mux trees are placed by a placer that does not know they
130
+
are an array. `AUTO_MEMORIES_REGFILES` lists spec files, one per RTL
131
+
module, and OpenROAD's `generate_ram` builds each as an array of placed
132
+
standard cells: a flop per bit, its write logic beside it, the read
133
+
trees in columns. The spec names the module, its words and bits, its
134
+
read and write ports by the module's own port names, and the cells:
135
+
136
+
```
137
+
module RegFile
138
+
mode netlist
139
+
words 16
140
+
bits 8
141
+
clock clock
142
+
read io_r0_addr io_r0_data
143
+
write io_w0_addr io_w0_data io_w0_en
144
+
cell flop DFFHQNx1_ASAP7_75t_R
145
+
...
146
+
```
147
+
148
+
`memories/regfile_ram.py` translates the spec to `generate_ram_netlist`
149
+
options (ports, banks, folds, the zero word, reset, read latency, write
150
+
style and priority, cells) and checks it against the module's ports,
151
+
from synthesis: a port in one and not the other, or a data port of
152
+
another width, stops the build rather than miswire the parent. The RAM
153
+
is a block `<m>_ram`, and a wrapper takes the module's place,
154
+
connecting the module's ports to the RAM's (an active-high reset
155
+
inverted for an active-low flop, an address narrowed or widened). What
156
+
happens next depends on `AUTO_MEMORIES_MACRO_PLACE`, the register files
157
+
to place as macros:
158
+
159
+
-**Not listed (the default): inlined.** Synthesis reads the wrapper
160
+
and the RAM's netlist in the module's place; from there on their cells
161
+
are placed, sized and buffered with the rest of the design. No macro,
162
+
no abstract, no dissolve. The better shape for a small design, where a
163
+
macro's outline and channels cost more core than the array saves.
164
+
-**Listed: a macro.**`<m>_ram` is a macro to synthesis and macro
165
+
placement: its abstract `<m>_ram.lef` and a liberty of its area and
166
+
pins land beside the FakeRAM views and it joins `blackboxes.txt`. In
167
+
`mode netlist` (the only one) `generate_ram` places only the array's
168
+
core and leaves its periphery (decode, select and input buffers) to
169
+
the parent, and the macro dissolves into its cells at the end of macro
170
+
placement (`regfile_dissolve.tcl`): the core lands FIRM where the
171
+
macro was placed, flipped as the macro was, on the parent's rows; the
172
+
periphery is left to global placement and the resizer, and dead logic
173
+
is eliminated as at synthesis. A large design whose floorplan plans
174
+
its register files lists them.
175
+
-**A macro of its own.** A register file wanted as a macro to the end
176
+
is a design of its own: run its module through the flow, its spec
177
+
listed and macro-placed so it dissolves there, and give the parent
178
+
its abstract as a macro. Its clock tree is then built and its timing
179
+
model is written from it. `mode macro` stops with that advice: a
180
+
timing model written from `generate_ram`'s netlist before any clock
181
+
tree would have the whole unbuffered clock net as one pin.
182
+
183
+
A word holds its value between writes either through a hold mux per
184
+
bit (`write_style mux`, the default here) or through one integrated
185
+
clock gate per word, enabled by its write selects (`write_style
186
+
clock_gate`, `generate_ram`'s own default). Where a row holds more than
187
+
one word (`bank_columns`), each word's bits sit side by side, so its
188
+
selects and write enables are no longer than its bits; and a word whose
189
+
bits would run more than 150 um is folded into more rows. `tap_columns`
190
+
and `service_sites` leave empty columns in the array for the buffers a
191
+
parent puts on the nets that cross it.
192
+
193
+
Listed register files appear in `memories.json` with kind `regfile`;
194
+
the rest have kind `fakeram` or `flops`.
195
+
196
+
Moving the dissolve into OpenROAD is left for later: it would make it
197
+
faster, testable at unit level and maintained with the database it
198
+
edits.
199
+
200
+
### Designs to measure it on
201
+
202
+
What a register file of placed cells buys is a shorter minimum clock
203
+
period (the read mux trees are columns, not a cloud) and a shorter
204
+
build (fewer cells for synthesis, placement and the resizer to work
205
+
on). The ORFS designs whose register file is flip-flops today and its
206
+
own module, the seam a spec needs, measured as flops against
207
+
`AUTO_MEMORIES_REGFILES`:
208
+
209
+
| Design | Module | Today | Shape | Ready? |
210
+
|---|---|---|---|---|
211
+
| asap7/riscv32i |`regfile`| flip-flops: `reg [31:0] rf[31:0]`, mapped to flops (no AUTO_MEMORIES) | 32 x 32, 2R1W, x0 reads 0 | Yes: the ports are the spec's (`zero_word 0`). Also exercises `OPENROAD_HIERARCHICAL=1` beside two FakeRAM macros. |
212
+
| asap7/ibex |`ibex_register_file_ff`| flip-flops: `RegFile = RegFileFF`, the default | 32 x 32, 2R1W, x0 reads 0 | Yes: `unused` names `test_en_i` and `dummy_instr_id_i`, ports the array does not use, and `async_reset rst_ni low` resets the flops to 0 as ibex does; simulated against ibex's RTL. |
213
+
| asap7/cva6 |`ariane_regfile` (`ariane_regfile_ff.sv`) | flip-flops: cv32a65x has `FpgaEn` 0 | 32 x 32, 2R1W | Not yet: the ports are packed arrays (`raddr_i[1:0][4:0]`), one port per slice, which a spec cannot yet address; asynchronous reset as ibex. |
214
+
| asap7/swerv_wrapper |`dec_gpr_ctl_*`| flip-flops | 32 x 32, 4R3W | Not yet: read enables, bank ids and scan ports around the array need a seam in the RTL. |
215
+
216
+
coralnpu's `Regfile` (8R4W) and `FRegfile` (3R2W) carry their
217
+
scoreboard and bypass logic in the same module, and picorv32 and
218
+
tinyRocket keep their register file inside the CPU module: those need a
219
+
seam in their RTL first.
220
+
221
+
### Status: at parity on riscv32i, on a smaller core
222
+
223
+
riscv32i with its register file generated and inlined (a spec in
224
+
`AUTO_MEMORIES_REGFILES`, `zero_word 0`), at riscv32i's own
225
+
utilisation, against `designs/asap7/riscv32i`. To global route, 950 ps
226
+
clock, minimum period from placement parasitics on the global-route
227
+
database:
228
+
229
+
|| flops | register file |
230
+
|---|---|---|
231
+
| minimum period | 950 ps | 943 ps |
232
+
| core area | 4 012 um2 | 3 503 um2 |
233
+
234
+
Both are limited by the same single-cycle path: the instruction into
235
+
the register file's read, the ALU, the data memory and back into the
236
+
register file's write. The register file's share of it, decode and read
237
+
tree, is about 260 ps in either.
238
+
239
+
What did not pay, measured on the same design:
240
+
241
+
- the register file as a macro dissolved after macro placement: it does
242
+
not fit riscv32i's 62 % beside the four FakeRAMs, and at 55 % it is
243
+
959 ps on a 4 644 um2 core;
244
+
- a hold mux per bit instead of the clock gate: 994 ps inlined, every
245
+
array endpoint carrying the mux;
246
+
- predecoded read selects: 960 ps;
247
+
- a tree of 2:1 muxes steered by the address bits instead of a decoded
248
+
one-hot read: 959 ps, the address bits' fanout (about 1 000 inputs a
249
+
port) needs as many buffer levels as the decode it replaces;
250
+
- four-input gates in the read tree, stronger cells: 950 to 954 ps,
251
+
within what any change to the placement moves.
252
+
125
253
## Platform support
126
254
127
255
**asap7 only.**`gen_memories.py` rejects any other platform. The
Copy file name to clipboardExpand all lines: docs/user/FlowVariables.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -104,6 +104,8 @@ configuration file.
104
104
| <aname="ADDITIONAL_SITES"></a>ADDITIONAL_SITES| Passed as -additional_sites to initialize_floorplan.||
105
105
| <aname="ASAP7_USE_VT"></a>ASAP7_USE_VT| A space separated list of VT options to use with the ASAP7 standard cell library: RVT, LVT, SLVT.| RVT|
106
106
| <aname="AUTO_MEMORIES"></a>AUTO_MEMORIES| Experimental. When set to 1, memories that yosys infers in the RTL (`$mem_v2` cells, module boundary only) are detected pre-synthesis, inventoried in `$(RESULTS_DIR)/memories.json`, and — when idiomatic as an SRAM macro — given generated .lib/.lef views under `$(RESULTS_DIR)/memories/`. Synthesis blackboxes those modules and all later stages read the generated views, so an existing design gets macro-based results without a memory compiler. Supported for the asap7 platform only. See docs/user/AutoMemories.md.| 0|
107
+
| <aname="AUTO_MEMORIES_MACRO_PLACE"></a>AUTO_MEMORIES_MACRO_PLACE| Experimental. Space-separated list of module names of AUTO_MEMORIES_REGFILES register files to place as macros, blackboxed in synthesis and placed by the macro placer (and, in `mode netlist`, dissolved after macro placement). A register file not listed is inlined: its generated netlist of standard cells replaces the module's body in synthesis, and its cells are placed, sized and buffered with the rest of the design -- the better shape for a small design, where a macro's outline and channels cost more core than the array saves. A name no spec builds stops the build. Requires AUTO_MEMORIES=1.||
108
+
| <aname="AUTO_MEMORIES_REGFILES"></a>AUTO_MEMORIES_REGFILES| Experimental. Space-separated list of register-file spec files, one per RTL module to build as a register file of placed standard cells rather than synthesise to flops. Each spec names its module, words, bits, read and write ports by the RTL's own port names, and the cells. memories/regfile_ram.py checks the spec against the module's ports and has OpenROAD's generate_ram build it as `<module>_ram`, under `$(RESULTS_DIR)/memories/`, with a wrapper in the module's place. A module named in AUTO_MEMORIES_MACRO_PLACE is blackboxed like an AUTO_MEMORIES macro and placed as one; any other is inlined as its generated netlist. A placed spec in `mode netlist` then dissolves into its cells after macro placement: its core FIRM where the macro was placed, in the macro's orientation, its periphery left to placement. Every register file appears in memories.json with kind `regfile`. Requires AUTO_MEMORIES=1.||
107
109
| <aname="BALANCE_ROWS"></a>BALANCE_ROWS| Balance rows during placement.| 0|
108
110
| <aname="BLOCKS"></a>BLOCKS| Blocks used as hard macros in a hierarchical flow. Do note that you have to specify block-specific inputs file in the directory mentioned by Makefile.||
109
111
| <aname="BUFFER_PORTS_ARGS"></a>BUFFER_PORTS_ARGS| Specify arguments to the buffer_ports call during placement. Only used if DONT_BUFFER_PORTS=0.||
0 commit comments