Skip to content

Fix WebGUI timing path issue in a 3DBlox design - #11198

Open
jorge-ferreira-pii wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:fix-WebGUI-timing-path
Open

Fix WebGUI timing path issue in a 3DBlox design#11198
jorge-ferreira-pii wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:fix-WebGUI-timing-path

Conversation

@jorge-ferreira-pii

Copy link
Copy Markdown
Contributor

Fix #11169

@jorge-ferreira-pii jorge-ferreira-pii self-assigned this Aug 20, 2026

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

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.

Code Review

This pull request introduces support for multi-chiplet designs in the web visualization tools by updating pin resolution and timing path shape collection to handle multiple chiplets and apply coordinate transformations. The code review identified critical issues where standard, non-chiplet designs are broken because of empty chiplet vectors in request_handler.cpp and web.cpp, which requires falling back to the top-level block. Additionally, the reviewer recommended optimizing pin resolution by returning early when a prefixed pin is not found in its corresponding chiplet, and warned about potential crashes in timing_report.cpp due to the removal of safety checks on empty data_nodes vectors.

Comment thread src/web/src/request_handler.cpp Outdated
Comment on lines +2546 to +2554
const std::vector<ChipletNode>& chiplets = gen_->chiplets();
collectTimingPathShapes(
chiplets, paths[path_index], new_rects, new_lines);

const std::string pin_name
= jsonOr<std::string>(req.json, "pin_name", "");
if (!pin_name.empty()) {
static const Color kStageColor{.r = 255, .g = 255, .b = 0, .a = 180};
auto [iterm, bterm] = resolvePin(block, pin_name);
auto [iterm, bterm, node] = resolvePin(chiplets, pin_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.

critical

For standard (non-3DBlox) designs, gen_->chiplets() is empty. If chiplets is empty, calling collectTimingPathShapes(chiplets, ...) will not draw any shapes, and resolvePin(chiplets, ...) will fail to resolve any pins. This completely breaks timing path and pin highlighting for standard designs. We should check if chiplets is empty and fall back to using the single top-level block.

        const std::vector<ChipletNode>& chiplets = gen_->chiplets();
        if (!chiplets.empty()) {
          collectTimingPathShapes(
              chiplets, paths[path_index], new_rects, new_lines);
        } else {
          collectTimingPathShapes(
              gen_->getBlock(), paths[path_index], new_rects, new_lines);
        }

        const std::string pin_name
            = jsonOr<std::string>(req.json, "pin_name", "");
        if (!pin_name.empty()) {
          static const Color kStageColor{.r = 255, .g = 255, .b = 0, .a = 180};
          odb::dbITerm* iterm = nullptr;
          odb::dbBTerm* bterm = nullptr;
          const ChipletNode* node = nullptr;
          if (!chiplets.empty()) {
            auto [i, b, n] = resolvePin(chiplets, pin_name);
            iterm = i;
            bterm = b;
            node = n;
          } else {
            auto [i, b] = resolvePin(gen_->getBlock(), pin_name);
            iterm = i;
            bterm = b;
          }

Comment thread src/web/src/web.cpp Outdated
std::vector<ColoredRect> rects;
std::vector<FlightLine> lines;
collectTimingPathShapes(block, path, rects, lines);
collectTimingPathShapes(generator_->chiplets(), path, rects, lines);

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

Similar to the issue in request_handler.cpp, if generator_->chiplets() is empty (standard designs), calling collectTimingPathShapes with the empty chiplets vector will result in no timing path shapes being collected. We should fall back to using block when chiplets is empty.

      if (!generator_->chiplets().empty()) {
        collectTimingPathShapes(generator_->chiplets(), path, rects, lines);
      } else {
        collectTimingPathShapes(block, path, rects, lines);
      }

Comment on lines +5598 to +5609
if (!node.name.empty()) {
const std::string prefix = node.name + "/";
if (pin_name.rfind(prefix, 0) == 0) {
const std::string local_name = pin_name.substr(prefix.length());
if (odb::dbITerm* iterm = node.block->findITerm(local_name.c_str())) {
return {iterm, nullptr, &node};
}
if (odb::dbBTerm* bterm = node.block->findBTerm(local_name.c_str())) {
return {nullptr, bterm, &node};
}
}
}

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

If the pin name starts with the chiplet's prefix but is not found within that chiplet's block, we can return early. Since chiplet names are unique, the pin cannot reside in any other chiplet, so continuing the loop or performing unscoped searches is redundant.

Suggested change
if (!node.name.empty()) {
const std::string prefix = node.name + "/";
if (pin_name.rfind(prefix, 0) == 0) {
const std::string local_name = pin_name.substr(prefix.length());
if (odb::dbITerm* iterm = node.block->findITerm(local_name.c_str())) {
return {iterm, nullptr, &node};
}
if (odb::dbBTerm* bterm = node.block->findBTerm(local_name.c_str())) {
return {nullptr, bterm, &node};
}
}
}
if (!node.name.empty()) {
const std::string prefix = node.name + "/";
if (pin_name.rfind(prefix, 0) == 0) {
const std::string local_name = pin_name.substr(prefix.length());
if (odb::dbITerm* iterm = node.block->findITerm(local_name.c_str())) {
return {iterm, nullptr, &node};
}
if (odb::dbBTerm* bterm = node.block->findBTerm(local_name.c_str())) {
return {nullptr, bterm, &node};
}
return {nullptr, nullptr, nullptr};
}
}

Comment thread src/web/src/timing_report.cpp Outdated
Comment on lines +261 to +264
start_idx = 0;
}
summary.start_pin = summary.data_nodes[start_idx].pin_name;
if (!summary.data_nodes.empty()) {
summary.end_pin = summary.data_nodes.back().pin_name;
}
summary.end_pin = summary.data_nodes.back().pin_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

If summary.data_nodes is empty, accessing summary.data_nodes[start_idx] and summary.data_nodes.back() will cause undefined behavior or crashes. It is safer to wrap both assignments in a check to ensure data_nodes is not empty.

        start_idx = 0;
      }
      if (!summary.data_nodes.empty()) {
        summary.start_pin = summary.data_nodes[start_idx].pin_name;
        summary.end_pin = summary.data_nodes.back().pin_name;
      }

@openroad-ci
openroad-ci force-pushed the fix-WebGUI-timing-path branch from 9fbb602 to af50766 Compare August 20, 2026 23:45
Signed-off-by: Jorge Ferreira <jorge.ferreira@precisioninno.com>
@openroad-ci
openroad-ci force-pushed the fix-WebGUI-timing-path branch from af50766 to 6728915 Compare August 20, 2026 23:51
@jorge-ferreira-pii
jorge-ferreira-pii marked this pull request as ready for review August 20, 2026 23:58
@jorge-ferreira-pii
jorge-ferreira-pii requested a review from a team as a code owner August 20, 2026 23:58
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.

web viewer: SIGSEGV when clicking a timing path in a 3DBlox (read_3dbx) design — null dbBlock deref in timing_highlight

1 participant