Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cbbbf9c
ram: add nangate45 cross-platform regression test
Mar 7, 2026
62711fe
ram: changes to test files after word simplification
braydenlouie Mar 25, 2026
3d95c43
ram: begin removal of hard-coded pin names
braydenlouie Mar 30, 2026
6a8b7a8
ram: removed hard-coded names and tested sky130
braydenlouie Mar 30, 2026
582763a
ram: preliminary testing with nangate testing complete
braydenlouie Mar 30, 2026
e9d4890
ram: fixes to initial gemini advice
braydenlouie Mar 30, 2026
30cc938
ram: clang-tidy change
braydenlouie Mar 30, 2026
be3543f
Merge branch 'The-OpenROAD-Project:master' into pdk-support
braydenlouie Mar 30, 2026
d495b3b
ram: review changes
braydenlouie Mar 31, 2026
7c8125a
ram: change to vok file
braydenlouie Mar 31, 2026
0dcdb68
ram: cmake list change
braydenlouie Mar 31, 2026
132725e
ram: tclint change
braydenlouie Mar 31, 2026
3ca123e
Merge branch 'The-OpenROAD-Project:master' into pdk-support
braydenlouie Apr 1, 2026
813287e
ram: changes to nangate7x7 test
braydenlouie Apr 1, 2026
eb55fa6
ram: reversion to user provided pwr/gnd pins
braydenlouie Apr 1, 2026
1e6c537
ram: clang-format and ng45 test changes
braydenlouie Apr 2, 2026
4ca740a
ram: updated ng45 ok files and tclfmt
braydenlouie Apr 3, 2026
57631b6
Merge branch 'The-OpenROAD-Project:master' into pdk-support
braydenlouie Apr 4, 2026
9d02db1
Merge branch 'The-OpenROAD-Project:master' into pdk-support
braydenlouie Apr 9, 2026
cab68f4
ram: review changes
braydenlouie Apr 11, 2026
22fbb07
ram: clang-tidy
braydenlouie Apr 11, 2026
0cd9418
ram: replaced additional pointer
braydenlouie Apr 11, 2026
0b0e823
ram: clang-tidy
braydenlouie Apr 11, 2026
55f02a6
ram: fixed swig error with spaceship operator
braydenlouie Apr 12, 2026
5b53d5a
ram: fixed swig spaceship operator
braydenlouie Apr 12, 2026
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
39 changes: 33 additions & 6 deletions src/ram/include/ram/ram.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <array>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <utility>
Comment thread
maliberty marked this conversation as resolved.
Expand Down Expand Up @@ -50,6 +51,30 @@ class TritonRoute;

namespace ram {

enum class PinRoleType
{
Clock,
DataIn,
DataOut,
WriteEnable,
TriEnable,
Select, // for mux support in future
Power,
Ground
};

struct PinRole

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would change Pin --> Port in all cases as technically you are dealing with the logical port here.

{
PinRoleType type;
int index;

// for map so that keys are comparable
bool operator<(const PinRole& other) const
{
return std::tie(type, index) < std::tie(other.type, other.index);

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.

warning: no header providing "std::tie" is directly included [misc-include-cleaner]

src/ram/include/ram/ram.h:9:

- #include <utility>
+ #include <tuple>
+ #include <utility>

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.

warning: no header providing "std::tie" is directly included [misc-include-cleaner]

src/ram/include/ram/ram.h:10:

- #include <utility>
+ #include <tuple>
+ #include <utility>

}
};

class RamGen
{
public:
Expand Down Expand Up @@ -96,15 +121,10 @@ class RamGen

private:
void findMasters();
std::map<PinRole, std::string> buildPinMap(odb::dbMaster*);

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.

warning: no header providing "std::map" is directly included [misc-include-cleaner]

src/ram/include/ram/ram.h:7:

- #include <memory>
+ #include <map>
+ #include <memory>

odb::dbMaster* findMaster(const std::function<bool(sta::LibertyPort*)>& match,
const char* name);
odb::dbNet* makeNet(const std::string& prefix, const std::string& name);
odb::dbInst* makeInst(
Layout* layout,
const std::string& prefix,
const std::string& name,
odb::dbMaster* master,
const std::vector<std::pair<std::string, odb::dbNet*>>& connections);
odb::dbInst* makeInst(
Cell* cell,
const std::string& prefix,
Expand Down Expand Up @@ -168,6 +188,13 @@ class RamGen
odb::dbMaster* buffer_cell_{nullptr};
odb::dbMaster* tapcell_{nullptr};

std::map<PinRole, std::string> storage_pins_;
std::map<PinRole, std::string> tristate_pins_;
std::map<PinRole, std::string> inv_pins_;
std::map<PinRole, std::string> and2_pins_;
std::map<PinRole, std::string> clock_gate_pins_;
std::map<PinRole, std::string> buffer_pins_;

std::vector<odb::dbBTerm*> addr_inputs_;
std::vector<odb::dbBTerm*> data_inputs_;
std::vector<std::vector<odb::dbBTerm*>> q_outputs_;
Expand Down
164 changes: 127 additions & 37 deletions src/ram/src/ram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
#include <fstream>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -120,18 +122,19 @@ std::unique_ptr<Cell> RamGen::makeBit(const std::string& prefix,
prefix,
"bit",
storage_cell_,
{{storage_cell_->findMTerm("CLK") ? "CLK" : "GATE", clock},
{"D", data_input},
{"Q", storage_net}});
{{storage_pins_[{PinRoleType::Clock, 0}], clock},
{storage_pins_[{PinRoleType::DataIn, 0}], data_input},
{storage_pins_[{PinRoleType::DataOut, 0}], storage_net}});

for (int read_port = 0; read_port < read_ports; ++read_port) {
makeInst(bit_cell.get(),
prefix,
fmt::format("obuf{}", read_port),
tristate_cell_,
{{"A", storage_net},
{"TE_B", select[read_port]},
{"Z", data_output[read_port]}});
makeInst(
bit_cell.get(),
prefix,
fmt::format("obuf{}", read_port),
tristate_cell_,
{{tristate_pins_[{PinRoleType::DataIn, 0}], storage_net},
{tristate_pins_[{PinRoleType::TriEnable, 0}], select[read_port]},
{tristate_pins_[{PinRoleType::DataOut, 0}], data_output[read_port]}});
}

return bit_cell;
Expand Down Expand Up @@ -178,7 +181,9 @@ void RamGen::makeSlice(const int slice_idx,
prefix,
"cg",
clock_gate_cell_,
{{"CLK", clock}, {"GATE", we0_net}, {"GCLK", gclock_net}});
{{clock_gate_pins_[{PinRoleType::Clock, 0}], clock},
{clock_gate_pins_[{PinRoleType::DataIn, 0}], we0_net},
{clock_gate_pins_[{PinRoleType::DataOut, 0}], gclock_net}});

