From 606c666180f6d0c5ee2cc246024d1cd47dff6019 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 3 Jul 2025 17:08:44 -0700 Subject: [PATCH] set_min/max_delay -from reg/D startpoint warning resolves #265 Signed-off-by: James Cherry --- include/sta/Liberty.hh | 7 ++++++- include/sta/Sdc.hh | 10 +++++---- include/sta/Sta.hh | 1 - liberty/Liberty.cc | 27 +++++++++++++++++-------- network/Network.cc | 2 +- sdc/Sdc.cc | 28 +++++++++++++++++-------- sdc/Sdc.tcl | 1 + search/Search.cc | 46 +++++++++++++++++++----------------------- search/Sta.cc | 20 +----------------- 9 files changed, 75 insertions(+), 67 deletions(-) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 4794c30e..f642f0f3 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -489,7 +489,6 @@ public: // timing arcs. bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } TestCell *testCell() const { return test_cell_; } - bool isLatchData(LibertyPort *port); void latchEnable(const TimingArcSet *arc_set, // Return values. const LibertyPort *&enable_port, @@ -803,6 +802,10 @@ public: // Has register/latch rise/fall edges from pin. bool isRegClk() const { return is_reg_clk_; } void setIsRegClk(bool is_clk); + bool isRegOutput() const { return is_reg_output_; } + void setIsRegOutput(bool is_reg_out); + bool isLatchData() const { return is_latch_data_; } + void setIsLatchData(bool is_latch_data); // Is the clock for timing checks. bool isCheckClk() const { return is_check_clk_; } void setIsCheckClk(bool is_clk); @@ -899,6 +902,8 @@ protected: bool min_period_exists_:1; bool is_clk_:1; bool is_reg_clk_:1; + bool is_reg_output_:1; + bool is_latch_data_: 1; bool is_check_clk_:1; bool is_clk_gate_clk_:1; bool is_clk_gate_enable_:1; diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index 86eb6cd1..c8aabae9 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -771,6 +771,7 @@ public: ClockSet *from_clks, InstanceSet *from_insts, const RiseFallBoth *from_rf); + bool isExceptionStartpoint(const Pin *pin) const; // Make an exception -through specification. ExceptionThru *makeExceptionThru(PinSet *pins, NetSet *nets, @@ -972,10 +973,11 @@ public: ExceptionStateSet *&states) const; // Return hierarchical -thru exceptions that start between // from_pin and to_pin. - ExceptionStateSet *exceptionThruStates(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max) const; + void exceptionThruStates(const Pin *from_pin, + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + ExceptionStateSet *&states) const; // Find the highest priority exception with first exception pt at // pin/clk end. void exceptionTo(ExceptionPathType type, diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index f4cc39b6..9707a1c4 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -531,7 +531,6 @@ public: void checkExceptionFromPins(ExceptionFrom *from, const char *file, int line) const; - bool exceptionFromInvalid(const Pin *pin) const; void deleteExceptionFrom(ExceptionFrom *from); // Make an exception -through specification. ExceptionThru *makeExceptionThru(PinSet *pins, diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 364e5dae..9eeb9536 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1244,10 +1244,13 @@ LibertyCell::addTimingArcSet(TimingArcSet *arc_set) timing_arc_sets_.push_back(arc_set); LibertyPort *from = arc_set->from(); + LibertyPort *to = arc_set->to(); const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) + || role == TimingRole::latchEnToQ()) { from->setIsRegClk(true); + to->setIsRegOutput(true); + } if (role->isTimingCheck()) from->setIsCheckClk(true); return set_index; @@ -1881,7 +1884,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d, latch_enables_.push_back(latch_enable); latch_d_to_q_map_[d_to_q] = latch_enable; latch_check_map_[setup_check] = latch_enable; - latch_data_ports_.insert(d); + d->setIsLatchData(true); debugPrint(debug, "liberty_latch", 1, "latch %s -> %s | %s %s -> %s | %s %s -> %s setup", d->name(), @@ -1933,12 +1936,6 @@ LibertyCell::inferLatchRoles(Report *report, } } -bool -LibertyCell::isLatchData(LibertyPort *port) -{ - return latch_data_ports_.hasKey(port); -} - void LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, // Return values. @@ -2100,6 +2097,8 @@ LibertyPort::LibertyPort(LibertyCell *cell, min_period_exists_(false), is_clk_(false), is_reg_clk_(false), + is_reg_output_(false), + is_latch_data_(false), is_check_clk_(false), is_clk_gate_clk_(false), is_clk_gate_enable_(false), @@ -2547,6 +2546,18 @@ LibertyPort::setIsRegClk(bool is_clk) is_reg_clk_ = is_clk; } +void +LibertyPort::setIsRegOutput(bool is_reg_out) +{ + is_reg_output_ = is_reg_out; +} + +void +LibertyPort::setIsLatchData(bool is_latch_data) +{ + is_latch_data_ = is_latch_data; +} + void LibertyPort::setIsCheckClk(bool is_clk) { diff --git a/network/Network.cc b/network/Network.cc index d80b21ef..578c0664 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -655,7 +655,7 @@ Network::isLatchData(const Pin *pin) const { LibertyPort *port = libertyPort(pin); if (port) - return port->libertyCell()->isLatchData(port); + return port->isLatchData(); else return false; } diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index a63c2348..11078100 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -3914,8 +3914,7 @@ Sdc::recordPathDelayInternalFrom(ExceptionPath *exception) if (from && from->hasPins()) { for (const Pin *pin : *from->pins()) { - if (!(network_->isRegClkPin(pin) - || network_->isTopLevelPort(pin))) { + if (!isExceptionStartpoint(pin)) { path_delay_internal_from_.insert(pin); if (exception->breakPath()) path_delay_internal_from_break_.insert(pin); @@ -3932,8 +3931,7 @@ Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception) && from->hasPins() && !path_delay_internal_from_.empty()) { for (const Pin *pin : *from->pins()) { - if (!(network_->isRegClkPin(pin) - || network_->isTopLevelPort(pin)) + if (!isExceptionStartpoint(pin) && !pathDelayFrom(pin)) { path_delay_internal_from_.erase(pin); if (exception->breakPath()) @@ -3943,6 +3941,21 @@ Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception) } } +bool +Sdc::isExceptionStartpoint(const Pin *pin) const +{ + Net *net = network_->net(pin); + const LibertyPort *port = network_->libertyPort(pin); + return ((network_->isTopLevelPort(pin) + && network_->direction(pin)->isAnyInput()) + || (port && port->isRegClk()) + || (port && port->isLatchData())) + // Pins connected to power/ground are invalid. + && !(net + && (network_->isPower(net) + || network_->isGround(net))); +} + bool Sdc::pathDelayFrom(const Pin *pin) { @@ -5304,13 +5317,13 @@ Sdc::filterRegQStates(const Pin *to_pin, } } -ExceptionStateSet * +void Sdc::exceptionThruStates(const Pin *from_pin, const Pin *to_pin, const RiseFall *to_rf, - const MinMax *min_max) const + const MinMax *min_max, + ExceptionStateSet *&states) const { - ExceptionStateSet *states = nullptr; exceptionThruStates(first_thru_pin_exceptions_.findKey(to_pin), to_rf, min_max, states); if (!first_thru_edge_exceptions_.empty()) { @@ -5325,7 +5338,6 @@ Sdc::exceptionThruStates(const Pin *from_pin, exceptionThruStates(first_thru_inst_exceptions_.findKey(to_inst), to_rf, min_max, states); } - return states; } void diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index 9a4f0f73..b5616f38 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -2355,6 +2355,7 @@ proc set_path_delay { cmd args min_max } { set from [parse_from_arg keys arg_error] set thrus [parse_thrus_arg args arg_error] set to [parse_to_arg keys flags arg_error] + check_exception_pins $from $to if { $arg_error } { delete_from_thrus_to $from $thrus $to } else { diff --git a/search/Search.cc b/search/Search.cc index 290643f1..5c0d22cd 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -980,11 +980,9 @@ Search::visitStartpoints(VertexVisitor *visitor) visitor->visit(vertex); const PinSet &startpoints = sdc_->pathDelayInternalFrom(); - if (!startpoints.empty()) { - for (const Pin *pin : startpoints) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - visitor->visit(vertex); - } + for (const Pin *pin : startpoints) { + Vertex *vertex = graph_->pinDrvrVertex(pin); + visitor->visit(vertex); } } @@ -2176,8 +2174,7 @@ PathVisitor::visitFromPath(const Pin *from_pin, to_tag = search_->thruClkTag(from_path, from_vertex, from_tag, true, edge, to_rf, arc_delay_min_max_eq, min_max, path_ap); - if (to_tag) - to_arrival = from_arrival + arc_delay; + to_arrival = from_arrival + arc_delay; } } } @@ -2272,11 +2269,10 @@ PathVisitor::visitFromPath(const Pin *from_pin, else { if (!(sdc_->isPathDelayInternalFromBreak(to_pin) || sdc_->isPathDelayInternalToBreak(from_pin))) { + to_tag = search_->thruTag(from_tag, edge, to_rf, min_max, path_ap); arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, path_ap); - if (!delayInf(arc_delay)) { + if (!delayInf(arc_delay)) to_arrival = from_arrival + arc_delay; - to_tag = search_->thruTag(from_tag, edge, to_rf, min_max, path_ap); - } } } if (to_tag) @@ -2643,7 +2639,7 @@ Search::mutateTag(Tag *from_tag, // Kill path delay tags past the -to pin. if ((exception->isPathDelay() - && sdc_->isCompleteTo(state, from_pin, from_rf, min_max)) + && sdc_->isCompleteTo(state, to_pin, to_rf, min_max)) // Kill loop tags at register clock pins. || (exception->isLoop() && to_is_reg_clk)) { @@ -2653,7 +2649,7 @@ Search::mutateTag(Tag *from_tag, } // Get the set of -thru exceptions starting at to_pin/edge. - new_states = sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max); + sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); if (new_states || state_change) { // Second pass to apply state changes and add updated existing // states to new states. @@ -2666,18 +2662,18 @@ Search::mutateTag(Tag *from_tag, // Found a -thru that we've been waiting for. state = state->nextState(); - // Don't propagate a completed false path -thru unless it is a - // clock. Clocks carry the completed false path to disable - // downstream paths that use the clock as data. - if ((state->isComplete() - && exception->isFalse() - && !from_is_clk) - // to_pin/edge completes a loop path. - || (exception->isLoop() - && state->isComplete())) { - delete new_states; - return nullptr; - } + // Don't propagate a completed false path -thru unless it is a + // clock. Clocks carry the completed false path to disable + // downstream paths that use the clock as data. + if ((state->isComplete() + && exception->isFalse() + && !from_is_clk) + // to_pin/edge completes a loop path. + || (exception->isLoop() + && state->isComplete())) { + delete new_states; + return nullptr; + } // Kill path delay tags past the -to pin. if (!((exception->isPathDelay() @@ -2691,7 +2687,7 @@ Search::mutateTag(Tag *from_tag, } else // Get the set of -thru exceptions starting at to_pin/edge. - new_states = sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max); + sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); if (new_states) return findTag(to_rf, path_ap, to_clk_info, to_is_clk, diff --git a/search/Sta.cc b/search/Sta.cc index 5ae35760..f885f134 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2027,7 +2027,7 @@ Sta::checkExceptionFromPins(ExceptionFrom *from, PinSet::ConstIterator pin_iter(from->pins()); while (pin_iter.hasNext()) { const Pin *pin = pin_iter.next(); - if (exceptionFromInvalid(pin)) { + if (!sdc_->isExceptionStartpoint(pin)) { if (line) report_->fileWarn(1554, file, line, "'%s' is not a valid start point.", cmd_network_->pathName(pin)); @@ -2039,24 +2039,6 @@ Sta::checkExceptionFromPins(ExceptionFrom *from, } } -bool -Sta::exceptionFromInvalid(const Pin *pin) const -{ - Net *net = network_->net(pin); - // Floating pins are invalid. - return (net == nullptr - && !network_->isTopLevelPort(pin)) - || (net - // Pins connected to power/ground are invalid. - && (network_->isPower(net) - || network_->isGround(net))) - || !((network_->isTopLevelPort(pin) - && network_->direction(pin)->isAnyInput()) - || network_->isRegClkPin(pin) - || network_->isLatchData(pin) - || network_->direction(pin)->isInternal()); -} - void Sta::deleteExceptionFrom(ExceptionFrom *from) {