diff --git a/src/cts/include/cts/TritonCTS.h b/src/cts/include/cts/TritonCTS.h index 4bdc943ebb0..d7a2c632de7 100644 --- a/src/cts/include/cts/TritonCTS.h +++ b/src/cts/include/cts/TritonCTS.h @@ -179,7 +179,8 @@ class TritonCTS int depth, bool fullTree, const std::unordered_set& sinks, - const std::unordered_set& dummies); + const std::unordered_set& dummies, + std::unordered_set& visitedNets); std::pair branchBufferCount(ClockInst* inst, int bufCounter, Clock& clockNet); diff --git a/src/cts/src/HTreeBuilder.cpp b/src/cts/src/HTreeBuilder.cpp index cc77b90bf9c..13297516266 100644 --- a/src/cts/src/HTreeBuilder.cpp +++ b/src/cts/src/HTreeBuilder.cpp @@ -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 " diff --git a/src/cts/src/TritonCTS.cpp b/src/cts/src/TritonCTS.cpp index 5ebefdfe55b..67ac97314dc 100644 --- a/src/cts/src/TritonCTS.cpp +++ b/src/cts/src/TritonCTS.cpp @@ -362,8 +362,18 @@ void TritonCTS::countSinksPostDbWrite( int depth, bool fullTree, const std::unordered_set& sinks, - const std::unordered_set& dummies) + const std::unordered_set& dummies, + std::unordered_set& visitedNets) { + if (net->getSigType() != odb::dbSigType::CLOCK) { + logger_->error(CTS, + 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 iterms = net->getITerms(); int driverX = 0; int driverY = 0; @@ -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; + } + } + + 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 @@ -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) { @@ -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(); @@ -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 visitedNets; countSinksPostDbWrite(builder.get(), topClockNet, sinkCount, @@ -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);