// Make clock and
// this AND gate needs to be fed a net created by a decoder
Expand All @@ -187,15 +192,18 @@ void RamGen::makeSlice(const int slice_idx,
prefix,
"gcand",
and2_cell_,
{{"A", selects[0]}, {"B", write_enable}, {"X", we0_net}});
{{and2_pins_[{PinRoleType::DataIn, 0}], selects[0]},
{and2_pins_[{PinRoleType::DataIn, 1}], write_enable},
{and2_pins_[{PinRoleType::DataOut, 0}], we0_net}});

// Make select inverters
for (int i = 0; i < selects.size(); ++i) {
makeInst(sel_cell.get(),
prefix,
fmt::format("select_inv_{}", i),
inv_cell_,
{{"A", selects[i]}, {"Y", select_b_nets[i]}});
{{inv_pins_[{PinRoleType::DataIn, 0}], selects[i]},
{inv_pins_[{PinRoleType::DataOut, 0}], select_b_nets[i]}});
}

ram_grid_.addCell(std::move(sel_cell), start_bit_idx + mask_size + slice_idx);
Expand Down Expand Up @@ -278,30 +286,36 @@ std::unique_ptr<Cell> RamGen::makeDecoder(
prefix,
fmt::format("and_layer{}", i),
and2_cell_,
{{"A", addr_nets[i]},
{"B", addr_nets[i + 1]},
{"X", decoder_out_net}});
{{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]},
{and2_pins_[{PinRoleType::DataIn, 1}], addr_nets[i + 1]},
{and2_pins_[{PinRoleType::DataOut, 0}], decoder_out_net}});
prev_net = input_net;
} else if (i == 0) {
makeInst(word_cell.get(),
prefix,
fmt::format("and_layer{}", i),
and2_cell_,
{{"A", addr_nets[i]}, {"B", input_net}, {"X", decoder_out_net}});
{{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]},
{and2_pins_[{PinRoleType::DataIn, 1}], input_net},
{and2_pins_[{PinRoleType::DataOut, 0}], decoder_out_net}});
prev_net = input_net;
} else if (i == layers - 1) { // last AND gate layer
makeInst(word_cell.get(),
prefix,
fmt::format("and_layer{}", i),
and2_cell_,
{{"A", addr_nets[i]}, {"B", addr_nets[i + 1]}, {"X", prev_net}});
{{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]},
{and2_pins_[{PinRoleType::DataIn, 1}], addr_nets[i + 1]},
{and2_pins_[{PinRoleType::DataOut, 0}], prev_net}});
prev_net = input_net;
} else { // middle AND gate layers
makeInst(word_cell.get(),
prefix,
fmt::format("and_layer{}", i),
and2_cell_,
{{"A", addr_nets[i]}, {"B", input_net}, {"X", prev_net}});
{{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]},
{and2_pins_[{PinRoleType::DataIn, 1}], input_net},
{and2_pins_[{PinRoleType::DataIn, 0}], prev_net}});
prev_net = input_net;
}
}
Expand All @@ -311,7 +325,8 @@ std::unique_ptr<Cell> RamGen::makeDecoder(
prefix,
fmt::format("buf_port{}", port),
buffer_cell_,
{{"A", decoder_out_net}, {"X", selects[port]}});
{{buffer_pins_[{PinRoleType::DataIn, 0}], decoder_out_net},
{buffer_pins_[{PinRoleType::DataOut, 0}], selects[port]}});
}

