From 0dc0369f5a8ecea169f5c065ef60b80aac37ec45 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Sun, 26 Apr 2026 17:59:42 -0700 Subject: [PATCH 01/11] try --- search/Sta.cc | 116 ++++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index cd93f297..691d6893 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -65,6 +65,7 @@ #include "PathAnalysisPt.hh" #include "Corner.hh" #include "Search.hh" +#include "GatedClk.hh" #include "Latches.hh" #include "PathGroup.hh" #include "CheckTiming.hh" @@ -5337,71 +5338,74 @@ pinInstances(PinSet &pins, return insts; } -InstanceSeq Sta::clockGatedRegisters() { - Network* network = this->network(); - InstanceSeq result; +// For each register, walk back along its clock arrival path(s) and check +// every cell on the way for clock gating. This mirrors what +// `report_path` does internally to print the target clock path +// (VertexPathIterator + Path::prevPath), and uses the same two +// predicates the path-end machinery uses to recognize gating: +// 1. LibertyCell::isClockGate() -- explicit ICG cells +// 2. GatedClk::gatedClkEnables() -- inferred clk*en gates +// Scan muxes do not pattern-match (2) and are not classified as gates. +// set_case_analysis / set_disable_timing are honored because the +// arrival paths returned by VertexPathIterator are the same paths +// findClkArrivals built (which already pruned inactive legs). +InstanceSeq +Sta::clockGatedRegisters() +{ + ensureGraph(); + ensureLevelized(); + search_->findClkArrivals(); - // Map of pins that are a derivative of icg instances - std::unordered_map pin_to_icg; - std::unique_ptr insts( - network->leafInstanceIterator()); + GatedClk *gated_clk = search_->gatedClk(); + std::set result; - // Iterate over all leaf instances + std::unique_ptr insts(network_->leafInstanceIterator()); while (insts->hasNext()) { - Instance* icg = insts->next(); - - // Skip any non-ICG cells - LibertyCell* lc = network->libertyCell(network->cell(icg)); - if (!lc || !lc->isClockGate()) continue; - - // Get clock gate output pin - PinSeq from; - std::unique_ptr pit(network->pinIterator(icg)); - while (pit->hasNext()) { - Pin* p = pit->next(); - LibertyPort* lp = network->libertyPort(p); - if (lp && lp->isClockGateOut()) from.push_back(p); - } - - // If no output pin, skip (shouldn't happen) - if (from.empty()) continue; - - // Find all pins that are a derivative of the ICG output pin - PinSet fanout = findFanoutPins( - &from, true, true, 0, 0, false, false); - for (const Pin* p : fanout) { - if (network->isRegClkPin(p)) { // filter for register clock pins only - pin_to_icg.emplace(p, icg); + Instance *reg = insts->next(); + LibertyCell *lc = network_->libertyCell(reg); + if (!lc || !lc->hasSequentials() || lc->isClockGate()) + continue; + + bool found = false; + std::unique_ptr pins(network_->pinIterator(reg)); + while (pins->hasNext() && !found) { + Pin *ck = pins->next(); + if (!network_->isRegClkPin(ck)) continue; + Vertex *v = graph_->pinLoadVertex(ck); + if (!v) continue; + + VertexPathIterator path_iter(v, this); + while (path_iter.hasNext() && !found) { + Path *path = path_iter.next(); + if (!path->isClock(this)) continue; + for (Path *p = path; p && !found; p = p->prevPath()) { + Vertex *pv = p->vertex(this); + if (!pv) continue; + const Instance *inst = network_->instance(pv->pin()); + LibertyCell *clc = network_->libertyCell(inst); + if (clc && clc->isClockGate()) { + found = true; + break; + } + PinSet enables(network_); + gated_clk->gatedClkEnables(pv, enables); + if (!enables.empty()) { + found = true; + break; + } + } } } + if (found) result.insert(reg); } - // Reverse lookup from registers to determine if they are gated by an ICG - std::unique_ptr rit( - network->leafInstanceIterator()); - while (rit->hasNext()) { - - // Skip any non-registers - Instance* reg = rit->next(); - LibertyCell* lc = network->libertyCell(network->cell(reg)); - if (!lc || !lc->hasSequentials()) continue; - std::unique_ptr pit(network->pinIterator(reg)); - - // Iterate over all pins - while (pit->hasNext()) { - Pin* clk_pin = pit->next(); - if (!network->isRegClkPin(clk_pin)) continue; - - // Search if the pin is a derivative of an ICG output pin - auto it = pin_to_icg.find(clk_pin); - if (it == pin_to_icg.end()) continue; - result.push_back(reg); - break; // avoid duplicates - } - } - return result; + InstanceSeq out; + out.reserve(result.size()); + for (auto i : result) out.push_back(const_cast(i)); + return out; } + bool Sta::crossesHierarchy(Edge *edge) const { From b6f2ec4a7fcdc339a5a623da8182105c934606da Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Sun, 26 Apr 2026 23:15:18 -0700 Subject: [PATCH 02/11] new implementation --- include/sta/Search.hh | 6 +++ include/sta/Sta.hh | 2 +- search/Search.cc | 69 +++++++++++++++++++++++++++++++++ search/Sta.cc | 89 ++++++++++++++++--------------------------- 4 files changed, 109 insertions(+), 57 deletions(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index b23ef5c4..402c893f 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -26,6 +26,7 @@ #include #include +#include #include "MinMax.hh" #include "UnorderedSet.hh" @@ -226,6 +227,8 @@ public: // Find arrivals for the clock tree. void findClkArrivals(); + const InstanceSet *clkGatesAt(const Vertex *vertex) const; + void updateClkGates(Vertex *vertex); void seedArrival(Vertex *vertex); EvalPred *evalPred() const { return eval_pred_; } SearchPred *searchAdj() const { return search_adj_; } @@ -519,6 +522,7 @@ protected: void seedClkVertexArrivals(const Pin *pin, Vertex *vertex); void findClkArrivals1(); + bool isClkGateInstance(Vertex *vertex); void findAllArrivals(bool thru_latches); void findArrivals1(Level level); @@ -608,6 +612,8 @@ protected: ArrivalVisitor *arrival_visitor_; // Clock arrivals are known. bool clk_arrivals_valid_; + // Per-vertex set of clock-gating cells + std::vector clock_gates_; // Some arrivals exist. bool arrivals_exist_; // Arrivals at end points exist (but may be invalid). diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index ebd4ed27..fd6fcd19 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -605,8 +605,8 @@ public: bool thru_disabled, bool thru_constants); - // Registers whose clock pin is in the fanout of an ICG cell InstanceSeq clockGatedRegisters(); + bool isClkGatedRegister(const Instance *inst); // The set of clocks that arrive at vertex in the clock network. ClockSet clocks(const Pin *pin); diff --git a/search/Search.cc b/search/Search.cc index 8a5f69a3..2da0cdcc 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -815,6 +815,7 @@ Search::arrivalsInvalid() requireds_exist_ = false; requireds_seeded_ = false; clk_arrivals_valid_ = false; + clock_gates_.clear(); arrival_iter_->clear(); required_iter_->clear(); // No need to keep track of incremental updates any more. @@ -942,6 +943,7 @@ Search::findClkArrivals() Stats stats(debug_, report_); debugPrint(debug_, "search", 1, "find clk arrivals"); arrival_iter_->clear(); + clock_gates_.assign(graph_->vertexCount() + 1, InstanceSet(network_)); seedClkVertexArrivals(); ClkArrivalSearchPred search_clk(this); arrival_visitor_->init(false, &search_clk); @@ -953,6 +955,67 @@ Search::findClkArrivals() clk_arrivals_valid_ = true; } +const InstanceSet * +Search::clkGatesAt(const Vertex *vertex) const +{ + VertexId idx = graph_->id(vertex); + if (idx >= clock_gates_.size()) + return nullptr; + return &clock_gates_[idx]; +} + +bool +Search::isClkGateInstance(Vertex *vertex) +{ + // Return if the cell is a clock gate based on liberty cell attributes. + LibertyCell *cell = network_->libertyCell(network_->instance(vertex->pin())); + return cell != nullptr && cell->isClockGate(); +} + +static void +intersect(InstanceSet &clk_gates, const InstanceSet &from_clk_gates) +{ + // In-place intersection of clk_gates and from_clk_gates that results in clk_gates. + for (auto it = clk_gates.begin(); it != clk_gates.end(); ) + it = from_clk_gates.count(*it) ? std::next(it) : clk_gates.erase(it); +} + +void +Search::updateClkGates(Vertex *vertex) +{ + // Clear existing clock gates for recomputation. + VertexId vid = graph_->id(vertex); + if (vid >= clock_gates_.size()) + return; + InstanceSet &clk_gates = clock_gates_[vid]; + clk_gates.clear(); + + // Loop through all clock-tagged predecessors. + bool first = true; + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Vertex *from = edge_iter.next()->from(graph_); + if (from == nullptr || !isClock(from)) + continue; + + // Get all clock gates from the predecessor. + const InstanceSet &from_clk_gates = clock_gates_[graph_->id(from)]; + if (first) { // covers the first predecessor + clk_gates = from_clk_gates; + first = false; + } else { + // Intersect current clock gates with predecessor clock gates. + intersect(clk_gates, from_clk_gates); + if (clk_gates.empty()) // no common gates, no need to continue + break; + } + } + + // If the vertex itself is a clock gate, add it to the set. + if (isClkGateInstance(vertex)) + clk_gates.insert(network_->instance(vertex->pin())); +} + void Search::seedClkVertexArrivals() { @@ -1148,6 +1211,8 @@ Search::findArrivalsSeed() arrival_iter_->ensureSize(); required_iter_->ensureSize(); } + if (clock_gates_.size() < graph_->vertexCount() + 1) + clock_gates_.assign(graph_->vertexCount() + 1, InstanceSet(network_)); seedInvalidArrivals(); } @@ -1279,6 +1344,10 @@ ArrivalVisitor::visit(Vertex *vertex) constrainedRequiredsInvalid(vertex, is_clk); } enqueueRefPinInputDelays(pin); + + // Update the clock gate set if it is a clock vertex. + if (search_->isClock(vertex)) + search_->updateClkGates(vertex); } // When a clock arrival changes, the required time changes for any diff --git a/search/Sta.cc b/search/Sta.cc index 691d6893..bf539eb3 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5338,73 +5338,50 @@ pinInstances(PinSet &pins, return insts; } -// For each register, walk back along its clock arrival path(s) and check -// every cell on the way for clock gating. This mirrors what -// `report_path` does internally to print the target clock path -// (VertexPathIterator + Path::prevPath), and uses the same two -// predicates the path-end machinery uses to recognize gating: -// 1. LibertyCell::isClockGate() -- explicit ICG cells -// 2. GatedClk::gatedClkEnables() -- inferred clk*en gates -// Scan muxes do not pattern-match (2) and are not classified as gates. -// set_case_analysis / set_disable_timing are honored because the -// arrival paths returned by VertexPathIterator are the same paths -// findClkArrivals built (which already pruned inactive legs). InstanceSeq Sta::clockGatedRegisters() { - ensureGraph(); - ensureLevelized(); - search_->findClkArrivals(); - - GatedClk *gated_clk = search_->gatedClk(); - std::set result; + InstanceSeq result; + // Find all leaf registers std::unique_ptr insts(network_->leafInstanceIterator()); while (insts->hasNext()) { - Instance *reg = insts->next(); - LibertyCell *lc = network_->libertyCell(reg); - if (!lc || !lc->hasSequentials() || lc->isClockGate()) + Instance *inst = insts->next(); + LibertyCell *cell = network_->libertyCell(inst); + + // Skip if the cell is not a register or a clock gate. + if (cell == nullptr || !cell->hasSequentials() || cell->isClockGate()) continue; - bool found = false; - std::unique_ptr pins(network_->pinIterator(reg)); - while (pins->hasNext() && !found) { - Pin *ck = pins->next(); - if (!network_->isRegClkPin(ck)) continue; - Vertex *v = graph_->pinLoadVertex(ck); - if (!v) continue; - - VertexPathIterator path_iter(v, this); - while (path_iter.hasNext() && !found) { - Path *path = path_iter.next(); - if (!path->isClock(this)) continue; - for (Path *p = path; p && !found; p = p->prevPath()) { - Vertex *pv = p->vertex(this); - if (!pv) continue; - const Instance *inst = network_->instance(pv->pin()); - LibertyCell *clc = network_->libertyCell(inst); - if (clc && clc->isClockGate()) { - found = true; - break; - } - PinSet enables(network_); - gated_clk->gatedClkEnables(pv, enables); - if (!enables.empty()) { - found = true; - break; - } - } - } - } - if (found) result.insert(reg); + // Check if the register is clock gated based on previous graph traversal. + if (isClkGatedRegister(inst)) + result.push_back(inst); } - - InstanceSeq out; - out.reserve(result.size()); - for (auto i : result) out.push_back(const_cast(i)); - return out; + return result; } +bool +Sta::isClkGatedRegister(const Instance *inst) +{ + std::unique_ptr + pins(network_->pinIterator(inst)); + while (pins->hasNext()) { + const Pin *pin = pins->next(); + + // Skip if the pin is not a register clock pin. + if (pin == nullptr || !network_->isRegClkPin(pin)) + continue; + Vertex *vertex = graph_->pinLoadVertex(pin); + if (vertex == nullptr) + continue; + + // If the vertex has a clock gate, the register is gated + const InstanceSet *gates = search_->clkGatesAt(vertex); + if (gates != nullptr && !gates->empty()) + return true; + } + return false; +} bool Sta::crossesHierarchy(Edge *edge) const From a3585400313fa586c3e8d36086404b67799a76ee Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Sun, 26 Apr 2026 23:44:52 -0700 Subject: [PATCH 03/11] better implementation --- include/sta/Search.hh | 7 +++-- search/Search.cc | 71 ++++++++++++++++++------------------------- search/Sta.cc | 4 +-- 3 files changed, 35 insertions(+), 47 deletions(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 402c893f..3b5bdfa1 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -227,7 +227,7 @@ public: // Find arrivals for the clock tree. void findClkArrivals(); - const InstanceSet *clkGatesAt(const Vertex *vertex) const; + bool isClkGated(const Vertex *vertex) const; void updateClkGates(Vertex *vertex); void seedArrival(Vertex *vertex); EvalPred *evalPred() const { return eval_pred_; } @@ -612,8 +612,9 @@ protected: ArrivalVisitor *arrival_visitor_; // Clock arrivals are known. bool clk_arrivals_valid_; - // Per-vertex set of clock-gating cells - std::vector clock_gates_; + // Per-vertex cache of whether the vertex is clock gated. + // Give each element its own distinct memory locations. + std::vector clk_gated_; // Some arrivals exist. bool arrivals_exist_; // Arrivals at end points exist (but may be invalid). diff --git a/search/Search.cc b/search/Search.cc index 2da0cdcc..089fc3a2 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -815,7 +815,7 @@ Search::arrivalsInvalid() requireds_exist_ = false; requireds_seeded_ = false; clk_arrivals_valid_ = false; - clock_gates_.clear(); + clk_gated_.clear(); arrival_iter_->clear(); required_iter_->clear(); // No need to keep track of incremental updates any more. @@ -943,7 +943,7 @@ Search::findClkArrivals() Stats stats(debug_, report_); debugPrint(debug_, "search", 1, "find clk arrivals"); arrival_iter_->clear(); - clock_gates_.assign(graph_->vertexCount() + 1, InstanceSet(network_)); + clk_gated_.assign(graph_->vertexCount() + 1, 0); seedClkVertexArrivals(); ClkArrivalSearchPred search_clk(this); arrival_visitor_->init(false, &search_clk); @@ -955,13 +955,11 @@ Search::findClkArrivals() clk_arrivals_valid_ = true; } -const InstanceSet * -Search::clkGatesAt(const Vertex *vertex) const +bool +Search::isClkGated(const Vertex *vertex) const { VertexId idx = graph_->id(vertex); - if (idx >= clock_gates_.size()) - return nullptr; - return &clock_gates_[idx]; + return idx < clk_gated_.size() && clk_gated_[idx] != 0; } bool @@ -972,48 +970,39 @@ Search::isClkGateInstance(Vertex *vertex) return cell != nullptr && cell->isClockGate(); } -static void -intersect(InstanceSet &clk_gates, const InstanceSet &from_clk_gates) -{ - // In-place intersection of clk_gates and from_clk_gates that results in clk_gates. - for (auto it = clk_gates.begin(); it != clk_gates.end(); ) - it = from_clk_gates.count(*it) ? std::next(it) : clk_gates.erase(it); -} - void Search::updateClkGates(Vertex *vertex) { // Clear existing clock gates for recomputation. VertexId vid = graph_->id(vertex); - if (vid >= clock_gates_.size()) + if (vid >= clk_gated_.size()) return; - InstanceSet &clk_gates = clock_gates_[vid]; - clk_gates.clear(); - // Loop through all clock-tagged predecessors. - bool first = true; - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Vertex *from = edge_iter.next()->from(graph_); - if (from == nullptr || !isClock(from)) - continue; - - // Get all clock gates from the predecessor. - const InstanceSet &from_clk_gates = clock_gates_[graph_->id(from)]; - if (first) { // covers the first predecessor - clk_gates = from_clk_gates; - first = false; - } else { - // Intersect current clock gates with predecessor clock gates. - intersect(clk_gates, from_clk_gates); - if (clk_gates.empty()) // no common gates, no need to continue + // Return if the cell is a clock gate based on liberty cell attributes. + bool gated = isClkGateInstance(vertex); + + // If the cell is not a clock gate, check if any of the predecessors are clock gates. + if (!gated) { + bool any_pred = false; + bool all_gated = true; + + // All paths through the vertex must pass through at least one clock gate. + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + + // Loop through all clock-tagged predecessors. + Vertex *from = edge_iter.next()->from(graph_); + if (from == nullptr || !isClock(from)) + continue; + any_pred = true; + if (!clk_gated_[graph_->id(from)]) { + all_gated = false; break; + } } + gated = any_pred && all_gated; } - - // If the vertex itself is a clock gate, add it to the set. - if (isClkGateInstance(vertex)) - clk_gates.insert(network_->instance(vertex->pin())); + clk_gated_[vid] = gated ? 1 : 0; } void @@ -1211,8 +1200,8 @@ Search::findArrivalsSeed() arrival_iter_->ensureSize(); required_iter_->ensureSize(); } - if (clock_gates_.size() < graph_->vertexCount() + 1) - clock_gates_.assign(graph_->vertexCount() + 1, InstanceSet(network_)); + if (clk_gated_.size() < graph_->vertexCount() + 1) + clk_gated_.assign(graph_->vertexCount() + 1, 0); seedInvalidArrivals(); } diff --git a/search/Sta.cc b/search/Sta.cc index bf539eb3..a28b3793 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5375,9 +5375,7 @@ Sta::isClkGatedRegister(const Instance *inst) if (vertex == nullptr) continue; - // If the vertex has a clock gate, the register is gated - const InstanceSet *gates = search_->clkGatesAt(vertex); - if (gates != nullptr && !gates->empty()) + if (search_->isClkGated(vertex)) return true; } return false; From 005432b3e36c6a876e139dd46295844943342104 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Mon, 27 Apr 2026 00:05:30 -0700 Subject: [PATCH 04/11] greptile --- include/sta/Search.hh | 1 - search/Sta.cc | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 3b5bdfa1..0261c8b4 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -613,7 +613,6 @@ protected: // Clock arrivals are known. bool clk_arrivals_valid_; // Per-vertex cache of whether the vertex is clock gated. - // Give each element its own distinct memory locations. std::vector clk_gated_; // Some arrivals exist. bool arrivals_exist_; diff --git a/search/Sta.cc b/search/Sta.cc index a28b3793..a32db3d0 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5341,6 +5341,7 @@ pinInstances(PinSet &pins, InstanceSeq Sta::clockGatedRegisters() { + ensureClkArrivals(); InstanceSeq result; // Find all leaf registers @@ -5363,6 +5364,7 @@ Sta::clockGatedRegisters() bool Sta::isClkGatedRegister(const Instance *inst) { + ensureClkArrivals(); std::unique_ptr pins(network_->pinIterator(inst)); while (pins->hasNext()) { From c8936cc9473259e50155a0586ba70479104e7d90 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Mon, 27 Apr 2026 00:07:04 -0700 Subject: [PATCH 05/11] jk remove --- search/Sta.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index a32db3d0..a28b3793 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5341,7 +5341,6 @@ pinInstances(PinSet &pins, InstanceSeq Sta::clockGatedRegisters() { - ensureClkArrivals(); InstanceSeq result; // Find all leaf registers @@ -5364,7 +5363,6 @@ Sta::clockGatedRegisters() bool Sta::isClkGatedRegister(const Instance *inst) { - ensureClkArrivals(); std::unique_ptr pins(network_->pinIterator(inst)); while (pins->hasNext()) { From ce7580a35de27eae6b3608e896297e9a3afc5637 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:15:41 -0700 Subject: [PATCH 06/11] update --- search/Search.cc | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/search/Search.cc b/search/Search.cc index 089fc3a2..85371df2 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -973,9 +973,8 @@ Search::isClkGateInstance(Vertex *vertex) void Search::updateClkGates(Vertex *vertex) { - // Clear existing clock gates for recomputation. - VertexId vid = graph_->id(vertex); - if (vid >= clk_gated_.size()) + VertexId id = graph_->id(vertex); + if (id >= clk_gated_.size()) return; // Return if the cell is a clock gate based on liberty cell attributes. @@ -983,8 +982,6 @@ Search::updateClkGates(Vertex *vertex) // If the cell is not a clock gate, check if any of the predecessors are clock gates. if (!gated) { - bool any_pred = false; - bool all_gated = true; // All paths through the vertex must pass through at least one clock gate. VertexInEdgeIterator edge_iter(vertex, graph_); @@ -992,17 +989,21 @@ Search::updateClkGates(Vertex *vertex) // Loop through all clock-tagged predecessors. Vertex *from = edge_iter.next()->from(graph_); - if (from == nullptr || !isClock(from)) + if (from == nullptr || !isClock(from)) { + debugPrint(debug_, "search", 1, "from edge %s is not a clock", + network_->pathName(from->pin())); continue; - any_pred = true; - if (!clk_gated_[graph_->id(from)]) { - all_gated = false; + } + + // If one predecessor is gated, the vertex is gated. + if (clk_gated_[graph_->id(from)]) { + gated = true; break; } } - gated = any_pred && all_gated; } - clk_gated_[vid] = gated ? 1 : 0; + // Update the node gated state + clk_gated_[id] = gated; } void From 3cdc9b933d0e052ceab491a2a83b895224a45995 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:25:01 -0700 Subject: [PATCH 07/11] add debugging code --- search/Search.cc | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/search/Search.cc b/search/Search.cc index 85371df2..f021c5af 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -966,8 +966,15 @@ bool Search::isClkGateInstance(Vertex *vertex) { // Return if the cell is a clock gate based on liberty cell attributes. - LibertyCell *cell = network_->libertyCell(network_->instance(vertex->pin())); - return cell != nullptr && cell->isClockGate(); + Pin *pin = vertex->pin(); + if (pin != nullptr) { + Instance *inst = network_->instance(pin); + if (inst != nullptr) { + LibertyCell *cell = network_->libertyCell(inst); + return cell != nullptr && cell->isClockGate(); + } + } + return false; } void @@ -977,8 +984,18 @@ Search::updateClkGates(Vertex *vertex) if (id >= clk_gated_.size()) return; + Instance *inst = network_->instance(vertex->pin()); + if (inst != nullptr) { + debugPrint(debug_, "clkgates", 1, "updating clk gates for %s (cell %s)", + network_->pathName(vertex->pin()), + network_->cellName(inst)); + } + // Return if the cell is a clock gate based on liberty cell attributes. bool gated = isClkGateInstance(vertex); + if (gated) + debugPrint(debug_, "clkgates", 1, " pin %s is a clock gate", + network_->pathName(vertex->pin())); // If the cell is not a clock gate, check if any of the predecessors are clock gates. if (!gated) { @@ -989,19 +1006,35 @@ Search::updateClkGates(Vertex *vertex) // Loop through all clock-tagged predecessors. Vertex *from = edge_iter.next()->from(graph_); - if (from == nullptr || !isClock(from)) { - debugPrint(debug_, "search", 1, "from edge %s is not a clock", - network_->pathName(from->pin())); + if (from == nullptr) { + debugPrint(debug_, "clkgates", 1, " from edge is undefined"); + continue; + } + + // Debug print the predecessor cell name. + Instance *from_inst = network_->instance(from->pin()); + std::string from_cell_name = from_inst != nullptr ? network_->cellName(from_inst) : "unknown"; + debugPrint(debug_, "clkgates", 1, " checking edge %s (cell %s)", + network_->pathName(from->pin()), from_cell_name.c_str()); + + if (!isClock(from)) { + debugPrint(debug_, "clkgates", 1, " from edge %s is not a clock (cell %s)", + network_->pathName(from->pin()), from_cell_name.c_str()); continue; } // If one predecessor is gated, the vertex is gated. if (clk_gated_[graph_->id(from)]) { + debugPrint(debug_, "clkgates", 1, " from edge %s is gated", + network_->pathName(from->pin())); gated = true; break; } } } + debugPrint(debug_, "clkgates", 1, + " final verdict: %s", gated ? "gated" : "not gated"); + // Update the node gated state clk_gated_[id] = gated; } From f855257f63a24e79782317c34c7837f2b8fc9295 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Mon, 27 Apr 2026 20:34:09 -0700 Subject: [PATCH 08/11] make icg identification much stricter --- include/sta/Liberty.hh | 6 ++++++ liberty/Liberty.cc | 8 +++++++- liberty/LibertyReader.cc | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 905d7cc9..dd8665ac 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -462,6 +462,9 @@ public: bool isClockGateOther() const; bool isClockGate() const; void setClockGateType(ClockGateType type); + void setHasClkGateClkPin() { has_clk_gate_clk_pin_ = true; } + void incrementClkPinCount() { clk_pin_count_++; } + void setHasClkGateEnablePin() { has_clk_gate_enable_pin_ = true; } const char *getDesignType() const; const TimingArcSetSeq &timingArcSets() const { return timing_arc_sets_; } // from or to may be nullptr to wildcard. @@ -626,6 +629,9 @@ protected: SwitchCellType switch_cell_type_; bool interface_timing_; ClockGateType clock_gate_type_; + bool has_clk_gate_clk_pin_; + uint8_t clk_pin_count_; + bool has_clk_gate_enable_pin_; TimingArcSetSeq timing_arc_sets_; TimingArcSetMap timing_arc_set_map_; LibertyPortPairTimingArcMap port_timing_arc_set_map_; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index edcd767d..3a6add11 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -940,6 +940,9 @@ LibertyCell::LibertyCell(LibertyLibrary *library, switch_cell_type_(SwitchCellType::fine_grain), interface_timing_(false), clock_gate_type_(ClockGateType::none), + has_clk_gate_clk_pin_(false), + clk_pin_count_(0), + has_clk_gate_enable_pin_(false), has_infered_reg_timing_arcs_(false), statetable_(nullptr), scale_factors_(nullptr), @@ -1167,7 +1170,10 @@ LibertyCell::isClockGateOther() const bool LibertyCell::isClockGate() const { - return clock_gate_type_ != ClockGateType::none; + return clock_gate_type_ != ClockGateType::none + && has_clk_gate_clk_pin_ + && clk_pin_count_ == 1 + && has_clk_gate_enable_pin_; } void diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index b664e410..9966c21c 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -3741,6 +3741,8 @@ LibertyReader::visitClock(LibertyAttr *attr) if (exists) { for (LibertyPort *port : *ports_) port->setIsClock(is_clk); + if (is_clk && cell_) + cell_->incrementClkPinCount(); } } } @@ -4049,12 +4051,14 @@ void LibertyReader::visitClockGateClockPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateClock); + if (cell_) cell_->setHasClkGateClkPin(); } void LibertyReader::visitClockGateEnablePin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateEnable); + if (cell_) cell_->setHasClkGateEnablePin(); } void From 3e5cf7d3123e1e7e34894b1728c21bc41f7c87d5 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Tue, 28 Apr 2026 09:54:13 -0700 Subject: [PATCH 09/11] address greptile --- include/sta/Liberty.hh | 2 -- liberty/Liberty.cc | 2 -- liberty/LibertyReader.cc | 16 ++++++++++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index dd8665ac..e90f121d 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -463,7 +463,6 @@ public: bool isClockGate() const; void setClockGateType(ClockGateType type); void setHasClkGateClkPin() { has_clk_gate_clk_pin_ = true; } - void incrementClkPinCount() { clk_pin_count_++; } void setHasClkGateEnablePin() { has_clk_gate_enable_pin_ = true; } const char *getDesignType() const; const TimingArcSetSeq &timingArcSets() const { return timing_arc_sets_; } @@ -630,7 +629,6 @@ protected: bool interface_timing_; ClockGateType clock_gate_type_; bool has_clk_gate_clk_pin_; - uint8_t clk_pin_count_; bool has_clk_gate_enable_pin_; TimingArcSetSeq timing_arc_sets_; TimingArcSetMap timing_arc_set_map_; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 3a6add11..4be7cf8b 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -941,7 +941,6 @@ LibertyCell::LibertyCell(LibertyLibrary *library, interface_timing_(false), clock_gate_type_(ClockGateType::none), has_clk_gate_clk_pin_(false), - clk_pin_count_(0), has_clk_gate_enable_pin_(false), has_infered_reg_timing_arcs_(false), statetable_(nullptr), @@ -1172,7 +1171,6 @@ LibertyCell::isClockGate() const { return clock_gate_type_ != ClockGateType::none && has_clk_gate_clk_pin_ - && clk_pin_count_ == 1 && has_clk_gate_enable_pin_; } diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 9966c21c..c372ccaf 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -3741,8 +3741,6 @@ LibertyReader::visitClock(LibertyAttr *attr) if (exists) { for (LibertyPort *port : *ports_) port->setIsClock(is_clk); - if (is_clk && cell_) - cell_->incrementClkPinCount(); } } } @@ -4051,14 +4049,24 @@ void LibertyReader::visitClockGateClockPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateClock); - if (cell_) cell_->setHasClkGateClkPin(); + if (cell_) { + bool value, exists; + getAttrBool(attr, value, exists); + if (exists && value) + cell_->setHasClkGateClkPin(); + } } void LibertyReader::visitClockGateEnablePin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateEnable); - if (cell_) cell_->setHasClkGateEnablePin(); + if (cell_) { + bool value, exists; + getAttrBool(attr, value, exists); + if (exists && value) + cell_->setHasClkGateEnablePin(); + } } void From 502b58f5def78a73aa583c09201249a7b64e8a1d Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:08:13 -0700 Subject: [PATCH 10/11] check if the port is tied to a constant --- search/Search.cc | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/search/Search.cc b/search/Search.cc index f021c5af..03ca9b3f 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -967,13 +967,40 @@ Search::isClkGateInstance(Vertex *vertex) { // Return if the cell is a clock gate based on liberty cell attributes. Pin *pin = vertex->pin(); - if (pin != nullptr) { - Instance *inst = network_->instance(pin); - if (inst != nullptr) { - LibertyCell *cell = network_->libertyCell(inst); - return cell != nullptr && cell->isClockGate(); + if (pin == nullptr) + return false; + Instance *inst = network_->instance(pin); + if (inst == nullptr) + return false; + LibertyCell *cell = network_->libertyCell(inst); + if (cell == nullptr || !cell->isClockGate()) + return false; + + // Locate functional enable pin on the clock gate. + const Pin *enable_pin = nullptr; + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + const Pin *inst_pin = pin_iter->next(); + const LibertyPort *port = network_->libertyPort(inst_pin); + if (port != nullptr && port->isClockGateEnable()) { + enable_pin = inst_pin; + break; } } + delete pin_iter; + if (enable_pin == nullptr) + return false; + + // Check if the enable pin is tied to a constant to invalidate the gate. + sim_->ensureConstantsPropagated(); + LogicValue value = sim_->logicValue(enable_pin); + if (value != LogicValue::zero && value != LogicValue::one) + return true; + + // Debug message + debugPrint(debug_, "clkgates", 1, + " enable pin %s tied to constant; not considered gated", + network_->pathName(enable_pin)); return false; } @@ -1000,7 +1027,7 @@ Search::updateClkGates(Vertex *vertex) // If the cell is not a clock gate, check if any of the predecessors are clock gates. if (!gated) { - // All paths through the vertex must pass through at least one clock gate. + // At least one path through the vertex must be considered gated. VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { From 81a3b0a6e149faa527cc938870aa93c2e01beee4 Mon Sep 17 00:00:00 2001 From: Stan Lee <135666755+stanminlee@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:20:54 -0700 Subject: [PATCH 11/11] checker --- search/Search.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/search/Search.cc b/search/Search.cc index 03ca9b3f..35744580 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1039,10 +1039,14 @@ Search::updateClkGates(Vertex *vertex) } // Debug print the predecessor cell name. - Instance *from_inst = network_->instance(from->pin()); + Pin *from_pin = from->pin(); + if (from_pin == nullptr) { + continue; + } + Instance *from_inst = network_->instance(from_pin); std::string from_cell_name = from_inst != nullptr ? network_->cellName(from_inst) : "unknown"; debugPrint(debug_, "clkgates", 1, " checking edge %s (cell %s)", - network_->pathName(from->pin()), from_cell_name.c_str()); + network_->pathName(from_pin), from_cell_name.c_str()); if (!isClock(from)) { debugPrint(debug_, "clkgates", 1, " from edge %s is not a clock (cell %s)",