2323#include " rsz/Resizer.hh"
2424#include " sta/Delay.hh"
2525#include " sta/ExceptionPath.hh"
26+ #include " sta/FuncExpr.hh"
2627#include " sta/Fuzzy.hh"
2728#include " sta/Graph.hh"
2829#include " sta/GraphClass.hh"
3839#include " sta/Sdc.hh"
3940#include " sta/Search.hh"
4041#include " sta/SearchClass.hh"
42+ #include " sta/Sequential.hh"
4143#include " sta/Sta.hh"
4244#include " sta/StringUtil.hh"
4345#include " sta/TimingArc.hh"
4446#include " sta/Transition.hh"
47+ #include " sta/VisitPathEnds.hh"
4548#include " utl/Logger.h"
4649
4750namespace rsz {
@@ -832,6 +835,44 @@ void RepairTargetCollector::collectViolatingEndpoints()
832835 * 100 ));
833836}
834837
838+ namespace {
839+
840+ // Track the worst-slack constrained max path end at a vertex and record its
841+ // latch time borrow. Non-latch path ends report zero borrow.
842+ class WorstPathEndBorrowVisitor : public sta ::PathEndVisitor
843+ {
844+ public:
845+ WorstPathEndBorrowVisitor (const sta::MinMax* min_max,
846+ const sta::StaState* sta)
847+ : min_max_(min_max), sta_(sta)
848+ {
849+ }
850+ sta::PathEndVisitor* copy () const override
851+ {
852+ return new WorstPathEndBorrowVisitor (*this );
853+ }
854+ void visit (sta::PathEnd* path_end) override
855+ {
856+ if (path_end->isUnconstrained () || path_end->minMax (sta_) != min_max_) {
857+ return ;
858+ }
859+ const sta::Slack end_slack = path_end->slack (sta_);
860+ if (sta::delayLess (end_slack, worst_slack_, sta_)) {
861+ worst_slack_ = end_slack;
862+ borrow_ = path_end->borrow (sta_);
863+ }
864+ }
865+ sta::Arrival borrow () const { return borrow_; }
866+
867+ private:
868+ const sta::MinMax* min_max_;
869+ const sta::StaState* sta_;
870+ sta::Slack worst_slack_ = sta::INF ;
871+ sta::Arrival borrow_ = 0.0 ;
872+ };
873+
874+ } // namespace
875+
835876sta::Slack RepairTargetCollector::getEndpointEffectiveSlack (
836877 sta::Vertex* endpoint) const
837878{
@@ -840,45 +881,121 @@ sta::Slack RepairTargetCollector::getEndpointEffectiveSlack(
840881 return reported_slack;
841882 }
842883
843- sta::PinSet* to_pins = new sta::PinSet (network_);
844- to_pins->insert (endpoint->pin ());
845- sta::ExceptionTo* to = sdc_->makeExceptionTo (to_pins,
846- nullptr ,
847- nullptr ,
848- sta::RiseFallBoth::riseFall (),
849- sta::RiseFallBoth::riseFall ());
884+ // Visit the path ends stored at this endpoint and read the time borrow of
885+ // the worst-slack one. This avoids the heavyweight findPathEnds() machinery
886+ // (exception filter, path group construction, sorting) per endpoint.
887+ sta::VisitPathEnds visit_ends (sta_);
888+ WorstPathEndBorrowVisitor borrow_visitor (max_, sta_);
889+ visit_ends.visitPathEnds (endpoint, &borrow_visitor);
850890
851- sta::StringSeq group_names;
852- sta::PathEndSeq path_ends
853- = search_->findPathEnds (nullptr , // from
854- nullptr , // thrus
855- to, // to
856- false , // unconstrained
857- sta_->scenes (), // scene
858- sta::MinMaxAll::max (), // min_max
859- 1 , // group_path_count
860- 1 , // endpoint_path_count
861- false , // unique_pins
862- false , // unique_edges
863- -sta::INF , // slack_min
864- sta::INF , // slack_max
865- true , // sort_by_slack
866- group_names, // group_names
867- true ,
868- false ,
869- true ,
870- true ,
871- true ,
872- true ); // checks
873-
874- if (path_ends.empty ()) {
891+ const sta::Arrival borrow = borrow_visitor.borrow ();
892+ if (sta::fuzzyLessEqual (borrow, 0 .0f )) {
875893 return reported_slack;
876894 }
877895
878- // Treat consumed latch transparency as hidden setup debt for repair_timing.
879- // Only positive borrow adds debt; flop endpoints report zero borrow.
880- const sta::Arrival borrow = path_ends[0 ]->borrow (search_);
881- return reported_slack - std::max<float >(borrow, 0 .0f );
896+ const std::vector<const sta::Pin*> output_pins = latchOutputPins (endpoint);
897+ const sta::Slack output_slack = latchOutputWorstSlack (output_pins);
898+ const sta::Slack uncovered_borrow
899+ = std::max<float >(borrow - std::max<float >(output_slack, 0 .0f ), 0 .0f );
900+ return reported_slack - uncovered_borrow;
901+ }
902+
903+ std::vector<const sta::Pin*> RepairTargetCollector::latchOutputPins (
904+ sta::Vertex* endpoint) const
905+ {
906+ std::vector<const sta::Pin*> output_pins;
907+ const std::function<void (
908+ sta::Instance*, sta::LibertyCell*, sta::LibertyPort*)>
909+ add_output_pin = [this , &output_pins](sta::Instance* inst,
910+ sta::LibertyCell* cell,
911+ sta::LibertyPort* state_port) {
912+ const sta::Pin* output_pin = network_->findPin (inst, state_port);
913+ if (output_pin != nullptr ) {
914+ output_pins.push_back (output_pin);
915+ return ;
916+ }
917+
918+ // Some libraries declare latch state as an internal port and expose it
919+ // through an output pin function, for example Q = IQ.
920+ sta::LibertyCellPortIterator port_iter (cell);
921+ while (port_iter.hasNext ()) {
922+ sta::LibertyPort* port = port_iter.next ();
923+ sta::FuncExpr* func = port->function ();
924+ if (!port->direction ()->isAnyOutput () || func == nullptr
925+ || !func->hasPort (state_port)) {
926+ continue ;
927+ }
928+
929+ output_pin = network_->findPin (inst, port);
930+ if (output_pin != nullptr
931+ && std::find (output_pins.begin (), output_pins.end (), output_pin)
932+ == output_pins.end ()) {
933+ output_pins.push_back (output_pin);
934+ }
935+ }
936+ };
937+
938+ const sta::Pin* endpoint_pin = endpoint->pin ();
939+ sta::LibertyPort* data_port = network_->libertyPort (endpoint_pin);
940+ sta::LibertyCell* cell = data_port->libertyCell ();
941+ sta::Instance* inst = network_->instance (endpoint_pin);
942+
943+ for (const sta::Sequential& seq : cell->sequentials ()) {
944+ if (!seq.isLatch () || seq.data () == nullptr
945+ || !seq.data ()->hasPort (data_port)) {
946+ continue ;
947+ }
948+
949+ if (seq.output () != nullptr ) {
950+ add_output_pin (inst, cell, seq.output ());
951+ }
952+ if (seq.outputInv () != nullptr ) {
953+ add_output_pin (inst, cell, seq.outputInv ());
954+ }
955+ }
956+
957+ return output_pins;
958+ }
959+
960+ sta::Slack RepairTargetCollector::latchOutputWorstSlack (
961+ const std::vector<const sta::Pin*>& output_pins) const
962+ {
963+ sta::Slack worst_slack = sta::INF ;
964+
965+ for (const sta::Pin* output_pin : output_pins) {
966+ sta::PinSet* from_pins = new sta::PinSet (network_);
967+ from_pins->insert (output_pin);
968+ sta::ExceptionFrom* from = sdc_->makeExceptionFrom (
969+ from_pins, nullptr , nullptr , sta::RiseFallBoth::riseFall ());
970+
971+ sta::StringSeq group_names;
972+ sta::PathEndSeq path_ends = search_->findPathEnds (from,
973+ nullptr ,
974+ nullptr ,
975+ false ,
976+ sta_->scenes (),
977+ sta::MinMaxAll::max (),
978+ 1 ,
979+ 1 ,
980+ false ,
981+ false ,
982+ -sta::INF ,
983+ sta::INF ,
984+ true ,
985+ group_names,
986+ true ,
987+ false ,
988+ true ,
989+ true ,
990+ true ,
991+ true );
992+
993+ for (sta::PathEnd* path_end : path_ends) {
994+ worst_slack = std::min (worst_slack, path_end->slack (search_));
995+ }
996+ }
997+
998+ return worst_slack;
882999}
8831000
8841001void RepairTargetCollector::collectViolatingStartpoints ()
0 commit comments