Skip to content

ram: Cross platform testing and support#9991

Open
braydenlouie wants to merge 19 commits intoThe-OpenROAD-Project:masterfrom
braydenlouie:pdk-support
Open

ram: Cross platform testing and support#9991
braydenlouie wants to merge 19 commits intoThe-OpenROAD-Project:masterfrom
braydenlouie:pdk-support

Conversation

@braydenlouie
Copy link
Copy Markdown
Contributor

@braydenlouie braydenlouie commented Mar 30, 2026

Summary

  • Replaces hardcoded sky130 port names with a PDK-independent pin mapping system
  • Introduces 'PortRole' struct with 'PortRoleType' enum that is used to classify Liberty ports by role instead of name
  • Verified against both sky130hd and nangate45

Type of Change

  • New feature
  • Refactoring

Impact

RamGen is no longer tied to sky130 port naming conventions and allows using any PDK with a valid Liberty file, with pin names resolved automatically. The existing sky130 behavior is preserved.

Verification

  • I have verified that the local build succeeds (./etc/Build.sh).
  • I have run the relevant tests and they pass.
  • My code follows the repository's formatting guidelines.
  • I have signed my commits (DCO).

Related Issues

#9468
#9392

OpenROAD Contributor and others added 5 commits March 30, 2026 00:20
add test make_7x7_nangate45 exercising generate_ram with Nangate45
standard cells. Uses an intentionally odd 7-word x 1-byte (56-bit)
configuration to stress the decoder logic on a non-power-of-2 count.

also fix a latent bug in makeCellBit() where the clock-pin name probe
only checked for CLK (sky130hd) or GATE (latch cells), silently
missing CK which is the Nangate45 DFF_X1 clock pin name.
The probe chain is extended to CLK -> CK -> GATE.

Resolves The-OpenROAD-Project#9468.

Signed-off-by: OpenROAD Contributor <contributor@openroad.org>
Signed-off-by: braydenlouie <braydenl9988@gmail.com>
Signed-off-by: braydenlouie <braydenl9988@gmail.com>
Signed-off-by: braydenlouie <braydenl9988@gmail.com>
Signed-off-by: braydenlouie <braydenl9988@gmail.com>
@braydenlouie
Copy link
Copy Markdown
Contributor Author

braydenlouie commented Mar 30, 2026

@rovinski I haven't completed testing with generate_ramyet, but the generate_ram_netlist (just placing all cells, no routing, pdngen, etc.) for Nangate45 is working. Just wanted to put this draft PR up so you can take a look at let me know if this implementation is viable.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

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 a more robust mechanism for resolving technology-specific pin names in the RamGen class by utilizing a new PinRole system and a buildPinMap method. It also adds a new regression test for a 7x7 RAM configuration using the Nangate45 library. Several issues were identified in the review: a critical copy-paste error in the decoder logic, incorrect parameters in the new 7x7 test file and its golden Verilog output, leftover debug logging, and redundant code blocks that should be cleaned up before merging.

Comment on lines +344 to +346
{{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]},
{and2_pins_[{PinRoleType::DataIn, 0}], input_net},
{and2_pins_[{PinRoleType::DataIn, 0}], prev_net}});
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.

critical

There appears to be a bug in the connections for this makeInst call. All three connections are incorrectly mapped to PinRoleType::DataIn with index 0, and there is no output connection. This looks like a copy-paste error.

Based on the logic of the surrounding code, the connections should likely be two distinct inputs and one output.

               {{and2_pins_[{PinRoleType::DataIn, 0}], addr_nets[i]},
                {and2_pins_[{PinRoleType::DataIn, 1}], input_net},
                {and2_pins_[{PinRoleType::DataOut, 0}], prev_net}};

Comment on lines +1 to +32
module RAM7x8 (
clk,
D,
Q,
addr_rw,
we
);
input clk;
input [7:0] D;
output reg [7:0] Q;
input [2:0] addr_rw;
input [0:0] we;

// memory array declaration
reg [7:0] mem[0:6];

// write logic
integer i;
always @(posedge clk) begin
for (i = 0; i < 1; i = i + 1) begin
if (we[i]) begin
mem[addr_rw][i*8 +:8] <= D[i*8 +:8];
end
end
end

