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
8 changes: 8 additions & 0 deletions src/ram/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ cc_library(
"src/MakeRam.cpp",
"src/layout.cpp",
"src/ram.cpp",
"src/regfile.cpp",
"src/regfile.h",
"src/regfile_cmd.cpp",
"src/views.cpp",
"src/views.h",
":swig",
":tcl",
],
hdrs = [
"include/ram/MakeRam.h",
"include/ram/layout.h",
"include/ram/ram.h",
"include/ram/regfile.h",
],
includes = [
"include",
Expand All @@ -36,6 +42,8 @@ cc_library(
"//src/drt",
"//src/grt",
"//src/odb/src/db",
"//src/odb/src/defout",
"//src/odb/src/lefout",
"//src/pdn",
"//src/ppl",
"//src/sta:opensta_lib",
Expand Down
3 changes: 3 additions & 0 deletions src/ram/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ target_sources(ram
src/ram.cpp
src/layout.cpp
src/MakeRam.cpp
src/regfile.cpp
src/regfile_cmd.cpp
src/views.cpp
)

target_include_directories(ram
Expand Down
63 changes: 62 additions & 1 deletion src/ram/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,70 @@ generate_ram [-mask_size bits]
| `-use_latch` | If set to `1`, uses a two-phase latch-based design instead of flip-flops for the storage elements. Functionally equivalent to flip-flop design with smaller storage cell area. Default: `0`. |
| `-write_behavioral_verilog` | Write a behavioral Verilog model of the RAM array to the specified file. |

### Register File Generation

The `generate_regfile` command builds a register file, many read and
write ports over few words, as an array of placed standard cells: word
rows by bit columns, each tile a flop, its write select and an AND per
read port, the read word lines decoded in a header column and the read
bit lines as OR trees in a footer row. Every cell is placed by
construction. The block is built into the current database, whose
libraries must hold the cells the spec names.

```tcl
generate_regfile -spec file
[-check_ports verilog_file]
[-verilog file]
[-def file]
[-lef file]
[-liberty file]
```

#### Options

| Switch Name | Description |
| ---------------------- | -------------------------------------- |
| `-spec` | The spec file, below. |
| `-check_ports` | A Verilog file declaring the spec's module. The command stops if the module's ports are not the ports the spec names, at the widths the spec implies. It reads ANSI headers, a SystemVerilog `#(...)` parameter list and widths written in its parameters' defaults, packed dimensions multiplied; a port declaration it cannot read stops it too. |
| `-verilog` | Write the cells as structural Verilog. |
| `-def` | Write the placed block as DEF. |
| `-lef` | Write an abstract LEF of the block. |
| `-liberty` | Write a timing model; a `_pre_layout.lib` beside it has ideal clocks. |

#### Spec

One key per line, `#` comments:

| Key | Description |
| ---------------------- | -------------------------------------- |
| `module name` | The module and block name. |
| `mode macro\|netlist` | `macro` (default): the block is a macro to its parent; every cell is placed, the address inverters placed but movable so the macro's own repair can size them. `netlist`: the block is placed as a macro and then dissolves into its parent's cells: the core (storage flops and read bitlines) is fixed where it lies, and the periphery (address inverters, decode, hold) is left unplaced, RAM-0052 says how much, for the parent's placer and resizer, which alone know the drivers and loads outside the array; the abstract's pins sit where their connections land in the array. Which shape is better, and whether the periphery should be unplaced or placed but movable, is still being measured. |
| `words n`, `bits n` | Depth and width. |
| `clock port` | The clock port. |
| `reset port` | A reset port the RTL has and the array ignores. |
| `write_priority or\|last` | Two ports writing one word in one cycle: `or` (the default) ORs their data, for an RTL that never does it; `last`, the later write port in the spec wins, as an RTL's later assignment does. |
| `unused port...` | One-bit inputs the RTL module has and the array ignores (ibex's `test_en_i`), so the block keeps the module's ports. |
| `async_reset port low\|high` | The reset clears every stored word to zero asynchronously, active low or high; the storage flops are `cell flop_r` with their set tied off by `cell tie_hi`, one tie (and for an active-high reset one inverter) per word. Read-address registers are not reset. One of `reset` or `async_reset`. |
| `read addr data` | A read port, by the module's port names. Repeat per port. |
| `read_banked addr0 data0 addr1 data1 ...` | A read port with an address and a data port per bank. |
| `write addr data [en]` | A write port. Repeat per port. |
| `cell role master` | The cells: `flop`, `and2`, `or2`, `ao22`, `inv`, `tap`; with `async_reset` also `flop_r` (pins D, CLK, RESETN, SETN, QN by default) and `tie_hi` (pin H). |
| `pins role pin...` | The pin names of a cell role (`flop`, `flop_r`, `tie_hi`, `and2`, `or2`, `ao22`, `inv`), for a library whose names differ from the defaults. |
| `flop_output Q\|QN` | Whether the flop's output is inverted. |
| `pin_layer layer`, `pin_layer_v layer` | The layers of the pins on the left and right edges, and on the top and bottom. |
| `pin_track offset pitch` | The track grid the pins are centred on, in microns. |
| `banks n`, `bank_columns n`, `bank_order contiguous\|interleaved` | Word columns side by side, how many of them stand in a row, and how words fall into them. |
| `bit_folds n` | The word split into `n` bit bands stacked one above the other. |
| `read_latency 0\|1` | `1` registers each read address on the clock first. |
| `zero_word n` | A word with no storage that reads 0 and drops writes (RISC-V x0). |
| `tap_columns n`, `service_sites n` | A tap column every `n` bit columns, and free sites beside each for the clock tree. |
| `store_name pattern`, `read_reg_name pattern` | Instance names of the flops, from `{word}`, `{bit}`, `{port}` and `{bank}`. |
| `lib knob value` | The timing model's parameters: `gate_delay_ps`, `wire_factor`, `input_load_ff`, `clock_load_ff`, `leakage_nw_per_cell`, `output_max_cap_ff`, `hold_ps`. |

## Example scripts

See [test/make_8x8_sky130.tcl](test/make_8x8_sky130.tcl).
See [test/make_8x8_sky130.tcl](test/make_8x8_sky130.tcl) and
[test/generate_regfile_asap7.tcl](test/generate_regfile_asap7.tcl).

## Regression tests

Expand Down
33 changes: 33 additions & 0 deletions src/ram/include/ram/regfile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026, The OpenROAD Authors

#pragma once

#include <string>

namespace odb {
class dbDatabase;
}

namespace utl {
class Logger;
}

namespace ram {

// A multi-port register file as placed standard cells: word rows by bit
// columns, every cell of its core placed by construction (see
// src/regfile.h). Reads the spec file, builds the block into `db`, whose
// libraries must hold the spec's cells, and writes each view whose path
// is not empty. `check_ports` names a Verilog file whose module the spec
// must match port for port before anything is built.
void generateRegfile(odb::dbDatabase* db,
utl::Logger* logger,
const std::string& spec_path,
const std::string& verilog_path,
const std::string& def_path,
const std::string& lef_path,
const std::string& liberty_path,
const std::string& check_ports);

} // namespace ram
12 changes: 12 additions & 0 deletions src/ram/src/ram.i
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "ord/OpenRoad.hh"
#include "ram/ram.h"
#include "ram/regfile.h"
#include "utl/Logger.h"

namespace ord {
Expand Down Expand Up @@ -96,6 +97,17 @@ generate_ram_netlist_cmd(int mask_size,
tapcell, max_tap_dist);
}

void generate_regfile_cmd(const char* spec_path,
const char* verilog_path,
const char* def_path,
const char* lef_path,
const char* liberty_path,
const char* check_ports)
{
generateRegfile(ord::getDb(), ord::getLogger(), spec_path, verilog_path,
def_path, lef_path, liberty_path, check_ports);
}

void ram_pdngen(const char* power_net_name, const char* ground_net_name,
const char* route_name, int route_width,
const char* ver_name, int ver_width, int ver_pitch,
Expand Down
25 changes: 25 additions & 0 deletions src/ram/src/ram.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,28 @@ proc generate_ram { args } {

ram::ram_routing
}

sta::define_cmd_args "generate_regfile" {-spec file
[-verilog file]
[-def file]
[-lef file]
[-liberty file]
[-check_ports verilog_file]}

# A multi-port register file as placed standard cells, from a spec file
# (src/regfile.h): the block is built into the current database, whose
# libraries must hold the spec's cells, and each view asked for is written.
proc generate_regfile { args } {
sta::parse_key_args "generate_regfile" args \
keys { -spec -verilog -def -lef -liberty -check_ports } flags {}
sta::check_argc_eq0 "generate_regfile" $args
if { ![info exists keys(-spec)] } {
utl::error RAM 51 "generate_regfile needs -spec."
}
set views {}
foreach k { -verilog -def -lef -liberty -check_ports } {
lappend views [expr { [info exists keys($k)] ? $keys($k) : "" }]
}
ram::generate_regfile_cmd $keys(-spec) {*}$views
ord::design_created
}
Loading
Loading