Skip to content

Add column mux ratio for ratio = 1, 2, 4#10016

Closed
tnguy19 wants to merge 9 commits intoThe-OpenROAD-Project:masterfrom
tnguy19:add-column-mux
Closed

Add column mux ratio for ratio = 1, 2, 4#10016
tnguy19 wants to merge 9 commits intoThe-OpenROAD-Project:masterfrom
tnguy19:add-column-mux

Conversation

@tnguy19
Copy link
Copy Markdown
Contributor

@tnguy19 tnguy19 commented Mar 31, 2026

Summary

Add -column_mux_ratio parameter to generate_ram and generate_ram_netlist with default value = 1.

The column_mux_ratio value indicate the number of words that will share the same physical row, and currently this implementation will work correctly for values = 1, 2, and 4*.

Muxes are implemented using AOI22s for ratio =2 (one AOI22+inverter) and ratio =4 (2 stage-1 AOI22s feeding a stage-2 AOI22).

Some changes to the code to accommodate this feature additions are also added.

*Mux_ratio=4 currently have correct functionality but faces congestion issue during cell placement

Type of Change

  • New feature

Impact

  • generate_ram and generate_ram_netlist can be use with option -column_mux_ratio with default value = 1. Scripts that do not have column_mux_ratio are not affected
  • Change from using num_words to num_rows to indicate the number of physical row that will be created. For column_mux_ratio >1, num_rows = num_words / column_mux_ratio physical rows are generated. Column mux are placed in the buffer row
  • Change naming instances with format storage_{row}_{bit} to storage_{row}_{word}_{bit} to reflect the placement of multiple words in one row when column_mux_ratio > 1

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

N/A

@rovinski

NOTE: PR is reopened in #10018

tnguy19 added 7 commits March 17, 2026 11:57
Signed-off-by: Thinh Nguyen <nguyenthinh19011@gmail.com>
Signed-off-by: Thinh Nguyen <nguyenthinh19011@gmail.com>
Signed-off-by: Thinh Nguyen <nguyenthinh19011@gmail.com>
Signed-off-by: Thinh Nguyen <nguyenthinh19011@gmail.com>
…OI22

Signed-off-by: Thinh Nguyen <nguyenthinh19011@gmail.com>
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 support for column muxing (ratios 1, 2, and 4) in the RAM generator, involving updates to layout grid calculations and the addition of AOI22-based multiplexing logic. Feedback identifies a potential out-of-bounds access when the number of rows is less than 2 and suggests moving local lambdas to a namespace-level scope. Additionally, the reviewer recommended improving the robustness of the port extraction logic and replacing hardcoded magic numbers with named constants for better maintainability.

Comment thread src/ram/src/ram.cpp

int num_inputs = std::ceil(std::log2(num_words));
// compute information to support col/mux ratio feature
int num_rows = num_words / column_mux_ratio;
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 current implementation does not support cases where num_rows (calculated as num_words / column_mux_ratio) is less than 2. For example, if num_words=4 and column_mux_ratio=4, num_rows will be 1, which leads to an out-of-bounds access in addr_inputs_ at line 1136 (addr_inputs_[num_word_bits]) because num_word_bits will equal num_inputs. Please add a check to ensure num_rows >= 2.

Suggested change
int num_rows = num_words / column_mux_ratio;
int num_rows = num_words / column_mux_ratio;
if (num_rows < 2) {
logger_->error(RAM,
35,
"The ram generator currently requires at least 2 rows "
"(num_words / column_mux_ratio >= 2).");
}
References
  1. Calls to logger_->error are expected to terminate the program, so additional error handling like returning an error code or throwing an exception is not necessary.

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.

Fine to include this suggestion (but can remove the word "currently" - there is no reason to support a single-row RAM)

Comment thread src/ram/src/ram.cpp
Comment on lines +485 to +492
auto isNotPort = [](const sta::FuncExpr* e) {
return e && e->op() == sta::FuncExpr::Op::not_ && e->left()
&& e->left()->op() == sta::FuncExpr::Op::port;
};
auto isAndOfNots = [&isNotPort](const sta::FuncExpr* e) {
return e && e->op() == sta::FuncExpr::Op::and_ && isNotPort(e->left())
&& isNotPort(e->right());
};
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

