Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion src/cts/include/cts/TritonCTS.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ class TritonCTS
int depth,
bool fullTree,
const std::unordered_set<odb::dbITerm*>& sinks,
const std::unordered_set<odb::dbInst*>& dummies);
const std::unordered_set<odb::dbInst*>& dummies,
std::unordered_set<odb::dbNet*>& visitedNets);
std::pair<int, int> branchBufferCount(ClockInst* inst,
int bufCounter,
Clock& clockNet);
Expand Down
3 changes: 3 additions & 0 deletions src/cts/src/HTreeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2123,6 +2123,9 @@ void HTreeBuilder::createSingleBufferClockNet()
ClockInst& rootBuffer = clock_.addClockBuffer(
"clkbuf_0", options_->getRootBuffer(), centerX, centerY);

if (topBufferName_.empty()) {
topBufferName_ = rootBuffer.getName();
}
// clang-format off
if (center != legalCenter) {
debugPrint(logger_, CTS, "legalizer", 2, "createSingleBufferClockNet "
Expand Down
51 changes: 27 additions & 24 deletions src/cts/src/TritonCTS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,18 @@ void TritonCTS::countSinksPostDbWrite(
int depth,
bool fullTree,
const std::unordered_set<odb::dbITerm*>& sinks,
const std::unordered_set<odb::dbInst*>& dummies)
const std::unordered_set<odb::dbInst*>& dummies,
std::unordered_set<odb::dbNet*>& visitedNets)
{
if (net->getSigType() != odb::dbSigType::CLOCK) {
logger_->error(CTS,
Comment thread
precisionmoon marked this conversation as resolved.
369,
"Unexpected data net '{}' found during clock tree traversal",
net->getName());
}
if (!visitedNets.insert(net).second) {
return; // cycle detected: this net was already visited on this path
}
odb::dbSet<odb::dbITerm> iterms = net->getITerms();
int driverX = 0;
int driverY = 0;
Expand Down Expand Up @@ -403,8 +413,18 @@ void TritonCTS::countSinksPostDbWrite(
bool terminate = fullTree
? (sinks.find(iterm) != sinks.end())
: !builder->isAnyTreeBuffer(getClockFromInst(inst));
odb::dbITerm* outputPin = iterm->getInst()->getFirstOutput();
bool trueSink = true;

// Macro tree top net also drives the register tree top buffer,
// avoid the recursion going into the register tree.
if (builder->getTreeType() != TreeType::RegisterTree) {
if (!depth && builder->getTopBufferName() != inst->getName()) {
terminate = true;
trueSink = false;
}
}
Comment on lines +420 to +425
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

This logic assumes that builder->getTopBufferName() is always populated. However, in some cases (e.g., single-buffer trees created via createSingleBufferClockNet), topBufferName_ may be an empty string. If it is empty, the condition builder->getTopBufferName() != inst->getName() will evaluate to true for the legitimate top buffer, causing the recursion to terminate prematurely and resulting in an incorrect sink count of zero.

Consider checking if the top buffer name is non-empty before applying this termination logic. Additionally, it would be best to ensure topBufferName_ is correctly set in HTreeBuilder::createSingleBufferClockNet to fully resolve the leak issue for all tree types.

      if (builder->getTreeType() != TreeType::RegisterTree) {
        const std::string& topBufferName = builder->getTopBufferName();
        if (!depth && !topBufferName.empty() && topBufferName != inst->getName()) {
          terminate = true;
          trueSink = false;
        }
      }


odb::dbITerm* outputPin = iterm->getInst()->getFirstOutput();
if (outputPin && outputPin->getNet() == net) {
// Skip feedback loop. When input pin and output pin are
// connected to the same net this can lead to infinite recursion. For
Expand All @@ -413,26 +433,6 @@ void TritonCTS::countSinksPostDbWrite(
trueSink = false;
}

if (!terminate && inst) {
if (inst->isBlock()) {
// Skip non-sink macro blocks
terminate = true;
trueSink = false;
} else {
sta::Cell* masterCell = network_->dbToSta(inst->getMaster());
if (masterCell) {
sta::LibertyCell* libCell = network_->libertyCell(masterCell);
if (libCell) {
if (libCell->hasSequentials()) {
// Skip non-sink registers
terminate = true;
trueSink = false;
}
}
}
}
}

if (!terminate) {
// ignore dummy buffer and inverters added to balance loads
if (outputPin && outputPin->getNet() != nullptr) {
Expand All @@ -447,7 +447,8 @@ void TritonCTS::countSinksPostDbWrite(
depth + 1,
fullTree,
sinks,
dummies);
dummies,
visitedNets);
} else {
std::string cellType = "Complex cell";
odb::dbInst* inst = iterm->getInst();
Expand Down Expand Up @@ -529,6 +530,7 @@ void TritonCTS::writeDataToDb()
CTS, 124, "Clock net \"{}\"", builder->getClock().getName());
logger_->info(CTS, 125, " Sinks {}", sinks.size());
} else {
std::unordered_set<odb::dbNet*> visitedNets;
countSinksPostDbWrite(builder.get(),
topClockNet,
sinkCount,
Expand All @@ -540,7 +542,8 @@ void TritonCTS::writeDataToDb()
0,
reportFullTree,
sinks,
clkDummies);
clkDummies,
visitedNets);
logger_->info(CTS, 98, "Clock net \"{}\"", builder->getClock().getName());
logger_->info(CTS, 99, " Sinks {}", sinkCount);
logger_->info(CTS, 100, " Leaf buffers {}", leafSinks);
Expand Down
Loading