// read logic
always @(*) begin
Q = mem[addr_rw];
end

endmodule
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.

high

The contents of this golden Verilog file seem incorrect for a 7x7 RAM. It appears to have been copied from an 8-bit wide RAM test and not fully updated.

Specifically:

  • The module name is RAM7x8, but should be RAM7x7.
  • The D and Q ports are 8 bits wide ([7:0]), but should be 7 bits ([6:0]).
  • The memory array mem is declared as reg [7:0], but should be reg [6:0].
  • The write logic uses an 8-bit part-select (i*8 +:8), but should use 7.

Please update this file to reflect the correct behavior for a 7-word by 7-bit RAM.

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.

Gemini is correct here. Something not right with the behavioral model.

Comment on lines +119 to +147
// Resolve the clock-pin name for the storage cell by querying the
// Liberty data. This avoids hard-coding technology-specific pin names
// (e.g. "CLK", "CK") and works with any technology node.
// const char* clk_pin = nullptr;
// const char* data_in_pin = nullptr;
// auto sta_cell = network_->dbToSta(storage_cell_);
// if (sta_cell) {
// auto liberty = network_->libertyCell(sta_cell);
// if (liberty) {
// auto port_iter = liberty->portIterator();
// while (port_iter->hasNext()) {
// auto lib_port = port_iter->next();
// if (lib_port->libertyPort() && lib_port->libertyPort()->isClock()) {
// clk_pin = lib_port->name();
// }
// if (lib_port->libertyPort() &&
// lib_port->libertyPort()->isLatchData()) {
// data_in_pin = lib_port->name();
// }
// }
// delete port_iter;
// }
// }
// if (!clk_pin) {
// logger_->error(RAM,
// 12,
// "No clock pin found on storage cell {}.",
// storage_cell_->getName());
// }
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.

medium

This large block of commented-out code should be removed. It appears to be legacy logic for resolving pin names that has been replaced by the new buildPinMap and *_pins_ maps. Keeping dead code can be confusing for future maintenance.

Comment on lines +449 to +454
logger_->info(RAM,
98,
"port name:{} dir:{} isPwrGnd:{}",
lib_port->name(),
dir->name(),
lib_port->isPwrGnd());
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.

medium

This info level log message inside a loop seems to be for debugging purposes. It can generate a lot of output and should be removed or changed to a more appropriate debug level before merging.

Comment on lines +569 to +576
for (const auto& [role, name] : clock_gate_pins_) {
logger_->info(RAM,
99,
"Clock gate pin - Role: {}/{}, Name: {}",
static_cast<int>(role.type),
role.index,
name);
}
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.

medium

This loop for logging clock gate pins appears to be for debugging. It should be removed before merging.

-word_size 7 \
-num_words 7 \
-read_ports 1 \
-read_ports 1 \
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.

medium

The -read_ports argument is specified twice. This line is a duplicate and should be removed.

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

// 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>


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>

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>

Signed-off-by: braydenlouie <braydenl9988@gmail.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

// 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:10:

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

Signed-off-by: braydenlouie <braydenl9988@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@github-actions
Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

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.

"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?

}
delete port_iter;
return pin_map;
}
Copy link
Copy Markdown
Collaborator

@rovinski rovinski Mar 31, 2026

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.

Comment on lines +1 to +32
module RAM7x8 (
clk,
D,
Q,
addr_rw,
we
);
input clk;
input [7:0] D;
output reg [7:0] Q;
input [2:0] addr_rw;
input [0:0] we;

// memory array declaration
reg [7:0] mem[0:6];

// write logic
integer i;
always @(posedge clk) begin
for (i = 0; i < 1; i = i + 1) begin
if (we[i]) begin
mem[addr_rw][i*8 +:8] <= D[i*8 +:8];
end
end
end

// read logic
always @(*) begin
Q = mem[addr_rw];
end

endmodule
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.

Gemini is correct here. Something not right with the behavioral model.

Signed-off-by: braydenlouie <braydenl9988@gmail.com>
Signed-off-by: braydenlouie <braydenl9988@gmail.com>
Signed-off-by: braydenlouie <braydenl9988@gmail.com>
Signed-off-by: braydenlouie <braydenl9988@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

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.

