Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions src/odb/src/3dblox/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ add_library(3dblox
dbvWriter.cpp
dbxWriter.cpp
3dblox.cpp
unfoldedModel.cpp
threeDBloxValidator.cpp
checker.cpp
)

Expand Down
189 changes: 9 additions & 180 deletions src/odb/src/3dblox/checker.cpp
Original file line number Diff line number Diff line change
@@ -1,195 +1,24 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2023-2026, The OpenROAD Authors

#include "checker.h"

#include <algorithm>
#include <cstddef>
#include <map>
#include <ranges>
#include <string>
#include <utility>
#include <vector>
#include "threeDBloxValidator.h"
#include "unfoldedModel.h"

#include "odb/db.h"
#include "odb/dbTransform.h"
#include "odb/geom.h"
#include "utl/Logger.h"
#include "utl/unionFind.h"
namespace odb {

std::string UnfoldedChip::getName() const
{
std::string name;
int index = 0;
for (auto chip_inst : chip_inst_path) {
name += chip_inst->getName();
if (index++ < chip_inst_path.size() - 1) {
name += "/";
}
}
return name;
}

Checker::Checker(utl::Logger* logger) : logger_(logger)
{
}

void Checker::check(odb::dbChip* chip)
{
for (auto chip_inst : chip->getChipInsts()) {
UnfoldedChip unfolded_chip;
unfoldChip(chip_inst, unfolded_chip);
}
odb::dbMarkerCategory* category
= odb::dbMarkerCategory::createOrReplace(chip, "3DBlox");
checkFloatingChips(category);
checkOverlappingChips(category);
// checkConnectionRegions(chip, category);
}

void Checker::unfoldChip(odb::dbChipInst* chip_inst,
UnfoldedChip& unfolded_chip)
{
unfolded_chip.chip_inst_path.push_back(chip_inst);
if (chip_inst->getMasterChip()->getChipType() == dbChip::ChipType::HIER) {
for (auto chip_inst : chip_inst->getMasterChip()->getChipInsts()) {
unfoldChip(chip_inst, unfolded_chip);
}
} else {
// calculate the cuboid of the chip
unfolded_chip.cuboid = chip_inst->getMasterChip()->getCuboid();
for (auto chip_inst : unfolded_chip.chip_inst_path | std::views::reverse) {
chip_inst->getTransform().apply(unfolded_chip.cuboid);
}
debugPrint(
logger_,
utl::ODB,
"3dblox",
1,
"Unfolded chip: {} cuboid: ({}, {}, {}), ({}, {}, {})",
unfolded_chip.getName(),
unfolded_chip.cuboid.xMin() / chip_inst->getDb()->getDbuPerMicron(),
unfolded_chip.cuboid.yMin() / chip_inst->getDb()->getDbuPerMicron(),
unfolded_chip.cuboid.zMin() / chip_inst->getDb()->getDbuPerMicron(),
unfolded_chip.cuboid.xMax() / chip_inst->getDb()->getDbuPerMicron(),
unfolded_chip.cuboid.yMax() / chip_inst->getDb()->getDbuPerMicron(),
unfolded_chip.cuboid.zMax() / chip_inst->getDb()->getDbuPerMicron());
unfolded_chips_.push_back(unfolded_chip);
// Cuboid is overwritten in each leaf - no restoration needed
}
unfolded_chip.chip_inst_path.pop_back();
}
void Checker::checkFloatingChips(odb::dbMarkerCategory* category)
{
utl::UnionFind uf(unfolded_chips_.size());

// Check all pairs for intersection and union them
for (size_t i = 0; i < unfolded_chips_.size(); i++) {
auto cuboid_i = unfolded_chips_[i].cuboid;
for (size_t j = i + 1; j < unfolded_chips_.size(); j++) {
auto cuboid_j = unfolded_chips_[j].cuboid;
if (cuboid_i.intersects(cuboid_j)) {
uf.unite(i, j);
}
}
}

// Group chips by their root parent
std::map<int, std::vector<UnfoldedChip*>> sets;
for (size_t i = 0; i < unfolded_chips_.size(); i++) {
sets[uf.find(i)].push_back(&unfolded_chips_[i]);
}

if (sets.size() > 1) {
// Convert to vector and sort by size
std::vector<std::vector<UnfoldedChip*>> insts_sets;
insts_sets.reserve(sets.size());
for (auto& [root, chips] : sets) {
insts_sets.emplace_back(chips);
}

std::ranges::sort(insts_sets,
[](const std::vector<UnfoldedChip*>& a,
const std::vector<UnfoldedChip*>& b) {
return a.size() > b.size();
});

odb::dbMarkerCategory* floating_chips_category
= odb::dbMarkerCategory::createOrReplace(category, "Floating chips");
logger_->warn(
utl::ODB, 151, "Found {} floating chip sets", insts_sets.size() - 1);

// Create marker for each set except the first one (the biggest one)
for (size_t i = 1; i < insts_sets.size(); i++) {
auto& insts_set = insts_sets[i];
odb::dbMarker* marker = odb::dbMarker::create(floating_chips_category);
for (auto& inst : insts_set) {
debugPrint(logger_,
utl::ODB,
"3dblox",
1,
"Floating chip: {}",
inst->getName());
marker->addShape(Rect(inst->cuboid.xMin(),
inst->cuboid.yMin(),
inst->cuboid.xMax(),
inst->cuboid.yMax()));
marker->addSource(inst->chip_inst_path.back());
}
}
}
}

void Checker::checkOverlappingChips(odb::dbMarkerCategory* category)
{
std::vector<std::pair<UnfoldedChip*, UnfoldedChip*>> overlaps;

// Check all pairs of chip instances for overlaps
for (size_t i = 0; i < unfolded_chips_.size(); i++) {
auto cuboid_i = unfolded_chips_[i].cuboid;
for (size_t j = i + 1; j < unfolded_chips_.size(); j++) {
auto cuboid_j = unfolded_chips_[j].cuboid;
if (cuboid_i.overlaps(cuboid_j)) {
overlaps.emplace_back(&unfolded_chips_[i], &unfolded_chips_[j]);
}
}
}

if (!overlaps.empty()) {
odb::dbMarkerCategory* overlapping_chips_category
= odb::dbMarkerCategory::createOrReplace(category, "Overlapping chips");
logger_->warn(utl::ODB, 156, "Found {} overlapping chips", overlaps.size());

for (const auto& [inst1, inst2] : overlaps) {
odb::dbMarker* marker = odb::dbMarker::create(overlapping_chips_category);

// Compute the intersection region
auto cuboid1 = inst1->cuboid;
auto cuboid2 = inst2->cuboid;
auto intersection = cuboid1.intersect(cuboid2);

// Add the intersection as a shape (project to 2D for visualization)
odb::Rect bbox(intersection.xMin(),
intersection.yMin(),
intersection.xMax(),
intersection.yMax());
marker->addShape(bbox);

// Add both chip instances as sources
marker->addSource(inst1->chip_inst_path.back());
marker->addSource(inst2->chip_inst_path.back());
UnfoldedModel model(logger_);
model.build(chip);

// Add a comment describing the overlap
std::string comment = "Chips " + inst1->getName() + " and "
+ inst2->getName() + " overlap";
debugPrint(logger_,
utl::ODB,
"3dblox",
1,
"Overlapping chips: {} and {}",
inst1->getName(),
inst2->getName());
marker->setComment(comment);
}
}
ThreeDBloxValidator validator(logger_);
validator.validate(model, chip);
}

} // namespace odb
27 changes: 9 additions & 18 deletions src/odb/src/3dblox/checker.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2023-2026, The OpenROAD Authors

#pragma once

#include <string>
#include <vector>
#include "odb/db.h"

#include "odb/3dblox.h"
#include "odb/geom.h"
namespace odb {
class dbChip;
class dbMarkerCategory;
class dbChipInst;
struct UnfoldedChip
{
std::string getName() const;
std::vector<dbChipInst*> chip_inst_path;
Cuboid cuboid;
};

// Facade for backward compatibility and ease of use
class Checker
{
public:
Expand All @@ -23,10 +17,7 @@ class Checker
void check(odb::dbChip* chip);

private:
void checkFloatingChips(odb::dbMarkerCategory* category);
void checkOverlappingChips(odb::dbMarkerCategory* category);
void unfoldChip(odb::dbChipInst* chip_inst, UnfoldedChip& unfolded_chip);
utl::Logger* logger_ = nullptr;
std::vector<UnfoldedChip> unfolded_chips_;
utl::Logger* logger_;
};
} // namespace odb

} // namespace odb
Loading