return word_cell;
Expand Down Expand Up @@ -388,6 +403,61 @@ dbMaster* RamGen::findMaster(
return best;
}

std::map<PinRole, std::string> RamGen::buildPinMap(dbMaster* master)

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.

warning: no header providing "std::map" is directly included [misc-include-cleaner]

src/ram/src/ram.cpp:10:

- #include <memory>
+ #include <map>
+ #include <memory>

{
auto sta_cell = network_->dbToSta(master);
auto liberty = network_->libertyCell(sta_cell);
std::map<PinRole, std::string> pin_map;
int in_idx = 0;

std::string tri_enable_name;

auto port_iter = liberty->portIterator();
while (port_iter->hasNext()) {
auto concrete = port_iter->next();
auto lib_port = concrete->libertyPort();
auto dir = concrete->direction();

if (lib_port->isPwrGnd()) {
auto pwr_gnd_type = lib_port->pwrGndType();
if (pwr_gnd_type == sta::PwrGndType::primary_power) {
pin_map[{PinRoleType::Power, 0}] = lib_port->name();
} else if (pwr_gnd_type == sta::PwrGndType::primary_ground) {
pin_map[{PinRoleType::Ground, 0}] = lib_port->name();
}
} else if (lib_port->isClock() || lib_port->isRegClk()
|| lib_port->isClockGateClock()) {
pin_map[{PinRoleType::Clock, 0}] = lib_port->name();
} else if (dir->isTristate()) {
pin_map[{PinRoleType::DataOut, 0}] = lib_port->name();
auto tri_expr = lib_port->tristateEnable();
if (tri_expr && tri_expr->op() == sta::FuncExpr::Op::port) {
tri_enable_name = tri_expr->port()->name();
} else if (tri_expr && tri_expr->op() == sta::FuncExpr::Op::not_) {
tri_enable_name = tri_expr->left()->port()->name();
}
} else if (dir->isAnyOutput()) { // catches isOutput() and isTristate()
pin_map[{PinRoleType::DataOut, 0}] = lib_port->name();
} else if (dir->isInput()) {
pin_map[{PinRoleType::DataIn, in_idx++}] = lib_port->name();
}
}

if (!tri_enable_name.empty()) {
// find and remove it from DataIn
Comment on lines +452 to +453

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why would a tristate enable appear as a DataIn? That doesn't appear possible in the prior loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The tristate enable pin has no distinguishing Liberty flag and is treated as an input port, so the only way to identify it is through the tristateEnable() expression on the tristate output port. Since port iteration order is arbitrary and PDK-dependent, the tristate output port may appear after the enable pin, meaning there is no guarantee the enable pin name is known before it is encountered during iteration. The other option is to do the check for the name first, before iterating over all the ports again and classifying them.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Got it. Perhaps a comment would help as it is a bit tricky

for (auto it = pin_map.begin(); it != pin_map.end(); ++it) {
if (it->second == tri_enable_name
&& it->first.type == PinRoleType::DataIn) {
pin_map.erase(it);
break;
}
}
pin_map[{PinRoleType::TriEnable, 0}] = tri_enable_name;
}
delete port_iter;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

avoid manual memory management with a smart pointer and no delete

std::unique_ptr port_iter = liberty->portIterator();

return pin_map;
}

@rovinski rovinski Mar 31, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should have some error checking to avoid using badly defined cells. e.g.

  • Exactly 1 primary power
  • Exactly 1 primary ground
  • Exactly the number of pins which are expected for the given cell type

Or I guess I suppose you could check those things after the cell map has been made, if easier.

It doesn't need to be solved in this PR, but you should mark as a TODO item to check against various kinds of flip-flops to make sure invalid ones are flagged, e.g. scan flops, flip-flops with reset, etc. Anything that doesn't have only clk, D, and Q ports. There are occasionally some flip-flops that have a Qn (negated Q) output and those are ok, Qn is just left disconnected.