It isn't sufficient to just change the vok file, if the verilog generator is wrong, then it needs to be fixed. How was the vok originally created?

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 Verilog Generator was fine. The original file was generated before we added the bit masking, so it defaulted to 8 bits in the mask. The test has been changed so that it has 7 bits per mask; the dimensions have changed, but so have the input and output port lengths.

#
# Test: ram/make_7x7_nangate45
# Verifies that generate_ram works correctly with the Nangate45 technology
# node using an intentionally odd 7-word x 1-byte (56-bit) configuration
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.

Suggested change
# node using an intentionally odd 7-word x 1-byte (56-bit) configuration
# node using an intentionally odd 7-word x 7-bit (49-bit) configuration

# Verifies that generate_ram works correctly with the Nangate45 technology
# node using an intentionally odd 7-word x 1-byte (56-bit) configuration
# to exercise the decoder logic on a non-power-of-2 word count.
# Addresses Issue #9468.
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.

Don't need to include that here

Suggested change
# Addresses Issue #9468.

ground_net->setSigType(odb::dbSigType::GROUND);

// find a way to get the power and ground net names associated with cells used
// finds power and gorund of a cell if not given
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.

Suggested change
// finds power and gorund of a cell if not given
// finds power and ground of a cell if not given

Comment on lines +591 to +596
if (!power_pin || power_pin[0] == '\0') {
power_pin = inv_ports_[{PortRoleType::Power, 0}].c_str();
}
if (!ground_pin || ground_pin[0] == '\0') {
ground_pin = inv_ports_[{PortRoleType::Ground, 0}].c_str();
}
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.

This isn't technically sufficient. Although it would be really odd, it is still technically possible for other masters to have different power/ground names. A complete solution would be to iterate through all of the masters and collect the names into a std::set. Then, you would do a global connect on each member of the set.

99% of the time the set will only have 1 member, but it is good to handle all cases.

This is actually a separate item in #9392: "Add automatic detection of standard cell power/ground names"

It would be nice to keep that as a separate PR to only focus on one change.

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.

Got it, I'll just keep the 'power_pin' and 'ground_pin' as required user-defined parameters now, and we can fix this in another PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: Brayden Louie <braydenl9988@gmail.com>
Signed-off-by: Brayden Louie <braydenl9988@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

clang-tidy review says "All clean, LGTM! 👍"

[WARNING ODB-0217] duplicate VIARULE (Via9Array-0) ignoring...
[INFO ODB-0394] Duplicate site FreePDK45_38x28_10R_NP_162NW_34O in Nangate45 already seen in Nangate45_tech
[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 135 library cells
[WARNING RAM-0022] No tapcell is specified.
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 think ng45 has a tapcell, right? If not, use an x1 fill cell as the tap cell.

Comment on lines +29 to +30
-ver_layer {metal4 0.28 8} \
-hor_layer {metal5 0.28 8} \
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.

Does this work? I thought the metals needed to be the first 2 above the first metal layer.

We generally don't want to use above M3 if it can be helped.

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.

I think Nangate45's density is just too high for us to route on metal2 and metal3. Any valid combination of pitch and width has resulted in violations with the detailed router. I will note that these pitch and width dimensions don't work when tapcells are inserted, so this test will probably change.

Signed-off-by: braydenlouie <braydenl9988@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: Brayden Louie <braydenl9988@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

clang-tidy review says "All clean, LGTM! 👍"

@braydenlouie braydenlouie marked this pull request as ready for review April 4, 2026 01:06
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

clang-tidy review says "All clean, LGTM! 👍"

@braydenlouie
Copy link
Copy Markdown
Contributor Author

@maliberty @rovinski PR is ready for review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

clang-tidy review says "All clean, LGTM! 👍"

@rovinski rovinski requested a review from maliberty April 9, 2026 18:35
Comment on lines +72 to +76
// for map so that keys are comparable
bool operator<(const PortRole& other) const
{
return std::tie(type, index) < std::tie(other.type, other.index);
}
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.

In c++20 you can use

auto operator<=>(const PortRole&) const = default;

and get all six operators

Comment on lines +447 to +448
if (!tri_enable_name.empty()) {
// find and remove it from DataIn
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.

master->getName(),
ground_count);
}
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();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants