Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 121 additions & 1 deletion docs/user/AutoMemories.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ into `$(RESULTS_DIR)/memories_inferred.json`, and
| `$(RESULTS_DIR)/memories/<m>.lib` | Generated Liberty view per converted memory. |
| `$(RESULTS_DIR)/memories/<m>_pre_layout.lib` | Ideal-clock (zero clock-tree insertion) variant for pre-CTS consumers that select lib files themselves. The Makefile flow uses `<m>.lib` throughout. |
| `$(RESULTS_DIR)/memories/<m>.lef` | Abstract LEF per converted memory. |
| `$(RESULTS_DIR)/memories/blackboxes.txt` | Names of the converted modules — what synthesis blackboxes. |
| `$(RESULTS_DIR)/memories/blackboxes.txt` | `<module> <area>` per converted module — what synthesis blackboxes, and the area in um² its `.lib` states, which `SYNTH_MINIMUM_KEEP_SIZE` costs it by. |

Synthesis (canonicalization) blackboxes the converted modules so the
liberty view wins over their behavioral bodies; floorplan through final
Expand Down Expand Up @@ -126,6 +126,124 @@ the left edge, alternating horizontal M4 `VDD`/`VSS` straps across the
macro (which the platform's PDN macro grid connects to M5), and an `OBS`
covering M1 to M4.

## Register files

A register file, many read and write ports over a few words, is no
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, and OpenROAD's `generate_regfile` builds each as an array of
placed standard cells: a flop per bit, its write mux beside it, the read
trees in columns. The spec names the module, its words and bits, its
read and write ports by the module's own port names, and the cells:

```
module RegFile
mode netlist
words 16
bits 8
clock clock
read io_r0_addr io_r0_data
write io_w0_addr io_w0_data io_w0_en
cell flop DFFHQNx1_ASAP7_75t_R
...
```

`generate_regfile` checks the spec against the module's ports and
writes `<m>.v`, the array as standard cells. What happens next depends
on `AUTO_MEMORIES_MACRO_PLACE`, the register files to place as macros:

- **Not listed (the default): inlined.** Synthesis reads the module as a
blackbox and `<m>.v` in its place; from there on its cells are placed,
sized and buffered with the rest of the design. No macro, no abstract,
no dissolve. The better shape for a small design, where a macro's
outline and channels cost more core than the array saves.
- **Listed: a macro.** `generate_regfile` also writes `<m>.lef` (an
abstract, its pins where their connections land in the array) and
`<m>.lib`/`<m>_pre_layout.lib` (a timing model) beside the FakeRAM
views, and the module joins `blackboxes.txt`, so synthesis and macro
placement see a macro. In `mode netlist` (the usual one) the macro
dissolves into its cells at the end of macro placement
(`regfile_dissolve.tcl`): the array's core lands FIRM where the macro
was placed, flipped as the macro was, on the parent's rows; the
address decode is left to global placement and the resizer, and dead
logic is eliminated as at synthesis. `mode macro` keeps the macro to
the end. A large design whose floorplan plans its register files lists
them.

A word holds its value between writes either through a hold term in
every bit's write mux (`write_style mux`, the default) or through one
integrated clock gate per word, enabled by its write selects
(`write_style clock_gate`, as OpenROAD's `generate_ram` writes): with
one write port a flop's D is then the write data itself.

Listed register files appear in `memories.json` with kind `regfile`;
the rest have kind `fakeram` or `flops`. `designs/asap7/regfile` is the
smallest example.

Moving parts of this into OpenROAD, the dissolve in particular, is left
for later: it would make them faster, testable at unit level and
maintained with the database they edit.

### Designs to measure it on

What a register file of placed cells buys is a shorter minimum clock
period (the read mux trees are columns, not a cloud) and a shorter
build (fewer cells for synthesis, placement and the resizer to work
on). The ORFS designs whose register file is flip-flops today and its
own module, the seam a spec needs, measured as flops against
`AUTO_MEMORIES_REGFILES`:

| Design | Module | Today | Shape | Ready? |
|---|---|---|---|---|
| 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. |
| asap7/ibex | `ibex_register_file_ff` | flip-flops: `RegFile = RegFileFF`, the default | 32 x 32, 2R1W, x0 reads 0 | Not yet: `test_en_i` and `dummy_instr_id_i` are ports the array does not use, which a spec cannot yet name, and `rst_ni` resets the flops to 0 where the generator's flops have no reset (the ISA leaves x1-x31 undefined after reset). |
| 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. |
| 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. |

coralnpu's `Regfile` (8R4W) and `FRegfile` (3R2W) carry their
scoreboard and bypass logic in the same module, and picorv32 and
tinyRocket keep their register file inside the CPU module: those need a
seam in their RTL first.

`designs/asap7/regfile` is the smallest case, for turnaround rather
than measurement.

### Status: at parity on riscv32i, on a smaller core

`designs/asap7/riscv32i-regfile` is riscv32i with its register file
generated, inlined and written through a clock gate per word, at
riscv32i's own utilisation. To global route, 950 ps clock, global-route
parasitics, against `designs/asap7/riscv32i`:

| | flops | register file |
|---|---|---|
| minimum period | 954 ps | 955 ps |
| core area | 4 012 um2 | 3 544 um2 |
| standard-cell area, without taps | 1 284 um2 | 973 um2 |
| clock-tree buffers and inverters | 122 | 179 |
| clock gates | 0 | 31 |
| hold, worst slack | +36 ps | +86 ps |

Both are limited by the same single-cycle path: the instruction into
the register file's read, the ALU, the data memory and back into the
register file's write. The register file's share of it, decode and read
tree, is about 260 ps in either.

What did not pay, measured on the same design:

- the register file as a macro dissolved after macro placement: it does
not fit riscv32i's 62 % beside the four FakeRAMs, and at 55 % it is
959 ps on a 4 644 um2 core;
- a hold mux per bit instead of the clock gate: 994 ps inlined, every
array endpoint carrying the mux;
- predecoded read selects: 960 ps;
- a tree of 2:1 muxes steered by the address bits instead of a decoded
one-hot read: 959 ps, the address bits' fanout (about 1 000 inputs a
port) needs as many buffer levels as the decode it replaces;
- four-input gates in the read tree, stronger cells: 950 to 954 ps,
within what any change to the placement moves.

## Platform support

**asap7 only.** `gen_memories.py` rejects any other platform. The
Expand Down Expand Up @@ -187,3 +305,5 @@ bazel-orfs implements this.

- [AUTO_MEMORIES](FlowVariables.md#AUTO_MEMORIES)
- [ADDITIONAL_MEMORIES](FlowVariables.md#ADDITIONAL_MEMORIES)
- [AUTO_MEMORIES_MACRO_PLACE](FlowVariables.md#AUTO_MEMORIES_MACRO_PLACE)
- [AUTO_MEMORIES_REGFILES](FlowVariables.md#AUTO_MEMORIES_REGFILES)
4 changes: 4 additions & 0 deletions docs/user/FlowVariables.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ configuration file.
| <a name="ADDITIONAL_SITES"></a>ADDITIONAL_SITES| Passed as -additional_sites to initialize_floorplan.| |
| <a name="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|
| <a name="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|
| <a name="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.| |
| <a name="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; OpenROAD's generate_regfile checks the spec against the module's ports and writes `<module>.lef`, `<module>.lib` and `<module>_pre_layout.lib` under `$(RESULTS_DIR)/memories/`. 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 address decode left to placement. Every register file appears in memories.json with kind `regfile`. Requires AUTO_MEMORIES=1.| |
| <a name="BALANCE_ROWS"></a>BALANCE_ROWS| Balance rows during placement.| 0|
| <a name="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.| |
| <a name="BUFFER_PORTS_ARGS"></a>BUFFER_PORTS_ARGS| Specify arguments to the buffer_ports call during placement. Only used if DONT_BUFFER_PORTS=0.| |
Expand Down Expand Up @@ -342,6 +344,8 @@ configuration file.
- [ABC_LOAD_IN_FF](#ABC_LOAD_IN_FF)
- [ADDER_MAP_FILE](#ADDER_MAP_FILE)
- [ADDITIONAL_MEMORIES](#ADDITIONAL_MEMORIES)
- [AUTO_MEMORIES_MACRO_PLACE](#AUTO_MEMORIES_MACRO_PLACE)
- [AUTO_MEMORIES_REGFILES](#AUTO_MEMORIES_REGFILES)
- [CACHED_REPORTS](#CACHED_REPORTS)
- [CLKGATE_MAP_FILE](#CLKGATE_MAP_FILE)
- [DFF_LIB_FILE](#DFF_LIB_FILE)
Expand Down
9 changes: 7 additions & 2 deletions flow/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,15 @@ do-auto-memories: $(RESULTS_DIR)/memories_inferred.json
--yosys-json $(RESULTS_DIR)/memories_inferred.json \
--out-dir $(RESULTS_DIR)/memories \
--json $(RESULTS_DIR)/memories.json \
$(foreach file,$(ADDITIONAL_MEMORIES),--memories $(file))
$(foreach file,$(VERILOG_FILES),--verilog $(file)) \
$(foreach file,$(ADDITIONAL_MEMORIES),--memories $(file)) \
$(foreach file,$(AUTO_MEMORIES_REGFILES),--regfile-spec $(file)) \
$(foreach m,$(AUTO_MEMORIES_MACRO_PLACE),--macro-place $(m)) \
$(if $(AUTO_MEMORIES_REGFILES),--openroad $(OPENROAD_EXE)) \
$(foreach file,$(TECH_LEF) $(SC_LEF),--lef $(file))

$(RESULTS_DIR)/memories.json: $(RESULTS_DIR)/memories_inferred.json $(ADDITIONAL_MEMORIES) \
$(wildcard $(SCRIPTS_DIR)/memories/*.py)
$(AUTO_MEMORIES_REGFILES) $(wildcard $(SCRIPTS_DIR)/memories/*.py)
$(UNSET_AND_MAKE) do-auto-memories

yosys-dependencies: $(RESULTS_DIR)/memories.json
Expand Down
22 changes: 22 additions & 0 deletions flow/designs/asap7/regfile/RegFile.regfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# RegFile as a register file of placed standard cells (generate_regfile).
# mode netlist: a macro through synthesis and macro placement, then its
# cells, the core FIRM where the macro was placed, the address decode
# left to placement.
module RegFile
mode netlist
words 16
bits 8
clock clock
read io_r0_addr io_r0_data
read io_r1_addr io_r1_data
write io_w0_addr io_w0_data io_w0_en
write io_w1_addr io_w1_data io_w1_en
cell flop DFFHQNx1_ASAP7_75t_R
cell and2 AND2x2_ASAP7_75t_R
cell or2 OR2x2_ASAP7_75t_R
cell ao22 AO22x2_ASAP7_75t_R
cell inv INVx1_ASAP7_75t_R
cell tap TAPCELL_ASAP7_75t_R
pin_layer M4
tap_columns 8
service_sites 40
20 changes: 20 additions & 0 deletions flow/designs/asap7/regfile/config.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# AUTO_MEMORIES_REGFILES in its smallest form: one register file, listed
# by spec, in a parent of flops. Seconds to global route.
export DESIGN_NICKNAME = regfile
export DESIGN_NAME = regfile_top
export PLATFORM = asap7

export VERILOG_FILES = $(DESIGN_HOME)/src/$(DESIGN_NICKNAME)/regfile.v
export SDC_FILE = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/constraint.sdc

export AUTO_MEMORIES = 1
export AUTO_MEMORIES_REGFILES = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/RegFile.regfile
# Placed as a macro and dissolved after macro placement, the path this
# design exists to exercise; unlisted, it would be inlined.
export AUTO_MEMORIES_MACRO_PLACE = RegFile

# The file's macro is 28.5 x 10 um; a core sized by utilization alone
# would be square and narrower than the macro and its halo.
export DIE_AREA = 0 0 60 45
export CORE_AREA = 2 2 58 43
export PLACE_DENSITY = 0.5
7 changes: 7 additions & 0 deletions flow/designs/asap7/regfile/constraint.sdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
set clk_name clock
set clk_port_name clock
set clk_period 1000
set in2reg_max 800
set reg2out_max 800
set in2out_max 600
source $env(PLATFORM_DIR)/constraints.sdc
11 changes: 11 additions & 0 deletions flow/designs/asap7/riscv32i-regfile/config.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# riscv32i with its register file built by AUTO_MEMORIES_REGFILES rather
# than synthesised to flip-flops: the same RTL, constraints and
# utilisation. Not in AUTO_MEMORIES_MACRO_PLACE, so the generated netlist
# is inlined in synthesis. Minutes to global route, the case to measure
# the generator on; docs/user/AutoMemories.md has the numbers.
export DESIGN_NICKNAME = riscv32i-regfile

include designs/asap7/riscv32i/config.mk

export AUTO_MEMORIES = 1
export AUTO_MEMORIES_REGFILES = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/regfile.regfile
1 change: 1 addition & 0 deletions flow/designs/asap7/riscv32i-regfile/io.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude_io_pin_region -region left:* -region right:* -region top:*
19 changes: 19 additions & 0 deletions flow/designs/asap7/riscv32i-regfile/regfile.regfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# riscv32i's `regfile` (src/riscv32i/regfile.v) as a register file of
# standard cells, inlined in synthesis: 32 x 32, two combinational reads,
# one write through a clock gate per word, x0 reads 0 and drops its
# writes.
module regfile
words 32
bits 32
clock clk
read ra1 rd1
read ra2 rd2
write wa3 wd3 we3
zero_word 0
write_style clock_gate
cell icg ICGx1_ASAP7_75t_R
cell flop DFFHQNx1_ASAP7_75t_R
cell and2 AND2x2_ASAP7_75t_R
cell or2 OR2x2_ASAP7_75t_R
cell ao22 AO22x2_ASAP7_75t_R
cell inv INVx1_ASAP7_75t_R
60 changes: 60 additions & 0 deletions flow/designs/src/regfile/regfile.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// A 16 x 8 register file with two read ports and two write ports,
// behavioural, as RTL writes one. With AUTO_MEMORIES_REGFILES naming its
// spec the flow never synthesises this module: OpenROAD's
// generate_regfile builds it of placed standard cells instead.
module RegFile(
input clock,
input [3:0] io_r0_addr,
output [7:0] io_r0_data,
input [3:0] io_r1_addr,
output [7:0] io_r1_data,
input io_w0_en,
input [3:0] io_w0_addr,
input [7:0] io_w0_data,
input io_w1_en,
input [3:0] io_w1_addr,
input [7:0] io_w1_data
);
reg [7:0] mem [0:15];
always @(posedge clock) begin
if (io_w0_en) mem[io_w0_addr] <= io_w0_data;
if (io_w1_en) mem[io_w1_addr] <= io_w1_data;
end
assign io_r0_data = mem[io_r0_addr];
assign io_r1_data = mem[io_r1_addr];
endmodule

// The parent: registers around the file and some logic of its own.
// Bit 0 of the file is never read, so its column is dead logic the flow
// removes after the file dissolves into its cells.
module regfile_top(
input clock,
input [3:0] ra0, ra1, wa0, wa1,
input [7:0] wd0, wd1,
input we0, we1,
output [7:0] rd0, rd1,
output [7:0] sum
);
reg [3:0] ra0_q, ra1_q, wa0_q, wa1_q;
reg [7:0] wd0_q, wd1_q;
reg we0_q, we1_q;
wire [7:0] d0, d1;
always @(posedge clock) begin
ra0_q <= ra0; ra1_q <= ra1; wa0_q <= wa0; wa1_q <= wa1;
wd0_q <= wd0; wd1_q <= wd1; we0_q <= we0; we1_q <= we1;
end
RegFile u_rf(
.clock(clock),
.io_r0_addr(ra0_q), .io_r0_data(d0),
.io_r1_addr(ra1_q), .io_r1_data(d1),
.io_w0_en(we0_q), .io_w0_addr(wa0_q), .io_w0_data(wd0_q),
.io_w1_en(we1_q), .io_w1_addr(wa1_q), .io_w1_data(wd1_q)
);
reg [6:0] rd0_q, rd1_q, sum_q;
always @(posedge clock) begin
rd0_q <= d0[7:1]; rd1_q <= d1[7:1]; sum_q <= d0[7:1] + d1[7:1];
end
assign rd0 = {rd0_q, 1'b0};
assign rd1 = {rd1_q, 1'b0};
assign sum = {sum_q, 1'b0};
endmodule
3 changes: 3 additions & 0 deletions flow/scripts/macro_place.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ source $::env(SCRIPTS_DIR)/macro_place_util.tcl

source_step_tcl POST MACRO_PLACE

# register files in `mode netlist` become their cells
source $::env(SCRIPTS_DIR)/regfile_dissolve.tcl

report_design_area

orfs_write_db $::env(RESULTS_DIR)/2_2_floorplan_macro.odb
Expand Down
Loading
Loading