According to the general rules, helper logic should be defined as a free function in a namespace rather than as a local lambda within a function. These lambdas (isNotPort and isAndOfNots) should be moved to a namespace-level scope.

References
  1. Helper logic should be defined as a free function in a namespace rather than as a local lambda within a function.

Comment thread src/ram/src/ram.cpp
Comment on lines +540 to +568
if (f->op() == sta::FuncExpr::Op::not_) {
// Compact form: not_(or_(and_(A1,A2), and_(B1,B2)))
auto or_expr = f->left();
auto and_a = or_expr->left();
auto and_b = or_expr->right();
aoi22_in_a1_ = and_a->left()->port()->name();
aoi22_in_a2_ = and_a->right()->port()->name();
aoi22_in_b1_ = and_b->left()->port()->name();
aoi22_in_b2_ = and_b->right()->port()->name();
} else {
// SOP expanded form used by sky130hd:
// or_(or_(or_(and_(not_(A1),not_(B1)), and_(not_(A1),not_(B2))),
// and_(not_(A2),not_(B1))),
// and_(not_(A2),not_(B2)))
// Port names are inside not_() wrappers, requiring an extra
// ->left() to reach the actual port compared to the compact form.
// e.g. and_(not_(A1), not_(B1))->left()->left()->port() gives A1
// NOTE: this branch is PDK-specific to sky130hd's liberty
// representation. If adding support for other PDKs, verify their
// AOI22 function expression form and extend this extraction
// accordingly.
auto t1 = f->left()->left()->left();
auto t2 = f->left()->left()->right();
auto t3 = f->left()->right();
aoi22_in_a1_ = t1->left()->left()->port()->name();
aoi22_in_a2_ = t3->left()->left()->port()->name();
aoi22_in_b1_ = t1->right()->left()->port()->name();
aoi22_in_b2_ = t2->right()->left()->port()->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

The logic for extracting port names from the AOI22 function expression is brittle as it relies on a specific nesting structure of the sta::FuncExpr tree (especially for the SOP expanded form). If a library represents the AOI22 function differently, this extraction will fail or produce incorrect results. Consider implementing a more robust matching algorithm or providing a way to specify these port names via configuration if the automatic detection fails.

Comment thread src/ram/src/ram.cpp
"inv",
inv_cell_,
{{"A", aoi_out}, {"Y", q_outputs_[0][global_bit]->getNet()}});
ram_grid_.addCell(std::move(mux_cell), base_col + 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 column offsets used for cell placement (e.g., base_col + 1, base_col + 2, base_col + 3) are hardcoded magic numbers. Following the general rules, these should be defined as named constants to improve code clarity and maintainability.

References
  1. Define tunable parameters as named constants instead of using hardcoded magic numbers.

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

Comment thread src/ram/src/ram.cpp
&& isNotPort(e->right());
};
aoi22_cell_ = findMaster(
[&isNotPort, &isAndOfNots](sta::LibertyPort* port) {
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: lambda capture 'isNotPort' is not used [clang-diagnostic-unused-lambda-capture]

Suggested change
[&isNotPort, &isAndOfNots](sta::LibertyPort* port) {
[ &isAndOfNots](sta::LibertyPort* port) {

tnguy19 added 2 commits March 31, 2026 19:28
Signed-off-by: Thinh Nguyen <nguyenthinh19011@gmail.com>
1. Add -column_mux_ratio flag to generate_ram and generate_ram_netlist (default=1)
2. Mux created from inline AOI22 mux placement for ratio=2 (single AOI22+INV per bit) and ratio=4 (2-stage AOI22 tree)
3. Fix addr BTerm naming: addr[i] -> addr_rw[i]
4. Fix makeSlice prefix to include word_idx: storage_{row}_{word}_{bit}
5. Use num_rows for column generation instead of num_words
6. Update make_8x8.ok with new naming convention for cells
7. Add make_8x8_mux2.ok for test with col_mux_ratio=2

Signed-off-by: Thinh Nguyen <nguyenthinh19011@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants