diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 905d7cc94..e90f121d4 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -462,6 +462,8 @@ public: bool isClockGateOther() const; bool isClockGate() const; void setClockGateType(ClockGateType type); + void setHasClkGateClkPin() { has_clk_gate_clk_pin_ = true; } + 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 +628,8 @@ protected: SwitchCellType switch_cell_type_; bool interface_timing_; ClockGateType clock_gate_type_; + bool has_clk_gate_clk_pin_; + bool has_clk_gate_enable_pin_; TimingArcSetSeq timing_arc_sets_; TimingArcSetMap timing_arc_set_map_; LibertyPortPairTimingArcMap port_timing_arc_set_map_; diff --git a/include/sta/Search.hh b/include/sta/Search.hh index b23ef5c4a..0261c8b48 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(); + bool isClkGated(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 cache of whether the vertex is clock gated. + std::vector clk_gated_; // 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 4adc0ddc6..eff131a4b 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -607,8 +607,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/liberty/Liberty.cc b/liberty/Liberty.cc index edcd767d0..4be7cf8bb 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -940,6 +940,8 @@ LibertyCell::LibertyCell(LibertyLibrary *library, switch_cell_type_(SwitchCellType::fine_grain), interface_timing_(false), clock_gate_type_(ClockGateType::none), + has_clk_gate_clk_pin_(false), + has_clk_gate_enable_pin_(false), has_infered_reg_timing_arcs_(false), statetable_(nullptr), scale_factors_(nullptr), @@ -1167,7 +1169,9 @@ LibertyCell::isClockGateOther() const bool LibertyCell::isClockGate() const { - return clock_gate_type_ != ClockGateType::none; + return clock_gate_type_ != ClockGateType::none + && has_clk_gate_clk_pin_ + && has_clk_gate_enable_pin_; } void diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index b664e4109..c372ccafc 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -4049,12 +4049,24 @@ void LibertyReader::visitClockGateClockPin(LibertyAttr *attr) { visitPortBoolAttr(attr, &LibertyPort::setIsClockGateClock); + 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_) { + bool value, exists; + getAttrBool(attr, value, exists); + if (exists && value) + cell_->setHasClkGateEnablePin(); + } } void diff --git a/search/Search.cc b/search/Search.cc index 8a5f69a30..357445806 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; + clk_gated_.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(); + clk_gated_.assign(graph_->vertexCount() + 1, 0); seedClkVertexArrivals(); ClkArrivalSearchPred search_clk(this); arrival_visitor_->init(false, &search_clk); @@ -953,6 +955,121 @@ Search::findClkArrivals() clk_arrivals_valid_ = true; } +bool +Search::isClkGated(const Vertex *vertex) const +{ + VertexId idx = graph_->id(vertex); + return idx < clk_gated_.size() && clk_gated_[idx] != 0; +} + +bool +Search::isClkGateInstance(Vertex *vertex) +{ + // Return if the cell is a clock gate based on liberty cell attributes. + Pin *pin = vertex->pin(); + 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; +} + +void +Search::updateClkGates(Vertex *vertex) +{ + VertexId id = graph_->id(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) { + + // At least one path through the vertex must be considered gated. + 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) { + debugPrint(debug_, "clkgates", 1, " from edge is undefined"); + continue; + } + + // Debug print the predecessor cell name. + 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()); + + 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; +} + void Search::seedClkVertexArrivals() { @@ -1148,6 +1265,8 @@ Search::findArrivalsSeed() arrival_iter_->ensureSize(); required_iter_->ensureSize(); } + if (clk_gated_.size() < graph_->vertexCount() + 1) + clk_gated_.assign(graph_->vertexCount() + 1, 0); seedInvalidArrivals(); } @@ -1279,6 +1398,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 56995b8be..d34ffecbd 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" @@ -5351,69 +5352,47 @@ pinInstances(PinSet &pins, return insts; } -InstanceSeq Sta::clockGatedRegisters() { - Network* network = this->network(); +InstanceSeq +Sta::clockGatedRegisters() +{ InstanceSeq result; - // Map of pins that are a derivative of icg instances - std::unordered_map pin_to_icg; - std::unique_ptr insts( - network->leafInstanceIterator()); - - // Iterate over all leaf instances + // Find all leaf registers + 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); - } + Instance *inst = insts->next(); + LibertyCell *cell = network_->libertyCell(inst); - // If no output pin, skip (shouldn't happen) - if (from.empty()) continue; + // Skip if the cell is not a register or a clock gate. + if (cell == nullptr || !cell->hasSequentials() || cell->isClockGate()) + 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); - } - } + // Check if the register is clock gated based on previous graph traversal. + if (isClkGatedRegister(inst)) + result.push_back(inst); } + return result; +} - // 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 - } +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 (search_->isClkGated(vertex)) + return true; } - return result; + return false; } bool