From 892677c55c57ae04056aca11991975804d66cfbc Mon Sep 17 00:00:00 2001 From: arthurjolo Date: Tue, 14 Apr 2026 19:21:09 +0000 Subject: [PATCH 1/5] cts: prevent sink count recursion from leaking into data nets + bug fix Signed-off-by: arthurjolo --- src/cts/include/cts/TritonCTS.h | 3 ++- src/cts/src/TritonCTS.cpp | 29 +++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) 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/TritonCTS.cpp b/src/cts/src/TritonCTS.cpp index 5ebefdfe55b..77ea15bced0 100644 --- a/src/cts/src/TritonCTS.cpp +++ b/src/cts/src/TritonCTS.cpp @@ -362,8 +362,16 @@ 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, "Count sinks recursion leaked into data net {}", net->getName()); + return; + } + 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 +411,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 @@ -447,7 +465,8 @@ void TritonCTS::countSinksPostDbWrite( depth + 1, fullTree, sinks, - dummies); + dummies, + visitedNets); } else { std::string cellType = "Complex cell"; odb::dbInst* inst = iterm->getInst(); @@ -529,6 +548,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 +560,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); From db6e55555e144f3fb1df65246c4673dd73fee55f Mon Sep 17 00:00:00 2001 From: arthurjolo Date: Tue, 14 Apr 2026 19:28:32 +0000 Subject: [PATCH 2/5] clang format Signed-off-by: arthurjolo --- src/cts/src/TritonCTS.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cts/src/TritonCTS.cpp b/src/cts/src/TritonCTS.cpp index 77ea15bced0..4e859acaa25 100644 --- a/src/cts/src/TritonCTS.cpp +++ b/src/cts/src/TritonCTS.cpp @@ -365,8 +365,11 @@ void TritonCTS::countSinksPostDbWrite( const std::unordered_set& dummies, std::unordered_set& visitedNets) { - if(net->getSigType() != odb::dbSigType::CLOCK) { - logger_->error(CTS, 369, "Count sinks recursion leaked into data net {}", net->getName()); + if (net->getSigType() != odb::dbSigType::CLOCK) { + logger_->error(CTS, + 369, + "Count sinks recursion leaked into data net {}", + net->getName()); return; } if (!visitedNets.insert(net).second) { @@ -415,7 +418,7 @@ void TritonCTS::countSinksPostDbWrite( // 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 (builder->getTreeType() != TreeType::RegisterTree) { if (!depth && builder->getTopBufferName() != inst->getName()) { terminate = true; trueSink = false; From 5c6fc6763794011db63e355e55a0c3501e58a3c6 Mon Sep 17 00:00:00 2001 From: arthurjolo Date: Tue, 14 Apr 2026 19:57:18 +0000 Subject: [PATCH 3/5] cts: address gemini review Signed-off-by: arthurjolo --- src/cts/src/HTreeBuilder.cpp | 3 +++ src/cts/src/TritonCTS.cpp | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) 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 4e859acaa25..a24789a5f9a 100644 --- a/src/cts/src/TritonCTS.cpp +++ b/src/cts/src/TritonCTS.cpp @@ -370,7 +370,6 @@ void TritonCTS::countSinksPostDbWrite( 369, "Count sinks recursion leaked into data net {}", net->getName()); - return; } if (!visitedNets.insert(net).second) { return; // cycle detected: this net was already visited on this path From 6523d8b4f8f8526c014c93f6ebee75db97cd22b3 Mon Sep 17 00:00:00 2001 From: arthurjolo Date: Thu, 16 Apr 2026 16:25:52 +0000 Subject: [PATCH 4/5] cts: update sink count recursion error message Signed-off-by: arthurjolo --- src/cts/src/TritonCTS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cts/src/TritonCTS.cpp b/src/cts/src/TritonCTS.cpp index a24789a5f9a..37ecea5d37a 100644 --- a/src/cts/src/TritonCTS.cpp +++ b/src/cts/src/TritonCTS.cpp @@ -368,7 +368,7 @@ void TritonCTS::countSinksPostDbWrite( if (net->getSigType() != odb::dbSigType::CLOCK) { logger_->error(CTS, 369, - "Count sinks recursion leaked into data net {}", + "Unexpected data net '{}' found during clock tree traversal", net->getName()); } if (!visitedNets.insert(net).second) { From 643fcb4399433e9f5a97a7979efcbbe74362eca3 Mon Sep 17 00:00:00 2001 From: arthurjolo Date: Fri, 17 Apr 2026 19:20:05 +0000 Subject: [PATCH 5/5] cts: remove workarround wrong recursion in count sinks post db write Signed-off-by: arthurjolo --- src/cts/src/TritonCTS.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/cts/src/TritonCTS.cpp b/src/cts/src/TritonCTS.cpp index 37ecea5d37a..67ac97314dc 100644 --- a/src/cts/src/TritonCTS.cpp +++ b/src/cts/src/TritonCTS.cpp @@ -433,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) {