void RamGen::findMasters()
{
if (!inv_cell_) {
Expand All @@ -397,6 +467,7 @@ void RamGen::findMasters()
},
"inverter");
}
inv_pins_ = buildPinMap(inv_cell_);

if (!tristate_cell_) {
tristate_cell_ = findMaster(
Expand All @@ -409,6 +480,7 @@ void RamGen::findMasters()
},
"tristate");
}
tristate_pins_ = buildPinMap(tristate_cell_);

if (!and2_cell_) {
and2_cell_ = findMaster(
Expand All @@ -423,21 +495,32 @@ void RamGen::findMasters()
},
"and2");
}
and2_pins_ = buildPinMap(and2_cell_);

if (!storage_cell_) {
// FIXME
storage_cell_ = findMaster(
[](sta::LibertyPort* port) {
if (!port->direction()->isOutput()) {
if (!port->isRegOutput()) {
return false;
}
auto function = port->function();
return function && function->op() == sta::FuncExpr::Op::and_
&& function->left()->op() == sta::FuncExpr::Op::port
&& function->right()->op() == sta::FuncExpr::Op::port;
// looking for DFF specifically
auto cell = port->libertyCell();
auto port_iter = cell->portIterator();
while (port_iter->hasNext()) {
auto p = port_iter->next()->libertyPort();
// check to filter out latches
if (p && p->isLatchData()) {
delete port_iter;
return false;
}
}
delete port_iter;
return true;
},
"storage");
}
storage_pins_ = buildPinMap(storage_cell_);

if (!clock_gate_cell_) {
clock_gate_cell_ = findMaster(
Expand All @@ -446,12 +529,15 @@ void RamGen::findMasters()
},
"clock gate");
}
clock_gate_pins_ = buildPinMap(clock_gate_cell_);

// for input buffers
if (!buffer_cell_) {
buffer_cell_ = findMaster(
[](sta::LibertyPort* port) { return port->libertyCell()->isBuffer(); },
"buffer");
}
buffer_pins_ = buildPinMap(buffer_cell_);
}

void RamGen::ramPdngen(const char* power_pin,
Expand Down Expand Up @@ -753,12 +839,13 @@ void RamGen::generate(const int mask_size,
for (int bit = 0; bit < mask_size; ++bit) {
int bit_idx = bit + slice * mask_size;
auto buffer_grid_cell = std::make_unique<Cell>();
makeInst(
buffer_grid_cell.get(),
"buffer",
fmt::format("in[{}]", bit_idx),
buffer_cell_,
{{"A", data_inputs_[bit_idx]->getNet()}, {"X", D_nets[bit_idx]}});
makeInst(buffer_grid_cell.get(),
"buffer",
fmt::format("in[{}]", bit_idx),
buffer_cell_,
{{buffer_pins_[{PinRoleType::DataIn, 0}],
data_inputs_[bit_idx]->getNet()},
{buffer_pins_[{PinRoleType::DataOut, 0}], D_nets[bit_idx]}});
ram_grid_.addCell(std::move(buffer_grid_cell), bit_idx + slice);
}
}
Expand All @@ -768,11 +855,13 @@ void RamGen::generate(const int mask_size,
if (num_inputs > 1) {
for (int i = num_inputs - 1; i >= 0; --i) {
auto inv_grid_cell = std::make_unique<Cell>();
makeInst(inv_grid_cell.get(),
"decoder",
fmt::format("inv_{}", i),
inv_cell_,
{{"A", addr_inputs_[i]->getNet()}, {"Y", inv_addr[i]}});
makeInst(
inv_grid_cell.get(),
"decoder",
fmt::format("inv_{}", i),
inv_cell_,
{{inv_pins_[{PinRoleType::DataIn, 0}], addr_inputs_[i]->getNet()},
{inv_pins_[{PinRoleType::DataOut, 0}], inv_addr[i]}});
cell_inv_layout->addCell(std::move(inv_grid_cell));
for (int filler_count = 0; filler_count < num_inputs - 1;
++filler_count) {
Expand All @@ -785,7 +874,8 @@ void RamGen::generate(const int mask_size,
"decoder",
fmt::format("inv_{}", 0),
inv_cell_,
{{"A", addr_inputs_[0]->getNet()}, {"Y", inv_addr[0]}});
{{inv_pins_[{PinRoleType::DataIn, 0}], addr_inputs_[0]->getNet()},
{inv_pins_[{PinRoleType::DataOut, 0}], inv_addr[0]}});
cell_inv_layout->addCell(std::move(inv_grid_cell));
}

Expand Down
1 change: 1 addition & 0 deletions src/ram/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ or_integration_tests(
"ram"
TESTS
make_8x8
make_7x7_nangate45

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you also update make_8x8 to make_8x8_sky130?

)

1 change: 1 addition & 0 deletions src/ram/test/Nangate45
Loading
Loading