From d343f097238a7f7c87b842eb7536ec3495e54da7 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 21 May 2020 07:47:34 -0700 Subject: [PATCH 01/70] format --- search/ReportPath.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index e0eb8811..06bc8831 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2167,10 +2167,10 @@ ReportPath::pathFromGenPropClk(const Path *clk_path, float insertion; bool exists; sdc_->clockInsertion(clk, clk_info->clkSrc(), - clk_edge->transition(), - clk_path->minMax(this), - early_late, - insertion, exists); + clk_edge->transition(), + clk_path->minMax(this), + early_late, + insertion, exists); return !exists && clk->isGeneratedWithPropagatedMaster(); } @@ -2187,8 +2187,8 @@ ReportPath::isGenPropClk(const Clock *clk, float insertion; bool exists; sdc_->clockInsertion(clk, clk->srcPin(), clk_rf, - min_max, early_late, - insertion, exists); + min_max, early_late, + insertion, exists); return !exists && clk->isGeneratedWithPropagatedMaster(); } From b49b957ad098eafdcfab54de7f33a03a173ff5ea Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 22 May 2020 16:35:57 -0700 Subject: [PATCH 02/70] liberty reader func parsing issue with missing pin --- liberty/LibertyReader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index f64950c7..914a7b34 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2026,7 +2026,7 @@ LibertyReader::parseCellFuncs() while (func_iter.hasNext()) { LibertyFunc *func = func_iter.next(); FuncExpr *expr = parseFunc(func->expr(), func->attrName(), func->line()); - if (func->invert()) { + if (func->invert() && expr) { if (expr->op() == FuncExpr::op_not) { FuncExpr *inv = expr; expr = expr->left(); From c2ba9fa5349979898fc88c1e4527a6ea28498751 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 25 May 2020 16:01:55 -0700 Subject: [PATCH 03/70] debug print --- search/Power.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/Power.cc b/search/Power.cc index 95eee7d1..ebe6ad5d 100644 --- a/search/Power.cc +++ b/search/Power.cc @@ -621,7 +621,7 @@ Power::findInputInternalPower(const Pin *pin, float port_internal = energy * duty * activity.activity(); debugPrint7(debug_, "power", 2, " %3s %6s %.2f %.2f %9.2e %9.2e %s\n", port->name(), - when->asString(), + when ? when->asString() : "", activity.activity() * 1e-9, duty, energy, From 6e6415f6e25deb9e15d3d7eb9e2776f4a15c68b3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 27 May 2020 20:24:01 -0700 Subject: [PATCH 04/70] RequiredVisitor::visitFromToPath to_tag_group null check --- search/Search.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/Search.cc b/search/Search.cc index 9c468452..6bd96bac 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -3445,7 +3445,7 @@ RequiredVisitor::visitFromToPath(const Pin *, const MinMax *req_min = min_max->opposite(); TagGroup *to_tag_group = sta_->search()->tagGroup(to_vertex); // Check to see if to_tag was pruned. - if (to_tag_group->hasTag(to_tag)) { + if (to_tag_group && to_tag_group->hasTag(to_tag)) { PathVertex to_path(to_vertex, to_tag, sta_); Required to_required = to_path.required(sta_); Required from_required = to_required - arc_delay; From 31b05c5d830eca5c10ab347fc1d51eba78df7e9f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 27 May 2020 20:41:27 -0700 Subject: [PATCH 05/70] set_max_delay -ignore_clock_latency -from clk1 -to clk2 from input --- search/Search.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/search/Search.cc b/search/Search.cc index 6bd96bac..e585456b 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -2262,8 +2262,13 @@ Search::pathClkPathArrival(const Path *path) const pathClkPathArrival1(path, src_clk_path); if (!src_clk_path.isNull()) return clkPathArrival(&src_clk_path); - else - return 0.0; + else { + // Check for input arrival clock. + ClockEdge *clk_edge = path->clkEdge(this); + if (clk_edge) + return clk_edge->time(); + } + return 0.0; } // PathExpanded::expand() and PathExpanded::clkPath(). From 0ede43b9097d966eb1fdd7c5dfc21dd7c4001175 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 29 May 2020 16:13:51 -0700 Subject: [PATCH 06/70] Sta::networkChanged don't mess with sdc graph annotations --- search/Sta.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index f0851495..2baa1312 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -537,8 +537,6 @@ Sta::clear() void Sta::networkChanged() { - // Remove sdc graph annotations. - sdc_->annotateGraph(false); // Everything else from clear(). search_->clear(); levelize_->clear(); From ee0e23c5733f1a1a2999a652b0a6f72d5ad024f9 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 30 May 2020 18:09:14 -0700 Subject: [PATCH 07/70] report_path -report_sigmas show incr --- graph/DelayFloat.cc | 87 ++++++++++++++++++++----------------- graph/DelayNormal1.cc | 70 ++++++++++++++++------------- graph/DelayNormal2.cc | 9 ++++ include/sta/DelayFloat.hh | 4 ++ include/sta/DelayNormal1.hh | 29 +++++++------ include/sta/DelayNormal2.hh | 29 +++++++------ search/ReportPath.cc | 26 +++++++---- search/ReportPath.hh | 3 ++ 8 files changed, 151 insertions(+), 106 deletions(-) diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc index 9046da8c..2b06819a 100644 --- a/graph/DelayFloat.cc +++ b/graph/DelayFloat.cc @@ -48,6 +48,46 @@ delayIsInitValue(const Delay &delay, return fuzzyEqual(delay, min_max->initValue()); } +const char * +delayAsString(const Delay &delay, + const StaState *sta) +{ + return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); +} + +const char * +delayAsString(const Delay &delay, + const StaState *sta, + int digits) +{ + return sta->units()->timeUnit()->asString(delay, digits); +} + +const char * +delayAsString(const Delay &delay, + const EarlyLate *, + const StaState *sta, + int digits) +{ + const Unit *unit = sta->units()->timeUnit(); + return unit->asString(delay, digits); +} + +float +delayAsFloat(const Delay &delay, + const EarlyLate *, + const StaState *) +{ + return delay; +} + +float +delaySigma2(const Delay &, + const EarlyLate *) +{ + return 0.0; +} + bool fuzzyGreater(const Delay &delay1, const Delay &delay2, @@ -92,6 +132,13 @@ fuzzyLessEqual(const Delay &delay1, return fuzzyGreaterEqual(delay1, delay2); } +Delay +delayRemove(const Delay &delay1, + const Delay &delay2) +{ + return delay1 - delay2; +} + float delayRatio(const Delay &delay1, const Delay &delay2) @@ -99,46 +146,6 @@ delayRatio(const Delay &delay1, return delay1 / delay2; } -const char * -delayAsString(const Delay &delay, - const StaState *sta) -{ - return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); -} - -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits) -{ - return sta->units()->timeUnit()->asString(delay, digits); -} - -const char * -delayAsString(const Delay &delay, - const EarlyLate *, - const StaState *sta, - int digits) -{ - const Unit *unit = sta->units()->timeUnit(); - return unit->asString(delay, digits); -} - -float -delayAsFloat(const Delay &delay, - const EarlyLate *, - const StaState *) -{ - return delay; -} - -float -delaySigma2(const Delay &, - const EarlyLate *) -{ - return 0.0; -} - } // namespace #endif // !SSTA diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index 4afad127..f0d58659 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -337,37 +337,6 @@ fuzzyLess(const Delay &delay1, return fuzzyGreater(delay1.mean(), delay2.mean()); } -Delay -operator+(float delay1, - const Delay &delay2) -{ - return Delay(delay1 + delay2.mean(), - delay2.sigma2()); -} - -Delay -operator/(float delay1, - const Delay &delay2) -{ - return Delay(delay1 / delay2.mean(), - delay2.sigma2()); -} - -Delay -operator*(const Delay &delay1, - float delay2) -{ - return Delay(delay1.mean() * delay2, - delay1.sigma2() * delay2 * delay2); -} - -float -delayRatio(const Delay &delay1, - const Delay &delay2) -{ - return delay1.mean() / delay2.mean(); -} - float delayAsFloat(const Delay &delay, const EarlyLate *early_late, @@ -425,6 +394,45 @@ delayAsString(const Delay &delay, return sta->units()->timeUnit()->asString(mean_sigma, digits); } +Delay +delayRemove(const Delay &delay1, + const Delay &delay2) +{ + return Delay(delay1.mean() - delay2.mean(), + delay1.sigma2() - delay2.sigma2()); +} + +float +delayRatio(const Delay &delay1, + const Delay &delay2) +{ + return delay1.mean() / delay2.mean(); +} + +Delay +operator+(float delay1, + const Delay &delay2) +{ + return Delay(delay1 + delay2.mean(), + delay2.sigma2()); +} + +Delay +operator/(float delay1, + const Delay &delay2) +{ + return Delay(delay1 / delay2.mean(), + delay2.sigma2()); +} + +Delay +operator*(const Delay &delay1, + float delay2) +{ + return Delay(delay1.mean() * delay2, + delay1.sigma2() * delay2 * delay2); +} + } // namespace #endif // (SSTA == 1) diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index 1edeafd4..db5a7eca 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -388,6 +388,15 @@ operator*(const Delay &delay1, delay1.sigma2Late() * delay2 * delay2); } +Delay +delayRemove(const Delay &delay1, + const Delay &delay2) +{ + return Delay(delay1.mean() - delay2.mean(), + delay1.sigma2Early() - delay2.sigma2Early(), + delay1.sigma2Late() - delay2.sigma2Late()); +} + float delayRatio(const Delay &delay1, const Delay &delay2) diff --git a/include/sta/DelayFloat.hh b/include/sta/DelayFloat.hh index 896145d3..70bd51f1 100644 --- a/include/sta/DelayFloat.hh +++ b/include/sta/DelayFloat.hh @@ -95,6 +95,10 @@ bool fuzzyLessEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max); + +Delay +delayRemove(const Delay &delay1, + const Delay &delay2); float delayRatio(const Delay &delay1, const Delay &delay2); diff --git a/include/sta/DelayNormal1.hh b/include/sta/DelayNormal1.hh index 19f20810..1b99f139 100644 --- a/include/sta/DelayNormal1.hh +++ b/include/sta/DelayNormal1.hh @@ -77,19 +77,6 @@ makeDelay2(float delay, inline float delayAsFloat(const Delay &delay) { return delay.mean(); } -// Most non-operator functions on Delay are not defined as member -// functions so they can be defined on floats, where there is no class -// to define them. - -Delay operator+(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator/(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator*(const Delay &delay1, - float delay2); - // mean late+/early- sigma float delayAsFloat(const Delay &delay, @@ -150,8 +137,24 @@ bool fuzzyGreater(const Delay &delay1, const Delay &delay2, const MinMax *min_max); +// delay1-delay2 subtracting sigma instead of addiing. +Delay delayRemove(const Delay &delay1, + const Delay &delay2); float delayRatio(const Delay &delay1, const Delay &delay2); +// Most non-operator functions on Delay are not defined as member +// functions so they can be defined on floats, where there is no class +// to define them. + +Delay operator+(float delay1, + const Delay &delay2); +// Used for parallel gate delay calc. +Delay operator/(float delay1, + const Delay &delay2); +// Used for parallel gate delay calc. +Delay operator*(const Delay &delay1, + float delay2); + } // namespace diff --git a/include/sta/DelayNormal2.hh b/include/sta/DelayNormal2.hh index 73b07419..d39a5ec3 100644 --- a/include/sta/DelayNormal2.hh +++ b/include/sta/DelayNormal2.hh @@ -84,19 +84,6 @@ makeDelay2(float delay, inline float delayAsFloat(const Delay &delay) { return delay.mean(); } -// Most non-operator functions on Delay are not defined as member -// functions so they can be defined on floats, where there is no class -// to define them. - -Delay operator+(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator/(float delay1, - const Delay &delay2); -// Used for parallel gate delay calc. -Delay operator*(const Delay &delay1, - float delay2); - // mean late+/early- sigma float delayAsFloat(const Delay &delay, @@ -157,8 +144,24 @@ bool fuzzyGreater(const Delay &delay1, const Delay &delay2, const MinMax *min_max); +// delay1-delay2 subtracting sigma instead of addiing. +Delay delayRemove(const Delay &delay1, + const Delay &delay2); float delayRatio(const Delay &delay1, const Delay &delay2); +// Most non-operator functions on Delay are not defined as member +// functions so they can be defined on floats, where there is no class +// to define them. + +Delay operator+(float delay1, + const Delay &delay2); +// Used for parallel gate delay calc. +Delay operator/(float delay1, + const Delay &delay2); +// Used for parallel gate delay calc. +Delay operator*(const Delay &delay1, + float delay2); + } // namespace diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 06bc8831..e870f8ec 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2608,7 +2608,7 @@ ReportPath::reportPath5(const Path *path, Vertex *vertex = path1->vertex(this); Pin *pin = vertex->pin(); Arrival time = path1->arrival(this) + time_offset; - float incr = 0.0; + Delay incr = 0.0; const char *line_case = nullptr; bool is_clk_start = network_->isRegClkPin(pin); bool is_clk = path1->isClock(search_); @@ -2633,8 +2633,7 @@ ReportPath::reportPath5(const Path *path, // from the input to the loads. Report the wire delay on the // input pin instead. Arrival next_time = next_path->arrival(this) + time_offset; - incr = delayAsFloat(next_time, min_max, this) - - delayAsFloat(time, min_max, this); + incr = delayIncr(next_time, time, min_max); time = next_time; line_case = "input_drive"; } @@ -2681,13 +2680,11 @@ ReportPath::reportPath5(const Path *path, line_case = "clk_ideal"; } else if (is_clk && !is_clk_start) { - incr = delayAsFloat(time, min_max, this) - - delayAsFloat(prev_time, min_max, this); + incr = delayIncr(time, prev_time, min_max); line_case = "clk_prop"; } else { - incr = delayAsFloat(time, min_max, this) - - delayAsFloat(prev_time, min_max, this); + incr = delayIncr(time, prev_time, min_max); line_case = "normal"; } if (report_input_pin_ @@ -2739,6 +2736,17 @@ ReportPath::reportPath5(const Path *path, } } +Delay +ReportPath::delayIncr(Delay time, + Delay prev, + const MinMax *min_max) +{ + if (report_sigmas_) + return delayRemove(time, prev); + else + return delayAsFloat(time, min_max, this) - delayAsFloat(prev, min_max, this); +} + bool ReportPath::nextArcAnnotated(const PathRef *next_path, size_t next_index, @@ -3177,8 +3185,8 @@ ReportPath::reportFieldDelayMinus(Delay value, else { const char *str = report_sigmas_ ? delayAsString(-value, this, digits_) - // : delayAsString(-value, early_late, this, digits_); - : units_->timeUnit()->asString(-delayAsFloat(value, early_late, this), digits_); + // Opposite min/max for negative value. + : delayAsString(-value, early_late->opposite(), this, digits_); if (stringEq(str, plus_zero_)) // Force leading minus sign. str = minus_zero_; diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 3166fa22..bec1046d 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -514,6 +514,9 @@ protected: PathRef &ref_path); const char *asRisingFalling(const RiseFall *rf); const char *asRiseFall(const RiseFall *rf); + Delay delayIncr(Delay time, + Delay prev, + const MinMax *min_max); // Path options. ReportPathFormat format_; From 6ff05e580b8485ad9f5729df5940c5ac6e9dbfb9 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 31 May 2020 15:31:47 -0700 Subject: [PATCH 08/70] clk uncertainty with pocv --- search/PathEnd.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/search/PathEnd.cc b/search/PathEnd.cc index aecfa1de..4084f20d 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -361,10 +361,11 @@ PathEnd::checkTgtClkDelay(const PathVertex *tgt_clk_path, if (clk_info->isPropagated()) { // Propagated clock. Propagated arrival is seeded with // early_late==path_min_max insertion delay. + Arrival clk_arrival = tgt_clk_path->arrival(sta); Arrival path_insertion = search->clockInsertion(tgt_clk, tgt_src_pin, tgt_clk_rf, min_max, min_max, tgt_path_ap); - latency=tgt_clk_path->arrival(sta)-tgt_clk_edge->time()-path_insertion; + latency = clk_arrival - tgt_clk_edge->time() - path_insertion; } else // Ideal clock. From 4c74fcfb6520c1b77d814ab1c3c7f32e7e99390c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 2 Jun 2020 11:08:48 -0700 Subject: [PATCH 09/70] report -min/max fanout, capacitance --- CMakeLists.txt | 7 +- include/sta/Sta.hh | 52 +++++- search/CheckCapacitanceLimits.cc | 306 +++++++++++++++++++++++++++++++ search/CheckCapacitanceLimits.hh | 95 ++++++++++ search/CheckFanoutLimits.cc | 258 ++++++++++++++++++++++++++ search/CheckFanoutLimits.hh | 70 +++++++ search/ReportPath.cc | 86 +++++---- search/ReportPath.hh | 55 +++--- search/Sta.cc | 170 ++++++++++++++++- tcl/Search.tcl | 78 +++++++- tcl/Sta.tcl | 34 +++- tcl/StaTcl.i | 78 ++++++++ 12 files changed, 1211 insertions(+), 78 deletions(-) create mode 100644 search/CheckCapacitanceLimits.cc create mode 100644 search/CheckCapacitanceLimits.hh create mode 100644 search/CheckFanoutLimits.cc create mode 100644 search/CheckFanoutLimits.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 2955d985..07ccac05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,8 @@ set(STA_SOURCE search/CheckMaxSkews.cc search/CheckMinPeriods.cc search/CheckMinPulseWidths.cc + search/CheckCapacitanceLimits.cc + search/CheckFanoutLimits.cc search/CheckSlewLimits.cc search/CheckTiming.cc search/ClkInfo.cc @@ -511,6 +513,9 @@ install(DIRECTORY include/sta DESTINATION include) ################################################################ -add_custom_target(tags etags -o TAGS ${STA_SOURCE} */*.hh include/sta/*.hh ${SWIG_TCL_FILES} +add_custom_target(tags etags -o TAGS + ${STA_SOURCE} + */*.hh include/sta/*.hh + ${STA_TCL_FILES} ${SWIG_TCL_FILES} WORKING_DIRECTORY ${STA_HOME} ) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 4f7d4e18..d7288116 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -45,6 +45,8 @@ class ReportPath; class CheckTiming; class DcalcAnalysisPt; class CheckSlewLimits; +class CheckFanoutLimits; +class CheckCapacitanceLimits; class CheckMinPulseWidths; class CheckMinPeriods; class CheckMaxSkews; @@ -612,6 +614,7 @@ public: // Return value. ClockSet &clks); + void checkSlewLimitPreamble(); // Return the pin with the min/max slew limit slack. // corner=nullptr checks all corners. Pin *pinMinSlewLimitSlack(const Corner *corner, @@ -636,6 +639,50 @@ public: Slew &slew, float &limit, float &slack); + + void checkFanoutLimitPreamble(); + // Return the pin with the min/max fanout limit slack. + Pin *pinMinFanoutLimitSlack(const MinMax *min_max); + // Return all pins with min/max fanout violations. + PinSeq *pinFanoutLimitViolations(const MinMax *min_max); + void reportFanoutLimitShortHeader(); + void reportFanoutLimitShort(Pin *pin, + const MinMax *min_max); + void reportFanoutLimitVerbose(Pin *pin, + const MinMax *min_max); + void checkFanouts(const Pin *pin, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack); + + void checkCapacitanceLimitPreamble(); + // Return the pin with the min/max capacitance limit slack. + // corner=nullptr checks all corners. + Pin *pinMinCapacitanceLimitSlack(const Corner *corner, + const MinMax *min_max); + // Return all pins with min/max capacitance violations. + // corner=nullptr checks all corners. + PinSeq *pinCapacitanceLimitViolations(const Corner *corner, + const MinMax *min_max); + void reportCapacitanceLimitShortHeader(); + void reportCapacitanceLimitShort(Pin *pin, + const Corner *corner, + const MinMax *min_max); + void reportCapacitanceLimitVerbose(Pin *pin, + const Corner *corner, + const MinMax *min_max); + void checkCapacitances(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const RiseFall *&tr, + float &capacitance, + float &limit, + float &slack); + // Min pulse width check with the least slack. // corner=nullptr checks all corners. MinPulseWidthCheck *minPulseWidthSlack(const Corner *corner); @@ -1227,6 +1274,8 @@ protected: virtual void makeLatches(); virtual void makeCheckTiming(); virtual void makeCheckSlewLimits(); + virtual void makeCheckFanoutLimits(); + virtual void makeCheckCapacitanceLimits(); virtual void makeCheckMinPulseWidths(); virtual void makeCheckMinPeriods(); virtual void makeCheckMaxSkews(); @@ -1268,7 +1317,6 @@ protected: Edge *d_q_edge, const ClockEdge *en_clk_edge); void clockSlewChanged(Clock *clk); - void checkSlewLimitPreamble(); void minPulseWidthPreamble(); void minPeriodPreamble(); void maxSkewPreamble(); @@ -1324,6 +1372,8 @@ protected: Corner *cmd_corner_; CheckTiming *check_timing_; CheckSlewLimits *check_slew_limits_; + CheckFanoutLimits *check_fanout_limits_; + CheckCapacitanceLimits *check_capacitance_limits_; CheckMinPulseWidths *check_min_pulse_widths_; CheckMinPeriods *check_min_periods_; CheckMaxSkews *check_max_skews_; diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc new file mode 100644 index 00000000..80b75aa1 --- /dev/null +++ b/search/CheckCapacitanceLimits.cc @@ -0,0 +1,306 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2020, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "CheckCapacitanceLimits.hh" + +#include "Fuzzy.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "DcalcAnalysisPt.hh" +#include "StaState.hh" +#include "Corner.hh" +#include "PortDirection.hh" + +namespace sta { + +class PinCapacitanceLimitSlackLess +{ +public: + PinCapacitanceLimitSlackLess(const Corner *corner, + const MinMax *min_max, + CheckCapacitanceLimits *check_capacitance_limit, + const StaState *sta); + bool operator()(Pin *pin1, + Pin *pin2) const; + +private: + const Corner *corner_; + const MinMax *min_max_; + CheckCapacitanceLimits *check_capacitance_limit_; + const StaState *sta_; + +}; + +PinCapacitanceLimitSlackLess::PinCapacitanceLimitSlackLess(const Corner *corner, + const MinMax *min_max, + CheckCapacitanceLimits *check_capacitance_limit, + const StaState *sta) : + corner_(corner), + min_max_(min_max), + check_capacitance_limit_(check_capacitance_limit), + sta_(sta) +{ +} + +bool +PinCapacitanceLimitSlackLess::operator()(Pin *pin1, + Pin *pin2) const +{ + const Corner *corner1, *corner2; + const RiseFall *rf1, *rf2; + float capacitance1, capacitance2; + float limit1, limit2, slack1, slack2; + check_capacitance_limit_->checkCapacitance(pin1, corner_, min_max_, + corner1, rf1, capacitance1, + limit1, slack1); + check_capacitance_limit_->checkCapacitance(pin2, corner_, min_max_, + corner2, rf2, capacitance2, + limit2, slack2); + return fuzzyLess(slack1, slack2) + || (fuzzyEqual(slack1, slack2) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(pin1, pin2)); +} + +//////////////////////////////////////////////////////////////// + +CheckCapacitanceLimits::CheckCapacitanceLimits(const StaState *sta) : + sta_(sta) +{ +} + +void +CheckCapacitanceLimits::init(const MinMax *min_max) +{ + const Network *network = sta_->network(); + Cell *top_cell = network->cell(network->topInstance()); + float top_limit; + bool top_limit_exists; + sta_->sdc()->capacitanceLimit(top_cell, min_max, + top_limit, top_limit_exists); + top_limit_= top_limit; + top_limit_exists_ = top_limit_exists; +} + +void +CheckCapacitanceLimits::checkCapacitance(const Pin *pin, + const Corner *corner1, + const MinMax *min_max, + // Return values. + const Corner *&corner, + const RiseFall *&rf, + float &capacitance, + float &limit, + float &slack) const +{ + corner = nullptr; + rf = nullptr; + capacitance = 0.0; + limit = 0.0; + slack = MinMax::min()->initValue(); + if (corner1) + checkCapacitance1(pin, corner1, min_max, + corner, rf, capacitance, limit, slack); + else { + for (auto corner1 : *sta_->corners()) { + checkCapacitance1(pin, corner1, min_max, + corner, rf, capacitance, limit, slack); + } + } +} + +void +CheckCapacitanceLimits::checkCapacitance1(const Pin *pin, + const Corner *corner1, + const MinMax *min_max, + // Return values. + const Corner *&corner, + const RiseFall *&rf, + float &capacitance, + float &limit, + float &slack) const +{ + float limit1; + bool limit1_exists; + findLimit(pin, min_max, limit1, limit1_exists); + if (limit1_exists) { + for (auto rf1 : RiseFall::range()) { + checkCapacitance(pin, corner1, min_max, rf1, limit1, + corner, rf, capacitance, slack, limit); + } + } +} + +void +CheckCapacitanceLimits::findLimit(const Pin *pin, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + exists = false; + const Network *network = sta_->network(); + Sdc *sdc = sta_->sdc(); + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->capacitanceLimit(port, min_max, limit, exists); + if (!exists) { + limit = top_limit_; + exists = top_limit_exists_; + } + } + else { + Cell *cell = network->cell(network->instance(pin)); + sdc->capacitanceLimit(cell, min_max, + limit, exists); + if (!exists) { + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->capacitanceLimit(min_max, limit, exists); + if (!exists + && port->direction()->isAnyOutput()) + port->libertyLibrary()->defaultMaxCapacitance(limit, exists); + } + } + } +} + +void +CheckCapacitanceLimits::checkCapacitance(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + const RiseFall *rf1, + float limit1, + // Return values. + const Corner *&corner1, + const RiseFall *&rf, + float &capacitance, + float &slack, + float &limit) const +{ + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + Sdc *sdc = sta_->sdc(); + float pin_cap, wire_cap, fanout; + bool has_set_load; + sdc->connectedCap(pin, rf1, op_cond, corner, min_max, + pin_cap, wire_cap, fanout, has_set_load); + float cap = pin_cap + wire_cap; + + float slack1 = (min_max == MinMax::max()) + ? limit1 - cap : cap - limit1; + if (corner == nullptr + || (slack1 < slack + // Break ties for the sake of regression stability. + || (fuzzyEqual(slack1, slack) + && rf1->index() < rf->index()))) { + corner1 = corner; + rf = rf1; + capacitance = cap; + slack = slack1; + limit = limit1; + } +} + +PinSeq * +CheckCapacitanceLimits::pinCapacitanceLimitViolations(const Corner *corner, + const MinMax *min_max) +{ + init(min_max); + const Network *network = sta_->network(); + PinSeq *violators = new PinSeq; + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + pinCapacitanceLimitViolations(inst, corner, min_max, violators); + } + delete inst_iter; + // Check top level ports. + pinCapacitanceLimitViolations(network->topInstance(), corner, min_max, violators); + sort(violators, PinCapacitanceLimitSlackLess(corner, min_max, this, sta_)); + return violators; +} + +void +CheckCapacitanceLimits::pinCapacitanceLimitViolations(Instance *inst, + const Corner *corner, + const MinMax *min_max, + PinSeq *violators) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network->direction(pin)->isAnyOutput()) { + const Corner *corner1; + const RiseFall *rf; + float capacitance, limit, slack; + checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack ); + if (rf && slack < 0.0) + violators->push_back(pin); + } + } + delete pin_iter; +} + +Pin * +CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(const Corner *corner, + const MinMax *min_max) +{ + init(min_max); + const Network *network = sta_->network(); + Pin *min_slack_pin = 0; + float min_slack = MinMax::min()->initValue(); + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + pinMinCapacitanceLimitSlack(inst, corner, min_max, min_slack_pin, min_slack); + } + delete inst_iter; + // Check top level ports. + pinMinCapacitanceLimitSlack(network->topInstance(), corner, min_max, + min_slack_pin, min_slack); + return min_slack_pin; +} + +void +CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(Instance *inst, + const Corner *corner, + const MinMax *min_max, + // Return values. + Pin *&min_slack_pin, + float &min_slack) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + const Corner *corner1; + const RiseFall *rf; + float capacitance, limit, slack; + checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); + if (rf + && (min_slack_pin == 0 + || slack < min_slack)) { + min_slack_pin = pin; + min_slack = slack; + } + } + delete pin_iter; +} + +} // namespace diff --git a/search/CheckCapacitanceLimits.hh b/search/CheckCapacitanceLimits.hh new file mode 100644 index 00000000..8344221f --- /dev/null +++ b/search/CheckCapacitanceLimits.hh @@ -0,0 +1,95 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2020, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "MinMax.hh" +#include "Transition.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" + +namespace sta { + +class StaState; +class Corner; + +class CheckCapacitanceLimits +{ +public: + CheckCapacitanceLimits(const StaState *sta); + void init(const MinMax *min_max); + // Requires init(). + // corner=nullptr checks all corners. + void checkCapacitance(const Pin *pin, + const Corner *corner1, + const MinMax *min_max, + // Return values. + // Corner is nullptr for no capacitance limit. + const Corner *&corner, + const RiseFall *&rf, + float &capacitance, + float &limit, + float &slack) const; + // corner=nullptr checks all corners. + PinSeq *pinCapacitanceLimitViolations(const Corner *corner, + const MinMax *min_max); + // corner=nullptr checks all corners. + Pin *pinMinCapacitanceLimitSlack(const Corner *corner, + const MinMax *min_max); + +protected: + void checkCapacitance(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + const RiseFall *rf1, + float limit1, + // Return values. + const Corner *&corner1, + const RiseFall *&rf, + float &capacitance, + float &slack, + float &limit) const; + void checkCapacitance1(const Pin *pin, + const Corner *corner1, + const MinMax *min_max, + // Return values. + const Corner *&corner, + const RiseFall *&rf, + float &capacitance, + float &limit, + float &slack) const; + void findLimit(const Pin *pin, + const MinMax *min_max, + // Return values. + float &limit, + bool &limit_exists) const; + void pinCapacitanceLimitViolations(Instance *inst, + const Corner *corner, + const MinMax *min_max, + PinSeq *violators); + void pinMinCapacitanceLimitSlack(Instance *inst, + const Corner *corner, + const MinMax *min_max, + // Return values. + Pin *&min_slack_pin, + float &min_slack); + + float top_limit_; + bool top_limit_exists_; + const StaState *sta_; +}; + +} // namespace diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc new file mode 100644 index 00000000..111f9797 --- /dev/null +++ b/search/CheckFanoutLimits.cc @@ -0,0 +1,258 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2020, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "CheckFanoutLimits.hh" + +#include "Fuzzy.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "PortDirection.hh" + +namespace sta { + +class PinFanoutLimitSlackLess +{ +public: + PinFanoutLimitSlackLess(const MinMax *min_max, + CheckFanoutLimits *check_fanout_limit, + const StaState *sta); + bool operator()(Pin *pin1, + Pin *pin2) const; + +private: + const MinMax *min_max_; + CheckFanoutLimits *check_fanout_limit_; + const StaState *sta_; + +}; + +PinFanoutLimitSlackLess::PinFanoutLimitSlackLess(const MinMax *min_max, + CheckFanoutLimits *check_fanout_limit, + const StaState *sta) : + min_max_(min_max), + check_fanout_limit_(check_fanout_limit), + sta_(sta) +{ +} + +bool +PinFanoutLimitSlackLess::operator()(Pin *pin1, + Pin *pin2) const +{ + float fanout1, fanout2; + float limit1, limit2, slack1, slack2; + check_fanout_limit_->checkFanout(pin1, min_max_, + fanout1, limit1, slack1); + check_fanout_limit_->checkFanout(pin2, min_max_, + fanout2, limit2, slack2); + return fuzzyLess(slack1, slack2) + || (fuzzyEqual(slack1, slack2) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(pin1, pin2)); +} + +//////////////////////////////////////////////////////////////// + +CheckFanoutLimits::CheckFanoutLimits(const StaState *sta) : + sta_(sta) +{ +} + +void +CheckFanoutLimits::init(const MinMax *min_max) +{ + const Network *network = sta_->network(); + Cell *top_cell = network->cell(network->topInstance()); + float top_limit; + bool top_limit_exists; + sta_->sdc()->fanoutLimit(top_cell, min_max, + top_limit, top_limit_exists); + top_limit_= top_limit; + top_limit_exists_ = top_limit_exists; +} + +void +CheckFanoutLimits::checkFanout(const Pin *pin, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack) const +{ + fanout = 0.0; + limit = 0.0; + slack = MinMax::min()->initValue(); + + float limit1; + bool limit1_exists; + findLimit(pin, min_max, limit1, limit1_exists); + if (limit1_exists) { + checkFanout(pin, min_max, limit1, + fanout, slack, limit); + } +} + +void +CheckFanoutLimits::findLimit(const Pin *pin, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + exists = false; + const Network *network = sta_->network(); + Sdc *sdc = sta_->sdc(); + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->fanoutLimit(port, min_max, limit, exists); + if (!exists) { + limit = top_limit_; + exists = top_limit_exists_; + } + } + else { + Cell *cell = network->cell(network->instance(pin)); + sdc->fanoutLimit(cell, min_max, + limit, exists); + if (!exists) { + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->fanoutLimit(min_max, limit, exists); + if (!exists + && port->direction()->isAnyOutput()) + port->libertyLibrary()->defaultMaxFanout(limit, exists); + } + } + } +} + +void +CheckFanoutLimits::checkFanout(const Pin *pin, + const MinMax *min_max, + float limit1, + // Return values. + float &fanout, + float &slack, + float &limit) const +{ + float fanout1 = this->fanout(pin); + float slack1 = (min_max == MinMax::max()) + ? limit1 - fanout1 + : fanout1 - limit1; + if (fuzzyLessEqual(slack1, slack)) { + fanout = fanout1; + slack = slack1; + limit = limit1; + } +} + +float +CheckFanoutLimits::fanout(const Pin *pin) const +{ + float fanout = 0; + const Network *network = sta_->network(); + Net *net = network->net(pin); + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network->isLoad(pin)) + fanout++; + } + delete pin_iter; + return fanout; +} + +PinSeq * +CheckFanoutLimits::pinFanoutLimitViolations(const MinMax *min_max) +{ + init(min_max); + const Network *network = sta_->network(); + PinSeq *violators = new PinSeq; + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + pinFanoutLimitViolations(inst, min_max, violators); + } + delete inst_iter; + + // Check top level ports. + pinFanoutLimitViolations(network->topInstance(), min_max, violators); + sort(violators, PinFanoutLimitSlackLess(min_max, this, sta_)); + return violators; +} + +void +CheckFanoutLimits::pinFanoutLimitViolations(Instance *inst, + const MinMax *min_max, + PinSeq *violators) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + float fanout; + float limit, slack; + checkFanout(pin, min_max, fanout, limit, slack ); + if (slack < 0.0) + violators->push_back(pin); + } + delete pin_iter; +} + +Pin * +CheckFanoutLimits::pinMinFanoutLimitSlack(const MinMax *min_max) +{ + init(min_max); + const Network *network = sta_->network(); + Pin *min_slack_pin = 0; + float min_slack = MinMax::min()->initValue(); + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + pinMinFanoutLimitSlack(inst, min_max, min_slack_pin, min_slack); + } + delete inst_iter; + // Check top level ports. + pinMinFanoutLimitSlack(network->topInstance(), min_max, + min_slack_pin, min_slack); + return min_slack_pin; +} + +void +CheckFanoutLimits::pinMinFanoutLimitSlack(Instance *inst, + const MinMax *min_max, + // Return values. + Pin *&min_slack_pin, + float &min_slack) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + float fanout; + float limit, slack; + checkFanout(pin, min_max, fanout, limit, slack); + if (min_slack_pin == 0 + || slack < min_slack) { + min_slack_pin = pin; + min_slack = slack; + } + } + delete pin_iter; +} + +} // namespace diff --git a/search/CheckFanoutLimits.hh b/search/CheckFanoutLimits.hh new file mode 100644 index 00000000..f8de4139 --- /dev/null +++ b/search/CheckFanoutLimits.hh @@ -0,0 +1,70 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2020, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "MinMax.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" + +namespace sta { + +class StaState; + +class CheckFanoutLimits +{ +public: + CheckFanoutLimits(const StaState *sta); + void init(const MinMax *min_max); + // Requires init(). + void checkFanout(const Pin *pin, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack) const; + PinSeq *pinFanoutLimitViolations(const MinMax *min_max); + Pin *pinMinFanoutLimitSlack(const MinMax *min_max); + +protected: + void checkFanout(const Pin *pin, + const MinMax *min_max, + float limit1, + // Return values. + float &fanout, + float &slack, + float &limit) const; + void findLimit(const Pin *pin, + const MinMax *min_max, + // Return values. + float &limit, + bool &limit_exists) const; + void pinFanoutLimitViolations(Instance *inst, + const MinMax *min_max, + PinSeq *violators); + void pinMinFanoutLimitSlack(Instance *inst, + const MinMax *min_max, + // Return values. + Pin *&min_slack_pin, + float &min_slack); + float fanout(const Pin *pin) const; + + float top_limit_; + bool top_limit_exists_; + const StaState *sta_; +}; + +} // namespace diff --git a/search/ReportPath.cc b/search/ReportPath.cc index e870f8ec..15407693 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1549,21 +1549,22 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, //////////////////////////////////////////////////////////////// void -ReportPath::reportSlewLimitShortHeader() +ReportPath::reportLimitShortHeader(const char *what) { string result; - reportSlewLimitShortHeader(result); + reportLimitShortHeader(what, result); report_->print(result); } void -ReportPath::reportSlewLimitShortHeader(string &result) +ReportPath::reportLimitShortHeader(const char *what, + string &result) { reportDescription("Pin", result); result += ' '; reportField("Limit", field_slew_, result); result += ' '; - reportField("Trans", field_slew_, result); + reportField(what, field_slew_, result); result += ' '; reportField("Slack", field_slew_, result); reportEndOfLine(result); @@ -1572,75 +1573,80 @@ ReportPath::reportSlewLimitShortHeader(string &result) } void -ReportPath::reportSlewLimitShort(Pin *pin, - const RiseFall *rf, - Slew slew, - float limit, - float slack) +ReportPath::reportLimitShort(const char *what, + Pin *pin, + float value, + float limit, + float slack) { string result; - reportSlewLimitShort(pin, rf, slew, limit, slack, result); + reportLimitShort(what, pin, value, limit, slack, result); report_->print(result); } void -ReportPath::reportSlewLimitShort(Pin *pin, - const RiseFall *, - Slew slew, - float limit, - float slack, - string &result) +ReportPath::reportLimitShort(const char *what, + Pin *pin, + float value, + float limit, + float slack, + string &result) { const char *pin_name = cmd_network_->pathName(pin); reportDescription(pin_name, result); reportSpaceFieldTime(limit, result); - reportSpaceFieldDelay(slew, EarlyLate::late(), result); + reportSpaceFieldDelay(value, EarlyLate::late(), result); reportSpaceSlack(slack, result); } void -ReportPath::reportSlewLimitVerbose(Pin *pin, - const Corner *corner, - const RiseFall *rf, - Slew slew, - float limit, - float slack, - const MinMax *min_max) +ReportPath::reportLimitVerbose(const char *what, + Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const MinMax *min_max) { string result; - reportSlewLimitVerbose(pin, corner, rf, slew, limit, slack, min_max, result); + reportLimitVerbose(what, pin, rf, value, limit, slack, min_max, result); report_->print(result); } void -ReportPath::reportSlewLimitVerbose(Pin *pin, - const Corner *, - const RiseFall *rf, - Slew slew, - float limit, - float slack, - const MinMax *min_max, - string &result) +ReportPath::reportLimitVerbose(const char *what, + Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const MinMax *min_max, + string &result) { result += "Pin "; result += cmd_network_->pathName(pin); result += ' '; - result += rf->shortName(); + if (rf) + result += rf->shortName(); + else + result += " "; reportEndOfLine(result); result += min_max->asString(); - result += "_transition "; + result += " "; + result += what; + result += " "; reportSpaceFieldTime(limit, result); reportEndOfLine(result); - result += "transition_time "; - reportField(delayAsFloat(slew), field_slew_, result); + result += what; + result += " "; + reportField(value, field_slew_, result); reportEndOfLine(result); - reportDashLine(strlen("transition_time") + field_slew_->width() + 1, - result); + reportDashLine(strlen(what) + field_slew_->width() + 6, result); - result += "Slack "; + result += "Slack "; reportSpaceSlack(slack, result); } diff --git a/search/ReportPath.hh b/search/ReportPath.hh index bec1046d..7ae4406c 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -137,34 +137,35 @@ public: void reportVerbose(MaxSkewCheck *check, string &result); - void reportSlewLimitShortHeader(); - void reportSlewLimitShortHeader(string &result); - void reportSlewLimitShort(Pin *pin, - const RiseFall *rf, - Slew slew, - float limit, - float slack); - void reportSlewLimitShort(Pin *pin, const - RiseFall *rf, - Slew slew, - float limit, - float slack, - string &result); - void reportSlewLimitVerbose(Pin *pin, - const Corner *corner, - const RiseFall *rf, - Slew slew, - float limit, - float slack, - const MinMax *min_max); - void reportSlewLimitVerbose(Pin *pin, - const Corner *corner, - const RiseFall *rf, - Slew slew, - float limit, - float slack, - const MinMax *min_max, + void reportLimitShortHeader(const char *what); + void reportLimitShortHeader(const char *what, string &result); + void reportLimitShort(const char *what, + Pin *pin, + float value, + float limit, + float slack); + void reportLimitShort(const char *what, + Pin *pin, + float value, + float limit, + float slack, + string &result); + void reportLimitVerbose(const char *what, + Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const MinMax *min_max); + void reportLimitVerbose(const char *what, + Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const MinMax *min_max, + string &result); protected: void makeFields(); diff --git a/search/Sta.cc b/search/Sta.cc index 2baa1312..c6811440 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -55,6 +55,8 @@ #include "PathGroup.hh" #include "CheckTiming.hh" #include "CheckSlewLimits.hh" +#include "CheckFanoutLimits.hh" +#include "CheckCapacitanceLimits.hh" #include "CheckMinPulseWidths.hh" #include "CheckMinPeriods.hh" #include "CheckMaxSkews.hh" @@ -256,6 +258,8 @@ Sta::Sta() : current_instance_(nullptr), check_timing_(nullptr), check_slew_limits_(nullptr), + check_fanout_limits_(nullptr), + check_capacitance_limits_(nullptr), check_min_pulse_widths_(nullptr), check_min_periods_(nullptr), check_max_skews_(nullptr), @@ -437,6 +441,18 @@ Sta::makeCheckSlewLimits() check_slew_limits_ = new CheckSlewLimits(this); } +void +Sta::makeCheckFanoutLimits() +{ + check_fanout_limits_ = new CheckFanoutLimits(this); +} + +void +Sta::makeCheckCapacitanceLimits() +{ + check_capacitance_limits_ = new CheckCapacitanceLimits(this); +} + void Sta::makeCheckMinPulseWidths() { @@ -483,6 +499,8 @@ Sta::~Sta() { // Delete "top down" to minimize chance of referencing deleted memory. delete check_slew_limits_; + delete check_fanout_limits_; + delete check_capacitance_limits_; delete check_min_pulse_widths_; delete check_min_periods_; delete check_max_skews_; @@ -4813,7 +4831,7 @@ void Sta::checkSlewLimitPreamble() { if (sdc_->haveClkSlewLimits()) - // Arrivals are needed to know what pin clock domains. + // Arrivals are needed to know pin clock domains. updateTiming(false); else findDelays(); @@ -4840,7 +4858,7 @@ Sta::pinSlewLimitViolations(const Corner *corner, void Sta::reportSlewLimitShortHeader() { - report_path_->reportSlewLimitShortHeader(); + report_path_->reportLimitShortHeader("Slew"); } void @@ -4854,7 +4872,7 @@ Sta::reportSlewLimitShort(Pin *pin, float limit, slack; check_slew_limits_->checkSlews(pin, corner, min_max, corner1, rf, slew, limit, slack); - report_path_->reportSlewLimitShort(pin, rf, slew, limit, slack); + report_path_->reportLimitShort("slew", pin, delayAsFloat(slew), limit, slack); } void @@ -4868,8 +4886,8 @@ Sta::reportSlewLimitVerbose(Pin *pin, float limit, slack; check_slew_limits_->checkSlews(pin, corner, min_max, corner1, rf, slew, limit, slack); - report_path_->reportSlewLimitVerbose(pin, corner1, rf, slew, - limit, slack, min_max); + report_path_->reportLimitVerbose("slew", pin, rf, delayAsFloat(slew), + limit, slack, min_max); } void @@ -4891,6 +4909,148 @@ Sta::checkSlews(const Pin *pin, ////////////////////////////////////////////////////////////////' +void +Sta::checkFanoutLimitPreamble() +{ + if (check_fanout_limits_ == nullptr) + makeCheckFanoutLimits(); +} + +Pin * +Sta::pinMinFanoutLimitSlack(const MinMax *min_max) +{ + checkFanoutLimitPreamble(); + return check_fanout_limits_->pinMinFanoutLimitSlack(min_max); +} + +PinSeq * +Sta::pinFanoutLimitViolations(const MinMax *min_max) +{ + checkFanoutLimitPreamble(); + return check_fanout_limits_->pinFanoutLimitViolations(min_max); +} + +void +Sta::reportFanoutLimitShortHeader() +{ + report_path_->reportLimitShortHeader("Fanout"); +} + +void +Sta::reportFanoutLimitShort(Pin *pin, + const MinMax *min_max) +{ + float fanout, limit, slack; + check_fanout_limits_->checkFanout(pin, min_max, + fanout, limit, slack); + report_path_->reportLimitShort("fanout", pin, fanout, limit, slack); +} + +void +Sta::reportFanoutLimitVerbose(Pin *pin, + const MinMax *min_max) +{ + float fanout, limit, slack; + check_fanout_limits_->checkFanout(pin, min_max, + fanout, limit, slack); + report_path_->reportLimitVerbose("fanout", pin, nullptr, fanout, + limit, slack, min_max); +} + +void +Sta::checkFanouts(const Pin *pin, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack) +{ + checkFanoutLimitPreamble(); + check_fanout_limits_->init(min_max); + check_fanout_limits_->checkFanout(pin, min_max, + fanout, limit, slack); +} + +////////////////////////////////////////////////////////////////' + +void +Sta::checkCapacitanceLimitPreamble() +{ + if (check_capacitance_limits_ == nullptr) + makeCheckCapacitanceLimits(); +} + +Pin * +Sta::pinMinCapacitanceLimitSlack(const Corner *corner, + const MinMax *min_max) +{ + checkCapacitanceLimitPreamble(); + return check_capacitance_limits_->pinMinCapacitanceLimitSlack(corner, min_max); +} + +PinSeq * +Sta::pinCapacitanceLimitViolations(const Corner *corner, + const MinMax *min_max) +{ + checkCapacitanceLimitPreamble(); + return check_capacitance_limits_->pinCapacitanceLimitViolations(corner, min_max); +} + +void +Sta::reportCapacitanceLimitShortHeader() +{ + report_path_->reportLimitShortHeader("Capacitance"); +} + +void +Sta::reportCapacitanceLimitShort(Pin *pin, + const Corner *corner, + const MinMax *min_max) +{ + const Corner *corner1; + const RiseFall *rf; + float capacitance, limit, slack; + check_capacitance_limits_->checkCapacitance(pin, corner, min_max, + corner1, rf, capacitance, + limit, slack); + report_path_->reportLimitShort("capacitance", pin, capacitance, limit, slack); +} + +void +Sta::reportCapacitanceLimitVerbose(Pin *pin, + const Corner *corner, + const MinMax *min_max) +{ + const Corner *corner1; + const RiseFall *rf; + float capacitance, limit, slack; + check_capacitance_limits_->checkCapacitance(pin, corner, min_max, + corner1, rf, capacitance, + limit, slack); + report_path_->reportLimitVerbose("capacitance", pin, rf, + capacitance, limit, slack, min_max); +} + +void +Sta::checkCapacitances(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const RiseFall *&rf, + float &capacitance, + float &limit, + float &slack) +{ + checkCapacitanceLimitPreamble(); + check_capacitance_limits_->init(min_max); + check_capacitance_limits_->checkCapacitance(pin, corner, min_max, + corner1, rf, capacitance, + limit, slack); +} + +////////////////////////////////////////////////////////////////' + void Sta::minPulseWidthPreamble() { diff --git a/tcl/Search.tcl b/tcl/Search.tcl index e108e7df..d22d5166 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -386,7 +386,7 @@ proc report_slew_limits { corner min_max all_violators verbose nosplit } { if { $all_violators } { set violators [pin_slew_limit_violations $corner $min_max] if { $violators != {} } { - puts "${min_max}_transition" + puts "${min_max} slew" puts "" if { $verbose } { foreach pin $violators { @@ -404,7 +404,7 @@ proc report_slew_limits { corner min_max all_violators verbose nosplit } { } else { set pin [pin_min_slew_limit_slack $corner $min_max] if { $pin != "NULL" } { - puts "${min_max}_transition" + puts "${min_max} slew" puts "" if { $verbose } { report_slew_limit_verbose $pin $corner $min_max @@ -418,6 +418,80 @@ proc report_slew_limits { corner min_max all_violators verbose nosplit } { } } +proc report_fanout_limits { min_max all_violators verbose nosplit } { + if { $all_violators } { + set violators [pin_fanout_limit_violations $min_max] + if { $violators != {} } { + puts "${min_max} fanout" + puts "" + if { $verbose } { + foreach pin $violators { + report_fanout_limit_verbose $pin $min_max + puts "" + } + } else { + report_fanout_limit_short_header + foreach pin $violators { + report_fanout_limit_short $pin $min_max + } + puts "" + } + } + } else { + set pin [pin_min_fanout_limit_slack $min_max] + if { $pin != "NULL" } { + puts "${min_max} fanout" + puts "" + if { $verbose } { + report_fanout_limit_verbose $pin $min_max + puts "" + } else { + report_fanout_limit_short_header + report_fanout_limit_short $pin $min_max + puts "" + } + } + } +} + +proc report_capacitance_limits { corner min_max all_violators verbose nosplit } { + if { $all_violators } { + set violators [pin_capacitance_limit_violations $corner $min_max] + if { $violators != {} } { + puts "${min_max} capacitance" + puts "" + if { $verbose } { + foreach pin $violators { + report_capacitance_limit_verbose $pin $corner $min_max + puts "" + } + } else { + report_capacitance_limit_short_header + foreach pin $violators { + report_capacitance_limit_short $pin $corner $min_max + } + puts "" + } + } + } else { + set pin [pin_min_capacitance_limit_slack $corner $min_max] + if { $pin != "NULL" } { + puts "${min_max} capacitance" + puts "" + if { $verbose } { + report_capacitance_limit_verbose $pin $corner $min_max + puts "" + } else { + report_capacitance_limit_short_header + report_capacitance_limit_short $pin $corner $min_max + puts "" + } + } + } +} + +################################################################ + proc report_path_ends { path_ends } { report_path_end_header set prev_end "NULL" diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index a24f7ee2..e2d94c9c 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -319,6 +319,8 @@ define_sta_cmd_args "report_check_types" \ [-recovery] [-removal]\ [-clock_gating_setup] [-clock_gating_hold]\ [-max_slew] [-min_slew]\ + [-max_fanout] [-min_fanout]\ + [-max_capacitance] [-min_capacitance]\ [-min_pulse_width] [-min_period] [-max_skew]\ [-digits digits] [-no_line_splits]\ [> filename] [>> filename]} @@ -358,35 +360,47 @@ proc_redirect report_check_types { set recovery 1 set clk_gating_setup 1 set max_slew 1 + set max_fanout 1 + set max_capacitance 1 } else { set setup 0 set recovery 0 set clk_gating_setup 0 set max_slew 0 + set max_fanout 0 + set max_capacitance 0 } if { $min_max == "min" || $min_max == "min_max" } { set hold 1 set removal 1 set clk_gating_hold 1 set min_slew 1 + set min_fanout 1 + set min_capacitance 1 } else { set hold 0 set min_delay 0 set removal 0 set clk_gating_hold 0 set min_slew 0 + set min_fanout 0 + set min_capacitance 0 } set min_pulse_width 1 set min_period 1 set max_skew 1 + set max_fanout 1 + set max_capacitance 1 } else { parse_key_args "report_check_types" args keys {} \ flags {-max_delay -min_delay -recovery -removal \ -clock_gating_setup -clock_gating_hold \ -max_slew -min_slew \ - -max_transition -min_transition \ + -max_fanout -min_fanout \ + -max_capacitance -min_capacitance \ -min_pulse_width \ - -min_period -max_skew} 1 + -min_period -max_skew \ + -max_transition -min_transition } 1 set setup [info exists flags(-max_delay)] set hold [info exists flags(-min_delay)] @@ -404,6 +418,10 @@ proc_redirect report_check_types { sta_warn "-min_transition deprecated. Use -min_slew." set min_slew 1 } + set max_fanout [info exists flags(-max_fanout)] + set min_fanout [info exists flags(-min_fanout)] + set max_capacitance [info exists flags(-max_capacitance)] + set min_capacitance [info exists flags(-min_capacitance)] set min_pulse_width [info exists flags(-min_pulse_width)] set min_period [info exists flags(-min_period)] set max_skew [info exists flags(-max_skew)] @@ -457,6 +475,18 @@ proc_redirect report_check_types { if { $min_slew } { report_slew_limits $corner "min" $violators $verbose $nosplit } + if { $max_fanout } { +# report_fanout_limits "max" $violators $verbose $nosplit + } + if { $min_fanout } { +# report_fanout_limits "min" $violators $verbose $nosplit + } + if { $max_capacitance } { +# report_capacitance_limits $corner "max" $violators $verbose $nosplit + } + if { $min_capacitance } { +# report_capacitance_limits $corner "min" $violators $verbose $nosplit + } if { $min_pulse_width } { if { $violators } { set checks [min_pulse_width_violations $corner] diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 6162de85..084596f2 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -4613,6 +4613,8 @@ report_delay_calc_cmd(Edge *edge, return Sta::sta()->reportDelayCalc(edge, arc, corner, min_max, digits); } +//////////////////////////////////////////////////////////////// + Pin * pin_min_slew_limit_slack(const Corner *corner, const MinMax *min_max) @@ -4653,6 +4655,82 @@ report_slew_limit_verbose(Pin *pin, //////////////////////////////////////////////////////////////// +Pin * +pin_min_fanout_limit_slack(const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->pinMinFanoutLimitSlack(min_max); +} + +PinSeq * +pin_fanout_limit_violations(const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->pinFanoutLimitViolations(min_max); +} + +void +report_fanout_limit_short_header() +{ + Sta::sta()->reportFanoutLimitShortHeader(); +} + +void +report_fanout_limit_short(Pin *pin, + const MinMax *min_max) +{ + Sta::sta()->reportFanoutLimitShort(pin, min_max); +} + +void +report_fanout_limit_verbose(Pin *pin, + const MinMax *min_max) +{ + Sta::sta()->reportFanoutLimitVerbose(pin, min_max); +} + +//////////////////////////////////////////////////////////////// + +Pin * +pin_min_capacitance_limit_slack(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->pinMinCapacitanceLimitSlack(corner, min_max); +} + +PinSeq * +pin_capacitance_limit_violations(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->pinCapacitanceLimitViolations(corner, min_max); +} + +void +report_capacitance_limit_short_header() +{ + Sta::sta()->reportCapacitanceLimitShortHeader(); +} + +void +report_capacitance_limit_short(Pin *pin, + const Corner *corner, + const MinMax *min_max) +{ + Sta::sta()->reportCapacitanceLimitShort(pin, corner, min_max); +} + +void +report_capacitance_limit_verbose(Pin *pin, + const Corner *corner, + const MinMax *min_max) +{ + Sta::sta()->reportCapacitanceLimitVerbose(pin, corner, min_max); +} + +//////////////////////////////////////////////////////////////// + TmpFloatSeq * design_power(const Corner *corner) { From 1560a77ba5c7265464157dcb041d91425643cb17 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 2 Jun 2020 15:19:09 -0700 Subject: [PATCH 10/70] report -max_fanout --- search/CheckFanoutLimits.cc | 14 ++++---- search/ReportPath.cc | 72 ++++++++++++++++++++----------------- search/ReportPath.hh | 23 ++++++------ search/Sta.cc | 26 ++++++++------ tcl/Search.tcl | 4 +-- tcl/Sta.tcl | 8 ++--- 6 files changed, 82 insertions(+), 65 deletions(-) diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 111f9797..dcc9cb05 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -166,13 +166,15 @@ CheckFanoutLimits::fanout(const Pin *pin) const float fanout = 0; const Network *network = sta_->network(); Net *net = network->net(pin); - NetPinIterator *pin_iter = network->pinIterator(net); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - if (network->isLoad(pin)) - fanout++; + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network->isLoad(pin)) + fanout++; + } + delete pin_iter; } - delete pin_iter; return fanout; } diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 15407693..ce01b5ea 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -143,7 +143,7 @@ ReportPath::~ReportPath() void ReportPath::makeFields() { - field_fanout_ = makeField("fanout", "Fanout", 5, false, nullptr, true); + field_fanout_ = makeField("fanout", "Fanout", 6, false, nullptr, true); field_capacitance_ = makeField("capacitance", "Cap", 6, false, units_->capacitanceUnit(), true); field_slew_ = makeField("slew", "Slew", 6, false, units_->timeUnit(), @@ -1549,43 +1549,43 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, //////////////////////////////////////////////////////////////// void -ReportPath::reportLimitShortHeader(const char *what) +ReportPath::reportLimitShortHeader(const ReportField *field) { string result; - reportLimitShortHeader(what, result); + reportLimitShortHeader(field, result); report_->print(result); } void -ReportPath::reportLimitShortHeader(const char *what, +ReportPath::reportLimitShortHeader(const ReportField *field, string &result) { reportDescription("Pin", result); result += ' '; - reportField("Limit", field_slew_, result); + reportField("Limit", field, result); result += ' '; - reportField(what, field_slew_, result); + reportField(field->title(), field, result); result += ' '; - reportField("Slack", field_slew_, result); + reportField("Slack", field, result); reportEndOfLine(result); - reportDashLine(field_description_->width() + field_slew_->width() * 3 + 3, + reportDashLine(field_description_->width() + field->width() * 3 + 3, result); } void -ReportPath::reportLimitShort(const char *what, +ReportPath::reportLimitShort(const ReportField *field, Pin *pin, float value, float limit, float slack) { string result; - reportLimitShort(what, pin, value, limit, slack, result); + reportLimitShort(field, pin, value, limit, slack, result); report_->print(result); } void -ReportPath::reportLimitShort(const char *what, +ReportPath::reportLimitShort(const ReportField *field, Pin *pin, float value, float limit, @@ -1594,13 +1594,16 @@ ReportPath::reportLimitShort(const char *what, { const char *pin_name = cmd_network_->pathName(pin); reportDescription(pin_name, result); - reportSpaceFieldTime(limit, result); - reportSpaceFieldDelay(value, EarlyLate::late(), result); - reportSpaceSlack(slack, result); + result += ' '; + reportField(limit, field, result); + result += ' '; + reportField(value, field, result); + result += ' '; + reportField(slack, field, result); } void -ReportPath::reportLimitVerbose(const char *what, +ReportPath::reportLimitVerbose(const ReportField *field, Pin *pin, const RiseFall *rf, float value, @@ -1609,12 +1612,12 @@ ReportPath::reportLimitVerbose(const char *what, const MinMax *min_max) { string result; - reportLimitVerbose(what, pin, rf, value, limit, slack, min_max, result); + reportLimitVerbose(field, pin, rf, value, limit, slack, min_max, result); report_->print(result); } void -ReportPath::reportLimitVerbose(const char *what, +ReportPath::reportLimitVerbose(const ReportField *field, Pin *pin, const RiseFall *rf, float value, @@ -1629,25 +1632,24 @@ ReportPath::reportLimitVerbose(const char *what, if (rf) result += rf->shortName(); else - result += " "; + result += ' '; reportEndOfLine(result); result += min_max->asString(); - result += " "; - result += what; - result += " "; - reportSpaceFieldTime(limit, result); + result += ' '; + result += field->name(); + result += ' '; + reportField(limit, field, result); reportEndOfLine(result); - result += what; - result += " "; - reportField(value, field_slew_, result); + result += field->name(); + result += " "; + reportField(value, field, result); reportEndOfLine(result); + reportDashLine(strlen(field->name()) + field->width() + 5, result); - reportDashLine(strlen(what) + field_slew_->width() + 6, result); - - result += "Slack "; - reportSpaceSlack(slack, result); + result += "Slack "; + reportField(slack, field, result); } //////////////////////////////////////////////////////////////// @@ -3221,20 +3223,24 @@ ReportPath::reportFieldDelay(Delay value, void ReportPath::reportField(float value, - ReportField *field, + const ReportField *field, string &result) { if (value == field_blank_) reportFieldBlank(field, result); else { - const char *value_str = field->unit()->asString(value, digits_); + Unit *unit = field->unit(); + const char *value_str = (unit) + ? unit->asString(value, digits_) + // fanout + : stringPrintTmp("%.0f", value); reportField(value_str, field, result); } } void ReportPath::reportField(const char *value, - ReportField *field, + const ReportField *field, string &result) { if (field->leftJustify()) @@ -3246,7 +3252,7 @@ ReportPath::reportField(const char *value, } void -ReportPath::reportFieldBlank(ReportField *field, +ReportPath::reportFieldBlank(const ReportField *field, string &result) { result += field->blank(); diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 7ae4406c..8f24cc9d 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -137,28 +137,28 @@ public: void reportVerbose(MaxSkewCheck *check, string &result); - void reportLimitShortHeader(const char *what); - void reportLimitShortHeader(const char *what, + void reportLimitShortHeader(const ReportField *field); + void reportLimitShortHeader(const ReportField *field, string &result); - void reportLimitShort(const char *what, + void reportLimitShort(const ReportField *field, Pin *pin, float value, float limit, float slack); - void reportLimitShort(const char *what, + void reportLimitShort(const ReportField *field, Pin *pin, float value, float limit, float slack, string &result); - void reportLimitVerbose(const char *what, + void reportLimitVerbose(const ReportField *field, Pin *pin, const RiseFall *rf, float value, float limit, float slack, const MinMax *min_max); - void reportLimitVerbose(const char *what, + void reportLimitVerbose(const ReportField *field, Pin *pin, const RiseFall *rf, float value, @@ -166,6 +166,9 @@ public: float slack, const MinMax *min_max, string &result); + ReportField *fieldSlew() const { return field_slew_; } + ReportField *fieldFanout() const { return field_fanout_; } + ReportField *fieldCapacitance() const { return field_capacitance_; } protected: void makeFields(); @@ -454,12 +457,12 @@ protected: ReportField *field, string &result); void reportField(float value, - ReportField *field, + const ReportField *field, string &result); void reportField(const char *value, - ReportField *field, + const ReportField *field, string &result); - void reportFieldBlank(ReportField *field, + void reportFieldBlank(const ReportField *field, string &result); void reportDashLine(string &result); void reportDashLine(int line_width, @@ -568,7 +571,7 @@ public: void setWidth(int width); bool leftJustify() const { return left_justify_; } Unit *unit() const { return unit_; } - const char *blank() { return blank_; } + const char *blank() const { return blank_; } void setEnabled(bool enabled); bool enabled() const { return enabled_; } diff --git a/search/Sta.cc b/search/Sta.cc index c6811440..3551f627 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4858,7 +4858,7 @@ Sta::pinSlewLimitViolations(const Corner *corner, void Sta::reportSlewLimitShortHeader() { - report_path_->reportLimitShortHeader("Slew"); + report_path_->reportLimitShortHeader(report_path_->fieldSlew()); } void @@ -4872,7 +4872,8 @@ Sta::reportSlewLimitShort(Pin *pin, float limit, slack; check_slew_limits_->checkSlews(pin, corner, min_max, corner1, rf, slew, limit, slack); - report_path_->reportLimitShort("slew", pin, delayAsFloat(slew), limit, slack); + report_path_->reportLimitShort(report_path_->fieldSlew(), pin, + delayAsFloat(slew), limit, slack); } void @@ -4886,7 +4887,8 @@ Sta::reportSlewLimitVerbose(Pin *pin, float limit, slack; check_slew_limits_->checkSlews(pin, corner, min_max, corner1, rf, slew, limit, slack); - report_path_->reportLimitVerbose("slew", pin, rf, delayAsFloat(slew), + report_path_->reportLimitVerbose(report_path_->fieldSlew(), pin, rf, + delayAsFloat(slew), limit, slack, min_max); } @@ -4933,7 +4935,7 @@ Sta::pinFanoutLimitViolations(const MinMax *min_max) void Sta::reportFanoutLimitShortHeader() { - report_path_->reportLimitShortHeader("Fanout"); + report_path_->reportLimitShortHeader(report_path_->fieldFanout()); } void @@ -4943,7 +4945,8 @@ Sta::reportFanoutLimitShort(Pin *pin, float fanout, limit, slack; check_fanout_limits_->checkFanout(pin, min_max, fanout, limit, slack); - report_path_->reportLimitShort("fanout", pin, fanout, limit, slack); + report_path_->reportLimitShort(report_path_->fieldFanout(), + pin, fanout, limit, slack); } void @@ -4953,7 +4956,8 @@ Sta::reportFanoutLimitVerbose(Pin *pin, float fanout, limit, slack; check_fanout_limits_->checkFanout(pin, min_max, fanout, limit, slack); - report_path_->reportLimitVerbose("fanout", pin, nullptr, fanout, + report_path_->reportLimitVerbose(report_path_->fieldFanout(), + pin, nullptr, fanout, limit, slack, min_max); } @@ -4999,7 +5003,7 @@ Sta::pinCapacitanceLimitViolations(const Corner *corner, void Sta::reportCapacitanceLimitShortHeader() { - report_path_->reportLimitShortHeader("Capacitance"); + report_path_->reportLimitShortHeader(report_path_->fieldCapacitance()); } void @@ -5013,7 +5017,8 @@ Sta::reportCapacitanceLimitShort(Pin *pin, check_capacitance_limits_->checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); - report_path_->reportLimitShort("capacitance", pin, capacitance, limit, slack); + report_path_->reportLimitShort(report_path_->fieldCapacitance(), + pin, capacitance, limit, slack); } void @@ -5027,8 +5032,9 @@ Sta::reportCapacitanceLimitVerbose(Pin *pin, check_capacitance_limits_->checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); - report_path_->reportLimitVerbose("capacitance", pin, rf, - capacitance, limit, slack, min_max); + report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), + pin, rf, capacitance, + limit, slack, min_max); } void diff --git a/tcl/Search.tcl b/tcl/Search.tcl index d22d5166..de21e0f9 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -247,7 +247,7 @@ proc parse_report_path_options { cmd args_var default_format set path_options(num_fmt) "%.${digits}f" set_report_path_digits $digits - # Numberic field width expands with digits. + # Numeric field width expands with digits. set field_width [expr $digits + $report_path_field_width_extra] if { $report_sigmas } { set delay_field_width [expr $field_width * 3 + $report_path_field_width_extra] @@ -257,7 +257,7 @@ proc parse_report_path_options { cmd args_var default_format foreach field {total incr} { set_report_path_field_width $field $delay_field_width } - foreach field {capacitance slew} { + foreach field {capacitance slew fanout} { set_report_path_field_width $field $field_width } diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index e2d94c9c..33493263 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -389,8 +389,8 @@ proc_redirect report_check_types { set min_pulse_width 1 set min_period 1 set max_skew 1 - set max_fanout 1 - set max_capacitance 1 + set max_fanout 0 + set max_capacitance 0 } else { parse_key_args "report_check_types" args keys {} \ flags {-max_delay -min_delay -recovery -removal \ @@ -476,10 +476,10 @@ proc_redirect report_check_types { report_slew_limits $corner "min" $violators $verbose $nosplit } if { $max_fanout } { -# report_fanout_limits "max" $violators $verbose $nosplit + report_fanout_limits "max" $violators $verbose $nosplit } if { $min_fanout } { -# report_fanout_limits "min" $violators $verbose $nosplit + report_fanout_limits "min" $violators $verbose $nosplit } if { $max_capacitance } { # report_capacitance_limits $corner "max" $violators $verbose $nosplit From 1aadb2d895ce10f48c1314bf4f743618444b2259 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 2 Jun 2020 16:28:12 -0700 Subject: [PATCH 11/70] report_check_types format tweaks --- search/ReportPath.cc | 17 ++++++++++++++--- tcl/Search.tcl | 2 +- tcl/Sta.tcl | 8 +++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index ce01b5ea..74ee3a9d 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1600,6 +1600,10 @@ ReportPath::reportLimitShort(const ReportField *field, reportField(value, field, result); result += ' '; reportField(slack, field, result); + result += (slack >= 0.0) + ? " (MET)" + : " (VIOLATED)"; + reportEndOfLine(result); } void @@ -1632,7 +1636,7 @@ ReportPath::reportLimitVerbose(const ReportField *field, if (rf) result += rf->shortName(); else - result += ' '; + result += ' '; reportEndOfLine(result); result += min_max->asString(); @@ -1646,10 +1650,17 @@ ReportPath::reportLimitVerbose(const ReportField *field, result += " "; reportField(value, field, result); reportEndOfLine(result); - reportDashLine(strlen(field->name()) + field->width() + 5, result); + int name_width = strlen(field->name()) + 5; + reportDashLine(name_width + field->width(), result); - result += "Slack "; + result += "Slack"; + for (int i = strlen("Slack"); i < name_width; i++) + result += ' '; reportField(slack, field, result); + result += (slack >= 0.0) + ? " (MET)" + : " (VIOLATED)"; + reportEndOfLine(result); } //////////////////////////////////////////////////////////////// diff --git a/tcl/Search.tcl b/tcl/Search.tcl index de21e0f9..23a06b0e 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -257,7 +257,7 @@ proc parse_report_path_options { cmd args_var default_format foreach field {total incr} { set_report_path_field_width $field $delay_field_width } - foreach field {capacitance slew fanout} { + foreach field {capacitance slew} { set_report_path_field_width $field $field_width } diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index 33493263..d42ee63d 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -46,7 +46,7 @@ proc define_report_path_fields {} { set_report_path_field_properties "incr" "Delay" $width 0 set_report_path_field_properties "capacitance" "Cap" $width 0 set_report_path_field_properties "slew" "Slew" $width 0 - set_report_path_field_properties "fanout" "Fanout" 5 0 + set_report_path_field_properties "fanout" "Fanout" 6 0 set_report_path_field_properties "edge" " " 1 0 set_report_path_field_properties "case" " " 11 0 } @@ -390,7 +390,9 @@ proc_redirect report_check_types { set min_period 1 set max_skew 1 set max_fanout 0 + set min_fanout 0 set max_capacitance 0 + set min_capacitance 0 } else { parse_key_args "report_check_types" args keys {} \ flags {-max_delay -min_delay -recovery -removal \ @@ -482,10 +484,10 @@ proc_redirect report_check_types { report_fanout_limits "min" $violators $verbose $nosplit } if { $max_capacitance } { -# report_capacitance_limits $corner "max" $violators $verbose $nosplit + report_capacitance_limits $corner "max" $violators $verbose $nosplit } if { $min_capacitance } { -# report_capacitance_limits $corner "min" $violators $verbose $nosplit + report_capacitance_limits $corner "min" $violators $verbose $nosplit } if { $min_pulse_width } { if { $violators } { From 6bdf3fcfeddb59b34138a238e5fdf04d1facf085 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 2 Jun 2020 18:11:50 -0700 Subject: [PATCH 12/70] enable max_fanout, max_cap checks --- search/CheckCapacitanceLimits.cc | 5 +++-- search/CheckFanoutLimits.cc | 25 ++++++++++++++----------- search/CheckSlewLimits.cc | 4 ++-- tcl/Sta.tcl | 4 ---- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index 80b75aa1..fffda964 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -263,7 +263,7 @@ CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(const Corner *corner, { init(min_max); const Network *network = sta_->network(); - Pin *min_slack_pin = 0; + Pin *min_slack_pin = nullptr; float min_slack = MinMax::min()->initValue(); LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); while (inst_iter->hasNext()) { @@ -294,7 +294,8 @@ CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(Instance *inst, float capacitance, limit, slack; checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); if (rf - && (min_slack_pin == 0 + && !fuzzyInf(slack) + && (min_slack_pin == nullptr || slack < min_slack)) { min_slack_pin = pin; min_slack = slack; diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index dcc9cb05..00550cd9 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -100,10 +100,9 @@ CheckFanoutLimits::checkFanout(const Pin *pin, float limit1; bool limit1_exists; findLimit(pin, min_max, limit1, limit1_exists); - if (limit1_exists) { + if (limit1_exists) checkFanout(pin, min_max, limit1, fanout, slack, limit); - } } void @@ -133,6 +132,7 @@ CheckFanoutLimits::findLimit(const Pin *pin, if (port) { port->fanoutLimit(min_max, limit, exists); if (!exists + && min_max == MinMax::max() && port->direction()->isAnyOutput()) port->libertyLibrary()->defaultMaxFanout(limit, exists); } @@ -220,7 +220,7 @@ CheckFanoutLimits::pinMinFanoutLimitSlack(const MinMax *min_max) { init(min_max); const Network *network = sta_->network(); - Pin *min_slack_pin = 0; + Pin *min_slack_pin = nullptr; float min_slack = MinMax::min()->initValue(); LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); while (inst_iter->hasNext()) { @@ -230,7 +230,7 @@ CheckFanoutLimits::pinMinFanoutLimitSlack(const MinMax *min_max) delete inst_iter; // Check top level ports. pinMinFanoutLimitSlack(network->topInstance(), min_max, - min_slack_pin, min_slack); + min_slack_pin, min_slack); return min_slack_pin; } @@ -245,13 +245,16 @@ CheckFanoutLimits::pinMinFanoutLimitSlack(Instance *inst, InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - float fanout; - float limit, slack; - checkFanout(pin, min_max, fanout, limit, slack); - if (min_slack_pin == 0 - || slack < min_slack) { - min_slack_pin = pin; - min_slack = slack; + if (network->direction(pin)->isAnyOutput()) { + float fanout; + float limit, slack; + checkFanout(pin, min_max, fanout, limit, slack); + if (!fuzzyInf(slack) + && (min_slack_pin == nullptr + || slack < min_slack)) { + min_slack_pin = pin; + min_slack = slack; + } } } delete pin_iter; diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index 2aa2253f..0e505158 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -346,7 +346,7 @@ CheckSlewLimits::pinMinSlewLimitSlack(const Corner *corner, { init(min_max); const Network *network = sta_->network(); - Pin *min_slack_pin = 0; + Pin *min_slack_pin = nullptr; float min_slack = MinMax::min()->initValue(); LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); while (inst_iter->hasNext()) { @@ -378,7 +378,7 @@ CheckSlewLimits::pinMinSlewLimitSlack(Instance *inst, float limit, slack; checkSlews(pin, corner, min_max, corner1, rf, slew, limit, slack); if (rf - && (min_slack_pin == 0 + && (min_slack_pin == nullptr || slack < min_slack)) { min_slack_pin = pin; min_slack = slack; diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index d42ee63d..6a9264cd 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -389,10 +389,6 @@ proc_redirect report_check_types { set min_pulse_width 1 set min_period 1 set max_skew 1 - set max_fanout 0 - set min_fanout 0 - set max_capacitance 0 - set min_capacitance 0 } else { parse_key_args "report_check_types" args keys {} \ flags {-max_delay -min_delay -recovery -removal \ From 0fed55b362f3e03493e228deb9db39cab4ca3150 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 2 Jun 2020 19:17:52 -0700 Subject: [PATCH 13/70] tcl delays_invalid --- tcl/StaTcl.i | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 084596f2..ffff36a5 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -5291,6 +5291,13 @@ arrivals_invalid() sta->arrivalsInvalid(); } +void +delays_invalid() +{ + Sta *sta = Sta::sta(); + sta->delaysInvalid(); +} + %} // inline //////////////////////////////////////////////////////////////// From 810e4716cddeaad1ec0bec29559fafcc1fb663cd Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 3 Jun 2020 18:37:05 -0700 Subject: [PATCH 14/70] exception -thru seg fault --- sdc/ExceptionPath.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 086d7e15..c65c7248 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -1931,8 +1931,8 @@ ExceptionThru::matches(const Pin *from_pin, EdgePins edge_pins(const_cast(from_pin), const_cast(to_pin)); return ((pins_ && pins_->hasKey(const_cast(to_pin))) || (edges_ && edges_->hasKey(&edge_pins)) - || (nets_ && nets_->hasKey(network->net(to_pin))) - || (insts_ && insts_->hasKey(network->instance(to_pin)))) + || (nets_ && to_pin && nets_->hasKey(network->net(to_pin))) + || (insts_ && to_pin && insts_->hasKey(network->instance(to_pin)))) && rf_->matches(to_rf); } From 5fc4fc17b66c0f319d4ed160da137aa8aac43b22 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 5 Jun 2020 11:50:18 -0700 Subject: [PATCH 15/70] pocv generated clk latency --- search/Crpr.cc | 1 + search/PathEnd.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/search/Crpr.cc b/search/Crpr.cc index 3d9fc1ee..31d032cf 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -302,6 +302,7 @@ CheckCrpr::findCrpr1(const PathVertex *src_clk_path, float tgt_clk_time = tgt_clk_path->clkEdge(this)->time(); float crpr_mean = abs(delayAsFloat(src_arrival) - src_clk_time - (delayAsFloat(tgt_arrival) - tgt_clk_time)); + // Remove the sigma from both source and target path arrivals. float crpr_sigma2 = delaySigma2(src_arrival, src_el) + delaySigma2(tgt_arrival, tgt_el); return makeDelay2(crpr_mean, -crpr_sigma2, -crpr_sigma2); diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 4084f20d..57a014c1 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -365,7 +365,7 @@ PathEnd::checkTgtClkDelay(const PathVertex *tgt_clk_path, Arrival path_insertion = search->clockInsertion(tgt_clk, tgt_src_pin, tgt_clk_rf, min_max, min_max, tgt_path_ap); - latency = clk_arrival - tgt_clk_edge->time() - path_insertion; + latency = delayRemove(clk_arrival - tgt_clk_edge->time(), path_insertion); } else // Ideal clock. From ff2e9d521af319f813d501d7dd7b26300706d548 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 5 Jun 2020 22:02:54 -0700 Subject: [PATCH 16/70] wireload for fanout=0 NaN --- dcalc/DmpDelayCalc.cc | 3 ++- dcalc/LumpedCapDelayCalc.cc | 3 ++- parasitics/ConcreteParasitics.cc | 11 +++++++---- parasitics/EstimateParasitics.cc | 3 ++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index acaa5880..77ea8c11 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -244,7 +244,8 @@ DmpCeffTwoPoleDelayCalc::findParasitic(const Pin *drvr_pin, parasitic_ap); // Estimated parasitics are not recorded in the "database", so // it for deletion after the drvr pin delay calc is finished. - unsaved_parasitics_.push_back(parasitic); + if (parasitic) + unsaved_parasitics_.push_back(parasitic); return parasitic; } } diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 79b8710c..f4292a1a 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -90,7 +90,8 @@ LumpedCapDelayCalc::findParasitic(const Pin *drvr_pin, parasitic_ap); // Estimated parasitics are not recorded in the "database", so // it for deletion after the drvr pin delay calc is finished. - unsaved_parasitics_.push_back(parasitic); + if (parasitic) + unsaved_parasitics_.push_back(parasitic); return parasitic; } } diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index f09f76d3..abc8baaa 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -1670,10 +1670,13 @@ ConcreteParasitics::estimatePiElmore(const Pin *drvr_pin, c2, rpi, c1, elmore_res, elmore_cap, elmore_use_load_cap); - return new ConcretePiElmoreEstimated(c2, rpi, c1, elmore_res, elmore_cap, - elmore_use_load_cap, - rf, op_cond, corner, min_max, - sdc_); + if (c1 > 0.0 || c2 > 0.0) + return new ConcretePiElmoreEstimated(c2, rpi, c1, elmore_res, elmore_cap, + elmore_use_load_cap, + rf, op_cond, corner, min_max, + sdc_); + else + return nullptr; } //////////////////////////////////////////////////////////////// diff --git a/parasitics/EstimateParasitics.cc b/parasitics/EstimateParasitics.cc index a9afef0a..3539811a 100644 --- a/parasitics/EstimateParasitics.cc +++ b/parasitics/EstimateParasitics.cc @@ -150,7 +150,8 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, float &elmore_cap, bool &elmore_use_load_cap) { - if (wireload_res == 0.0) { + if (wireload_res == 0.0 + || fanout == 0) { // No resistance, so load is capacitance only. c2 = wireload_cap + net_pin_cap; rpi = 0.0; From 0b4cd9ab242fee6548384e5d13e5a0c6a173dc56 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 6 Jun 2020 13:26:18 -0700 Subject: [PATCH 17/70] min_period -> min period --- search/ReportPath.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 74ee3a9d..d13eb4d4 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1368,7 +1368,7 @@ ReportPath::reportVerbose(MinPeriodCheck *check, string &result) reportEndOfLine(result); reportLine("Period", check->period(), EarlyLate::early(), result); - reportLine("min_period", -check->minPeriod(this), + reportLine("min period", -check->minPeriod(this), EarlyLate::early(), result); reportDashLine(result); reportSlack(check->slack(this), result); From ed9f69acca2a22b05b5c56df0bad182a200440bb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 Jun 2020 16:21:31 -0700 Subject: [PATCH 18/70] report min_period Period -> period --- search/CheckFanoutLimits.cc | 2 ++ search/ReportPath.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 00550cd9..93f5e3ba 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -83,6 +83,8 @@ CheckFanoutLimits::init(const MinMax *min_max) top_limit, top_limit_exists); top_limit_= top_limit; top_limit_exists_ = top_limit_exists; + if (top_limit_exists_) + printf("luse\n"); } void diff --git a/search/ReportPath.cc b/search/ReportPath.cc index d13eb4d4..fc8424ae 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1367,7 +1367,7 @@ ReportPath::reportVerbose(MinPeriodCheck *check, string &result) result += pin_name; reportEndOfLine(result); - reportLine("Period", check->period(), EarlyLate::early(), result); + reportLine("period", check->period(), EarlyLate::early(), result); reportLine("min period", -check->minPeriod(this), EarlyLate::early(), result); reportDashLine(result); From 88c2c61ed768589abea25e6f73c8926e991011d0 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 Jun 2020 16:21:51 -0700 Subject: [PATCH 19/70] report min_period Period -> period --- search/CheckFanoutLimits.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 93f5e3ba..00550cd9 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -83,8 +83,6 @@ CheckFanoutLimits::init(const MinMax *min_max) top_limit, top_limit_exists); top_limit_= top_limit; top_limit_exists_ = top_limit_exists; - if (top_limit_exists_) - printf("luse\n"); } void From 6b4f2cc1305c0b8a761ad3a02284a17f8efd1017 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 Jun 2020 17:16:15 -0700 Subject: [PATCH 20/70] remove set_max_transition pin support --- doc/OpenSTA.odt | Bin 76409 -> 76405 bytes include/sta/Sdc.hh | 10 ------ include/sta/Sta.hh | 3 -- sdc/Sdc.cc | 34 -------------------- sdc/WriteSdc.cc | 18 ----------- search/CheckSlewLimits.cc | 63 ++++++++++++++++---------------------- search/Sta.cc | 8 ----- tcl/Sdc.tcl | 9 ++---- tcl/StaTcl.i | 9 ------ 9 files changed, 29 insertions(+), 125 deletions(-) diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 92407f08f6f1a8698ea0eb3047570b8edd8293ff..f1db19de4ac2a8bde5d008b3d53a6230c54c002e 100644 GIT binary patch delta 21969 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%0A+CP$f}5Wi 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 delta 22012 zcmXuoQ*>rsv^DD3X2rH`+qP|+Z*1GPZ9A#hPQ|u;zPqV@UIu$# z27_0W0R=+?0)hepGJ*?FgqH^Wk49}~mH#n0UWEYAC_N&G6lTGY-A$Ul?G1f~-bN8& zX;N-{>l87{meyuWgwYq^P+)9iRNR{fgTWuLt_>Ab`UVtkjCIP+avBdd;Q(tu5SG*nSGc7CwZnd4IEGZ5sitW;(8MHb z{t*$D-@o{X4EIJGWL9q(^#@{Aq$6^lb}7=71GS^7S~Q4bWrv(~Q;5?}cuj$I2W?sk z<|MGN(3}ahId@R$b|D4ol5T@e^?1`?{IR+feX7zjun6bR`5?HLLR z>VNj!45C(S0Pq_dr=GR|n*rI`|B=#xBgIvfx2#Z7%SOmo9&iE`8^cQ7<}?d?oIoCq zx~of82R}NRv>Rvh3zkUdX)J&-(f#iS^gZ0J^_VmK(cra_({vl@cw_K^+tc)S#)E*p z9z)VD;#)*6h$%Mm?}}0%lY!e>rO1%?`Q1Up4pHn$AqT@?faYbePhi5d`7i(3Z&wU+ zq+0{`-gDT8MFk?;Hf9&!oh;Q#h9T?%kHD}OPFf0MkX%ZD(--%_h&E}P9MY-|sA()@ z-9(TW0D-V8s2>u4gcFpBJy~I8m;eH%`!{O&^A&S4>V@ah*b~?3s6VbtTW>;t;+T`q zQcvP;-99IPW5K=F4xcY{(gTIOmP^ngkKby5*;2OekWhP0;RR-qMjXxPr7yMuaeEODMquXUl`OH-eaRQxxt zZ+6Q&fpBlkLnrgGy)kY$(nBpt_)2=|o+p&@%m z2TTxup|a>FO_&lHo#wW7Z|hBaC!*~uQ~Rd*R{1_)_d7B}$mw*@+JeZS1<3+k&U($P zp^Gb$Bhj^Q6i)PjaNHtfT6xSgle&xV+9XQ#tRWHn`J`scZJgDCHnzeynbSmZaoE-T zrikeBvmziM6G%>BU?I;!oS9SDLC)rGKyBH*zzvSPqg)LbHCg<9Ujz*XRv?hfx?Zqd z*v15ab~{AxSrxdUVDKDbS>ag1$#@164h(BVBHK9NrLH;Fh6z>*!1A!}l1)cYpT~7k zmYQ)BtXb2IE9n*v%a`YZEJHjN?RY2uL8`(13Q<;nLhbJI{B|$qvJd$(l#aaofe?G0 z?Y-W`Vqc7J+}oO8hQ`28g+_p$dzc1AV-)~gK!UrVME@LwrmMArHHUbTqS<8jO>LCl z?M~g8g|uDZjP%FZzuvGpy}Ep+(*Ocuxmp z=llPV`Im3Yp#>8<$wdn@^r=JzVEQ!4_W;-Ewid9hlq#eQ*RoO=Khu8=|u z-pcvq=DmmjW%;lN$Pl;+vYOhNFwZBET|rHq?)7L=+2e&Gmc<){Kyz%uQ%feAk|Fr} zeV24ATz*D>7n|$)R*$nb1eOGpxA76lJ3Sp6kU?oe67D+i-o}otcW@qq(_M~W2?bP> zUnEGb11zB!Pg)41A{y#KLmR+?n5$H59|KPcrZE*sTFEpUA+eOP%L5eJe&3ssU42-R zCr0$m9R7^`vJsNYMnG}}u3O_AZnm%Gp;*z)G#tRTaH6@qJ`z0fn5a8a1J^b-#56K7 za)hycn%}e5Pe+eB6zi1=K_Yye$RhZ)h};X?rb8QA?B~9{|IpSdnF>IGag@}t4n|Xp z8_G$?o**|0XR2ZZx}iEP$c|pTbKVJOny|W`fl{T*y1g(?v&ZLYZiz*G!dV`?Gfb|Z z`lkLc+@#p^0ZR?HWy3*|v(h!;OMXFB3-F0l5rm{aw2oCgo!5k0P`Hs>b)0idbo>yT z(@eO-Y&`x81L--|kPA3Q>UK_$>pFicMf6}+U;~xJq1`w7dj$T1c%sMqLKXH0grgpr zds42FPLq2q9%C30r-oD}!AvNekL&xT2);DO-zdgNA+Eo52x&&XKZ`eM8Jbo=O*AOD zjzQ6?+H<6{Uk7eTlBA1oC0r8kCsx$DFCo3E#|}ys?HwOTs%Lc zjhxK|<7cX?jk@<#!*gJB#F7eHOdKMy00@+bc47t1SN*0m11?%t$;EEeEQcKn2tn@5J<{(f&?^1qQGPni3=@?AOonLUO_J)YzV7Vd8Y17>Ct$HWb>> zo1bMx->1BH!s#*7ps;VN=HkvdnGT7;5+$8BpIK$S(%Zy1v4W^>X1brH{P zGNEiKSCI}CKJ79aH?)*%w}l904pUboy_6ihsvy=`BLrB>Eb!7qbkW(Y6GfPF=?Ksi z^c^4A>ic!z=Xl`tswfVfLn)41mse2*k)gx5xXzLs@l^W#99OD-)>Ns|MWt4SW+=BJ zsaIhbORosW7-F0%S5@fCs5t~*jG_=jVc$ftRec44d`Eqwj<9`MSn&e2O;EzXR9t^D zeDbc?vH=|5H}?rT9tP-e1Yx&_)HrIXL%7O9$x^LxYO({j^7^Cu=pXn39{bWkT!7nH#GoXa3Qf znKfkFsHGCtWR9pFv==XK`m};LL0ekKDH|omSH2W@rsAXgz_agq;9%72TL^kfW9S3= z&;rQ73hH!DEZ+&qxCnH(3!ZCOdjH7D&@dw6lP5z1=ld-!!o1v&v>2gf5cFG`xfk9F z=+}larAmwLOSoas3KUD^^sn~bNO*_Yv0B8pVzL}$9zg$`W{1I3*I~`;cpnP0vuN3P zC~P{!^j`kD5C2G{0d3F`#;u>e11EMsNCW=;v-j`|8o9S~_E-EDstE$tuRu7_sWb-} ziUlf6iIp<2zyCzW-0wNFE3-@b4Wu21!Xj`+q1E~$7 zE+bWCq-%PTW4K86E1LR}lJ-XjItMWSbo*2I*Q z#!wu3V?^kef@c+sSoV&+Z}qR&i4`y;uTU|>W#|dfo%|=4@|qSefXaC(V`i-ISMtK) zlb~fHr_rZaY%KM6zG)l_A2@sQ>qMBqV7o^iyO{sGXUD2>46u%l>o*1wylNuE`tA>fPJ=3#|}G=2n~a%7fiL08?$UX zOfBF^X`n)7-EE#@*@Kbp?&lg$Qi`%QVOL6y8TKjM9)d ztETWbyiy}gB$PY8Mn$hxn<6dFT~olaf&H}__(ES)fqg@g-TR?~EgJyH{K0!0A!|rJ z^tX~8)q+uTtrZE8sI0lN`K{0*y|h2){_ou0B$3Uj^<&~YklNX-Kb^}0=A5;5F|@6P zc{tnkXY%6oaQ5oJHs2G+UTTCB%0D}8+73O88mF||M+kQkJ39&GbO~3$y54-#o!e+! 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 - sort(pins, PinPathNameLess(network_)); - ConstPinSeq::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - float slew; - bool exists; - sdc_->slewLimit(pin, min_max, slew, exists); - if (exists) { - fprintf(stream_, "set_max_transition "); - writeTime(slew); - fprintf(stream_, " "); - writeGetPin(pin, false); - fprintf(stream_, "\n"); - } - } - writeClkSlewLimits(); } diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index 0e505158..2975bc53 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -181,6 +181,10 @@ CheckSlewLimits::findLimit(const Pin *pin, { exists = false; if (!sta_->graphDelayCalc()->isIdealClk(vertex)) { + // Default to top ("design") limit. + exists = top_limit_exists_; + limit = top_limit_; + const Network *network = sta_->network(); Sdc *sdc = sta_->sdc(); bool is_clk = sta_->search()->isClock(vertex); @@ -203,24 +207,21 @@ CheckSlewLimits::findLimit(const Pin *pin, exists = true; } } - if (!exists) { - // Default to top ("design") limit. - exists = top_limit_exists_; - limit = top_limit_; - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->slewLimit(port, min_max, limit1, exists1); - // Use the tightest limit. - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->slewLimit(port, min_max, limit1, exists1); + // Use the tightest limit. + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; } - else { - sdc->slewLimit(pin, min_max, - limit1, exists1); + } + else { + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->slewLimit(min_max, limit1, exists1); // Use the tightest limit. if (exists1 && (!exists @@ -228,26 +229,14 @@ CheckSlewLimits::findLimit(const Pin *pin, limit = limit1; exists = true; } - LibertyPort *port = network->libertyPort(pin); - if (port) { - port->slewLimit(min_max, limit1, exists1); - // Use the tightest limit. - if (exists1) { - if (!exists - || min_max->compare(limit, limit1)) { - limit = limit1; - exists = true; - } - } - else if (port->direction()->isAnyOutput() - && min_max == MinMax::max()) { - port->libertyLibrary()->defaultMaxSlew(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } + if (port->direction()->isAnyOutput() + && min_max == MinMax::max()) { + port->libertyLibrary()->defaultMaxSlew(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; } } } diff --git a/search/Sta.cc b/search/Sta.cc index 3551f627..ef93de5d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -998,14 +998,6 @@ Sta::setSlewLimit(Port *port, sdc_->setSlewLimit(port, min_max, slew); } -void -Sta::setSlewLimit(Pin *pin, - const MinMax *min_max, - float slew) -{ - sdc_->setSlewLimit(pin, min_max, slew); -} - void Sta::setSlewLimit(Cell *cell, const MinMax *min_max, diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl index 8ead4e8c..f2ae6f89 100644 --- a/tcl/Sdc.tcl +++ b/tcl/Sdc.tcl @@ -2600,7 +2600,7 @@ proc set_max_transition { args } { set slew [time_ui_sta $slew] set objects [lindex $args 1] - parse_clk_cell_port_pin_args $objects clks cells ports pins + parse_clk_cell_port_args $objects clks cells ports set tr [parse_rise_fall_flags flags] @@ -2617,12 +2617,12 @@ proc set_max_transition { args } { lappend path_types "data" } - if { ($ports != {} || $pins != {} || $cells != {}) \ + if { ($ports != {} || $cells != {}) \ && ([info exists flags(-clock_path)] \ || [info exists flags(-data_path)] || [info exists flags(-rise)] || [info exists flags(-fall)]) } { - sta_warn "-data_path, -clock_path, -rise, -fall ignored for ports, pins and designs." + sta_warn "-data_path, -clock_path, -rise, -fall ignored for ports and designs." } # -clock_path/-data_path and transition only apply to clock objects. @@ -2637,9 +2637,6 @@ proc set_max_transition { args } { foreach port $ports { set_slew_limit_port $port "max" $slew } - foreach pin $pins { - set_slew_limit_pin $pin "max" $slew - } } ################################################################ diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index ffff36a5..444d27ac 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -3516,15 +3516,6 @@ set_slew_limit_port(Port *port, Sta::sta()->setSlewLimit(port, min_max, slew); } -void -set_slew_limit_pin(Pin *pin, - const MinMax *min_max, - float slew) -{ - cmdLinkedNetwork(); - Sta::sta()->setSlewLimit(pin, min_max, slew); -} - void set_slew_limit_cell(Cell *cell, const MinMax *min_max, From 32adfad72ecdd2fd74ebf5543dc7c6900c4c9637 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 Jun 2020 20:11:15 -0700 Subject: [PATCH 21/70] liberty default_fanout_load, fanout_load for report -max_fanout --- include/sta/Liberty.hh | 11 +++++- include/sta/Sta.hh | 12 +++--- liberty/Liberty.cc | 31 +++++++++++++++- liberty/LibertyReader.cc | 16 ++++++++ liberty/LibertyReaderPvt.hh | 1 + search/CheckCapacitanceLimits.cc | 44 +++++++++++++++------- search/CheckFanoutLimits.cc | 64 +++++++++++++++++++++++--------- search/CheckFanoutLimits.hh | 2 +- search/CheckSlewLimits.cc | 18 +++------ search/Sta.cc | 12 +++--- 10 files changed, 151 insertions(+), 60 deletions(-) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 205cb4ec..4f39606e 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -202,7 +202,9 @@ public: void defaultMaxFanout(float &fanout, bool &exists) const; void setDefaultMaxFanout(float fanout); - float defaultFanoutLoad() const { return default_fanout_load_; } + void defaultFanoutLoad(// Return values. + float &fanout, + bool &exists) const; void setDefaultFanoutLoad(float load); // Logic thresholds. @@ -307,6 +309,7 @@ protected: RiseFallValues default_inout_pin_res_; RiseFallValues default_output_pin_res_; float default_fanout_load_; + bool default_fanout_load_exists_; float default_max_cap_; bool default_max_cap_exists_; float default_max_fanout_; @@ -623,6 +626,10 @@ public: LibertyLibrary *libertyLibrary() const { return liberty_cell_->libertyLibrary(); } LibertyPort *findLibertyMember(int index) const; LibertyPort *findLibertyBusBit(int index) const; + void fanoutLoad(// Return values. + float &fanout_load, + bool &exists) const; + void setFanoutLoad(float fanout_load); float capacitance(const RiseFall *rf, const MinMax *min_max) const; void capacitance(const RiseFall *rf, @@ -748,6 +755,8 @@ protected: RiseFallMinMax capacitance_; MinMaxFloatValues slew_limit_; // inputs and outputs MinMaxFloatValues cap_limit_; // outputs + float fanout_load_; // inputs + bool fanout_load_exists_; MinMaxFloatValues fanout_limit_; // outputs float min_period_; float min_pulse_width_[RiseFall::index_count]; diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index a9f6aa5a..cef2e98c 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -647,12 +647,12 @@ public: const MinMax *min_max); void reportFanoutLimitVerbose(Pin *pin, const MinMax *min_max); - void checkFanouts(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack); + void checkFanout(const Pin *pin, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack); void checkCapacitanceLimitPreamble(); // Return the pin with the min/max capacitance limit slack. diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 99958888..4c328b77 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -71,6 +71,7 @@ LibertyLibrary::LibertyLibrary(const char *name, default_output_pin_cap_(0.0), default_bidirect_pin_cap_(0.0), default_fanout_load_(0.0), + default_fanout_load_exists_(false), default_max_cap_(0.0), default_max_cap_exists_(false), default_max_fanout_(0.0), @@ -425,10 +426,20 @@ LibertyLibrary::setDefaultMaxCapacitance(float cap) default_max_cap_exists_ = true; } +void +LibertyLibrary::defaultFanoutLoad(// Return values. + float &fanout, + bool &exists) const +{ + fanout = default_fanout_load_; + exists = default_fanout_load_exists_; +} + void LibertyLibrary::setDefaultFanoutLoad(float load) { default_fanout_load_ = load; + default_fanout_load_exists_ = true; } void @@ -1811,8 +1822,8 @@ LibertyPort::LibertyPort(LibertyCell *cell, function_(nullptr), tristate_enable_(nullptr), scaled_ports_(nullptr), - // capacitance_ intentionally not initialized so - // liberty reader can apply default capacitance. + fanout_load_(0.0), + fanout_load_exists_(false), min_period_(0.0), pulse_clk_trigger_(nullptr), pulse_clk_sense_(nullptr), @@ -2040,6 +2051,22 @@ LibertyPort::setCapacitanceLimit(float cap, cap_limit_.setValue(min_max, cap); } +void +LibertyPort::fanoutLoad(// Return values. + float &fanout_load, + bool &exists) const +{ + fanout_load = fanout_load_; + exists = fanout_load_exists_; +} + +void +LibertyPort::setFanoutLoad(float fanout_load) +{ + fanout_load_ = fanout_load; + fanout_load_exists_ = true; +} + void LibertyPort::fanoutLimit(const MinMax *min_max, // Return values. diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 914a7b34..b83235e8 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -307,6 +307,7 @@ LibertyReader::defineVisitors() &LibertyReader::visitRiseCapRange); defineAttrVisitor("fall_capacitance_range", &LibertyReader::visitFallCapRange); + defineAttrVisitor("fanout_load", &LibertyReader::visitFanoutLoad); defineAttrVisitor("max_fanout", &LibertyReader::visitMaxFanout); defineAttrVisitor("min_fanout", &LibertyReader::visitMinFanout); defineAttrVisitor("max_transition", &LibertyReader::visitMaxTransition); @@ -2967,6 +2968,21 @@ LibertyReader::defaultCap(LibertyPort *port) return cap; } +void +LibertyReader::visitFanoutLoad(LibertyAttr *attr) +{ + if (ports_) { + float fanout; + bool exists; + getAttrFloat(attr, fanout, exists); + if (exists) { + visitPorts([&] (LibertyPort *port) { + port->setFanoutLoad(fanout); + }); + } + } +} + void LibertyReader::visitMaxFanout(LibertyAttr *attr) { diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index a29862d4..edeb0f24 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -206,6 +206,7 @@ public: virtual void visitFallCap(LibertyAttr *attr); virtual void visitRiseCapRange(LibertyAttr *attr); virtual void visitFallCapRange(LibertyAttr *attr); + virtual void visitFanoutLoad(LibertyAttr *attr); virtual void visitMaxFanout(LibertyAttr *attr); virtual void visitMinFanout(LibertyAttr *attr); virtual void visitFanout(LibertyAttr *attr, diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index fffda964..1eb6d74d 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -145,6 +145,7 @@ CheckCapacitanceLimits::checkCapacitance1(const Pin *pin, } } +// return the tightest limit. void CheckCapacitanceLimits::findLimit(const Pin *pin, const MinMax *min_max, @@ -152,28 +153,45 @@ CheckCapacitanceLimits::findLimit(const Pin *pin, float &limit, bool &exists) const { - exists = false; + // Default to top ("design") limit. + limit = top_limit_; + exists = top_limit_exists_; + const Network *network = sta_->network(); Sdc *sdc = sta_->sdc(); + float limit1; + bool exists1; if (network->isTopLevelPort(pin)) { Port *port = network->port(pin); - sdc->capacitanceLimit(port, min_max, limit, exists); - if (!exists) { - limit = top_limit_; - exists = top_limit_exists_; + sdc->capacitanceLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; } } else { Cell *cell = network->cell(network->instance(pin)); sdc->capacitanceLimit(cell, min_max, - limit, exists); - if (!exists) { - LibertyPort *port = network->libertyPort(pin); - if (port) { - port->capacitanceLimit(min_max, limit, exists); - if (!exists - && port->direction()->isAnyOutput()) - port->libertyLibrary()->defaultMaxCapacitance(limit, exists); + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->capacitanceLimit(min_max, limit1, exists1); + if (!exists1 + && port->direction()->isAnyOutput()) + port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; } } } diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 00550cd9..0620b5a9 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -105,6 +105,7 @@ CheckFanoutLimits::checkFanout(const Pin *pin, fanout, slack, limit); } +// return the tightest limit. void CheckFanoutLimits::findLimit(const Pin *pin, const MinMax *min_max, @@ -112,29 +113,46 @@ CheckFanoutLimits::findLimit(const Pin *pin, float &limit, bool &exists) const { - exists = false; + // Default to top ("design") limit. + limit = top_limit_; + exists = top_limit_exists_; + const Network *network = sta_->network(); Sdc *sdc = sta_->sdc(); + float limit1; + bool exists1; if (network->isTopLevelPort(pin)) { Port *port = network->port(pin); - sdc->fanoutLimit(port, min_max, limit, exists); - if (!exists) { - limit = top_limit_; - exists = top_limit_exists_; + sdc->fanoutLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; } } else { Cell *cell = network->cell(network->instance(pin)); sdc->fanoutLimit(cell, min_max, - limit, exists); - if (!exists) { - LibertyPort *port = network->libertyPort(pin); - if (port) { - port->fanoutLimit(min_max, limit, exists); - if (!exists - && min_max == MinMax::max() - && port->direction()->isAnyOutput()) - port->libertyLibrary()->defaultMaxFanout(limit, exists); + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->fanoutLimit(min_max, limit1, exists1); + if (!exists1 + && min_max == MinMax::max() + && port->direction()->isAnyOutput()) + port->libertyLibrary()->defaultMaxFanout(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; } } } @@ -149,7 +167,7 @@ CheckFanoutLimits::checkFanout(const Pin *pin, float &slack, float &limit) const { - float fanout1 = this->fanout(pin); + float fanout1 = fanoutLoad(pin); float slack1 = (min_max == MinMax::max()) ? limit1 - fanout1 : fanout1 - limit1; @@ -161,7 +179,7 @@ CheckFanoutLimits::checkFanout(const Pin *pin, } float -CheckFanoutLimits::fanout(const Pin *pin) const +CheckFanoutLimits::fanoutLoad(const Pin *pin) const { float fanout = 0; const Network *network = sta_->network(); @@ -170,8 +188,18 @@ CheckFanoutLimits::fanout(const Pin *pin) const NetPinIterator *pin_iter = network->pinIterator(net); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->isLoad(pin)) - fanout++; + if (network->isLoad(pin)) { + LibertyPort *port = network->libertyPort(pin); + float fanout_load; + bool exists; + port->fanoutLoad(fanout_load, exists); + if (!exists) { + LibertyLibrary *lib = port->libertyLibrary(); + lib->defaultFanoutLoad(fanout_load, exists); + } + if (exists) + fanout += fanout_load; + } } delete pin_iter; } diff --git a/search/CheckFanoutLimits.hh b/search/CheckFanoutLimits.hh index f8de4139..380e6c43 100644 --- a/search/CheckFanoutLimits.hh +++ b/search/CheckFanoutLimits.hh @@ -60,7 +60,7 @@ protected: // Return values. Pin *&min_slack_pin, float &min_slack); - float fanout(const Pin *pin) const; + float fanoutLoad(const Pin *pin) const; float top_limit_; bool top_limit_exists_; diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index 2975bc53..db939c25 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -170,6 +170,7 @@ CheckSlewLimits::checkSlews1(Vertex *vertex, } } +// return the tightest limit. void CheckSlewLimits::findLimit(const Pin *pin, const Vertex *vertex, @@ -202,7 +203,6 @@ CheckSlewLimits::findLimit(const Pin *pin, if (exists1 && (!exists || min_max->compare(limit, limit1))) { - // Use the tightest clock limit. limit = limit1; exists = true; } @@ -210,7 +210,6 @@ CheckSlewLimits::findLimit(const Pin *pin, if (network->isTopLevelPort(pin)) { Port *port = network->port(pin); sdc->slewLimit(port, min_max, limit1, exists1); - // Use the tightest limit. if (exists1 && (!exists || min_max->compare(limit, limit1))) { @@ -222,23 +221,16 @@ CheckSlewLimits::findLimit(const Pin *pin, LibertyPort *port = network->libertyPort(pin); if (port) { port->slewLimit(min_max, limit1, exists1); - // Use the tightest limit. + if (!exists1 + && port->direction()->isAnyOutput() + && min_max == MinMax::max()) + port->libertyLibrary()->defaultMaxSlew(limit1, exists1); if (exists1 && (!exists || min_max->compare(limit, limit1))) { limit = limit1; exists = true; } - if (port->direction()->isAnyOutput() - && min_max == MinMax::max()) { - port->libertyLibrary()->defaultMaxSlew(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } } } } diff --git a/search/Sta.cc b/search/Sta.cc index ef93de5d..c7b807c3 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4954,12 +4954,12 @@ Sta::reportFanoutLimitVerbose(Pin *pin, } void -Sta::checkFanouts(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack) +Sta::checkFanout(const Pin *pin, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack) { checkFanoutLimitPreamble(); check_fanout_limits_->init(min_max); From 14cfe33bfdd011ef4d5ad5e6a6b9bf357f4d7eb3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 Jun 2020 20:37:46 -0700 Subject: [PATCH 22/70] check slew clk option --- include/sta/Sta.hh | 19 ++++----- search/CheckSlewLimits.cc | 81 +++++++++++++++++++++------------------ search/CheckSlewLimits.hh | 26 +++++++------ search/Sta.cc | 31 +++++++-------- 4 files changed, 85 insertions(+), 72 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index cef2e98c..0c1a7c45 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -627,15 +627,16 @@ public: void reportSlewLimitVerbose(Pin *pin, const Corner *corner, const MinMax *min_max); - void checkSlews(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&tr, - Slew &slew, - float &limit, - float &slack); + void checkSlew(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + bool check_clks, + // Return values. + const Corner *&corner1, + const RiseFall *&tr, + Slew &slew, + float &limit, + float &slack); void checkFanoutLimitPreamble(); // Return the pin with the min/max fanout limit slack. diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index db939c25..7cea9457 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -68,10 +68,10 @@ PinSlewLimitSlackLess::operator()(Pin *pin1, const RiseFall *rf1, *rf2; Slew slew1, slew2; float limit1, limit2, slack1, slack2; - check_slew_limit_->checkSlews(pin1, corner_, min_max_, - corner1, rf1, slew1, limit1, slack1); - check_slew_limit_->checkSlews(pin2, corner_, min_max_, - corner2, rf2, slew2, limit2, slack2); + check_slew_limit_->checkSlew(pin1, corner_, min_max_, true, + corner1, rf1, slew1, limit1, slack1); + check_slew_limit_->checkSlew(pin2, corner_, min_max_, true, + corner2, rf2, slew2, limit2, slack2); return fuzzyLess(slack1, slack2) || (fuzzyEqual(slack1, slack2) // Break ties for the sake of regression stability. @@ -99,15 +99,16 @@ CheckSlewLimits::init(const MinMax *min_max) } void -CheckSlewLimits::checkSlews(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf, - Slew &slew, - float &limit, - float &slack) const +CheckSlewLimits::checkSlew(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + bool check_clks, + // Return values. + const Corner *&corner1, + const RiseFall *&rf, + Slew &slew, + float &limit, + float &slack) const { corner1 = nullptr; rf = nullptr; @@ -115,11 +116,11 @@ CheckSlewLimits::checkSlews(const Pin *pin, limit = 0.0; slack = MinMax::min()->initValue(); if (corner) - checkSlews1(pin, corner, min_max, + checkSlews1(pin, corner, min_max, check_clks, corner1, rf, slew, limit, slack); else { for (auto corner : *sta_->corners()) { - checkSlews1(pin, corner, min_max, + checkSlews1(pin, corner, min_max, check_clks, corner1, rf, slew, limit, slack); } } @@ -129,6 +130,7 @@ void CheckSlewLimits::checkSlews1(const Pin *pin, const Corner *corner, const MinMax *min_max, + bool check_clks, // Return values. const Corner *&corner1, const RiseFall *&rf, @@ -140,11 +142,11 @@ CheckSlewLimits::checkSlews1(const Pin *pin, sta_->graph()->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex && !vertex->isDisabledConstraint()) - checkSlews1(vertex, corner, min_max, + checkSlews1(vertex, corner, min_max, check_clks, corner1, rf, slew, limit, slack); if (bidirect_drvr_vertex && !vertex->isDisabledConstraint()) - checkSlews1(bidirect_drvr_vertex, corner, min_max, + checkSlews1(bidirect_drvr_vertex, corner, min_max, check_clks, corner1, rf, slew, limit, slack); } @@ -152,6 +154,7 @@ void CheckSlewLimits::checkSlews1(Vertex *vertex, const Corner *corner1, const MinMax *min_max, + bool check_clks, // Return values. const Corner *&corner, const RiseFall *&rf, @@ -162,9 +165,10 @@ CheckSlewLimits::checkSlews1(Vertex *vertex, for (auto rf1 : RiseFall::range()) { float limit1; bool limit1_exists; - findLimit(vertex->pin(), vertex, rf1, min_max, limit1, limit1_exists); + findLimit(vertex->pin(), vertex, rf1, min_max, check_clks, + limit1, limit1_exists); if (limit1_exists) { - checkSlew(vertex, corner1, min_max, rf1, limit1, + checkSlew(vertex, corner1, rf1, min_max, limit1, corner, rf, slew, slack, limit); } } @@ -176,6 +180,7 @@ CheckSlewLimits::findLimit(const Pin *pin, const Vertex *vertex, const RiseFall *rf, const MinMax *min_max, + bool check_clks, // Return values. float &limit, bool &exists) const @@ -188,23 +193,25 @@ CheckSlewLimits::findLimit(const Pin *pin, const Network *network = sta_->network(); Sdc *sdc = sta_->sdc(); - bool is_clk = sta_->search()->isClock(vertex); float limit1; bool exists1; - // Look for clock slew limits. - ClockSet clks; - clockDomains(vertex, clks); - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); - PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; - sdc->slewLimit(clk, rf, clk_data, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; + if (check_clks) { + // Look for clock slew limits. + bool is_clk = sta_->search()->isClock(vertex); + ClockSet clks; + clockDomains(vertex, clks); + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; + sdc->slewLimit(clk, rf, clk_data, min_max, + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } } } if (network->isTopLevelPort(pin)) { @@ -253,8 +260,8 @@ CheckSlewLimits::clockDomains(const Vertex *vertex, void CheckSlewLimits::checkSlew(Vertex *vertex, const Corner *corner1, - const MinMax *min_max, const RiseFall *rf1, + const MinMax *min_max, float limit1, // Return values. const Corner *&corner, @@ -314,7 +321,7 @@ CheckSlewLimits::pinSlewLimitViolations(Instance *inst, const RiseFall *rf; Slew slew; float limit, slack; - checkSlews(pin, corner, min_max, corner1, rf, slew, limit, slack ); + checkSlew(pin, corner, min_max, true, corner1, rf, slew, limit, slack); if (rf && slack < 0.0) violators->push_back(pin); } @@ -357,7 +364,7 @@ CheckSlewLimits::pinMinSlewLimitSlack(Instance *inst, const RiseFall *rf; Slew slew; float limit, slack; - checkSlews(pin, corner, min_max, corner1, rf, slew, limit, slack); + checkSlew(pin, corner, min_max, true, corner1, rf, slew, limit, slack); if (rf && (min_slack_pin == nullptr || slack < min_slack)) { diff --git a/search/CheckSlewLimits.hh b/search/CheckSlewLimits.hh index 0fa728df..c5042175 100644 --- a/search/CheckSlewLimits.hh +++ b/search/CheckSlewLimits.hh @@ -36,16 +36,17 @@ public: void init(const MinMax *min_max); // Requires init(). // corner=nullptr checks all corners. - void checkSlews(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - // Corner is nullptr for no slew limit. - const Corner *&corner1, - const RiseFall *&rf, - Slew &slew, - float &limit, - float &slack) const; + void checkSlew(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + bool check_clks, + // Return values. + // Corner is nullptr for no slew limit. + const Corner *&corner1, + const RiseFall *&rf, + Slew &slew, + float &limit, + float &slack) const; // corner=nullptr checks all corners. PinSeq *pinSlewLimitViolations(const Corner *corner, const MinMax *min_max); @@ -57,6 +58,7 @@ protected: void checkSlews1(const Pin *pin, const Corner *corner, const MinMax *min_max, + bool check_clks, // Return values. const Corner *&corner1, const RiseFall *&rf, @@ -66,6 +68,7 @@ protected: void checkSlews1(Vertex *vertex, const Corner *corner1, const MinMax *min_max, + bool check_clks, // Return values. const Corner *&corner, const RiseFall *&rf, @@ -74,8 +77,8 @@ protected: float &slack) const; void checkSlew(Vertex *vertex, const Corner *corner1, - const MinMax *min_max, const RiseFall *rf1, + const MinMax *min_max, float limit1, // Return values. const Corner *&corner, @@ -87,6 +90,7 @@ protected: const Vertex *vertex, const RiseFall *rf, const MinMax *min_max, + bool check_clks, // Return values. float &limit1, bool &limit1_exists) const; diff --git a/search/Sta.cc b/search/Sta.cc index c7b807c3..042de42c 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4862,8 +4862,8 @@ Sta::reportSlewLimitShort(Pin *pin, const RiseFall *rf; Slew slew; float limit, slack; - check_slew_limits_->checkSlews(pin, corner, min_max, - corner1, rf, slew, limit, slack); + check_slew_limits_->checkSlew(pin, corner, min_max, true, + corner1, rf, slew, limit, slack); report_path_->reportLimitShort(report_path_->fieldSlew(), pin, delayAsFloat(slew), limit, slack); } @@ -4877,28 +4877,29 @@ Sta::reportSlewLimitVerbose(Pin *pin, const RiseFall *rf; Slew slew; float limit, slack; - check_slew_limits_->checkSlews(pin, corner, min_max, - corner1, rf, slew, limit, slack); + check_slew_limits_->checkSlew(pin, corner, min_max, true, + corner1, rf, slew, limit, slack); report_path_->reportLimitVerbose(report_path_->fieldSlew(), pin, rf, delayAsFloat(slew), limit, slack, min_max); } void -Sta::checkSlews(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf, - Slew &slew, - float &limit, - float &slack) +Sta::checkSlew(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + bool check_clks, + // Return values. + const Corner *&corner1, + const RiseFall *&rf, + Slew &slew, + float &limit, + float &slack) { checkSlewLimitPreamble(); check_slew_limits_->init(min_max); - check_slew_limits_->checkSlews(pin, corner, min_max, - corner1, rf, slew, limit, slack); + check_slew_limits_->checkSlew(pin, corner, min_max, check_clks, + corner1, rf, slew, limit, slack); } ////////////////////////////////////////////////////////////////' From e138b1ce221e5dfd6e97f378af5ecb3985363e6d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 9 Jun 2020 09:43:59 -0700 Subject: [PATCH 23/70] sta::checkCap --- include/sta/Sta.hh | 18 +++++++++--------- search/Sta.cc | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 0c1a7c45..5eae24fb 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -671,15 +671,15 @@ public: void reportCapacitanceLimitVerbose(Pin *pin, const Corner *corner, const MinMax *min_max); - void checkCapacitances(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&tr, - float &capacitance, - float &limit, - float &slack); + void checkCapacitance(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const RiseFall *&tr, + float &capacitance, + float &limit, + float &slack); // Min pulse width check with the least slack. // corner=nullptr checks all corners. diff --git a/search/Sta.cc b/search/Sta.cc index 042de42c..3011db7e 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5031,15 +5031,15 @@ Sta::reportCapacitanceLimitVerbose(Pin *pin, } void -Sta::checkCapacitances(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf, - float &capacitance, - float &limit, - float &slack) +Sta::checkCapacitance(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const RiseFall *&rf, + float &capacitance, + float &limit, + float &slack) { checkCapacitanceLimitPreamble(); check_capacitance_limits_->init(min_max); @@ -5048,7 +5048,7 @@ Sta::checkCapacitances(const Pin *pin, limit, slack); } -////////////////////////////////////////////////////////////////' +//////////////////////////////////////////////////////////////// void Sta::minPulseWidthPreamble() From 3b77a15847911a91785cf9a9b74865f1e2dbdb0d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 9 Jun 2020 16:03:44 -0700 Subject: [PATCH 24/70] check cap limit --- search/CheckCapacitanceLimits.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index 1eb6d74d..b3ff2e31 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -21,6 +21,7 @@ #include "Network.hh" #include "Sdc.hh" #include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" #include "StaState.hh" #include "Corner.hh" #include "PortDirection.hh" @@ -212,12 +213,7 @@ CheckCapacitanceLimits::checkCapacitance(const Pin *pin, { const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - Sdc *sdc = sta_->sdc(); - float pin_cap, wire_cap, fanout; - bool has_set_load; - sdc->connectedCap(pin, rf1, op_cond, corner, min_max, - pin_cap, wire_cap, fanout, has_set_load); - float cap = pin_cap + wire_cap; + float cap = sta_->graphDelayCalc()->loadCap(pin, dcalc_ap); float slack1 = (min_max == MinMax::max()) ? limit1 - cap : cap - limit1; From 4c0225acc31162958e5124383322d93e66391986 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 9 Jun 2020 20:02:59 -0700 Subject: [PATCH 25/70] liberty is_memory_cell --- include/sta/Liberty.hh | 3 +++ liberty/Liberty.cc | 7 +++++++ liberty/LibertyReader.cc | 12 ++++++++++++ liberty/LibertyReaderPvt.hh | 1 + search/Power.cc | 3 ++- 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 4f39606e..5818e4bb 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -405,6 +405,8 @@ public: void setDontUse(bool dont_use); bool isMacro() const { return is_macro_; } void setIsMacro(bool is_macro); + bool isMemory() const { return is_memory_; } + void setIsMemory(bool is_memory); bool isPad() const { return is_pad_; } void setIsPad(bool is_pad); bool interfaceTiming() const { return interface_timing_; } @@ -525,6 +527,7 @@ protected: float area_; bool dont_use_; bool is_macro_; + bool is_memory_; bool is_pad_; bool has_internal_ports_; bool interface_timing_; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 4c328b77..38a64706 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -850,6 +850,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library, area_(0.0), dont_use_(false), is_macro_(false), + is_memory_(false), is_pad_(false), has_internal_ports_(false), interface_timing_(false), @@ -995,6 +996,12 @@ LibertyCell::setIsMacro(bool is_macro) is_macro_ = is_macro; } +void +LibertyCell::setIsMemory(bool is_memory) +{ + is_memory_ = is_memory; +} + void LibertyCell::LibertyCell::setIsPad(bool is_pad) { diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index b83235e8..7e075d8a 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -286,6 +286,7 @@ LibertyReader::defineVisitors() defineAttrVisitor("area", &LibertyReader::visitArea); defineAttrVisitor("dont_use", &LibertyReader::visitDontUse); defineAttrVisitor("is_macro", &LibertyReader::visitIsMacro); + defineAttrVisitor("is_memory", &LibertyReader::visitIsMemory); defineAttrVisitor("is_pad", &LibertyReader::visitIsPad); defineAttrVisitor("interface_timing", &LibertyReader::visitInterfaceTiming); defineAttrVisitor("scaling_factors", &LibertyReader::visitScalingFactors); @@ -2426,6 +2427,17 @@ LibertyReader::visitIsMacro(LibertyAttr *attr) } } +void +LibertyReader::visitIsMemory(LibertyAttr *attr) +{ + if (cell_) { + bool is_memory, exists; + getAttrBool(attr, is_memory, exists); + if (exists) + cell_->setIsMemory(is_memory); + } +} + void LibertyReader::visitIsPad(LibertyAttr *attr) { diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index edeb0f24..2a356a61 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -181,6 +181,7 @@ public: virtual void visitArea(LibertyAttr *attr); virtual void visitDontUse(LibertyAttr *attr); virtual void visitIsMacro(LibertyAttr *attr); + virtual void visitIsMemory(LibertyAttr *attr); virtual void visitIsPad(LibertyAttr *attr); virtual void visitInterfaceTiming(LibertyAttr *attr); virtual void visitScalingFactors(LibertyAttr *attr); diff --git a/search/Power.cc b/search/Power.cc index ebe6ad5d..ad670771 100644 --- a/search/Power.cc +++ b/search/Power.cc @@ -154,7 +154,8 @@ Power::power(const Corner *corner, if (cell) { PowerResult inst_power; power(inst, corner, inst_power); - if (cell->isMacro()) + if (cell->isMacro() + || cell->isMemory()) macro.incr(inst_power); else if (cell->isPad()) pad.incr(inst_power); From 7a893295493e5f4a5dd7f29550c4ad9892a746fa Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 11 Jun 2020 08:45:14 -0700 Subject: [PATCH 26/70] set_cmd_units --- tcl/Sdc.tcl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl index f2ae6f89..1839abc3 100644 --- a/tcl/Sdc.tcl +++ b/tcl/Sdc.tcl @@ -297,6 +297,11 @@ proc set_unit_values { unit key unit_name key_var } { } else { set prefix [string index $value 0] set suffix [string range $value 1 end] + # unit includes "1" prefix + if { [string is digit $prefix] } { + set prefix [string index $value 1] + set suffix [string range $value 2 end] + } if { [string equal -nocase $suffix $unit_name] } { set scale [unit_prefix_scale $unit $prefix] set_cmd_unit_scale $unit $scale From a258b31bb96f89017bcddecd09f0313d7e71d84e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 11 Jun 2020 10:29:08 -0700 Subject: [PATCH 27/70] report -max_fanout -violators only check output pins --- search/CheckFanoutLimits.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 0620b5a9..780a4f2b 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -234,11 +234,13 @@ CheckFanoutLimits::pinFanoutLimitViolations(Instance *inst, InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - float fanout; - float limit, slack; - checkFanout(pin, min_max, fanout, limit, slack ); - if (slack < 0.0) - violators->push_back(pin); + if (network->direction(pin)->isAnyOutput()) { + float fanout; + float limit, slack; + checkFanout(pin, min_max, fanout, limit, slack ); + if (slack < 0.0) + violators->push_back(pin); + } } delete pin_iter; } From dd29fafe5d9bfa75854bef2dc6d571fa502addc5 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 11 Jun 2020 20:48:48 -0700 Subject: [PATCH 28/70] report_power seg fault on missing pin --- search/Power.cc | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/search/Power.cc b/search/Power.cc index ad670771..ba5ecdd5 100644 --- a/search/Power.cc +++ b/search/Power.cc @@ -775,25 +775,26 @@ Power::findInputDuty(const Pin *to_pin, const LibertyPort *from_port = pwr->relatedPort(); if (from_port) { const Pin *from_pin = network_->findPin(inst, from_port); - FuncExpr *when = pwr->when(); - Vertex *from_vertex = graph_->pinLoadVertex(from_pin); - if (func && func->hasPort(from_port)) { - PwrActivity from_activity = findActivity(from_pin); - PwrActivity to_activity = findActivity(to_pin); - float duty1 = evalActivityDifference(func, inst, from_port).duty(); - if (to_activity.activity() == 0.0) - return 0.0; - else - return from_activity.activity() / to_activity.activity() * duty1; + if (from_pin) { + FuncExpr *when = pwr->when(); + Vertex *from_vertex = graph_->pinLoadVertex(from_pin); + if (func && func->hasPort(from_port)) { + PwrActivity from_activity = findActivity(from_pin); + PwrActivity to_activity = findActivity(to_pin); + float duty1 = evalActivityDifference(func, inst, from_port).duty(); + if (to_activity.activity() == 0.0) + return 0.0; + else + return from_activity.activity() / to_activity.activity() * duty1; + } + else if (when) + return evalActivity(when, inst).duty(); + else if (search_->isClock(from_vertex)) + return 1.0; + return 0.5; } - else if (when) - return evalActivity(when, inst).duty(); - else if (search_->isClock(from_vertex)) - return 1.0; - return 0.5; } - else - return 0.0; + return 0.0; } static bool From 8ff25b6c3b398eeb314801f17414aae748f1e52e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 12 Jun 2020 14:51:46 -0700 Subject: [PATCH 29/70] check limits flush inits --- include/sta/Sta.hh | 3 +++ search/CheckCapacitanceLimits.cc | 25 ++++++------------------- search/CheckCapacitanceLimits.hh | 4 ---- search/CheckFanoutLimits.cc | 25 ++++++------------------- search/CheckFanoutLimits.hh | 4 ---- search/CheckSlewLimits.cc | 24 +++++------------------- search/CheckSlewLimits.hh | 4 ---- search/Sta.cc | 4 ---- 8 files changed, 20 insertions(+), 73 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 5eae24fb..e4e911a1 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -627,6 +627,7 @@ public: void reportSlewLimitVerbose(Pin *pin, const Corner *corner, const MinMax *min_max); + // requires checkSlewLimitPreamble() void checkSlew(const Pin *pin, const Corner *corner, const MinMax *min_max, @@ -648,6 +649,7 @@ public: const MinMax *min_max); void reportFanoutLimitVerbose(Pin *pin, const MinMax *min_max); + // requires checkFanoutLimitPreamble() void checkFanout(const Pin *pin, const MinMax *min_max, // Return values. @@ -671,6 +673,7 @@ public: void reportCapacitanceLimitVerbose(Pin *pin, const Corner *corner, const MinMax *min_max); + // requires checkCapacitanceLimitPreamble() void checkCapacitance(const Pin *pin, const Corner *corner, const MinMax *min_max, diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index b3ff2e31..e4bd4d64 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -84,19 +84,6 @@ CheckCapacitanceLimits::CheckCapacitanceLimits(const StaState *sta) : { } -void -CheckCapacitanceLimits::init(const MinMax *min_max) -{ - const Network *network = sta_->network(); - Cell *top_cell = network->cell(network->topInstance()); - float top_limit; - bool top_limit_exists; - sta_->sdc()->capacitanceLimit(top_cell, min_max, - top_limit, top_limit_exists); - top_limit_= top_limit; - top_limit_exists_ = top_limit_exists; -} - void CheckCapacitanceLimits::checkCapacitance(const Pin *pin, const Corner *corner1, @@ -154,12 +141,14 @@ CheckCapacitanceLimits::findLimit(const Pin *pin, float &limit, bool &exists) const { - // Default to top ("design") limit. - limit = top_limit_; - exists = top_limit_exists_; - const Network *network = sta_->network(); Sdc *sdc = sta_->sdc(); + + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->capacitanceLimit(top_cell, min_max, + limit, exists); + float limit1; bool exists1; if (network->isTopLevelPort(pin)) { @@ -234,7 +223,6 @@ PinSeq * CheckCapacitanceLimits::pinCapacitanceLimitViolations(const Corner *corner, const MinMax *min_max) { - init(min_max); const Network *network = sta_->network(); PinSeq *violators = new PinSeq; LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); @@ -275,7 +263,6 @@ Pin * CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(const Corner *corner, const MinMax *min_max) { - init(min_max); const Network *network = sta_->network(); Pin *min_slack_pin = nullptr; float min_slack = MinMax::min()->initValue(); diff --git a/search/CheckCapacitanceLimits.hh b/search/CheckCapacitanceLimits.hh index 8344221f..cabbd3d4 100644 --- a/search/CheckCapacitanceLimits.hh +++ b/search/CheckCapacitanceLimits.hh @@ -30,8 +30,6 @@ class CheckCapacitanceLimits { public: CheckCapacitanceLimits(const StaState *sta); - void init(const MinMax *min_max); - // Requires init(). // corner=nullptr checks all corners. void checkCapacitance(const Pin *pin, const Corner *corner1, @@ -87,8 +85,6 @@ protected: Pin *&min_slack_pin, float &min_slack); - float top_limit_; - bool top_limit_exists_; const StaState *sta_; }; diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 780a4f2b..9d64ceca 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -72,19 +72,6 @@ CheckFanoutLimits::CheckFanoutLimits(const StaState *sta) : { } -void -CheckFanoutLimits::init(const MinMax *min_max) -{ - const Network *network = sta_->network(); - Cell *top_cell = network->cell(network->topInstance()); - float top_limit; - bool top_limit_exists; - sta_->sdc()->fanoutLimit(top_cell, min_max, - top_limit, top_limit_exists); - top_limit_= top_limit; - top_limit_exists_ = top_limit_exists; -} - void CheckFanoutLimits::checkFanout(const Pin *pin, const MinMax *min_max, @@ -113,12 +100,14 @@ CheckFanoutLimits::findLimit(const Pin *pin, float &limit, bool &exists) const { - // Default to top ("design") limit. - limit = top_limit_; - exists = top_limit_exists_; - const Network *network = sta_->network(); Sdc *sdc = sta_->sdc(); + + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->fanoutLimit(top_cell, min_max, + limit, exists); + float limit1; bool exists1; if (network->isTopLevelPort(pin)) { @@ -209,7 +198,6 @@ CheckFanoutLimits::fanoutLoad(const Pin *pin) const PinSeq * CheckFanoutLimits::pinFanoutLimitViolations(const MinMax *min_max) { - init(min_max); const Network *network = sta_->network(); PinSeq *violators = new PinSeq; LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); @@ -248,7 +236,6 @@ CheckFanoutLimits::pinFanoutLimitViolations(Instance *inst, Pin * CheckFanoutLimits::pinMinFanoutLimitSlack(const MinMax *min_max) { - init(min_max); const Network *network = sta_->network(); Pin *min_slack_pin = nullptr; float min_slack = MinMax::min()->initValue(); diff --git a/search/CheckFanoutLimits.hh b/search/CheckFanoutLimits.hh index 380e6c43..7baacae0 100644 --- a/search/CheckFanoutLimits.hh +++ b/search/CheckFanoutLimits.hh @@ -28,8 +28,6 @@ class CheckFanoutLimits { public: CheckFanoutLimits(const StaState *sta); - void init(const MinMax *min_max); - // Requires init(). void checkFanout(const Pin *pin, const MinMax *min_max, // Return values. @@ -62,8 +60,6 @@ protected: float &min_slack); float fanoutLoad(const Pin *pin) const; - float top_limit_; - bool top_limit_exists_; const StaState *sta_; }; diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index 7cea9457..5abade33 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -85,19 +85,6 @@ CheckSlewLimits::CheckSlewLimits(const StaState *sta) : { } -void -CheckSlewLimits::init(const MinMax *min_max) -{ - const Network *network = sta_->network(); - Cell *top_cell = network->cell(network->topInstance()); - float top_limit; - bool top_limit_exists; - sta_->sdc()->slewLimit(top_cell, min_max, - top_limit, top_limit_exists); - top_limit_= top_limit; - top_limit_exists_ = top_limit_exists; -} - void CheckSlewLimits::checkSlew(const Pin *pin, const Corner *corner, @@ -187,12 +174,13 @@ CheckSlewLimits::findLimit(const Pin *pin, { exists = false; if (!sta_->graphDelayCalc()->isIdealClk(vertex)) { - // Default to top ("design") limit. - exists = top_limit_exists_; - limit = top_limit_; - const Network *network = sta_->network(); Sdc *sdc = sta_->sdc(); + + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->slewLimit(top_cell, min_max, + limit, exists); float limit1; bool exists1; if (check_clks) { @@ -292,7 +280,6 @@ PinSeq * CheckSlewLimits::pinSlewLimitViolations(const Corner *corner, const MinMax *min_max) { - init(min_max); const Network *network = sta_->network(); PinSeq *violators = new PinSeq; LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); @@ -332,7 +319,6 @@ Pin * CheckSlewLimits::pinMinSlewLimitSlack(const Corner *corner, const MinMax *min_max) { - init(min_max); const Network *network = sta_->network(); Pin *min_slack_pin = nullptr; float min_slack = MinMax::min()->initValue(); diff --git a/search/CheckSlewLimits.hh b/search/CheckSlewLimits.hh index c5042175..8c65a049 100644 --- a/search/CheckSlewLimits.hh +++ b/search/CheckSlewLimits.hh @@ -33,8 +33,6 @@ class CheckSlewLimits { public: CheckSlewLimits(const StaState *sta); - void init(const MinMax *min_max); - // Requires init(). // corner=nullptr checks all corners. void checkSlew(const Pin *pin, const Corner *corner, @@ -108,8 +106,6 @@ protected: // Return value. ClockSet &clks) const; - float top_limit_; - bool top_limit_exists_; const StaState *sta_; }; diff --git a/search/Sta.cc b/search/Sta.cc index 3011db7e..6f1c8dd7 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4896,8 +4896,6 @@ Sta::checkSlew(const Pin *pin, float &limit, float &slack) { - checkSlewLimitPreamble(); - check_slew_limits_->init(min_max); check_slew_limits_->checkSlew(pin, corner, min_max, check_clks, corner1, rf, slew, limit, slack); } @@ -4963,7 +4961,6 @@ Sta::checkFanout(const Pin *pin, float &slack) { checkFanoutLimitPreamble(); - check_fanout_limits_->init(min_max); check_fanout_limits_->checkFanout(pin, min_max, fanout, limit, slack); } @@ -5042,7 +5039,6 @@ Sta::checkCapacitance(const Pin *pin, float &slack) { checkCapacitanceLimitPreamble(); - check_capacitance_limits_->init(min_max); check_capacitance_limits_->checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); From 035c9c3ceed48e915c6ed2da89ffe2e6f44812cb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 12 Jun 2020 19:58:35 -0700 Subject: [PATCH 30/70] liberty scale scalar values --- liberty/LibertyReader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 7e075d8a..71804fe5 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -3934,7 +3934,7 @@ LibertyReader::makeFloatTable(LibertyAttr *attr, } else if (value->isFloat()) // Scalar value. - row->push_back(value->floatValue()); + row->push_back(value->floatValue() * scale); else libWarn(attr, "%s is not a list of floats.\n", attr->name()); if (row->size() != cols) { From 0bc7169bd31e36c4041fd060b1db3aaa682102fb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 12 Jun 2020 19:59:02 -0700 Subject: [PATCH 31/70] check limits ignore -inf slack results --- search/CheckCapacitanceLimits.cc | 2 +- search/CheckFanoutLimits.cc | 2 +- search/CheckSlewLimits.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index e4bd4d64..fe09ff0d 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -252,7 +252,7 @@ CheckCapacitanceLimits::pinCapacitanceLimitViolations(Instance *inst, const RiseFall *rf; float capacitance, limit, slack; checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack ); - if (rf && slack < 0.0) + if (rf && slack < 0.0 && !fuzzyInf(slack)) violators->push_back(pin); } } diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 9d64ceca..76d9fc77 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -226,7 +226,7 @@ CheckFanoutLimits::pinFanoutLimitViolations(Instance *inst, float fanout; float limit, slack; checkFanout(pin, min_max, fanout, limit, slack ); - if (slack < 0.0) + if (slack < 0.0 && !fuzzyInf(slack)) violators->push_back(pin); } } diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index 5abade33..a14a77fe 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -309,7 +309,7 @@ CheckSlewLimits::pinSlewLimitViolations(Instance *inst, Slew slew; float limit, slack; checkSlew(pin, corner, min_max, true, corner1, rf, slew, limit, slack); - if (rf && slack < 0.0) + if (rf && slack < 0.0 && !fuzzyInf(slack)) violators->push_back(pin); } delete pin_iter; From 9ff2d20e05b24b1f1a815819b61b694be58a1862 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 14 Jun 2020 17:58:50 -0700 Subject: [PATCH 32/70] report -max_fanout do not report constant nets --- search/CheckFanoutLimits.cc | 7 ++++++- search/Sta.cc | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 76d9fc77..96571697 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -20,6 +20,7 @@ #include "Liberty.hh" #include "Network.hh" #include "Sdc.hh" +#include "Sim.hh" #include "PortDirection.hh" namespace sta { @@ -259,10 +260,14 @@ CheckFanoutLimits::pinMinFanoutLimitSlack(Instance *inst, float &min_slack) { const Network *network = sta_->network(); + const Sim *sim = sta_->sim(); InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->direction(pin)->isAnyOutput()) { + if (stringEq(network->pathName(pin),"rdrv/Q")) + printf("luse\n"); + if (network->direction(pin)->isAnyOutput() + && !sim->logicZeroOne(pin)) { float fanout; float limit, slack; checkFanout(pin, min_max, fanout, limit, slack); diff --git a/search/Sta.cc b/search/Sta.cc index 6f1c8dd7..44c8f313 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4907,6 +4907,8 @@ Sta::checkFanoutLimitPreamble() { if (check_fanout_limits_ == nullptr) makeCheckFanoutLimits(); + // Sim values required to suppress reporting constant nets. + ensureLevelized(); } Pin * From df65a4ca402cf6ca343be6e1b9da815db7a0a508 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 14 Jun 2020 18:09:51 -0700 Subject: [PATCH 33/70] report -max_cap do not report constant nets --- search/CheckCapacitanceLimits.cc | 29 ++++++++++++++++++----------- search/CheckFanoutLimits.cc | 6 +++--- search/Sta.cc | 2 ++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index fe09ff0d..9a3c335f 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -25,6 +25,7 @@ #include "StaState.hh" #include "Corner.hh" #include "PortDirection.hh" +#include "Sim.hh" namespace sta { @@ -244,10 +245,12 @@ CheckCapacitanceLimits::pinCapacitanceLimitViolations(Instance *inst, PinSeq *violators) { const Network *network = sta_->network(); + const Sim *sim = sta_->sim(); InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->direction(pin)->isAnyOutput()) { + if (network->direction(pin)->isAnyOutput() + && !sim->logicZeroOne(pin)) { const Corner *corner1; const RiseFall *rf; float capacitance, limit, slack; @@ -287,19 +290,23 @@ CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(Instance *inst, float &min_slack) { const Network *network = sta_->network(); + const Sim *sim = sta_->sim(); InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - const Corner *corner1; - const RiseFall *rf; - float capacitance, limit, slack; - checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); - if (rf - && !fuzzyInf(slack) - && (min_slack_pin == nullptr - || slack < min_slack)) { - min_slack_pin = pin; - min_slack = slack; + if (network->direction(pin)->isAnyOutput() + && !sim->logicZeroOne(pin)) { + const Corner *corner1; + const RiseFall *rf; + float capacitance, limit, slack; + checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); + if (rf + && !fuzzyInf(slack) + && (min_slack_pin == nullptr + || slack < min_slack)) { + min_slack_pin = pin; + min_slack = slack; + } } } delete pin_iter; diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 96571697..b62e7aa3 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -220,10 +220,12 @@ CheckFanoutLimits::pinFanoutLimitViolations(Instance *inst, PinSeq *violators) { const Network *network = sta_->network(); + const Sim *sim = sta_->sim(); InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->direction(pin)->isAnyOutput()) { + if (network->direction(pin)->isAnyOutput() + && !sim->logicZeroOne(pin)) { float fanout; float limit, slack; checkFanout(pin, min_max, fanout, limit, slack ); @@ -264,8 +266,6 @@ CheckFanoutLimits::pinMinFanoutLimitSlack(Instance *inst, InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (stringEq(network->pathName(pin),"rdrv/Q")) - printf("luse\n"); if (network->direction(pin)->isAnyOutput() && !sim->logicZeroOne(pin)) { float fanout; diff --git a/search/Sta.cc b/search/Sta.cc index 44c8f313..984891be 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4974,6 +4974,8 @@ Sta::checkCapacitanceLimitPreamble() { if (check_capacitance_limits_ == nullptr) makeCheckCapacitanceLimits(); + // Sim values required to suppress reporting constant nets. + ensureLevelized(); } Pin * From bb9060160dc47b04df18ab00bc200c7b1119b222 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 14 Jun 2020 18:58:00 -0700 Subject: [PATCH 34/70] report_net load count include bidirect pins --- tcl/Network.tcl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcl/Network.tcl b/tcl/Network.tcl index 901bcf7f..352207a2 100644 --- a/tcl/Network.tcl +++ b/tcl/Network.tcl @@ -331,7 +331,8 @@ proc report_net_caps { net pins corner digits } { } if [$pin is_driver] { incr driver_count - } elseif [$pin is_load] { + } + if [$pin is_load] { incr load_count } } From 68bd76e1a822e8d6d4905f8d91cac6faff190eb0 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 14 Jun 2020 21:23:26 -0700 Subject: [PATCH 35/70] report max_fanout, max_cap do not report disabled pins --- search/CheckCapacitanceLimits.cc | 17 +++++++++++++---- search/CheckCapacitanceLimits.hh | 1 + search/CheckFanoutLimits.cc | 18 +++++++++++++----- search/CheckFanoutLimits.hh | 1 + 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index 9a3c335f..c4d2c021 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -249,8 +249,7 @@ CheckCapacitanceLimits::pinCapacitanceLimitViolations(Instance *inst, InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->direction(pin)->isAnyOutput() - && !sim->logicZeroOne(pin)) { + if (checkPin(pin)) { const Corner *corner1; const RiseFall *rf; float capacitance, limit, slack; @@ -294,8 +293,7 @@ CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(Instance *inst, InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->direction(pin)->isAnyOutput() - && !sim->logicZeroOne(pin)) { + if (checkPin(pin)) { const Corner *corner1; const RiseFall *rf; float capacitance, limit, slack; @@ -312,4 +310,15 @@ CheckCapacitanceLimits::pinMinCapacitanceLimitSlack(Instance *inst, delete pin_iter; } +bool +CheckCapacitanceLimits::checkPin(const Pin *pin) +{ + const Network *network = sta_->network(); + const Sim *sim = sta_->sim(); + const Sdc *sdc = sta_->sdc(); + return network->direction(pin)->isAnyOutput() + && !sim->logicZeroOne(pin) + && !sdc->isDisabled(pin); +} + } // namespace diff --git a/search/CheckCapacitanceLimits.hh b/search/CheckCapacitanceLimits.hh index cabbd3d4..39e03789 100644 --- a/search/CheckCapacitanceLimits.hh +++ b/search/CheckCapacitanceLimits.hh @@ -84,6 +84,7 @@ protected: // Return values. Pin *&min_slack_pin, float &min_slack); + bool checkPin(const Pin *pin); const StaState *sta_; }; diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index b62e7aa3..4ca89ca8 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -224,8 +224,7 @@ CheckFanoutLimits::pinFanoutLimitViolations(Instance *inst, InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->direction(pin)->isAnyOutput() - && !sim->logicZeroOne(pin)) { + if (checkPin(pin)) { float fanout; float limit, slack; checkFanout(pin, min_max, fanout, limit, slack ); @@ -262,12 +261,10 @@ CheckFanoutLimits::pinMinFanoutLimitSlack(Instance *inst, float &min_slack) { const Network *network = sta_->network(); - const Sim *sim = sta_->sim(); InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network->direction(pin)->isAnyOutput() - && !sim->logicZeroOne(pin)) { + if (checkPin(pin)) { float fanout; float limit, slack; checkFanout(pin, min_max, fanout, limit, slack); @@ -282,4 +279,15 @@ CheckFanoutLimits::pinMinFanoutLimitSlack(Instance *inst, delete pin_iter; } +bool +CheckFanoutLimits::checkPin(const Pin *pin) +{ + const Network *network = sta_->network(); + const Sim *sim = sta_->sim(); + const Sdc *sdc = sta_->sdc(); + return network->direction(pin)->isAnyOutput() + && !sim->logicZeroOne(pin) + && !sdc->isDisabled(pin); +} + } // namespace diff --git a/search/CheckFanoutLimits.hh b/search/CheckFanoutLimits.hh index 7baacae0..280cb9b8 100644 --- a/search/CheckFanoutLimits.hh +++ b/search/CheckFanoutLimits.hh @@ -59,6 +59,7 @@ protected: Pin *&min_slack_pin, float &min_slack); float fanoutLoad(const Pin *pin) const; + bool checkPin(const Pin *pin); const StaState *sta_; }; From 613be3b5a43baaa1dd7134f060f73cbfbf5f02a6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 15 Jun 2020 14:49:08 -0700 Subject: [PATCH 36/70] report max_slew skip constants --- search/CheckSlewLimits.cc | 114 +++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index a14a77fe..da181844 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -127,12 +127,10 @@ CheckSlewLimits::checkSlews1(const Pin *pin, { Vertex *vertex, *bidirect_drvr_vertex; sta_->graph()->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex - && !vertex->isDisabledConstraint()) + if (vertex) checkSlews1(vertex, corner, min_max, check_clks, corner1, rf, slew, limit, slack); - if (bidirect_drvr_vertex - && !vertex->isDisabledConstraint()) + if (bidirect_drvr_vertex) checkSlews1(bidirect_drvr_vertex, corner, min_max, check_clks, corner1, rf, slew, limit, slack); } @@ -149,14 +147,18 @@ CheckSlewLimits::checkSlews1(Vertex *vertex, float &limit, float &slack) const { - for (auto rf1 : RiseFall::range()) { - float limit1; - bool limit1_exists; - findLimit(vertex->pin(), vertex, rf1, min_max, check_clks, - limit1, limit1_exists); - if (limit1_exists) { - checkSlew(vertex, corner1, rf1, min_max, limit1, - corner, rf, slew, slack, limit); + if (!vertex->isDisabledConstraint() + && !vertex->isConstant() + && !sta_->graphDelayCalc()->isIdealClk(vertex)) { + for (auto rf1 : RiseFall::range()) { + float limit1; + bool limit1_exists; + findLimit(vertex->pin(), vertex, rf1, min_max, check_clks, + limit1, limit1_exists); + if (limit1_exists) { + checkSlew(vertex, corner1, rf1, min_max, limit1, + corner, rf, slew, slack, limit); + } } } } @@ -173,38 +175,26 @@ CheckSlewLimits::findLimit(const Pin *pin, bool &exists) const { exists = false; - if (!sta_->graphDelayCalc()->isIdealClk(vertex)) { - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); + const Network *network = sta_->network(); + Sdc *sdc = sta_->sdc(); - // Default to top ("design") limit. - Cell *top_cell = network->cell(network->topInstance()); - sdc->slewLimit(top_cell, min_max, - limit, exists); - float limit1; - bool exists1; - if (check_clks) { - // Look for clock slew limits. - bool is_clk = sta_->search()->isClock(vertex); - ClockSet clks; - clockDomains(vertex, clks); - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); - PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; - sdc->slewLimit(clk, rf, clk_data, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->slewLimit(port, min_max, limit1, exists1); + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->slewLimit(top_cell, min_max, + limit, exists); + float limit1; + bool exists1; + if (check_clks) { + // Look for clock slew limits. + bool is_clk = sta_->search()->isClock(vertex); + ClockSet clks; + clockDomains(vertex, clks); + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; + sdc->slewLimit(clk, rf, clk_data, min_max, + limit1, exists1); if (exists1 && (!exists || min_max->compare(limit, limit1))) { @@ -212,20 +202,30 @@ CheckSlewLimits::findLimit(const Pin *pin, exists = true; } } - else { - LibertyPort *port = network->libertyPort(pin); - if (port) { - port->slewLimit(min_max, limit1, exists1); - if (!exists1 - && port->direction()->isAnyOutput() - && min_max == MinMax::max()) - port->libertyLibrary()->defaultMaxSlew(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } + } + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->slewLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + else { + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->slewLimit(min_max, limit1, exists1); + if (!exists1 + && port->direction()->isAnyOutput() + && min_max == MinMax::max()) + port->libertyLibrary()->defaultMaxSlew(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; } } } From a4dcb62c025012179e06123e6ea8ce87354c6e99 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 16 Jun 2020 09:23:19 -0700 Subject: [PATCH 37/70] ReportPath::loadCap leak --- search/ReportPath.cc | 4 +++- search/Sim.cc | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index fc8424ae..07b6e3d8 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2911,7 +2911,9 @@ ReportPath::loadCap(Pin *drvr_pin, Parasitic *parasitic = nullptr; if (arc_delay_calc_) parasitic = arc_delay_calc_->findParasitic(drvr_pin, rf, dcalc_ap); - return graph_delay_calc_->loadCap(drvr_pin, parasitic, rf, dcalc_ap); + float load_cap = graph_delay_calc_->loadCap(drvr_pin, parasitic, rf, dcalc_ap); + arc_delay_calc_->finishDrvrPin(); + return load_cap; } //////////////////////////////////////////////////////////////// diff --git a/search/Sim.cc b/search/Sim.cc index 3ee5d57d..3a1d6fa8 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -837,8 +837,12 @@ Sim::removePropagatedValue(const Pin *pin) if (!exists) { debugPrint1(debug_, "sim", 2, "pin %s remove prop constant\n", network_->pathName(pin)); - Vertex *vertex = graph_->pinLoadVertex(pin); - setSimValue(vertex, LogicValue::unknown); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + setSimValue(vertex, LogicValue::unknown); + if (bidirect_drvr_vertex) + setSimValue(bidirect_drvr_vertex, LogicValue::unknown); } } } From b6e3ab889e1242ebb5e648a3db10892e6a9e346a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 22 Jun 2020 18:59:34 -0700 Subject: [PATCH 38/70] report_check_types -max_fanout/-max_cap do not report ideal clks --- search/CheckCapacitanceLimits.cc | 8 +++++++- search/CheckFanoutLimits.cc | 8 +++++++- search/Sta.cc | 10 +++++----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc index c4d2c021..ced71a7d 100644 --- a/search/CheckCapacitanceLimits.cc +++ b/search/CheckCapacitanceLimits.cc @@ -26,6 +26,8 @@ #include "Corner.hh" #include "PortDirection.hh" #include "Sim.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" namespace sta { @@ -316,9 +318,13 @@ CheckCapacitanceLimits::checkPin(const Pin *pin) const Network *network = sta_->network(); const Sim *sim = sta_->sim(); const Sdc *sdc = sta_->sdc(); + const Graph *graph = sta_->graph(); + GraphDelayCalc *dcalc = sta_->graphDelayCalc(); + Vertex *vertex = graph->pinLoadVertex(pin); return network->direction(pin)->isAnyOutput() && !sim->logicZeroOne(pin) - && !sdc->isDisabled(pin); + && !sdc->isDisabled(pin) + && !(vertex && sta_->graphDelayCalc()->isIdealClk(vertex)); } } // namespace diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 4ca89ca8..352482f2 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -22,6 +22,8 @@ #include "Sdc.hh" #include "Sim.hh" #include "PortDirection.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" namespace sta { @@ -285,9 +287,13 @@ CheckFanoutLimits::checkPin(const Pin *pin) const Network *network = sta_->network(); const Sim *sim = sta_->sim(); const Sdc *sdc = sta_->sdc(); + const Graph *graph = sta_->graph(); + GraphDelayCalc *dcalc = sta_->graphDelayCalc(); + Vertex *vertex = graph->pinLoadVertex(pin); return network->direction(pin)->isAnyOutput() && !sim->logicZeroOne(pin) - && !sdc->isDisabled(pin); + && !sdc->isDisabled(pin) + && !(vertex && dcalc->isIdealClk(vertex)); } } // namespace diff --git a/search/Sta.cc b/search/Sta.cc index 984891be..42e42b14 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4817,7 +4817,7 @@ Sta::slowDrvrIterator() return new SlowDrvrIterator(insts); } -////////////////////////////////////////////////////////////////' +//////////////////////////////////////////////////////////////// void Sta::checkSlewLimitPreamble() @@ -4907,8 +4907,8 @@ Sta::checkFanoutLimitPreamble() { if (check_fanout_limits_ == nullptr) makeCheckFanoutLimits(); - // Sim values required to suppress reporting constant nets. - ensureLevelized(); + // For sim values and ideal clocks. + findDelays(); } Pin * @@ -4974,8 +4974,8 @@ Sta::checkCapacitanceLimitPreamble() { if (check_capacitance_limits_ == nullptr) makeCheckCapacitanceLimits(); - // Sim values required to suppress reporting constant nets. - ensureLevelized(); + // For sim values and ideal clocks. + findDelays(); } Pin * From 07e12621869ed54e5f4cc522bc78f92ee70be0d8 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 23 Jun 2020 17:11:48 -0700 Subject: [PATCH 39/70] report_units --- doc/OpenSTA.odt | Bin 76405 -> 76613 bytes include/sta/Units.hh | 6 ++++-- liberty/Units.cc | 48 ++++++++++++++++++++++++++++++++++++++----- tcl/Sta.tcl | 11 ++++++++++ tcl/StaTcl.i | 20 ++++++++++++++++++ 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index f1db19de4ac2a8bde5d008b3d53a6230c54c002e..1eb3cf7b93cd14ffec2e4e2e2987be2122f31235 100644 GIT binary patch delta 45073 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

g=raW!Y{sh>dUIYy>S2(n1Q8J3WTa|E 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

~Wxn zB+!Th{CK2}&%=a(0j=5D#B?>uDHDhV|MNN;Utj#Y!&qImSr_eG{qIfZ;Ad(V zu^^zO4+?GWR9(7`^;n)~;Jaj7KU@%~=Hy5}JXmK^mvBBE;Jvr7qT~iYbbtl&)HKdJ zXuS8!yu)bUI6}RoD&xlxY85n9Uk4jC}7=DhY=pP@Dl7;R;{^ zMJfg`r<`ZJWyTttb>+D%yEdZdBzNGJ)eST;Ptw`3(!bxD9853NudS`Y*KulD&WAXW zwXI>`WjDl`HsuZ$qstC+AaWp%4II0p)ZdT8^nu&9E6HLfz5w#u?grcWFPWU3F8S8T z`V<7VuLix{Zztf}hi~aM&J=arn9sn>*iEO#hY{>LC}4Wgmwz>BvcFd=nrQ+1s=!?A zkWy%&)`*Y7=m~wEGxfn~jLiYrtj%}ojr%Xgd=hAPcHcIEe-%3p&aUHcb8%kQRAJMh4txP?>)gRxbr3r${y9=6g zHWUzgpd0EsxO3J=3rO}1zyrV*dLRCtebMc}`hK*oqRC1=NNc~9mBh<~YVQqI2oXJt z)Z`)6Rc>FakULrdzOm#4S@|oVkKOiZgZi!}-{k3bS1dGQ1uuHRZ`bE1RI?yXF=?s7 zI#p$26hdPO@}?@V&oBklGxA;>%@gA)6rad&#<1&?sW=4X5ZE3ijoM;g-??OhE}Zgenw$E4oF- zi`)XO00tkKJbNYV2|B@;xBj~if_P(9u+l%G4W1+EAY4L7J_@?CSQvs6#n^#V>4d|2 zn3)$vcy>XnBbNy=WQSMU5@ft8H$;(_PtNDpSR zn+uo~p2gtC@_5C}6hDwdqM6*)ZG%N6#)AdV)#=r6#){e(Kw;acBe$$1>VzFO6D9-&y$g36GSy*{8bNcKLpW{{Y?cW|#$D|c|! z1@U=v5&6}}El%Vb&67&5d_o`#WdU}~#Rfk@hog>ymx(7#8}!uvYfte@x^gn$p6gX_ zj>@WDvw@NPALHYj^7_KEXwqyWau# zgpzCRp5_A0@Wp+FS%wB^KLf1{eY~XT_ToCSS1+b40;+V0#Wk@1^KnEOtqP~z6ZLTT zE)_8U0byT{KnJVc5tHNam|t-CqC1(0F1yAI=FC8t8j!&2OSg4V=u%8+aZ}}e2|D0c zrB-?jZ8k8#CP&}OH6wE|+^E3-m7jK0VrlamI8>YwiGa!Oa%Hu3&A{p;P4OTRvRd|~ z*0@19^Ys-t=2%Kw(}gv~7E;zpcHb*cF$7TBACMTdFi|~OiU3wgF-~;0MC-tq?^1<8 z_wSbl|A@Y(1y0OV^DL^N?|$wb`U~6(tDhaSo9n2ABKt zT9XH8c}w#eRya0_Ub7U0IBd0-Kym#tMNG>r0%%htAw=g4STMtjbXUI~PE(j4Lk;i( z4GfB3`C{xvDnI8*l!pj!z@bSSKEBVVJ&1wBDirC|(Ha*PG#;rS*wZyAPw2`!ZXxFF z4P*;VUbO*qGpE=Pv4j;2sfe=-aZs9LtzM;!1kzkHCpG#Y;oY!*}xAh4E#IpFb<`l|DeBabg^Led91C8xH>P?J#0ToT9LVm1W zEK`#J(U|SPzP#a~!5Lb2uJ2sSM{>7ol?KzJhf?+8xd6mC;o7PF7nk`u{}h1fZtC<# ztEPig9GI}P4aFiQxy>MFiW^173?I6@DQ-krOTxG^wm42j3)r$D?}?O{XB|33#-9EF zJFl}Cc;a*R5nLYL)i3DO{*PA)!u!{QxDGnUFw$Q55Nu>Y0@b){S$C^F$WoaG+x!2tu_IIOV3(Qq`ggQ*Mc;qOnuK)M2kG| zbzEsq?rd!m(X8?caY%-kG$fI2yU!O`x)9BwdyKmBNf2>G>D_qe4<3nvstU1=4eU!P z#h`|mQ0wA{UhsXD#;_Q|69Qwf4!XSZG8LINvWfL1%Tb2}i+i7r00Zc)4yT8|(L{GV zj2#<=)uc|H5)wUKRYRY9+9YFBrrj@LZs*ZjxFgX6ojFMh3S*EZr#H*oMH&3A4>i5gk~JS zF)BU4FnJUelo^W2-6ds_ zG7sk%W{$>9TadU-Q(tcksxwq+1v?Wsy~}pHlk88I_wiE3soh_pdX_2ASDcT&=SJ6| zU0;L*AkGyxWhUoDph28`^V8w;#4wdm#DlZSW=P008 zIr#PzO=#^M6-`XFjC~Y=2kwK@9RR@VPx?l-A7umfwCNSI(BXMG{Gizf$&;%bSGwja$VqYQb*{t`J=28HY)L_T~c`FwNT_ zrpl6CBWVGGsfxly8ca3mdb3dW5rpM?PeKj6&Y{AU9KPa}dr+7eKxlDcpjz@E$#OFhDAR+pZEzWhh=rEtglbBB${&`47iD_&>qW!19I*R)=q5^1_%nPAmX0)X|lFzuTac}^6ucWu;aG&csO}ooHVBSJOYEX8x zaetFbz3+C}a+B{dTZ<=!y2HC&Cye~aKv`6{U!3cqYiaR}hlob*MpTd3mnMq1>xQZ} zN5#sE9!Z4RUkgw)Saib$zCs4lT4%WUuC3HZ7iwby9Q`9kV&fdkw}PLdkm>jSRV(aU zCE@^V>J*Q#*Ykzjf*_Zcl#mfZ^@3=y!K%ImCnwI8More0Mn}dUO8bM&X{Yg6)lTI2 z3$LqPI{0?wTHody0^TVtGwu7LDSSrv9*!%|gOG(<{Cc3q4U$*}dN0GVN%rGL$gC@z zzYm)CXc8dcbYAoY(&cHq8D}wNJ*RO1MZL^>ZNX~oGTtZx#)3IWKkZpXf{)QO-anX0eGZRpAX zKiTLch)QaW{@}j3@cZCnwpdrMwZUks^85wtA19>K?^@A&JA&fC->n9ar-Wg!C- z`G(bh?EEmeLmwx`!pCA2ySqsUX*KxSM^{$KI$&Cl1jcNDR8ju#qUwKY8a$QZ=$N9| zk?5k>(Ep{8T zC7xPpEwbtvwCU8z|K1JAsxc>ND4MkzP1#YCYs`m|R%B(HvLYLYw4RgDN4VPNppP9& zzHE%b}Sbf_P(axWy!ZkHXs z%lTo;6P?$I6(s8WpZqV*eiTq#CQrX7WN%WQfVzBsjb`CR*dHV0g8%G!0SPk6dfyTt z99lPMQz-le-``Ae;XOG4Y`A96yH%Y z`!^Zi^HKOOZ&UPD%Gsily`~OKtLQ*}dxUs%@`Z76r4MQ@37u@bnY96hDI-Hnqp#mY z+UXyc+^wp}F1|uf&yD`V)7+2NCqA{nv)6#if|`AZqiulfzo1&{T$+qtR}T6EoPGd? z-hBD1r;{f)FRIDZ5b(uqBo-kiY~3)qoBd$=@KASzs77+=TEHulrl$;WIwRdj4_yz( zGXeSLzO899CvIg%=hKx!r zVJ~(Jd?ciwB9Sx$-QgfB%?x-Bjgt58HBt#5^{(&3F`%y!hb|XgK1s%F3#Gm!!(yL> z&d@>oQT74RhY0kfE^NDeS@W6#kLq3;*3c3dumlXZ7sLQ%C}?o)1ds)>alQKh#X^%H z@&bmdbsP|liM{}a1DTvd8bE?}b-MdZmkgs>>J?=P35aFp9uMoHYAa+Iv8{kL!o3il z3F|^KNsMM)S1gnkdZ$zVl4sDLiD85$8c*`bUKOw+`?6j6ezOtCew$Me+~`8aF{EsahWV|A3E#jF19;Yc)+O~NWzgMQ^ab(xs(tYZ;9!A#%{i~SYK7dx#@lK(DV<#K%ahZu zlu-%THzcr^8le_J%GZ)TL|_h$m1{pSv$mQ8IA`_@*Q?=I#5Dpf3YreMX}!Hv0c$V1 zFLf5LB^Mo;9uH%spbg+JZASi6UiQBHpE>^VsJSovfV&==f%DPLUn85qdI)uJu-(hg6&3}z>48WsX89HBOmt;*z$c#vpG&i$l?l5`(56&EcHBhL6`>fea%W3?~JMV9|lqN^5g+ z4^@aoR=^)d65VxP9YfTWj8@)5sFElm$WKOydQ3kVk^y|s$~P+`g+}8+a1XfS_CRVp0R^S1OiMu^pT1)rR857rJqNOx4o^PHW{85} zDJ_e4fL(EYlN3EC*o~?>e}qM5&+TIvx-;a`72xkaS z8yDr2oHl{bff;$fZ!`Vi3;ZpEo%vd28l$V;5?6^0vgzQ!+jgFHD3wTVQw@ht&g<~UWkf6OXlC4x8K-b!6!$o_kIjJywul7YKi>-nB7*}kI!XW~K&$%5 zn;kBNA-CiuG?2hfeZrN&LV3RgLhI+Zf*0WkCE}V-_t{T8_M5K|_}}z^pDP57YzM4P znPb3SaYh9@mPH&Gj>_Kmk#IqMIV0rEJ$no{JYKv;MgX zro7?=@OKP4+z95aTyKfYT`HX9ikoBe)Pp|C7?H$DwFHZBS_?tLJzbNMjSSNwhFK6R z)XbXyiBk7K0_`e_=5nrI$ko81y+x=bMV(x9aKTS*NJR&*Wp5c{$D5T7K0aF69<01W zE|HmDX`s6@>hBW7n}PpfOWaqo=UuHDwJx*YHXnAal4Aind)`l zt{ZIji7hBY9@Z&m@V#F}`xmv@IMkS;Olc-WEh-a`Awe{==7E=qZ9Kp0Z{gX4gaC$2;LZQMK-aU2R4F?t4f}e=-m^alGLJix4jU*C!6%M% zn*#pex(UjrtNDJ+eNM#ANfUJ5b=FHT`0tnVk{~I8EDBmgc`H9ku6MvU z$l3_p?-H*}K`FzU<>BH{z2Qew7x(UH+VDeXfFA8>0wbn(`Yi4Mp0D6j;7(MS-#J-t zMQnyH*nQ~7TM%uUf<{aDAe9!;^rZD24*``+g-I-L=8m*Fhk^4d1;Y!UBT`Zcv1sH( z;5zZQJMv-qbhTlntBTrzAc2pWzu%ht_4<$&a6}`T>m3=I%$*lCHUJi8C0v|wbI zHd@-H7k0w~qj)tpLuxgRuQiL7;aZ#e%`L-8k%7D$T*J9YboGqa-M_yt34*SXpLu{A z_&G}+=mci2Qo>vSwNtITAe3bvc}%NPoliSvW_wx#Lu#rRd3Qz~xWXs8z2W+24fU=< zrS|GIO!=xtOO2U|R{Sy4$IoCpgn)#Z*_SR8VffGXc|*`YpK0Nysy(!z2X=6*Gjc0& zpI>*SUQ(yB^u%35bZs6n9~IWbGz>iK7_>k5zv=WIp@*(JOyZ=0Mm>G)y!b$f=uGWq zPtwO9IX- zEv;NDHdb~}c^bzXtYvIure7@^e)oN$de6aM)2Jh-le628fo)1P-T#Y;a}Mq#=(m1s z+u7K*ZQItyHh!_~WMkX5ZEb9BY$tD?``lagR`sbf|IAeNR9DaRw?BOdJ8z5(aZ}Xr z7Ww+7;Zm$x@;<_D6CoPG@$hhIi&IkfAV(%#@TCnQ8=SJyXOw6#6d1VMR5(71R{Qyo zogEZ{@i$SKAfH920P|dDtNqirmW$j9r@u*-VNZR#nDGYv?&xyM(emV14=1zRy;yq{ z(b!v8{6gyM^9GJIDtG0B--Nb|eW4<|kQa`@G{hbA`&)<--G|^<&*75^zCgZZ0Da$d zPRl|O#8}wa;%!I&-=AeXBkkLX6fwq!(3sf0LzBD|5$JHR#V8`%lBO5 zo)GR3i456vg@h=Rwf3y^bgD*l?TE>KkL*2ZR>DgWUp9&dM&hXg!+E4VDHmE#L!Q84 zV6lQ|;mC~cc1tVnYt6n3v|=n{a}hD+!o=={nU_BAeCC0Nb<2IsiH$%pNjd8Vog`*9L_84=HQGGq`1bple1pG&zQKRE7lmZxR1N)32CPPG`##a$vcCs_vno=7;3c$0 zIdDZFe$l)RlWiK48)bCaNVuFlkQ!bS1W>odPg(sw#-L$c0F8|6+qYvpHQPj_uZWdv zCW7RT{Q}lK<=sDll>jBo%hC1)T~ISPP7X6zE3^_UcO2ZN8M)H zv>W>WQAVLRgpc^%7v>?qZiNGT9S9rYGaiAY`zm4q@tBm>Na`vZan5z-0$$_UJi0#5 zKEpVE9>Nz=-OUbfpMBwob|5RCxKPBIN0<` z9Jw{4BkR=s78R&>uaR&f+-rva9{0u3O3;+4S-J2Y(XX&&b7y!cd5&jkGHv@{>;TvF zAvWzTV2&{|9-EwSDgPxuU(gpiLr@c6Xu>icV+3LMC@(}Pu84u%i>!TGdxzk|I@4+n za5tB>_)DUSv8_WBdTwyq`$TVc_<+;A0?9l9KAO2o`iq7(Z$L0nffT^6Ns4I8um7ql zcr}2e-<=})Sv17W+Y>Yg8(fOlvMo5|<9*$>E9(cIh%jcSn%vn~y(_gr*wuY5!h=5Q z_a?3W$B`_+HB-)Xwa1F_hrWa8M&kno3}DF#uxIbGdH7P-p;S5iheamYaCJ&gPPuxZ z964Dm3{K5%Xs(i$C^$Eg`WPlLKNp`b+zmp6>Sgi0UcYvqM+^F9YaN1`@PGzLSLm)y zpJS1{flO(xsDM!04=(3b-<|Lmfe!9^9B3pH;z8_Nc4lo%aD)mTs+ z{)(@1I!v5M_U(JTXEu+pRXW=L?7Rl-F8>nMYadmbogG2{%<01}ay20UL80kwH>bJW zb>^$YarwG!?Y)lGuI6ZUP*yc#D>?u3tzu^H(Zfig_^n;(70WwOy03K@b+=q;mIm!> zG{_`KGp3`#O2fv`+#^2%L<&udg=}++5^C z**t}`DuR=jPrR>Jst&I$k!V7eya$OT6RU0;f9c)lEtQ*hLZ|_ip`^Fg0AzZGQbc>H zX}9k*NlnlDxQRmM{EgWkV2U7dgGVt$hy936c&d7wyz=z32+|V-rPNF}C@%1S$;|A-J_Gd&XobFa*hZ=&0) zI#+l1>kwYYJdh*{`pK%@338jkisT;8ibOdRdt3;S%`kb34R7}$g38ON~6nKJ8j*Y2?7c< zssuBRPUozhT*@<^DDb~0zxaA+-4q(Jr|s!ovbV9ijL|-GYyEU?bNb$j<=9=fcf*56DXv=mp>V6qxa&)rL!W`kv~Wly1lmNf_}!rl$}>YS zp2V-1F#_l#-^f0EA{L*%nO{9gS;gVj)yWTz6`Yi=5I+Zp&d1<1lmQep1I|s?bEI>O#fVdbHsm?#zWw> z1%u*(M)WV&&F@5n;$o~~!ppc-VPCGrRVSg`w`-uGu^o6_v4-tAKteX{AFcv8qC35w zjgU8(xK}$OVYRCe^>9w)o!yu6FVD4aQ36=P0frP7FzS0RtY%J)<_WlVSO|5L1GQV4 zhY&#G-#Jywxj#@9f*fa%G;yZ0-bUvp4x=Dflij7bHfSOaH<1zeoNdPN*s-)6@AqJ2 zjFlGPQkplgzZ(7F7}6}jryS-PfTlqyVZdhLa@vkD9nAZ&Cmd#Ba`p-gfHJ`;C$?r` zGTZhs70rdl(TXj=Wm*m~4b20vD_Uk@vXB6(5K(@>r}c&RP2+;VnqU1dHkSxIF_5YB;dowy>0Q%;a z*MpNECyhh z?Q$i~_zy6`+MZKcUhkB0=h}r#*j-EqEQOGOu6S4fzd*mppW=piLthr27>8-z^79BC zL8hH_4LON_zSO9md-N{o-z042s5Vv93TjrhoDsV{c9=j5D9LzVu<3<8{GmfPP6 z%5l#1+aL`^vlh^j5>72{8xBKXnem!X5u6M+Q+N8vyZglWdE|pu+@6c88U8u2MIp=(I@71}0rd7tSD>}IpGrJy3T_x= zZEY6S;`a7GoVo(V-?+P^^ZOOJAT}(;SR0a9K%v$0o6K&6E__4(j7bD#Mj;e>U{OTX zRv=rEIJ!IzO%&O@FpJI3Q7b#ft-tf%7ubuHfk1t!Hvh{O$h-`EXCCm~q<=t27+(qt zA<}E}A+4rp)JDr^RHKma?pb(k#z!e0G#q9ii)EUII1$jYBt`+2OVR~*lF`QECkcg& z0JPRm@6tWH^srvtL!cOX6>1Wa!681b9$n8=1@#7q9Yygi!x zQ)8u!v>j#J%&u})H->*lhAVQ@iD15LFl48vc&t$B_Shh_d=Bp{r1p7nZI249zU3hI zjkQq4j$d-@>D8BY;cI!)t2RU0E(}J{yXf1;)2YFjn?LM+YFSGEQ_BhpMO-l_W|%2B zXmnTOdjj6&U&U4tE?_p?H6I2n?GMmr%zmxXybmnyx1*tVkqL%Ot2JvVrXSOczjhX$ zM@z^uY336@d;o=}@^`pE@j@@B0IT3F8s}1=8J#g0`YgnHbx=YIZHq&i1R}hzXQ7^) z%^_{E!7W>qz54s?m02=U&nhYS--Z;Aa2&80R*ut~;bpz(aG+SFNxJ|>D1H4n4JA@2 zEB)CbVGDkPQ@0rY1_^|aYxx8ct^XtVLxBMPKMDl8@P`5k9roHtz+z0s4~sHmDh;sU zMVh5pUthNghZNhMCwLhP%E_OEdzzO+4C%JtR}hTt2>1gx;+!{|J>-&&5WqL<9Ggwp zCBU=Ffouer+67KN=0@YriORNLtMCR2&{|0-+kYkDGdxk%r8>{mo)lR}O~>c5N(os* z(2k7kqn!oa0!CNjJMJaqE|28W`oIy+`Y!{&6 zx4vLbLi(y{=4i?GAYgwpITOqT=R>B;wHOIr6aE6^q_2z+5BMUZ2jeNK;=vWb9r{IR z{i>otI}(KKm37f+aRYnQE~@bp0tK;31`8v^A~;W!0xo2-M*14p)`!%QENEnGj(wci z#8Ra`G1_z4{tAc~=HtUP)Dw^Ih~7Q!gq$^P3KqcL=7ZM66U_CQ)hZL4x8j4$9pMRR z+@S>6ZlhLsiJ{Y#iMMd%8C-D_xcH+yX8O}4XW66aTT{DB*4PJ=MS%6zp@Suo$3beC`g=wbAEq-e>x}hHM^*}^)uve77`)ufeQAHzvzgt_Kcfz%t zD@*A*K>n|JRsdh42CS~vxt&-BvuLfxc{a*GX|wbgo>uNWZNsFhZnO%^ z>0aGUfqoLuz=}(~U*u%7CaQT1Almp#sHp}( z-kWc*RL}H&rZ6s%Y$kU2K5ZQ7rHD!8mid4L2wJTu9G=ZTu0XrvGa#t#%Jgw_qP72- zPR*efr9unQYCy>(QM@aWohN_TH-_jLOG~e+&~N{~Dd4PjCrVxEM3O((3ttw}3a_6V z_Pldf8U)=6^$1AOiK`>RwQH-5GYbXyv;-BYsad_najO1Fk8b>9jS!;_rl{&3M>j0J z9Yle|>r7IhKbsl4H=DqbRdo%$uKbw}sAmy8d0)9{<`Kq2y8nfp&&<Abh)XPW0Lvw1g67Pa@749zbt-Se`MWkw($Y4%TtfdDXH6@?=@xclRTc%p7 zZCJ}MwmkKOE_UK<;54pKn=B6!--Cb3Dzk>Je)fNNou}y1$s_0Sp>bWpXT7_+DYB5* zJg<^~4-{R1ofC{#x-Z*c&0-Vy7rjS|ucuvWB1#9%tdtzzdtY-E<4-UMBUSm)EDW4EY?L@U z0H5X4Cg9J6`VPSC4Vp0G2vMJVh&I2~Eqm@=h~(T+l&A|Eab;SbNV&qb;hooR3z6?z z`EyF|AFF!<`+K{eTwB#nC`YDSB%NWsW_KM9VHcEQ6SeyWee6~a?XBYWX*j2KF zDb2uvHJ4k{L_q*Isb&8GVoNVMlnOmyj6B^$SWTT@WYo)zPvUN4}7{7-0w zP>}deC2C%#oy&CG3=cd3IRTI*8nzXdELhOiLiRkQxHwyt>oSj6P}?Mfy_eIHUXqBb zT=cD##{IF83{BGmSEa)Tb!1)j%$OmGxLfEEu4F_SldBT!6Id6r`9@sXq!p%)o1{P{oSPL{$t>D_po3g323#$rati830t?nue9N=bRKhgb2KK6SJPfkQV+AL;0oL4!g*ts&cnitb zCYwrFnZb^9)~YBb%un8hx}(91b97SkX2QC)-x@5k8F>`Kcizd< zGGqYBongi^3RCJmj=$^AdCA9c{ozq*Q7j)W91fcIw`gU`hBwW)>}tjnRQS;>&g?R6 zSlqyM!lwqG*#DOM8<-dv+6CGDij#tt+`iT#FG*Z&%HQ0VC1TXhAAbAo>%w9JMnTEp zXvtO#fk(>E;)sy_60T4X@`N%eb?LC+fAU9x;5r-ex;RdUyNFDEA=%+JT;tB6)OR3! zeD=X;MlFHm`oFbiGGShVW232+GHrxkEL_ThYP#qB2y^GId)uqMu+dseaw3{ba-e^_ zc_Gt_Tpi!kEoWuP(|2T}9Uq6Iv_$g&3H8z;5cty!#=GOz_vzkr+u~aeX5XVC79bTM zRfU{0)=bS>nUXYCCTteO@JA2qrc%Uon9&_>UmbL0gXa+}$G>W*!(yyaz{TUZ(ImB` zum8JJjF&pnj&5-`Bq5(jgp|b9NVJVAiD_h*c|=oo1PgW&%Y-S!nKo4Jpb-kS#kDoi zta;Xp>pEJjm8UQ!spBP{)S6!1=B7)2jg92jar;1yQ>Sj#;i7+{~ z#e_fY~9F3{RzTVNV+K zytHZ?57;zq{{b41T)?)*K~-^?PouGr898~`R4{l-MtUAI>R|qw7Ff!MVooR)r6Y~Q z`4tgn-d|#1KdlW%qF9d@?(5LG->|Oh7W!;e!OH1>yV59iJHKUzu<( z731S2w_#TSSl@1`(M*OaVj=o6x%k%tI&I@3F-c^%CP{jJX%^r%mPpL?b1Dg3wdm!&%6EFpLjxi}akyw|Z>r z?f3_6?=TW##n_D`?)5_CrOR<80+mb|-iY-QChWV^GIJk|@ra^nR2caylYWqGhhmyR zxGG2*j|pUa>1)Eh42i`kPAFs$AE~@saLwRnWF9aDL8jGR>S&0Eboj$EXZT+hch+&F zUQd>Qu6Nj#56$7>pz#8(71TD39 z4oOG+?c;B$8as_m~D&Euaqmv8YYw5*XkKC{k0b+BJ z$l2ehwFu9VVVW!h1qC_)7ZskHAK#?bIbrjKAB}((x*aLN!Wre|Kol}iJEX2O+tckSY`@H(3|COz_DvsHZ5Z4`A_z?V^G083LrnWTR zyCjghIrYk|`3bzZ^=ls$2D(9AF~puWj>iE=@8xDv&gjO;hNGM9JJ!qcVkAY34lv(p z%90aAB^EYa)idVSrQWV^8GhIDDP*T9Yom5M-hf=kD_dNG` z9vG+X;Y80aO^=GqF?J&>>FN7PAB(ihufaV3Raa}mPC}}=J-4(vmUS$z@M|*Vu-E_s z_3-_#4CM3JRpqqo)oCn~r!`SENN+GF^LVKBA3qSY{tl?`6{!Hy<^Gxq@i5?#RS_-CE^_c!y1$K_7-jX;DW)@= zRRRf2%WF^c!JugYAYdwaUB&ezt7H03A@kC?O9zwy;TO5_nwZaj&&Ih52Py}+db{0q z8O-)C`$)>T-;tXzTqIB^;VJ>so$@%i z@%eJShV}NB-Rm6 zk`n&^Vt>d{YHjRVUK^cUL+<}c$UxG%Ri@JHQoiYGvmeo|`qBS+72IprK@T>Sv`ov0 zZCs376B+QV8i1q}-R5y}+0i$(e%iX)%mJ zQZXTJPwX2b8ni_>kk)>S2LVvLCU)t0q5ZV&-{Up+y_WO0JRRqaX3x3ow#yFwvz9JN zTLPC;v44MsS`Qhm9`o7<%M>81o* z5`7>Lwt2M!E4OfhGSvsmtPEp7z;wIA++QD62g9ED&h=ff?GE~*y7q;x3%XN#oB|dd z5*O!oxz+6Q{x$Z@{2@EaJi@tIo}!35_AbGPg?vH4pS)T zC>%XxHIjGnFly_bAzgyylo9?*^!N!4UY#MQShnSZzp$<=##MgvoRwBt!_!_T#y=YP z5lOuVZGB7heQf6)NPc>#f_kXQszMK0e8(U2wL|TCf~M&3w4uBDi#O1udGG1S5fq4pLX!Bo@ zMygV<7#uS8)$g`fpc}qZfa;dDC(AZ>0I|8^rf1!{))*$;kd|1|x+`{-o&3HWX|_ER zXaeWhEjCf(W}V&r5jVsDQ$95q=%Fl|P~Nsy>v^o@wmlBh2f5=&UT2ur%%+&g%CnLl zFbi;Ij(;IvSd6JduzXg|An28)+HQZ5lHUYU$iRStVZ=JkT=m>K0Bg9j4mQWi@M%$l zF8~5P77Y?YZ;#QC$R?NygL4*Ua`h=N?}6b!UM}ryp&usGM4r059U=TNb27n2V{C1) z{its%KMRUuXNWK`B`8;MBGS)T1G9F=j&(F9o=}#`acd{$jp@l7H9;k!qM49~5j%x9HCPzO;2uu0p!f_XH{Rg zFTitfGG!KUk7K7L;5&WiZ0KKskd&JT(rYWQC)wh9g1wy-`q0e9x$4au!C;Mo6h{o@ z4CP30QV2`aj&(dg-klCqxc%|4HLdUs=BwhX>O2Da@vsD zBgE&`LXJ9U|2AquvTy3WFo9S92Uxs}-Q|NUmG`;87yJ$dgyj0E8MTJsPb3l%AuPcbPswNF2kuLaOmFLCaFjwp+#zHj@zA8L5b-{j&spSQ<)kJ+NQh>bcB*5i9^ z-&cCCMAuy|ngmMky^bgJrWCbme$n@pH6mx=odCRP!x)zWDbO>$o^X*BC&m@O{NsU} zgPWVzkmCHtaG|WNF})Ll%Xzh3#Mifo=43$+@#xG$RNIL7eXf{e1|di6?W#YUpAfcI zj3AUjzSFUwd#tVHerZCRijFqtabURfw1#HC7}=usMoRv1hleMymV<*9T{?XE4aUUZ zFb5EX>L?4&s0PQMCu4i-xlrUT$hVNCl~S@16ipmDK18M*NX}&u9xixq94*Cl1>#79 z=Zu(8!hD~q9}i>KFWR1&x*5fYQF;x?HTB z?wD(A<`RzI!05qXl~`IITA8AFNCS9&Ve$Kf_@{ml`}ogF^$5b$Y5C`~enl z8(IkUG>~gWe_{>Z94PKpS^S&&Q3zUO^$oubw}(w~jl+o??s4*yHj~!_i%mX$GtJcX zOE-XNVd$+UE!s0wDNdYWLZCh!zUa`v&0joJj+01b_uZ+8k#e5CfuyuVN@T%Ocg=|c z@|b0=pQ;RB*@+5;nzD{wh6X49paVj@ze=lB(JWXD%?derV798z>oW@HBu0waZsle> zNP5KiRk#?bg+0(dF+GBR$LZ`dCOsN3I>kc@DO;k7bgT^36{b&9i{Yct{b9*ei#^2A zKjf00E;e87sq}UFr9E}SGE~@Y;gS*$u1O|tcP3+#sI{=m39>IF&4ieC=nW8y;JW2r zpMJ3(zl*cMg5m@fePV*f{(75ojvuTWoCvM)tk1WmH$TmU#qBt*MkC9Ls?U6Fzc65? zL1Eui&0d;y!c7d{qju!Xun0;0YGI&rlvNf@v#CR=nZ`>y;wqG1V?tYBq$(E9ciw6+ zVQebVVGk0ZhG4 z=2PlUt=NWGjv^C9V%(Fq%Eb=%3=7PIumUKkVWVD964upTy#aLbEh+B; zI$4nY3^=~7NdonB7ZBdu&9wz=o9T{b5rW5({t(N&bUVWVsu1h0f6M*1l=Y--g^j^{ zXiUI!cp>I<90ue!|8DTrSp}KbK3i#oz^FDeO-t(3P!$=Ak@dLjlr}ReN!A*66r5$*_=CfxpYA2u(yxhW~3 z7Y(ogGZhpjWh&ffvD4HP*!>RWivfN-%AlR0clsWuVQnN^Osw#tlnp)^*N1dc>w0~k zkYj{9y=HPb9PL5v5%~RDViaU$4e@}p-4;JAyRyEO^n`12{noox_nkmSxTQ2#Y5loP;i>0Zca#2-l-g3Q5#(>G#@LBH`ikwZ$r zKb0gn3;%aMqZ&vPhc9tXG^3jvC1XGX7(|?JpioY@ryFAx&GreKy>t6WR+0^RrfjO^ zmt~ZK?(4x}A8eUMGh5cX3j}y0iqNzb-eK&~SI_qiIRRlJ<`V&$p7|b|_n-)&mM)+- zt!G%;uWwd$oqM(XAyoy^fM3Fu|h!~Tpd7TZiZG*;Cwe-_YdW}V1gSa>PsEu zgpl>~9gt!6*sE~!&iVcx`{kE%{|@Xoi#-~!llJ&H`8(emOOnt`VtqHrvPawl$HN{k zrfw0n!Uh2=r~isnoI;vg2GTHclNDgz=y)oS__px{Nylt)vHd&2@kpTQ!ZB2$uY~ny z6*`K^QJXYUrXXPNu3dFY=msu9=$wxya^A2y*MY{;L>~fQJ*n>Gn`(@?*5Lh4(}W+t zY+1YKrkSqY?9X}}SdO+qhIFj%sQPo%IkFJK?#v;8rs-B);?U8ZE>@E&`U)`iwXO|a z*ubQDAC4d!xldc@h@}U^KW*6)as3F7VJ{>!ZHUlgT8c*PX|&AldF0)}_i3`OxOc&1 z@?7Q&!{ak~(ybzhat#cmji6>#hsOEwh=T?a%_&{rqcGFnZCJ=2%e5GI_L_Ug>N}nN z7%y<>h*%CByJg(y;2)oNGY4QxPt+TioaV2*D-610ODS-(9*GFGc9i-n@yv-vT^~~= z-qA+4BzZ=H{OROKQ<1R1ASw(vJtrmwK0niaZ1PF>GUHM$yC36TmaB9%KKmW^SO{Y6 z+>4WUCFWt86k|F_5~tJfns&Ampr8xF!*!KZqWY^?k?b$Gs7La^L=WI8E>pCjCbdRo z>5AUFDI)1eU<6V(gXesBlS|}Rsb@9)RJJSARaI0%l^_4r6#dLoMC)FvXmrqStL6Bn zx=~q!Qb#A|P(oEb6_q3Z^ljmjRf%mWvYSt`rKmU8*g9lJNpmAU>GL+3S*&TN_1Hw) zAqYejkP0jR9eqDX??4j4ATgi=!okNouaH|If`9<`@5XR5bHN>s_kN!*hFdHwkMXFy z2e%*Yg3XbjKZn1bEV7&}yA~62Wkw^aR^#tr_Ead}v|lnK3NvB~tjnQUgzi>UX02;V zkPW}j)b8wHve&in77z+6!gRZR*)}!Hhc)aC@Y~7}2d|d9yp6)w2!H`AhRohxV5&fT zG^J;6qCsN11G&k^BAueDkvS3Pk&c7=}pelkXRC6=4}#nU>M$ucc}P8 z;)a%C7%gw12E+E}je!C7K-E#k=2|fF3)43!rYXD!SpvZ-$lm8c-wwiPufkoOM398{ zx!D=QN`WG#9#*Z(y^1%Uq!4Kq_7`?Rr&k48HLhlGz-5p8v*m@mcBOi6@x5K$SA8%@ zVy8|(Tr{sYE&}bhRhR1}hPj_4Wk(61&sRy9@V!aNtzu{LOuPZi+Li2EQq})D+OyLW z)!fDY5e^5Ku2~4~)3gzAE9MO|Nyh9m$*Bj%5>aR0-@$|zjDHZ*o1Lg8)MtGPX-fQj zT&mR)&VbN7k4)ddqFVm7O@M(XKS9eh7x>I}Kt6zj(4q6BA^_{pwizI$>ya{ulI;ra z_aKe>pN<9)%3uR5c&_ml^AESFZ8t#T%r>*$TKk_~^hIdUy9d(6)5O_pos2ey+cb5s zqbzAmjBL$lORA%XZ@hg7Y(u{416`YIn^-sJMrkUR_ZKDx`_E)(UK*Duurd)R#1F4i zVkru($CbF^QIgoJ+;S1bCJm6d@2TN1dNjF-6P*i4pI`t8g_}n(*R?2-34LG=W#|-= z;qT+nqAn}+kIIhv^FPsKC27aO+fB_3u?qIke)tdj$&0*)9~O9b{56VB$8i!nL7?`3 zHx~@axUA$nrb?-wq?fOGLPfk=FvcBDH|G|ZerdBbNd}9AiKo^m^wN<;O)Xx}^etRo zc4{2mP?-R@z!LU1W+&ZKm2hXgPUi~QT!y?)BWCyc+pIwI;A9g_s})5ABPb@zpp&9& zT-}s8uZEk2C+`_I5lZE;i2Ymci!&5U53a<-U8<2Mu6 zJd|zNbHX!XCY`Oda8WjzBG)ExlVE%q{YZQ0tzl0&wg%cz>M(~-7I zE#<5whGu`HO+tm$%gs`|De63_NE*30n8j;S9ZBmxpGHtAuT*S(!6XU7^^~96ao`jZ zI*8y?QS|l%c%D#K%soN2NatH5Gu^gt9b47;i#``)Wpp2o>NXaEbOdZ_-tu3etLl8Y z?w$auZ7XrSOIu#<$G6!YCy_~>X1_IEmi@HLNEUo+#9UWCxN+6+iz0f0%0p+<(kXK5cNz-&JjR$;d$+LUmFD6M4R1$RIg|xA}_9 z|IfaDi=#o0FVzl8Lbc;SV=3Ima20nK62S9`Tmx~>btwzrVHWFiRRB*6;EVpJFDoYPpd zE+Wxc{zNQKIi>$3HYhCEv5RAG0Z~|2aiVJB71u=$jM##u21kWzc$xV-DSIw5El$_R zDMVc!{Lud|s;m6`w!{4V=$+N2Cv|Z=S45g?HTCj=o!PhsUw+aRaRI|v9F!Q6IuvCs zB|t{6 zJ9)QVp6TF|VObCQk>Hbo|C%ux_jL}mjETY3Qi^c;D)8q&|tTR{>hyH4cbb(VxHqyjFfJITxXKM{?g zlx)(qEMSwZoC|WU$vp-^1k2$kRt<@TQ^JGywA_MOqzhI#EHnClIp)|rw4*)~;d5#h zb2}kKbEJ5PGMtv9mkfU@&nksKSpMjUDbGWb9`N3J#D zR6aHvqxCcqF2RS^kwVnK+0-qbp@#gyjNZ5xxfod?4Zim46g(fN&fi5CfZ1oiNWfjY4+cHmDF4BQdw6? zt;mNife!Ype4VZiJ%e}LA>{zq+AL==YzgDwuLs<42IQCx;(Nb&TZL|AGmNd2$#;Z)Aw{5I1YJcZ@qL*8Gvf zkG94C4JucGp8t%*(l{E=Rt7~@JAS$iGF@^Has&#%y=_>^nDwtD;+iQ48m>E>!;~%_ zk>%qWq4^v$9ezsgXI(eAs1@F{%K+jBU4noj2FUHy`-WlAlbOjc{1;l+Eh@(gE%p0} z*6{-kKF&++V?xi8T=RJ4o|ZEzrPisdq_{v&jJ%2oIY;N6V$B~p{I6^Bv>jn*0^W@` z?oxW2`x3hf?iH7x^o2btKdMs>oz6{`!V%Dlo!m6oPP~AN$dCW+q3A@vRq^9hwB+$$G70O%kd*e6hg1JZBd*>?4%tr>~m0}3`nW7 zT(S8MR#|b=cY#ZKiq5(-Ok}o`z8qhEJZm*yNp^qPY6cXZf81;^J4*9@%yi7V`o3hq zYGNyPozR#tkwd5ZuZ_1Xg42~}opV04#F{ANKOlvc`THmm=%*opaYHqnV~tJpw}5au z+$-IUH9pfiB{q#rL-9pVo%pg6vR2^LKffZC;ws?8v_7vds-+!i2gO8v&e zlhKC0j|KF{s~^rF2Ciy=sb!)gpz^cJh^~PxoPJVT+Qm}_9*sIv=~&)fGHQ+ z2*7YfbA@{Kc`J;{x(;OvW3&(ccR_=#{#9_L+IF(fxFZmzi1Y04xlI;k*5`}BW1tcv zhgoeyb&jWRdRyzBx}|sjF#q0a9E(n#7Wb@4n*WSOjnZ(4|b4|6uDjZ><;PoI=>&DF#uXhxN+FMx0vuHKYFVlF`)87ShP^-XFW# zvoCs+*HGM=fU%GbG5v9l7YIvd7f`R}4C3;Da7u~aC}SzLgWWANd1qVTHrWISX?Cvmk^VYt$FlV?B=5yocgkH&)a<-Y_6?RUBKm-5f?XR zZ0pal8PnH)BT4Xm%hPfFui%)nSAT`+XTdrD3>{l-x)(b)*CkZpiBHS_DIdbN>1~?{ za>#ro@1;37zAmrGu5p5VOw!k~lrJq(z&yc&ykvN;O$MG5klL1wrxokL__m;tcu~xV zcP@mFM6`U9S=Ae>RvV6(~@ zI6W~LI1AY#Qma%sB7~`3uT6kT>OS{tX5_mfa2 zGm}V^xX#usqkux!YvlsVD@Om#cC#N)NVGa$7sKw|YP9OQ`%}T=+)4^kDN|9y&|@at z&OlP+hx)#63D0qW3As`VPUm7zNj37nesXnkOL6#0dDhxxi{@Rg`P$})IT z$k+#9TYUK}uWwDVS|mkg`*|UU?z?(d4qC*PHn4i8Nx6%RbcF<&YNK8i3hBC=IWiq- zUBTiy1Pk(oy^X;8(0l>Oar`Ss7cjwrWnne5K&HJALCa^; z@X+Hbu%X8udm6tbV(DTdNxq4hi88X;s@`}HQR|39zqVBC2%lj$4jWhFvkJ%A zES7|i=r(|bplf@uBzh<2vsFGhhXnT>8d`$CI1jE@T+1k`sb?tYyjLB2j@$Vzed&;@ z`}!x=tpW@1O$)q^V!?hnRA4qb%6bHm0{|sxKS84_EF*NDbYXJzNg;az|8?wkvjN71 zDho`<$*^SXy-_DGN98EBa_Tkf5@SxjW?3ElfI@fTy9y(4cTOuOd)x>Z#dOeT1~!5y zGgjWoAp+()Df<27N+^#G9N(bgu8honL=qh4Mujv18S@6MW?4uA`T zh?~ z(E7`oiIu=dqH}$0*W(82lD#OPXfOG#4Zf55!>&DRbm02Gt>AWIu|fhoVt;L<+_RFN zhQc04Y)K7zS#TSn*rV%J6%eY#Tl41uCTf(2v^!_tF}vwH&qEjZ6H<}8G6NbPEso|W z=xX0_>rg|R_satWLY=q9pQs~pG;AzezG4Xd=CH4Fh;5QHMQtA@Jxs`bjlrW#ZQpYy zd4T%%L3^TNi12*yzsNvoZP6B}%X7%xgzisQbt?@U1qH6$_*^UAR^IkeJn47qKaHR0i}e-%l9_3jiO77{uPiQWUKrF0@XK8X97|ES95qc^8ED@Ikd@`Y z=!&s0FYg%tAwtH6y zm5-!^h^bG&YfD&2a!MNGSHmt2KhB7wns1&>QagH_&1APO+KENk?zYj1#c#9xH(ptE zxC;STfE)+v(uq;UQP453?B$_GM%b$dNM_xyon=NKbwTdPPOU9w0rRjWkU0Z8dZa-j zdBoebUUqg>X5sbN)Vu+mSp&zAFQSY=I@ne?C-oNnL=+fYa^ZK7kBDEZRbXGDzx~WZ zPsB5+w!^{ZLSQspvf4(jO?O%`HQTY}1P_t=0j=pG7+6k_rpG8_RgJPe(&Ngp+-&AiPrd!|os|r;Po`n}MiaN9AXd&=+bXU4nC0v`3_)W; zP)D-S{l0K_sBP^!l|63%Pcp^CSbs>}hCC>2FvzcoHW0fEU>>HH9ENXOQ76QQI8t^{ zs=4VnE@stJo^+~g;hr~dl(;^3)nmIf0Cd^@Gv4U-TBip@eVl2(l#jNvJga$I5XCfn zjK`h-2ie{HYbMw#wEu%wYV+-D2yA4-ct~3-bQa`MC(|0PWwA=M>E}t(cv9r5y z7#*nC8h)IuBTgZ6RxTQMpw%5qk{mn~Jaq=&>=8o3x2XPxmO&As|M>n|*z08GUtE7J)3T#TKz~2ml}8n<^{e zFDiNotE}idPZlwq$-OA<(kN`?WII|VDe{a@!O)gWfJamCZ-iAQHf*0UZ>>B{p4fAHXpT=)}oHnI<+ogA~tJ!5?hJ&`7VGWZIXs|Yk*#18LaNBCtRF-k_kOwTd#yk_-k-X!_&@!tf{I*hK&~XXFgLo)XW{euVFNJFG*mmc)jTJYm@Bn!=a1 z6P;&bF+H;V*a%6(QK@|44}FWPYm>wcQXF+nFX;V z2f(7E6hsVv*OI$*@!C%!avIYXev)>aBKgf}Wcui0+To`e(n7xzrBSxWT^|n9BdZxem5Q>Ra~Ogr}*ombqbBNn2#UrC;qp! zh`yWzD<6_W=kZE{Vq#~r^mLNTX36n=65FJe&~4cU4CPINKMy1YF#GIyV`(_E5r=A1 zHqQ%ddAwOo707*x0~H7eG2({lYTMFI;C_ndwTHuyrr9{(98rg=!5B*F8ck7xABHQ& zACbtY83wVAvaSKPq1A~*)E~VR_zj`hj|M-m%5SAZ`dapAgsQw`qihW@^%EdMydhi9 z@ec+d!+4iZ6J?kWsw>^)P4xZn84xY*jdo2$`NFuAWQA)Z6J6t8_Z%)QckcvL@cdm- zdob}4eeIH^f|<zxHamXC54HrM)|0n#K!e7_Y4nc4Z(AS^P-h4l=x z7cjl(F0rSZeJnnXf{K+%xWJ^{uO!x>VT_K@nK|SZ&O?CYh%^?H)*7)TY=VBW0_XA3 z*9ojac~%{^^%BAIm&jfSCj}RM(_aA}Qa;a`P_Q!o?qE+Hz-07ZXVR#SYb&`(&oMx{ z`owg-*?&G=Jwb9FxgY%8SwgJ*!XjW`?pIbJfVTLq2ZemN>PV_|9rwF}J;LpgYc++* zT&H#7Kvz3Ew%ztEL`$%A9BP%nw4zD0o+P05P}wZz`_s)pH%1`7xeQn@b(K=ornsZd zW(U=JBD&SI;LmQ=?741}qz8_$jbMk1AScM3`P;Zbm~SF~+dyo>>=UEd`i>G4SEbC_ zIL0ka6bkoCGreX)=o$4IW*BRufZ-vNt{q4WG7$zcC8wS3gV1UE;DcvlD&BilyFd&B-Qj|Qa{P-uh8qfWa2Ov1w94d$) z1sV=*bY7fhe#Z1EE0?G3*{s2l@CZk!4{S&!|NX1hu()J7D0uHvqrv9sbcd$f$%yjy zCy)lJS~@!BdBOIa^?}Wo(N5ViQL$fvcWjiwwN9sOF)lW8bNt|Ydu+rlkw4rK2zj1F zn?Z`EA`(+%xf)H$WO~iKA7eGmX|-}U-pY*n%_UtV0+9HyY3ec=f+;e zP@cQv)QLb=p1QTjf&~42y=3GpqwLWc8&KSn73UKDW~IxT-dx}tR#%{5%0CZwoFP5K z^e*9Rr8_~|7I85YwQ56~xW0qpJM5DnPR&=iiD#Q6^n1<#IoTR*32}JxA3O9*=y!_= zDy}d0f52B2tB;?VK>mUP@W!5jYKfd%ETmYNoVVEX`1^2m-kKR-f(UX>%6ILcgmoox z^S@Y$B!ahCfuO|dEovZ3;_MbX@F@{)n-ncJe;sXn)^lXycYO?&25KVbHkE*WyF<;- zxyf%&8)*dP%J4m^!n)hmZCg9q>dsd~8UQQ zz;rJ&$?1g|cmf=*fbTN;a5xngXIg{(QLDPtKHb@%sX>rsUBlSf;AjNF1McV6E3EbRAPzqNcHhNqX1M=#o_=sujq@$pq;k#)Hlz z*AY2&tG|(>Og4SoB@%}3;}#ok30}dbNa$_0GiYqK;?LyWlj#$=deFit6yv;H_#~8< zDIvZdgYXf{1@p9$7QdiqM!A}b&VL91KlO%&vm9xB?5EN#?-x#+QRM^bgELFpd)A93 zoN09ThSA8J7SSCP2tzXb$@E$>6hT42ps=I#->m&A7UVf4oUtC;kvcIkbRy4oQ3ii< zMtyq^IYGjUub_SraQ5{Boba17Ovr@g7)rfozAe5L*BaR&YFEn;PvHoG+bQ8md8>}g zC!gS4_cJj?vhj_z<+g$M;O&*N-iq?{?-A>^2svLzU*owQeu)s&wsy5~o9y>ie_TSm zetVi$Hc0KiUpKxnv1p9HLp(fumC%_35CW~QND(sweAL83*N}Di} z@11Q5Aluk<$a_dSKf*Oas`lwpdpIy&-LbYHpd1HWWIN^U-dNUXfn{UNML7@9nIZYu;;lF_~0KIM&vbp*O4(l+;r+RSx+ygO89EIDJ1Wk5nz_FooYX3%!uxs z+-OeR_r=nG8x8*g*_{gm+{pbXZSoI-@g~pdZex(wNiEW-yUFsJ)w^{lys{k+ntuU`1MRQ3!(et?2)}{Gu|d2=A^>yL1GQjtOre(uv6Tf+y+C*kOMlYV08cSMdYz5PZ zBYNz-v*N$Xm-MKf++#w0_v#F4=kt3X$GRqV6Vjt_ES$319A%sI(}*oal(W8fJZV_W zSuP?;!W>sU&CZyx=m3`WozDhRP6$z#z_(eTbN$y@LMY3bS{EY%g{12R6vgGSB(NRD zT5hSd2x!6(2FKiz%n#({Zpt^zT=Px=@y0gA^ux5y9d`Eptf)t+>jTmAQ;$(6nw!qg z#_uSz<4&h;K4?)FA*kl5fviOH#$qZin0b8g!b@jg$680C&WC;uSMg1kgxT%NxrU=E zs^=XNa;XN;zC-Icst3R{{`4PHoCmv-LbW3ieezH?@4Va37BO6|Vu_OBhcvFCM>FJ` zN)deHplB8}H0&A*>Qam_WPV#{({BC~S>JR`5hEY~v4uZ)YZ^>TZ*Z9| zn+?fdU|-sFWBabJ6(li%q;DOx5Xtil%`4FSnWWlcn2Z`m+$gM3-p<_%GrD+7*$;!A z3>qCj(7TB5e$t3j(R)=PvF=OoQpc=o-prrdExHn?+&v)UP>nba&YfiEQrzO`@06*= zwa;4Sso0h+#gSk7;5mMLIV-n{-t+Vn)=j4s7*MLa@nOCnh&K7GOzjrSk>hG&@^ zh~7RjpSKTnovs}xwieL4Oa~XqQQKXKGEwNhthG72>zoi|ObeQ-)mhrfT}LTe3nyBO zWV7##4K%jESuHt~tO?q-S1C{Q*#ANCiJj;d$y>hvKAnb=tb9@MV1Nbw2G$hP%+AtX zoOtqjoX?}%M}6&13pJPnd_2Gz_lDodSc%Va@vR^Kv$y7gtDpGMu>2Zsu=-NSBdvaB zDJOl?-`A>Wcl-?e&Fos?a~xVnqUuLEwPuARK%|oc(dPR_6M{k2_$QK(b~hIQ%w;zp zKuz%N!>ME*7*h4s7+7yG{>3}bz~i`87c~(fOqPI0Cm4l5b7DLXmaAVYOpylp#@_0Z zr0SX=`P-eAY&4pqtYZ!a{>ye@EL#uVfxMF*h}(&Z$$LGHpH zYK0F$wc+-_=;(c)jWe4VgHc#S4_TqXFm$p;5HgI1{FrLEQf4~1Kr{cUz)e=WWg4UK zReCe5afMGne^^C9Ly=YZ&3;Q>WRei_8$h8BoMbJ5x9J=#S2Si&OXb`?6NCyQ$jhnq$jpb& zsv5!J-#$`365tc$u&SG4UM-4pG6P$kK63* z;y7=2uCV0iCTl-pq(+<`#PV-Vl8(Y0HinESM-4Q0F<)wF0yPmp2V5dL*Dp%yu>mu_ zhBb+9)dd^)i za?B>Y*&kd2@!E&w@ssz2*~r|6b6t1&EyP#gKuuIHF_zHpxycd+lbA=W-VC9^w{9+P zQ&_e#bXqwEOYR!cWjhBJZhwfI^!dK!pVa+I?aLq4G5vbi*{r>$MYj^Zj)A*AJd_Vp z9~pm4LxP!7?I0+=didg*b%2Zlvn+bFrlUZkMDoGRA^UBBY>A0o3_y``bI^sN~V zZ<5#KI&hE7cPbKV@a}%;CtKNp`Z=9_eMS1N$e8|uD=tb=Qt5-9+_zRgj2DAhl&KL& zND0Go`|R}gsEJ*;JYiEG4T0`f!q@jsvOz>GC$PHWN%of+-TTj~Eb2N{r4C>*vvQO% z%h+;wsg3al@dM4}io}l;?@Kd4TrA@l!RPcDDX-aUrYR}32wlZDWOG^cEe2m=Tr~`; zP5E$+YOI_*n*%1L1K?K1`d7kk2=hl9**W8^qOG{U(abN9l3gO_wj?4VbGT5{fNFxG zimUd8oAo1gWqzVOoX<8Ap74Sn^JVXKC(S-6CJ!)JC(i+|_g@BJ{mQgtT$%TW(^9T4vqOm1lXJh3iqxoVofG}? zHvMund_ryY&cf@A5^(=%rsd5>udrX7h7q)W>g$|kF~WD+WNIj%rwPFa(^#vWt?=qb0j~?FyV@DpnG2TzNv;IGPHFdTPM#t zxk_QkyufQwQ=sCH#6RGnVT<4`(_qMKMT&9P{)l#nn(eu9GEp!Mw1@ z&v}}(}%i63rQoLUkS_VQ-FMlSU2XI)5^*KSXm}t`gTa-Y#!uYmQv!qc;l~V!8$_3ikBJrJs(itg$1eAvq{YLfM`Z zhG{K_No<8pjLu*ugNlfYa~bdcem-rS%~*OFV}D~AczBDY(67$LFB?`ma*BA}Rxtdr zyALeOPSLJ=ZmIrRu;&?gGDWCsh@;FZdz}kNoxkfHiwj4xSw>E5qlM7T9~=b?Zz^Y_ zAJ974L~4YZd26IU1qRR(x~gujX!K02jf9JSA5m$iYqpg#7vk8-Yy_tJLs|I`dfLzJqGNk$& zr}8q)1)fbj)>6XcWRKQ5;9r#OmFCZ z-^C{1RJre({5?9;Vc-XFp?B%BjZ~)lgp+eoR|Nu=bCA%Bd28$rUzIBh%P_D^+@?eN`M|@(AW>o8K(?(qdCF^rp$P;2~>+g=} z_60Cm;eF%BajMmo3Pn`g?F_eyCDRY2I9dKmF6Dg^#XCF`};#KZ&j53Y)_LGU-puWI} zx}c)~{=j{{{`&b-Pw6FliKlsk`L`<6!V z3alP7YyP9Po+2rYQgqIJdYbj>c7*b+4AgAk{IG7)ajeHr%PJZ(eo#<}ZqYn>bE|(wYr~QA~ZZk&FGuUd_ooMYp%3z^RkrUH`@FSyopL*A3 z%^?evIvSMfP?eYkJVJw5raq1?oiyM(i<^W3B)$c@`#|3S>us9stcK6A-yFVI@av89 zB*g=Yu+*7_gw~@j`Y^wHP8AObG}IZxBZx6U_1xn+0qi?dbS9yRrt-sQdeVm66J_cmNV4MNOde`R;3cyJu`1^(<6Z8_yuqL zDJV%w*;ruaphN9FJYN31*nX=<;(k2{zfac{IA|2@e-g>LAh{@KHVxxm@l7Uk{*)lV zORG;pB)UU*853W& z5v^l!H+DrFMFA#3orfM1%#Sb@iI0*59F!w*@tOobXdW#cfFO%&-N6Ro3CE6V1>YOv zb`3WQsqwDpN5RQAUl?bUazrk97FHzA(1&o6xlr35wNiZ_*7pJlRXm%)J({)-d@q+z zuUpi}!r>x*uV6S8Jw;V%!VF?Kt!lGKlxHU*22LAVZ5BD|QCQVFy-AwC1v<@W z$QnI^<@NI~T+NM@^RwT*5Q|3B$w=IJxiOw` zKlOW#?kKnM;OSI&3Hql=ew~)s;s0*pQc&;o)C1sXdG*~`p$1pr6 zB&ldM{~3mt0zqgK5nIdw=G(_J_-8xawSnC|@|HmGS-}bYm*43>j9Z*Y=x<7z1ItK$ zsUS_Uc+%3Oreaaj?ght$^HG?XD1~*0VU4Axwy-`oiokbS!Y@T;@6JMa<2n$=O1m~i zBsYWnrGW9e}Jx_geFzP-UsSX?EvpM3diu%I- z!w|g!ym9i*3RI%1Q}bc_b)Rx&^fvpF;Zaoqr2uMjdJmaD4IhyLx|C>Yud)5f1}UC{ z`Eexu$8Phj^3(T9A z(mf~-uQ8Urgk+bQW_~Wz5AV;7u-h6m+`)dBIdlv{0eF-pd)_L!wcSBhg1%JOP*;Ap zgXgcKG&xA)q_;S>llN2;90R$6N1Zi8f=LON2T&&5q}+_C(c>L}v-xu@#;+5h=T?Lm z1fpc5ssZloc@)Rn3O-YnZ7!&X+EYE5uVg$mHbFVf^~nz(guR}zg<+b$%sMX*wt<`H zpwTqAn1NHVd@_)FdS#5#P!jh-DFY>K)R3Ai)po>2Y0kRe{DdS%-mvCkBX6Z_% zK<*utNTq@`1iYnd&=G9KLzqcFJiyP8tKtR89#^*P0h+zhyYn@da4(zhfzfEyAt8NOk6B!1lMM|)@iC3#VJdkMsn}T~oCpn0D5F=J%}us)ii4LaO(loE`yh@71owVD+4CUvkW3TVe|S4y%8a#zHf z8T|AkoY8wcqvLy8UmJ7$XNY9Dn=IrXk@OkzNTXS1pIjkvq*6Ll-${k7i{e~pQ%n)( zAy%Nxm87aj{mK4@0sK^kRZ7V!RnDA;9yA_K(mtb8)B=5x(~lviSnoh9Eq^ph*Y|jZ z*wG0RuVBqVk5r;fKOWtLG3LG?9_6D#)8_GIcB?$2fAglQ;KC?`Is26zs`SvEJdKYn zNnJ7eq5?vFo8VV;xlEQp7wdPUi1V3z*?U_CCV6CRn+_}aEl_WXkB~Q_ZRD{9s&aBx z%C~-{aO+w%Z=I8*R9pd?qJE_t>bSG8jkt#u9r?P2{HXjYwMDMVTO-N3arLI>-|Qq0 zb{Z}ROd*wh)zYrVQyt!t#iUck^P%#Nr`m@6iNvun-zzpmoB4Ne@1J#*fTI_B(D{TP zG3ORwtg=I%S-}@)#anS>t7rCV%>y*Iv#D0f(TD$tx8>AZLQU~*)N?XosFF@uj`V(4 zoEVL-e<)7QZN$LwK-dv0R4T@!#vQa)yTT;f+$9wGc|Jey@s;FbSBv>du5qdZqgLRf zzg{#7w_=14T?uf)CcU|9o6wDIuH`dYE$+xb~$B%JVfH6#nf0>7X0kyE@jZc^8^ z{^|o0)3PbtEBsKusHGw>v@IBZo}tyGD#m=G{8hhp-_(rg&J;5pJeQNk{T{=)t_HVAvLb|m#MW4@PbU?#7Pm;q z=)LF|Y!EL8=bx%_YtlU2rT$`PQ}Ous7I0`){jaC_V*z@}1a|;Z2up$(wjf-bB1s51 zCA@8yEy1 zR+52yyvPWZD=qIcu(q=fGh$5rJ_x`0e5TB7_IaM_d>|*;6vsMz>TLe4sIb;`P2hhS z9`MrZdBLqB-0enEHFNO?)E;24JQM`x33o3dzE{283e;Eq1H?)uJC740#9?r6~Ue82bDae21=)_0lp?d}d;ED}z)@8n#P zJPMDb3SHr*2mMD6^$)$?;yHYJT)N-KrNcUt5E2zZ6-#$P8JNWM{TM5+uk5&M z61*#=8gRXaJ$7?XQ`mU?NYFyLUztXgaq`!makH_a>w0K>F2`(3WN(37<(x^r3LSS{$_EP1Msa*dmWxiqhM!e5uQmuvc_>N_L61= z_UTnN0k;L=$ppo~$hw3fqt9-a?#YZeCt#VcyUG(67}V#7#85|1tX|p&%2CW4etu9l zW=12WEXGonwK&Ak}OK|O-Ht}!nR)_KYUrO762sns1V)+ZsnMUa`rFKX+C zj#|2V^`-EN`_$@o$G~Wc**JQ^Zku_e)2*2|->f3dK{J2+9U87vm8`tr{3Fi(BF(qp zY3{A=z;R2I9eS9#!Wm?$J>8a;4N*+GWAF%0uc&$8ZIJ8&L(O|+i6*U2F_s$g7eg~# z1q?^QqF&R*;=rEB{KB&Lnur8?lN+QtlMSEEOknRK-g|Gqx+?1coi~^TtOyj@3lDcF z7o~8K{bA|*NoOM7EdXSanU4iclV}@+`;Cdvj-^Ry8kQuJ$OxkzOIqjRw#*|w3xUBg zP!@Fq_li{6R7N7E_V;Gy{SsYYIDO1GEb>eJ$MiRl3-^E}7|dfxhbGFH6%3v%uBt2y ze}tdmLe^8LN14AAMoGRKoiu}@CkE*HuJxU zQt%$fMZgP!eaQbFl%ViT^T20NdkAJB3TiRz;&G2l8CU<*VeL{Fy=%3k?Mgkogr7u2 z{oBM;pnxShY0D6ma+0N^QXX9OtGR%Tejkx>4Sr2~ z+{%JiD_6D8U4jq94XZfV6xSLz5*z}pCkSZ4Cz!r64^6-bj?Z0>q@yGeLj(KUU2EYR zk6`kQxhuoiVhchQK3ufqWc>}>T^Xs*2VuI)(@RnkEcua|dP=VqPVQ_bhGuj`e& z3WD$RU1n9!LlM%2+x_M=H>Dg%6hK^v0tGM<$PQVc0P5m@pd|kbh2O)`+3dZkqnm@P5G6YY z2M0u+5=aEg$@YPg6;eY9Bx{bK1ja)BJ@OON0NwuP07w$;zxZZ^7AS%87fl@&-TdYQ z0sx;Ngml37+J6=OC#NC(o5uc@H|lRpzuSBSEGZfJ9I`07wx7kp6Ev8lwXeK@$Uk5DF+D5z0T1|E9UW_5nXZSm}Xuf7kt6 zISdH-P7kD~_^Yx1c^I<)+yHdHA;v-hDgPgcP=A1sGX@~;8+oFW|6dg1|3cw}g5=Tx z8UOd-_E3MR{?l>-Mj(-3Aruf`Z|q=gVeaC};%sK|e|tqXJBZT(>#gGyfPi=Zg}nm} z`N9aq|GU%w^BO|_uUZj!K!A(6tE;txrOSWz`v315`(I^f$R;BY@9(BRy|oldgc^d+ O1f+v&qunits()->find(unit_name); + if (unit) + return unit->scaleAbreviation(); + else + return ""; +} + +const char * +unit_suffix(const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->suffix(); + else + return ""; +} + //////////////////////////////////////////////////////////////// VertexIterator * From 9075688077c186ec0620c791b26728de500832bf Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 25 Jun 2020 13:38:47 -0700 Subject: [PATCH 40/70] set_max_delay set_output_delay set_clock_uncertainty --- include/sta/PathEnd.hh | 7 +++++-- search/PathEnd.cc | 29 ++++++++++++++++++----------- search/ReportPath.cc | 34 ++++++++++++++++++++++------------ search/ReportPath.hh | 2 ++ 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index c60d8fdc..e5f983d4 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -425,6 +425,7 @@ private: // Path constrained by an output delay. // If there is a reference pin, clk_path_ is the reference pin clock. +// If there is a path delay PathEndPathDelay is used instead of this. class PathEndOutputDelay : public PathEndClkConstrainedMcp { public: @@ -555,6 +556,7 @@ private: // Path constrained by set_min/max_delay. // "Clocked" when path delay ends at timing check pin. +// May end at output with set_output_delay. class PathEndPathDelay : public PathEndClkConstrained { public: @@ -587,6 +589,7 @@ public: virtual PathDelay *pathDelay() const { return path_delay_; } virtual ArcDelay margin(const StaState *sta) const; virtual float sourceClkOffset(const StaState *sta) const; + virtual ClockEdge *targetClkEdge(const StaState *sta) const; virtual float targetClkTime(const StaState *sta) const; virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; virtual float targetClkOffset(const StaState *sta) const; @@ -594,6 +597,7 @@ public: virtual Required requiredTime(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, const StaState *sta) const; + bool hasOutputDelay() const { return output_delay_ != nullptr; } protected: PathEndPathDelay(PathDelay *path_delay, @@ -610,8 +614,7 @@ protected: PathDelay *path_delay_; TimingArc *check_arc_; Edge *check_edge_; - // Output delay is nullptr when there is no timing check or output - // delay at the endpoint. + // Output delay is nullptr when there is no output delay at the endpoint. OutputDelay *output_delay_; // Source clk arrival for set_min/max_delay -ignore_clk_latency. Arrival src_clk_arrival_; diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 57a014c1..786a8af0 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -1845,6 +1845,17 @@ PathEnd::pathDelaySrcClkOffset(const PathRef &path, return offset; } +ClockEdge * +PathEndPathDelay::targetClkEdge(const StaState *sta) const +{ + if (!clk_path_.isNull()) + return clk_path_.clkEdge(sta); + else if (output_delay_) + return output_delay_->clkEdge(); + else + return nullptr; +} + float PathEndPathDelay::targetClkTime(const StaState *sta) const { @@ -1858,14 +1869,12 @@ PathEndPathDelay::targetClkTime(const StaState *sta) const Arrival PathEndPathDelay::targetClkArrivalNoCrpr(const StaState *sta) const { - if (!clk_path_.isNull()) { - ClockEdge *tgt_clk_edge = targetClkEdge(sta); - if (tgt_clk_edge) - return targetClkDelay(sta) - + targetClkUncertainty(sta); - else - return clk_path_.arrival(sta); - } + ClockEdge *tgt_clk_edge = targetClkEdge(sta); + if (tgt_clk_edge) + return targetClkDelay(sta) + + targetClkUncertainty(sta); + else if (!clk_path_.isNull()) + return clk_path_.arrival(sta); else return 0.0; } @@ -1887,9 +1896,7 @@ PathEndPathDelay::requiredTime(const StaState *sta) const return src_clk_arrival_ + delay + margin(sta); } else { - Arrival tgt_clk_arrival = 0.0; - if (!clk_path_.isNull()) - tgt_clk_arrival = targetClkArrival(sta); + Arrival tgt_clk_arrival = targetClkArrival(sta); float src_clk_offset = sourceClkOffset(sta); // Path delay includes target clk latency and timing check setup/hold // margin or external departure at target. diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 07b6e3d8..456cb2fd 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -705,12 +705,16 @@ void ReportPath::reportEndpoint(const PathEndPathDelay *end, string &result) { - Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); - const char *reg_desc = clkRegLatchDesc(end); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); - reportEndpoint(inst_name, reason, result); + if (end->hasOutputDelay()) + reportEndpointOutputDelay(end, result); + else { + Instance *inst = network_->instance(end->vertex(this)->pin()); + const char *inst_name = cmd_network_->pathName(inst); + string clk_name = tgtClkName(end); + const char *reg_desc = clkRegLatchDesc(end); + auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + reportEndpoint(inst_name, reason, result); + } } void @@ -746,13 +750,11 @@ ReportPath::reportFull(const PathEndPathDelay *end, float delay = path_delay->delay(); reportLine(delay_msg.c_str(), delay, delay, early_late, result); if (!path_delay->ignoreClkLatency()) { - const Path *tgt_clk_path = end->targetClkPath(); - if (tgt_clk_path) { - float delay = 0.0; - if (path_delay) - delay = path_delay->delay(); + Clock *tgt_clk = end->targetClk(this); + if (tgt_clk) { + const Path *tgt_clk_path = end->targetClkPath(); if (reportClkPath() - && isPropagated(tgt_clk_path)) + && isPropagated(tgt_clk_path, tgt_clk)) reportTgtClk(end, delay, result); else { Arrival tgt_clk_delay = end->targetClkDelay(this); @@ -832,6 +834,13 @@ ReportPath::reportFull(const PathEndOutputDelay *end, void ReportPath::reportEndpoint(const PathEndOutputDelay *end, string &result) +{ + reportEndpointOutputDelay(end, result); +} + +void +ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end, + string &result) { Vertex *vertex = end->vertex(this); Pin *pin = vertex->pin(); @@ -851,6 +860,7 @@ ReportPath::reportEndpoint(const PathEndOutputDelay *end, if (tgt_clk) { string clk_name = tgtClkName(end); auto reason = stdstrPrint("internal path endpoint clocked by %s", clk_name.c_str()); + reportEndpoint(pin_name, reason, result); } else diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 8f24cc9d..c7ae2e75 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -203,6 +203,8 @@ protected: string &result); void reportEndpoint(const PathEndOutputDelay *end, string &result); + void reportEndpointOutputDelay(const PathEndClkConstrained *end, + string &result); void reportEndpoint(const PathEndPathDelay *end, string &result); void reportEndpoint(const PathEndGatedClock *end, From 1f0d7ffddeb55edc068f24bdf55ae58dba0bbb4b Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 27 Jun 2020 16:24:17 -0700 Subject: [PATCH 41/70] report_net pin locations --- tcl/Network.tcl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tcl/Network.tcl b/tcl/Network.tcl index 352207a2..b35daed8 100644 --- a/tcl/Network.tcl +++ b/tcl/Network.tcl @@ -382,7 +382,7 @@ proc report_net_pin { pin verbose corner digits } { puts -nonewline [port_capacitance_str $liberty_port $digits] } } - puts "" + puts "[pin_location_str $pin]" } elseif [$pin is_top_level_port] { puts -nonewline " [get_full_name $pin] [pin_direction $pin] port" if { $verbose } { @@ -404,12 +404,17 @@ proc report_net_pin { pin verbose corner digits } { puts -nonewline " pin [capacitances_str $cap_r_min $cap_r_max $cap_f_min $cap_f_max $digits]" } } - puts "" + puts "[pin_location_str $pin]" } elseif [$pin is_hierarchical] { puts " [get_full_name $pin] [pin_direction $pin]" } } +# default handler +proc pin_location_str { pin } { + return "" +} + ################################################################ proc report_pin_ { pin } { From eefd98482ab1ebfc2b75f648c2d9318471a13449 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 30 Jun 2020 19:24:30 -0700 Subject: [PATCH 42/70] report_checks -max_fanout non-liberty ports --- search/CheckFanoutLimits.cc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc index 352482f2..fcb7ea3e 100644 --- a/search/CheckFanoutLimits.cc +++ b/search/CheckFanoutLimits.cc @@ -182,15 +182,19 @@ CheckFanoutLimits::fanoutLoad(const Pin *pin) const Pin *pin = pin_iter->next(); if (network->isLoad(pin)) { LibertyPort *port = network->libertyPort(pin); - float fanout_load; - bool exists; - port->fanoutLoad(fanout_load, exists); - if (!exists) { - LibertyLibrary *lib = port->libertyLibrary(); - lib->defaultFanoutLoad(fanout_load, exists); + if (port) { + float fanout_load; + bool exists; + port->fanoutLoad(fanout_load, exists); + if (!exists) { + LibertyLibrary *lib = port->libertyLibrary(); + lib->defaultFanoutLoad(fanout_load, exists); + } + if (exists) + fanout += fanout_load; } - if (exists) - fanout += fanout_load; + else + fanout += 1; } } delete pin_iter; From e8639dfafdaa2bdf62f89b17b15bf2376f281766 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 2 Jul 2020 06:28:14 -0700 Subject: [PATCH 43/70] get_property clock is_generated --- search/Property.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/search/Property.cc b/search/Property.cc index 2877fc86..386d551f 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -916,7 +916,9 @@ getProperty(Clock *clk, else if (stringEqual(property, "sources")) return PropertyValue(&clk->pins()); else if (stringEqual(property, "propagated")) - return PropertyValue(clk->isPropagated() ? "1" : "0"); + return PropertyValue(clk->isPropagated()); + else if (stringEqual(property, "is_generated")) + return PropertyValue(clk->isGenerated()); else throw PropertyUnknown("clock", property); } From d35e38a450c9229927cf179c7d6619b9a9817acc Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 2 Jul 2020 08:45:33 -0700 Subject: [PATCH 44/70] Sta::checkFanout, checkCapacitance no preamble --- search/Sta.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index 42e42b14..c18a9d9f 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4962,7 +4962,6 @@ Sta::checkFanout(const Pin *pin, float &limit, float &slack) { - checkFanoutLimitPreamble(); check_fanout_limits_->checkFanout(pin, min_max, fanout, limit, slack); } @@ -5042,7 +5041,6 @@ Sta::checkCapacitance(const Pin *pin, float &limit, float &slack) { - checkCapacitanceLimitPreamble(); check_capacitance_limits_->checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); From 338a82add442c69b208da77e9ea5633774063ffb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Jul 2020 10:16:10 -0700 Subject: [PATCH 45/70] pocv comparisons use +/- sigma --- graph/DelayNormal1.cc | 63 ++++++++++++------- graph/DelayNormal2.cc | 142 +++++++++++++++++++++++------------------- 2 files changed, 119 insertions(+), 86 deletions(-) diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index f0d58659..344a3c1f 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -24,6 +24,8 @@ #include "Fuzzy.hh" #include "Units.hh" #include "StaState.hh" +// temporary hack +#include "Sta.hh" // SSTA compilation. #if (SSTA == 1) @@ -162,32 +164,31 @@ Delay::operator-=(const Delay &delay) bool Delay::operator==(const Delay &delay) const { - return mean_ == delay.mean_ - && sigma2_ == delay.sigma2_; + return fuzzyEqual(*this, delay); } bool Delay::operator>(const Delay &delay) const { - return mean_ > delay.mean_; + return fuzzyGreater(*this, delay); } bool Delay::operator>=(const Delay &delay) const { - return mean_ >= delay.mean_; + return fuzzyGreaterEqual(*this, delay); } bool Delay::operator<(const Delay &delay) const { - return mean_ < delay.mean_; + return fuzzyLess(*this, delay); } bool Delay::operator<=(const Delay &delay) const { - return mean_ <= delay.mean_; + return fuzzyLessEqual(*this, delay); } //////////////////////////////////////////////////////////////// @@ -241,28 +242,36 @@ bool fuzzyLess(const Delay &delay1, const Delay &delay2) { - return fuzzyLess(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool fuzzyLess(const Delay &delay1, float delay2) { - return fuzzyLess(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delay2); } bool fuzzyLessEqual(const Delay &delay1, const Delay &delay2) { - return fuzzyLessEqual(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool fuzzyLessEqual(const Delay &delay1, - float delay2) + float delay2) { - return fuzzyLessEqual(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delay2); } bool @@ -271,37 +280,45 @@ fuzzyLessEqual(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyLessEqual(delay1.mean(), delay2.mean()); + return fuzzyLessEqual(delay1, delay2); else - return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + return fuzzyGreaterEqual(delay1, delay2); } bool fuzzyGreater(const Delay &delay1, const Delay &delay2) { - return fuzzyGreater(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool fuzzyGreater(const Delay &delay1, float delay2) { - return fuzzyGreater(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delay2); } bool fuzzyGreaterEqual(const Delay &delay1, const Delay &delay2) { - return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool fuzzyGreaterEqual(const Delay &delay1, float delay2) { - return fuzzyGreaterEqual(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delay2); } bool @@ -310,9 +327,9 @@ fuzzyGreater(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreater(delay1.mean(), delay2.mean()); + return fuzzyGreater(delay1, delay2); else - return fuzzyLess(delay1.mean(), delay2.mean()); + return fuzzyLess(delay1, delay2); } bool @@ -321,9 +338,9 @@ fuzzyGreaterEqual(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + return fuzzyGreaterEqual(delay1, delay2); else - return fuzzyLessEqual(delay1.mean(), delay2.mean()); + return fuzzyLessEqual(delay1, delay2); } bool @@ -332,9 +349,9 @@ fuzzyLess(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyLess(delay1.mean(), delay2.mean()); + return fuzzyLess(delay1, delay2); else - return fuzzyGreater(delay1.mean(), delay2.mean()); + return fuzzyGreater(delay1, delay2); } float diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index db5a7eca..8b6f02ba 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -190,25 +190,25 @@ Delay::operator==(const Delay &delay) const bool Delay::operator>(const Delay &delay) const { - return mean_ > delay.mean_; + return mean_ + sqrt(sigma2_late_) > delay.mean_ + sqrt(delay.sigma2_late_); } bool Delay::operator>=(const Delay &delay) const { - return mean_ >= delay.mean_; + return mean_ + sqrt(sigma2_late_) >= delay.mean_ + sqrt(delay.sigma2_late_); } bool Delay::operator<(const Delay &delay) const { - return mean_ < delay.mean_; + return mean_ - sqrt(sigma2_early_) < delay.mean_ - sqrt(delay.sigma2_early_); } bool Delay::operator<=(const Delay &delay) const { - return mean_ <= delay.mean_; + return mean_ - sqrt(sigma2_early_) <= delay.mean_ - sqrt(delay.sigma2_early_); } //////////////////////////////////////////////////////////////// @@ -265,28 +265,36 @@ bool fuzzyLess(const Delay &delay1, const Delay &delay2) { - return fuzzyLess(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool fuzzyLess(const Delay &delay1, float delay2) { - return fuzzyLess(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delay2); } bool fuzzyLessEqual(const Delay &delay1, const Delay &delay2) { - return fuzzyLessEqual(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool fuzzyLessEqual(const Delay &delay1, float delay2) { - return fuzzyLessEqual(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delay2); } bool @@ -295,37 +303,45 @@ fuzzyLessEqual(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyLessEqual(delay1.mean(), delay2.mean()); + return fuzzyLessEqual(delay1, delay2); else - return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + return fuzzyGreaterEqual(delay1, delay2); } bool fuzzyGreater(const Delay &delay1, const Delay &delay2) { - return fuzzyGreater(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool fuzzyGreater(const Delay &delay1, float delay2) { - return fuzzyGreater(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool fuzzyGreaterEqual(const Delay &delay1, const Delay &delay2) { - return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + Sta *sta = Sta::sta(); + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delay2); } bool fuzzyGreaterEqual(const Delay &delay1, float delay2) { - return fuzzyGreaterEqual(delay1.mean(), delay2); + Sta *sta = Sta::sta(); + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delay2); } bool @@ -334,9 +350,9 @@ fuzzyGreater(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreater(delay1.mean(), delay2.mean()); + return fuzzyGreater(delay1, delay2); else - return fuzzyLess(delay1.mean(), delay2.mean()); + return fuzzyLess(delay1, delay2); } bool @@ -345,9 +361,9 @@ fuzzyGreaterEqual(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + return fuzzyGreaterEqual(delay1, delay2); else - return fuzzyLessEqual(delay1.mean(), delay2.mean()); + return fuzzyLessEqual(delay1, delay2); } bool @@ -356,52 +372,9 @@ fuzzyLess(const Delay &delay1, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyLess(delay1.mean(), delay2.mean()); + return fuzzyLess(delay1, delay2); else - return fuzzyGreater(delay1.mean(), delay2.mean()); -} - -Delay -operator+(float delay1, - const Delay &delay2) -{ - return Delay(delay1 + delay2.mean(), - delay2.sigma2Early(), - delay2.sigma2Late()); -} - -Delay -operator/(float delay1, - const Delay &delay2) -{ - return Delay(delay1 / delay2.mean(), - delay2.sigma2Early(), - delay2.sigma2Late()); -} - -Delay -operator*(const Delay &delay1, - float delay2) -{ - return Delay(delay1.mean() * delay2, - delay1.sigma2Early() * delay2 * delay2, - delay1.sigma2Late() * delay2 * delay2); -} - -Delay -delayRemove(const Delay &delay1, - const Delay &delay2) -{ - return Delay(delay1.mean() - delay2.mean(), - delay1.sigma2Early() - delay2.sigma2Early(), - delay1.sigma2Late() - delay2.sigma2Late()); -} - -float -delayRatio(const Delay &delay1, - const Delay &delay2) -{ - return delay1.mean() / delay2.mean(); + return fuzzyGreater(delay1, delay2); } float @@ -463,6 +436,49 @@ delayAsString(const Delay &delay, return sta->units()->timeUnit()->asString(mean_sigma, digits); } +Delay +delayRemove(const Delay &delay1, + const Delay &delay2) +{ + return Delay(delay1.mean() - delay2.mean(), + delay1.sigma2Early() - delay2.sigma2Early(), + delay1.sigma2Late() - delay2.sigma2Late()); +} + +float +delayRatio(const Delay &delay1, + const Delay &delay2) +{ + return delay1.mean() / delay2.mean(); +} + +Delay +operator+(float delay1, + const Delay &delay2) +{ + return Delay(delay1 + delay2.mean(), + delay2.sigma2Early(), + delay2.sigma2Late()); +} + +Delay +operator/(float delay1, + const Delay &delay2) +{ + return Delay(delay1 / delay2.mean(), + delay2.sigma2Early(), + delay2.sigma2Late()); +} + +Delay +operator*(const Delay &delay1, + float delay2) +{ + return Delay(delay1.mean() * delay2, + delay1.sigma2Early() * delay2 * delay2, + delay1.sigma2Late() * delay2 * delay2); +} + } // namespace #endif // (SSTA == 2) From 220f280c0224aff4d1f5e3a97a1524b7f637f839 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Jul 2020 12:11:23 -0700 Subject: [PATCH 46/70] Delay*.hh protects --- graph/DelayNormal1.cc | 6 +++--- graph/DelayNormal2.cc | 6 +++--- include/sta/Delay.hh | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index 344a3c1f..1042bbdb 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// SSTA compilation. +#if (SSTA == 1) + #include "Delay.hh" #include // sqrt @@ -27,9 +30,6 @@ // temporary hack #include "Sta.hh" -// SSTA compilation. -#if (SSTA == 1) - namespace sta { inline float diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index 8b6f02ba..9ab1432a 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// SSTA compilation. +#if (SSTA == 2) + #include "Delay.hh" #include // sqrt @@ -25,9 +28,6 @@ #include "Units.hh" #include "StaState.hh" -// SSTA compilation. -#if (SSTA == 2) - namespace sta { inline float diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index 06ca2929..5b68ae8c 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "StaConfig.hh" - #pragma once +#include "StaConfig.hh" + #if (SSTA == 1) // Delays are Normal PDFs. #include "DelayNormal1.hh" From e7ed3170f32c8a52961a3ee2290afec5294b90f8 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Jul 2020 16:56:15 -0700 Subject: [PATCH 47/70] write_verilog power/ground port dcls --- verilog/VerilogWriter.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 52ffc0f4..ed475971 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -165,12 +165,20 @@ VerilogWriter::verilogPortDir(PortDirection *dir) return "input"; else if (dir == PortDirection::output()) return "output"; - else if (dir == PortDirection::bidirect()) - return "inout"; else if (dir == PortDirection::tristate()) return "output"; - else + else if (dir == PortDirection::bidirect()) + return "inout"; + else if (dir == PortDirection::power()) + return "input"; + else if (dir == PortDirection::ground()) + return "input"; + else if (dir == PortDirection::internal()) return nullptr; + else { + internalError("unknown port direction"); + return nullptr; + } } void From 2b30d90555298b268f047f792f04c55498c16ac1 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Jul 2020 18:19:26 -0700 Subject: [PATCH 48/70] DelayNormal.cc condition --- graph/DelayNormal1.cc | 6 +++--- graph/DelayNormal2.cc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index 1042bbdb..344a3c1f 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -14,9 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// SSTA compilation. -#if (SSTA == 1) - #include "Delay.hh" #include // sqrt @@ -30,6 +27,9 @@ // temporary hack #include "Sta.hh" +// SSTA compilation. +#if (SSTA == 1) + namespace sta { inline float diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index 9ab1432a..8b6f02ba 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -14,9 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// SSTA compilation. -#if (SSTA == 2) - #include "Delay.hh" #include // sqrt @@ -28,6 +25,9 @@ #include "Units.hh" #include "StaState.hh" +// SSTA compilation. +#if (SSTA == 2) + namespace sta { inline float From 535a09edcc4e0e2d0bc363938bd72f8d7cd5db59 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Jul 2020 18:19:39 -0700 Subject: [PATCH 49/70] get_cells -of_objects ports --- tcl/Sdc.tcl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl index 1839abc3..9566572f 100644 --- a/tcl/Sdc.tcl +++ b/tcl/Sdc.tcl @@ -502,9 +502,16 @@ proc get_cells { args } { if { $args != {} } { sta_warn "patterns argument not supported with -of_objects." } - parse_pin_net_args $keys(-of_objects) pins nets + parse_port_pin_net_arg $keys(-of_objects) pins nets foreach pin $pins { - lappend insts [$pin instance] + if { [$pin is_top_level_port] } { + set net [get_nets [get_name $pin]] + if { $net != "NULL" } { + lappend nets $net + } + } else { + lappend insts [$pin instance] + } } foreach net $nets { set pin_iter [$net pin_iterator] From 3d492eddeec17fc6c2b19b6609ae41edb16a8c56 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 4 Jul 2020 08:26:11 -0700 Subject: [PATCH 50/70] get -filter spaces around op not required --- tcl/Sdc.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl index 9566572f..8f2e42c0 100644 --- a/tcl/Sdc.tcl +++ b/tcl/Sdc.tcl @@ -1004,7 +1004,7 @@ proc get_ports { args } { return $ports } -variable filter_regexp1 {@?([a-zA-Z_]+) +(==|=~) +([0-9a-zA-Z_\*]+)} +variable filter_regexp1 {@?([a-zA-Z_]+) *(==|=~) *([0-9a-zA-Z_\*]+)} variable filter_or_regexp "($filter_regexp1) +\\|\\| +($filter_regexp1)" variable filter_and_regexp "($filter_regexp1) +&& +($filter_regexp1)" From 2627ed3812e6d99fdd348dbdb3ed28d5df86dcb4 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 5 Jul 2020 08:08:11 -0700 Subject: [PATCH 51/70] doc --- doc/OpenSTA.odt | Bin 76613 -> 76617 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 1eb3cf7b93cd14ffec2e4e2e2987be2122f31235..a6045afa559e5b349c99f12619a7ff0e4178411d 100644 GIT binary patch delta 31386 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 delta 31353 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 From 3d4b0cf1a321ec1103b87a2be4b0535ecbfd94ac Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 5 Jul 2020 08:08:18 -0700 Subject: [PATCH 52/70] no crpr for ideal clks --- search/Crpr.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/search/Crpr.cc b/search/Crpr.cc index 31d032cf..64496ffb 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -165,6 +165,8 @@ CheckCrpr::checkCrpr1(const Path *src_path, const MinMax *src_clk_min_max = src_clk_path ? src_clk_path->minMax(this) : src_path->minMax(this); if (crprPossible(src_clk, tgt_clk) + && src_clk_info->isPropagated() + && tgt_clk_info->isPropagated() // Note that crpr clk min/max is NOT the same as the path min max. // For path from latches that are borrowing the enable path // is from the opposite min/max of the data. @@ -372,9 +374,12 @@ CheckCrpr::outputDelayCrpr1(const Path *src_path, { crpr = 0.0; crpr_pin = nullptr; + ClkInfo *src_clk_info = src_path->tag(this)->clkInfo(); Clock *tgt_clk = tgt_clk_edge->clock(); Clock *src_clk = src_path->clock(this); - if (tgt_clk->isGenerated() + if (src_clk_info->isPropagated() + && tgt_clk->isGenerated() + && tgt_clk->isPropagated() && crprPossible(src_clk, tgt_clk)) { PathVertex tgt_genclk_path; portClkPath(tgt_clk_edge, tgt_clk_edge->clock()->defaultPin(), tgt_path_ap, @@ -393,7 +398,7 @@ CheckCrpr::crprPossible(Clock *clk1, return clk1 && clk2 && !clk1->isVirtual() && !clk2->isVirtual() - // Generated clock can have crpr in the source path. + // Generated clocks can have crpr in the source path. && (clk1 == clk2 || clk1->isGenerated() || clk2->isGenerated() From 6b448fe2c90cf4a50fe2ec7c3834bc4be0f0c89c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 5 Jul 2020 16:37:50 -0700 Subject: [PATCH 53/70] write_sdc set_load net --- sdc/WriteSdc.cc | 42 ++++++++++++++++++++++++++++++++++++++++-- sdc/WriteSdcPvt.hh | 6 +++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 71515f65..15b27e19 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -1578,7 +1578,8 @@ WriteSdc::writeEnvironment() const writeCommentSection("Environment"); writeOperatingConditions(); writeWireload(); - writePinLoads(); + writePortLoads(); + writeNetLoads(); writeDriveResistances(); writeDrivingCells(); writeInputTransitions(); @@ -1606,7 +1607,44 @@ WriteSdc::writeWireload() const } void -WriteSdc::writePinLoads() const +WriteSdc::writeNetLoads() const +{ + if (sdc_->net_wire_cap_map_) { + for (auto net_cap : *sdc_->net_wire_cap_map_) { + Net *net = net_cap.first; + MinMaxFloatValues &caps = net_cap.second; + float min_cap, max_cap; + bool min_exists, max_exists; + caps.value(MinMax::min(), min_cap, min_exists); + caps.value(MinMax::max(), max_cap, max_exists); + if (min_exists && max_exists + && min_cap == max_cap) + writeNetLoad(net, MinMaxAll::all(), min_cap); + else { + if (min_exists) + writeNetLoad(net, MinMaxAll::min(), min_cap); + if (max_exists) + writeNetLoad(net, MinMaxAll::max(), max_cap); + } + } + } +} + +void +WriteSdc::writeNetLoad(Net *net, + const MinMaxAll *min_max, + float cap) const +{ + fprintf(stream_, "set_load "); + fprintf(stream_, "%s ", minMaxFlag(min_max)); + writeCapacitance(cap); + fprintf(stream_, " "); + writeGetNet(net); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writePortLoads() const { CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); while (port_iter->hasNext()) { diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index 83ee4d2e..4cc69ddd 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -117,7 +117,11 @@ public: void writeEnvironment() const; void writeOperatingConditions() const; void writeWireload() const; - void writePinLoads() const; + void writeNetLoads() const; + void writeNetLoad(Net *net, + const MinMaxAll *min_max, + float cap) const; + void writePortLoads() const; void writePortLoads(Port *port) const; void writePortFanout(Port *port) const; void writeDriveResistances() const; From 17b48a681ba6e55974e4a587a0cd642c98c6a75e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 09:45:45 -0700 Subject: [PATCH 54/70] get_property pin name --- search/Property.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/search/Property.cc b/search/Property.cc index 386d551f..0a840dfa 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -732,13 +732,13 @@ getProperty(const Pin *pin, Sta *sta) { auto network = sta->cmdNetwork(); - if (stringEqual(property, "direction")) - return PropertyValue(network->direction(pin)->name()); - else if (stringEqual(property, "name") - || stringEqual(property, "full_name")) - return PropertyValue(network->pathName(pin)); - else if (stringEqual(property, "lib_pin_name")) + if (stringEqual(property, "name") + || stringEqual(property, "lib_pin_name")) return PropertyValue(network->portName(pin)); + else if (stringEqual(property, "full_name")) + return PropertyValue(network->pathName(pin)); + else if (stringEqual(property, "direction")) + return PropertyValue(network->direction(pin)->name()); else if (stringEqual(property, "clocks")) { ClockSet clks; sta->clocks(pin, clks); From 27cc8f1614fc94b744d509acf9516880c10fab98 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 15:18:13 -0700 Subject: [PATCH 55/70] report_path -format json --- include/sta/Network.hh | 7 +++++ include/sta/SearchClass.hh | 3 +- network/Network.cc | 11 +++++++ search/ReportPath.cc | 62 ++++++++++++++++++++++++++++++++++++++ search/ReportPath.hh | 5 +++ tcl/Search.tcl | 2 +- tcl/StaTcl.i | 2 ++ 7 files changed, 90 insertions(+), 2 deletions(-) diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 4a6e092e..879ad2c5 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -323,6 +323,13 @@ public: virtual VertexId vertexId(const Pin *pin) const = 0; virtual void setVertexId(Pin *pin, VertexId id) = 0; + // Return the physical X/Y coordinates of the pin. + virtual void location(const Pin *pin, + // Return values. + double x, + double y, + bool exists) const; + int pinCount(); int pinCount(Instance *inst); int leafPinCount(); diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index 2eaae23c..c6f49dd3 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -119,7 +119,8 @@ enum class ReportPathFormat { full, shorter, endpoint, summary, - slack_only + slack_only, + json }; static const int tag_index_bits = 24; diff --git a/network/Network.cc b/network/Network.cc index 8ac29711..535566f9 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -935,6 +935,17 @@ Network::findInstPinsMatching(const Instance *instance, } } +void +Network::location(const Pin *pin, + // Return values. + double x, + double y, + bool exists) const +{ + x = y = 0.0; + exists = false; +} + int Network::instanceCount(Instance *inst) { diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 456cb2fd..09c2443c 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2503,6 +2503,27 @@ ReportPath::reportPath(const PathEnd *end, void ReportPath::reportPath(const Path *path, string &result) +{ + switch (format_) { + case ReportPathFormat::full: + case ReportPathFormat::full_clock: + case ReportPathFormat::full_clock_expanded: + reportPathFull(path, result); + break; + case ReportPathFormat::json: + reportPathJson(path, result); + break; + case ReportPathFormat::summary: + case ReportPathFormat::slack_only: + default: + internalError("unsupported path type"); + break; + } +} + +void +ReportPath::reportPathFull(const Path *path, + string &result) { reportPathHeader(result); PathExpanded expanded(path, this); @@ -2510,6 +2531,47 @@ ReportPath::reportPath(const Path *path, false, result); } +void +ReportPath::reportPathJson(const Path *path, + string &result) +{ + result += "{ \"path\": [\n"; + PathExpanded expanded(path, this); + for (auto i = expanded.startIndex(); i < expanded.size(); i++) { + PathRef *path = expanded.path(i); + const Pin *pin = path->vertex(this)->pin(); + result += " {\n"; + result += " \"pin\": \""; + result += network_->pathName(pin); + result += "\",\n"; + + double x, y; + bool exists; + string tmp; + network_->location(pin, x, y, exists); + if (exists) { + result += " \"x\": "; + stringPrint(tmp, "%.3f", x); + result += tmp + ",\n"; + result += " \"y\": "; + stringPrint(tmp, "%.3f", y); + result += tmp + "\n"; + } + + result += " \"arrival\": "; + stringPrint(tmp, "%.3e", path->arrival(this)); + result += tmp + ",\n"; + + result += " \"slew\": "; + stringPrint(tmp, "%.3e", path->slew(this)); + result += tmp + ",\n"; + + result += " }\n"; + } + result += " ]\n"; + result += "}\n"; +} + void ReportPath::reportPath1(const Path *path, PathExpanded &expanded, diff --git a/search/ReportPath.hh b/search/ReportPath.hh index c7ae2e75..3fda967c 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -63,6 +63,7 @@ public: void reportPathEnd(PathEnd *end, PathEnd *prev_end); void reportPathEnds(PathEndSeq *ends); + // for debugging void reportPath(const Path *path); void reportShort(const PathEndUnconstrained *end, @@ -341,6 +342,10 @@ protected: string &result); void reportPath(const Path *path, string &result); + void reportPathFull(const Path *path, + string &result); + void reportPathJson(const Path *path, + string &result); void reportPathHeader(string &result); void reportPath1(const Path *path, PathExpanded &expanded, diff --git a/tcl/Search.tcl b/tcl/Search.tcl index 23a06b0e..41491223 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -227,7 +227,7 @@ proc parse_report_path_options { cmd args_var default_format if [info exists path_options(-format)] { set format $path_options(-format) set formats {full full_clock full_clock_expanded short \ - end slack_only summary} + end slack_only summary json} if { [lsearch $formats $format] == -1 } { sta_error "-format $format not recognized." } diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 8e6ff2e1..4500e3a6 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -1065,6 +1065,8 @@ using namespace sta; $1 = ReportPathFormat::summary; else if (stringEq(arg, "slack_only")) $1 = ReportPathFormat::slack_only; + else if (stringEq(arg, "json")) + $1 = ReportPathFormat::json; else { tclError(interp, "Error: unknown path type %s.", arg); return TCL_ERROR; From 9cb7222f56b08d1502063ec75fb3ea5b2b7dd066 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 15:35:03 -0700 Subject: [PATCH 56/70] pin_location --- tcl/StaTcl.i | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 4500e3a6..1a8bb558 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -5311,6 +5311,20 @@ delays_invalid() sta->delaysInvalid(); } +const char * +pin_location(Pin *pin) +{ + Network *network = cmdNetwork(); + double x, y; + bool exists; + network->location(pin, x, y, exists); + // return x/y as tcl list + if (exists) + return sta::stringPrintTmp("%f %f", x, y); + else + return ""; +} + %} // inline //////////////////////////////////////////////////////////////// From ccff78468b2d59f76c6ae2d4b1e32d3c8b34e0ad Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 15:42:53 -0700 Subject: [PATCH 57/70] pin_location_str --- tcl/Network.tcl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tcl/Network.tcl b/tcl/Network.tcl index b35daed8..4dbcb7da 100644 --- a/tcl/Network.tcl +++ b/tcl/Network.tcl @@ -410,9 +410,15 @@ proc report_net_pin { pin verbose corner digits } { } } -# default handler +# Used by report_net proc pin_location_str { pin } { - return "" + set loc [pin_location $pin] + if { $loc != "" } { + lassign $loc x y + return " ([format_distance $x 0], [format_distance $y 0])" + } else { + return "" + } } ################################################################ From 2cab7b18e559b9cd609201ab85c1fa60612a85d2 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 15:59:16 -0700 Subject: [PATCH 58/70] Network::location(pin) --- include/sta/Network.hh | 6 +++--- network/Network.cc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 879ad2c5..eab973ad 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -326,9 +326,9 @@ public: // Return the physical X/Y coordinates of the pin. virtual void location(const Pin *pin, // Return values. - double x, - double y, - bool exists) const; + double &x, + double &y, + bool &exists) const; int pinCount(); int pinCount(Instance *inst); diff --git a/network/Network.cc b/network/Network.cc index 535566f9..412afcfb 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -938,9 +938,9 @@ Network::findInstPinsMatching(const Instance *instance, void Network::location(const Pin *pin, // Return values. - double x, - double y, - bool exists) const + double &x, + double &y, + bool &exists) const { x = y = 0.0; exists = false; From 6d95ef44e5447c42d5cd70ca7ee9b37ac67911e7 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 16:28:58 -0700 Subject: [PATCH 59/70] SdcNetwork::location(pin) --- include/sta/SdcNetwork.hh | 6 +++++- network/SdcNetwork.cc | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index 6172a17d..d5aece26 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -103,6 +103,11 @@ public: virtual VertexId vertexId(const Pin *pin) const; virtual void setVertexId(Pin *pin, VertexId id); + virtual void location(const Pin *pin, + // Return values. + double &x, + double &y, + bool &exists) const; virtual Net *net(const Term *term) const; virtual Pin *pin(const Term *term) const; @@ -120,7 +125,6 @@ public: virtual char pathEscape() const; virtual void setPathEscape(char escape); - virtual bool isEditable() const; virtual LibertyLibrary *makeLibertyLibrary(const char *name, const char *filename); diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index f8b4de73..707687f3 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -223,6 +223,16 @@ NetworkNameAdapter::setVertexId(Pin *pin, network_->setVertexId(pin, id); } +void +NetworkNameAdapter::location(const Pin *pin, + // Return values. + double &x, + double &y, + bool &exists) const +{ + network_->location(pin, x, y, exists); +} + bool NetworkNameAdapter::isBundle(const Port *port) const { From 875778b25a0f7f33150f7e2e650cb1fe6a7285af Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 16:49:25 -0700 Subject: [PATCH 60/70] json xy digits --- search/ReportPath.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 09c2443c..293554ac 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2551,10 +2551,10 @@ ReportPath::reportPathJson(const Path *path, network_->location(pin, x, y, exists); if (exists) { result += " \"x\": "; - stringPrint(tmp, "%.3f", x); + stringPrint(tmp, "%.6f", x); result += tmp + ",\n"; result += " \"y\": "; - stringPrint(tmp, "%.3f", y); + stringPrint(tmp, "%.6f", y); result += tmp + "\n"; } From 7f037334bf239bcf940849b76a08ba4e98e7c69a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 6 Jul 2020 18:35:36 -0700 Subject: [PATCH 61/70] compiler warning --- liberty/LibertyExpr.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/liberty/LibertyExpr.cc b/liberty/LibertyExpr.cc index 60ce4f55..4933cad9 100644 --- a/liberty/LibertyExpr.cc +++ b/liberty/LibertyExpr.cc @@ -16,6 +16,7 @@ #include "FuncExpr.hh" +#include // min #include "Report.hh" #include "StringUtil.hh" #include "Liberty.hh" @@ -129,14 +130,9 @@ LibExprParser::copyInput(char *buf, { size_t length = strlen(func_); if (length == 0) - return 0; + return 0; else { - size_t count; - - if (length < max_size) - count = length; - else - count = max_size; + size_t count = std::min(length, max_size); strncpy(buf, func_, count); func_ += count; return count; From bf161cc759bee7dc75e9bde5a95528475fabece4 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 7 Jul 2020 17:07:50 -0700 Subject: [PATCH 62/70] compiler warning --- liberty/LibertyExpr.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/liberty/LibertyExpr.cc b/liberty/LibertyExpr.cc index 60ce4f55..4933cad9 100644 --- a/liberty/LibertyExpr.cc +++ b/liberty/LibertyExpr.cc @@ -16,6 +16,7 @@ #include "FuncExpr.hh" +#include // min #include "Report.hh" #include "StringUtil.hh" #include "Liberty.hh" @@ -129,14 +130,9 @@ LibExprParser::copyInput(char *buf, { size_t length = strlen(func_); if (length == 0) - return 0; + return 0; else { - size_t count; - - if (length < max_size) - count = length; - else - count = max_size; + size_t count = std::min(length, max_size); strncpy(buf, func_, count); func_ += count; return count; From 64a196da75f08da7370d7c535d82bcbdb8e997b5 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 7 Jul 2020 17:08:17 -0700 Subject: [PATCH 63/70] report json syntax --- search/ReportPath.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 293554ac..b16fda52 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2551,11 +2551,11 @@ ReportPath::reportPathJson(const Path *path, network_->location(pin, x, y, exists); if (exists) { result += " \"x\": "; - stringPrint(tmp, "%.6f", x); + stringPrint(tmp, "%.9f", x); result += tmp + ",\n"; result += " \"y\": "; - stringPrint(tmp, "%.6f", y); - result += tmp + "\n"; + stringPrint(tmp, "%.9f", y); + result += tmp + ",\n"; } result += " \"arrival\": "; @@ -2564,9 +2564,12 @@ ReportPath::reportPathJson(const Path *path, result += " \"slew\": "; stringPrint(tmp, "%.3e", path->slew(this)); - result += tmp + ",\n"; + result += tmp + "\n"; - result += " }\n"; + result += " }"; + if (i < expanded.size() - 1) + result += ","; + result += "\n"; } result += " ]\n"; result += "}\n"; From b3b46773411b0824a34ccc7d9e774fad85896cd8 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 8 Jul 2020 08:01:43 -0700 Subject: [PATCH 64/70] worst slack empty check --- search/WorstSlack.cc | 68 +++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index bce83e93..f3278e1d 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -178,41 +178,43 @@ void WorstSlack::sortQueue(PathAPIndex path_ap_index, const StaState *sta) { - Search *search = sta->search(); - const Debug *debug = sta->debug(); - debugPrint0(debug, "wns", 3, "sort queue\n"); + if (queue_.size() > 0) { + Search *search = sta->search(); + const Debug *debug = sta->debug(); + debugPrint0(debug, "wns", 3, "sort queue\n"); - VertexSeq vertices; - vertices.reserve(queue_.size()); - VertexSet::Iterator queue_iter(queue_); - while (queue_iter.hasNext()) { - Vertex *vertex = queue_iter.next(); - vertices.push_back(vertex); + VertexSeq vertices; + vertices.reserve(queue_.size()); + VertexSet::Iterator queue_iter(queue_); + while (queue_iter.hasNext()) { + Vertex *vertex = queue_iter.next(); + vertices.push_back(vertex); + } + WnsSlackLess slack_less(path_ap_index, sta); + sort(vertices, slack_less); + + int vertex_count = vertices.size(); + int threshold_index = min(min_queue_size_, vertex_count - 1); + Vertex *threshold_vertex = vertices[threshold_index]; + slack_threshold_ = search->wnsSlack(threshold_vertex, path_ap_index); + debugPrint1(debug, "wns", 3, "threshold %s\n", + delayAsString(slack_threshold_, sta)); + + // Reinsert vertices with slack < threshold. + queue_.clear(); + VertexSeq::Iterator queue_iter2(vertices); + while (queue_iter2.hasNext()) { + Vertex *vertex = queue_iter2.next(); + Slack slack = search->wnsSlack(vertex, path_ap_index); + if (fuzzyGreater(slack, slack_threshold_)) + break; + queue_.insert(vertex); + } + max_queue_size_ = queue_.size() * 2; + Vertex *worst_slack_vertex = vertices[0]; + Slack worst_slack_slack = search->wnsSlack(worst_slack_vertex, path_ap_index); + setWorstSlack(worst_slack_vertex, worst_slack_slack, sta); } - WnsSlackLess slack_less(path_ap_index, sta); - sort(vertices, slack_less); - - int vertex_count = vertices.size(); - int threshold_index = min(min_queue_size_, vertex_count - 1); - Vertex *threshold_vertex = vertices[threshold_index]; - slack_threshold_ = search->wnsSlack(threshold_vertex, path_ap_index); - debugPrint1(debug, "wns", 3, "threshold %s\n", - delayAsString(slack_threshold_, sta)); - - // Reinsert vertices with slack < threshold. - queue_.clear(); - VertexSeq::Iterator queue_iter2(vertices); - while (queue_iter2.hasNext()) { - Vertex *vertex = queue_iter2.next(); - Slack slack = search->wnsSlack(vertex, path_ap_index); - if (fuzzyGreater(slack, slack_threshold_)) - break; - queue_.insert(vertex); - } - max_queue_size_ = queue_.size() * 2; - Vertex *worst_slack_vertex = vertices[0]; - Slack worst_slack_slack = search->wnsSlack(worst_slack_vertex, path_ap_index); - setWorstSlack(worst_slack_vertex, worst_slack_slack, sta); } void From 46d2446f88904a2a4c47ddb814fa1950cb89b726 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 9 Jul 2020 08:42:52 -0700 Subject: [PATCH 65/70] LibertyCell::isInverter --- include/sta/Liberty.hh | 3 +++ liberty/Liberty.cc | 21 +++++++++++++++++++++ search/Property.cc | 2 ++ 3 files changed, 26 insertions(+) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 5818e4bb..c7f374b4 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -493,6 +493,7 @@ public: Report *report, Debug *debug); bool isBuffer() const; + bool isInverter() const; // Only valid when isBuffer() returns true. void bufferPorts(// Return values. LibertyPort *&input, @@ -522,6 +523,8 @@ protected: void makeTimingArcPortMaps(); bool hasBufferFunc(const LibertyPort *input, const LibertyPort *output) const; + bool hasInverterFunc(const LibertyPort *input, + const LibertyPort *output) const; LibertyLibrary *liberty_library_; float area_; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 38a64706..efb7919d 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1064,6 +1064,27 @@ LibertyCell::hasBufferFunc(const LibertyPort *input, && func->port() == input; } +bool +LibertyCell::isInverter() const +{ + LibertyPort *input; + LibertyPort *output; + bufferPorts(input, output); + return input && output + && hasInverterFunc(input, output); +} + +bool +LibertyCell::hasInverterFunc(const LibertyPort *input, + const LibertyPort *output) const +{ + FuncExpr *func = output->function(); + return func + && func->op() == FuncExpr::op_not + && func->left()->op() == FuncExpr::op_port + && func->left()->port() == input; +} + void LibertyCell::bufferPorts(// Return values. LibertyPort *&input, diff --git a/search/Property.cc b/search/Property.cc index 0a840dfa..d69bd3f1 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -566,6 +566,8 @@ getProperty(const LibertyCell *cell, return PropertyValue(cell->libertyLibrary()); else if (stringEqual(property, "is_buffer")) return PropertyValue(cell->isBuffer()); + else if (stringEqual(property, "is_inverter")) + return PropertyValue(cell->isInverter()); else if (stringEqual(property, "dont_use")) return PropertyValue(cell->dontUse()); else From b54125a1ae6c41018dd2b618267594049d4f28e6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 9 Jul 2020 11:26:06 -0700 Subject: [PATCH 66/70] get_property pin is_register_clock --- search/Property.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/search/Property.cc b/search/Property.cc index d69bd3f1..177cb858 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -688,6 +688,8 @@ getProperty(const LibertyPort *port, float cap = port->capacitance(RiseFall::rise(), MinMax::max()); return PropertyValue(sta->units()->capacitanceUnit()->asString(cap, 6)); } + else if (stringEqual(property, "is_register_clock")) + return PropertyValue(port->isRegClk()); else if (stringEqual(property, "drive_resistance_rise_min")) return PropertyValue(port->driveResistance(RiseFall::rise(), MinMax::min())); @@ -741,6 +743,10 @@ getProperty(const Pin *pin, return PropertyValue(network->pathName(pin)); else if (stringEqual(property, "direction")) return PropertyValue(network->direction(pin)->name()); + else if (stringEqual(property, "is_register_clock")) { + const LibertyPort *port = network->libertyPort(pin); + return PropertyValue(port && port->isRegClk()); + } else if (stringEqual(property, "clocks")) { ClockSet clks; sta->clocks(pin, clks); From b7a572cfe2e6b46ab3ed3b348fd74bdb417845f3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 9 Jul 2020 16:10:21 -0700 Subject: [PATCH 67/70] LibertyPort::capacitance() --- include/sta/Liberty.hh | 1 + include/sta/RiseFallMinMax.hh | 3 +++ liberty/Liberty.cc | 12 ++++++++++++ sdc/RiseFallMinMax.cc | 17 +++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index c7f374b4..ec898916 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -636,6 +636,7 @@ public: float &fanout_load, bool &exists) const; void setFanoutLoad(float fanout_load); + float capacitance() const; float capacitance(const RiseFall *rf, const MinMax *min_max) const; void capacitance(const RiseFall *rf, diff --git a/include/sta/RiseFallMinMax.hh b/include/sta/RiseFallMinMax.hh index 57f2900d..69711fe4 100644 --- a/include/sta/RiseFallMinMax.hh +++ b/include/sta/RiseFallMinMax.hh @@ -36,6 +36,9 @@ public: float &value, bool &exists) const; bool hasValue() const; + void maxValue(// Return values + float &max_value, + bool &exists) const; bool empty() const; bool hasValue(const RiseFall *rf, const MinMax *min_max) const; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index efb7919d..11c9c317 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1928,6 +1928,18 @@ LibertyPort::setCapacitance(const RiseFall *rf, } } +float +LibertyPort::capacitance() const +{ + float cap; + bool exists; + capacitance_.maxValue(cap, exists); + if (exists) + return cap; + else + return 0.0; +} + float LibertyPort::capacitance(const RiseFall *rf, const MinMax *min_max) const diff --git a/sdc/RiseFallMinMax.cc b/sdc/RiseFallMinMax.cc index 47e66a28..35c95b23 100644 --- a/sdc/RiseFallMinMax.cc +++ b/sdc/RiseFallMinMax.cc @@ -163,6 +163,23 @@ RiseFallMinMax::hasValue() const return !empty(); } +void +RiseFallMinMax::maxValue(// Return values + float &max_value, + bool &exists) const +{ + max_value = MinMax::max()->initValue(); + exists = false; + for (int rf_index=0;rf_index Date: Fri, 10 Jul 2020 17:08:44 -0700 Subject: [PATCH 68/70] report clk used as data respect -format --- search/ReportPath.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index b16fda52..8a50e8cc 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2052,7 +2052,18 @@ ReportPath::reportSrcClkAndPath(const Path *path, early_late, result); if (clk_insertion > 0.0) reportClkSrcLatency(clk_insertion, clk_time, early_late, result); - reportPath1(path, expanded, true, time_offset, result); + if (reportClkPath()) + reportPath1(path, expanded, true, time_offset, result); + else { + Arrival clk_arrival = clk_end_time; + Arrival end_arrival = path->arrival(this) + time_offset; + Delay clk_delay = end_arrival - clk_arrival; + reportLine("clock network delay", clk_delay, + end_arrival, early_late, result); + Vertex *end_vertex = path->vertex(this); + reportLine(descriptionField(end_vertex).c_str(), + end_arrival, early_late, clk_end_rf, result); + } } else { if (is_path_delay) { From 9468da1ae8bd2fa9dadaa25366d542e179d79988 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 11 Jul 2020 16:24:48 -0700 Subject: [PATCH 69/70] Delay compare ops --- dcalc/DmpDelayCalc.cc | 2 +- dcalc/GraphDelayCalc1.cc | 6 +- graph/DelayFloat.cc | 114 ++++++++++++++++++++++------------ graph/DelayNormal1.cc | 88 ++++++++++---------------- graph/DelayNormal2.cc | 98 ++++++++++++----------------- include/sta/DelayFloat.hh | 81 ++++++++++++++++-------- include/sta/DelayNormal1.hh | 56 ++++++++--------- include/sta/DelayNormal2.hh | 26 ++++---- sdf/SdfReader.cc | 2 +- sdf/SdfWriter.cc | 24 +++---- search/CheckMaxSkews.cc | 6 +- search/CheckMinPeriods.cc | 6 +- search/CheckMinPulseWidths.cc | 6 +- search/ClkInfo.cc | 4 +- search/Genclks.cc | 6 +- search/Latches.cc | 6 +- search/PathEnd.cc | 18 +++--- search/PathEnum.cc | 4 +- search/PathGroup.cc | 12 ++-- search/PathVertex.cc | 2 +- search/Power.cc | 4 +- search/Property.cc | 8 +-- search/ReportPath.cc | 24 +++---- search/Search.cc | 24 +++---- search/Sta.cc | 45 ++++++++------ search/WorstSlack.cc | 26 ++++---- 26 files changed, 357 insertions(+), 341 deletions(-) diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 77ea8c11..44823aa7 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -347,7 +347,7 @@ DmpCeffTwoPoleDelayCalc::loadDelay(Parasitic *pole_residue, { ComplexFloat pole2, residue2; parasitics_->poleResidue(pole_residue, 1, pole2, residue2); - if (!fuzzyZero(drvr_slew_) + if (!delayZero(drvr_slew_) && pole2.imag() == 0.0 && residue2.imag() == 0.0) { double p2 = pole2.real(); diff --git a/dcalc/GraphDelayCalc1.cc b/dcalc/GraphDelayCalc1.cc index 33f4bdbd..7f3ab44e 100644 --- a/dcalc/GraphDelayCalc1.cc +++ b/dcalc/GraphDelayCalc1.cc @@ -1246,7 +1246,7 @@ GraphDelayCalc1::findArcDelay(LibertyCell *drvr_cell, // Merge slews. const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (fuzzyGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax()) + if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax()) && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)) graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { @@ -1446,7 +1446,7 @@ GraphDelayCalc1::annotateLoadDelays(Vertex *drvr_vertex, else { const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index); if (!merge - || fuzzyGreater(load_slew, slew, slew_min_max)) + || delayGreater(load_slew, slew, slew_min_max)) graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); } } @@ -1459,7 +1459,7 @@ GraphDelayCalc1::annotateLoadDelays(Vertex *drvr_vertex, Delay wire_delay_extra = extra_delay + wire_delay; const MinMax *delay_min_max = dcalc_ap->delayMinMax(); if (!merge - || fuzzyGreater(wire_delay_extra, delay, delay_min_max)) { + || delayGreater(wire_delay_extra, delay, delay_min_max)) { graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra); if (observer_) diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc index 2b06819a..d4372543 100644 --- a/graph/DelayFloat.cc +++ b/graph/DelayFloat.cc @@ -35,19 +35,6 @@ initDelayConstants() delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); } -const Delay & -delayInitValue(const MinMax *min_max) -{ - return delay_init_values[min_max->index()]; -} - -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max) -{ - return fuzzyEqual(delay, min_max->initValue()); -} - const char * delayAsString(const Delay &delay, const StaState *sta) @@ -73,45 +60,47 @@ delayAsString(const Delay &delay, return unit->asString(delay, digits); } -float -delayAsFloat(const Delay &delay, - const EarlyLate *, - const StaState *) +const Delay & +delayInitValue(const MinMax *min_max) { - return delay; -} - -float -delaySigma2(const Delay &, - const EarlyLate *) -{ - return 0.0; + return delay_init_values[min_max->index()]; } bool -fuzzyGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) +delayIsInitValue(const Delay &delay, + const MinMax *min_max) { - if (min_max == MinMax::max()) - return fuzzyGreater(delay1, delay2); - else - return fuzzyLess(delay1, delay2); + return fuzzyEqual(delay, min_max->initValue()); } bool -fuzzyGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) +delayZero(const Delay &delay) { - if (min_max == MinMax::max()) - return fuzzyGreaterEqual(delay1, delay2); - else - return fuzzyLessEqual(delay1, delay2); + return fuzzyZero(delay); } bool -fuzzyLess(const Delay &delay1, +delayInf(const Delay &delay) +{ + return fuzzyInf(delay); +} + +bool +delayEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyEqual(delay1, delay2); +} + +bool +delayLess(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyLess(delay1, delay2); +} + +bool +delayLess(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { @@ -122,7 +111,14 @@ fuzzyLess(const Delay &delay1, } bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyLessEqual(delay1, delay2); +} + +bool +delayLessEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { @@ -132,6 +128,42 @@ fuzzyLessEqual(const Delay &delay1, return fuzzyGreaterEqual(delay1, delay2); } +bool +delayGreater(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyGreater(delay1, delay2); +} + +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreater(delay1, delay2); + else + return fuzzyLess(delay1, delay2); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyGreaterEqual(delay1, delay2); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreaterEqual(delay1, delay2); + else + return fuzzyLessEqual(delay1, delay2); +} + Delay delayRemove(const Delay &delay1, const Delay &delay2) diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index 344a3c1f..fc70e885 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -164,31 +164,7 @@ Delay::operator-=(const Delay &delay) bool Delay::operator==(const Delay &delay) const { - return fuzzyEqual(*this, delay); -} - -bool -Delay::operator>(const Delay &delay) const -{ - return fuzzyGreater(*this, delay); -} - -bool -Delay::operator>=(const Delay &delay) const -{ - return fuzzyGreaterEqual(*this, delay); -} - -bool -Delay::operator<(const Delay &delay) const -{ - return fuzzyLess(*this, delay); -} - -bool -Delay::operator<=(const Delay &delay) const -{ - return fuzzyLessEqual(*this, delay); + return delayEqual(*this, delay); } //////////////////////////////////////////////////////////////// @@ -218,20 +194,20 @@ delayIsInitValue(const Delay &delay, } bool -fuzzyZero(const Delay &delay) +delayZero(const Delay &delay) { return fuzzyZero(delay.mean()) && fuzzyZero(delay.sigma2()); } bool -fuzzyInf(const Delay &delay) +delayInf(const Delay &delay) { return fuzzyInf(delay.mean()); } bool -fuzzyEqual(const Delay &delay1, +delayEqual(const Delay &delay1, const Delay &delay2) { return fuzzyEqual(delay1.mean(), delay2.mean()) @@ -239,7 +215,7 @@ fuzzyEqual(const Delay &delay1, } bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); @@ -248,7 +224,7 @@ fuzzyLess(const Delay &delay1, } bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, float delay2) { Sta *sta = Sta::sta(); @@ -257,7 +233,18 @@ fuzzyLess(const Delay &delay1, } bool -fuzzyLessEqual(const Delay &delay1, +delayLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return delayLess(delay1, delay2); + else + return delayGreater(delay1, delay2); +} + +bool +delayLessEqual(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); @@ -266,7 +253,7 @@ fuzzyLessEqual(const Delay &delay1, } bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, float delay2) { Sta *sta = Sta::sta(); @@ -275,18 +262,18 @@ fuzzyLessEqual(const Delay &delay1, } bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyLessEqual(delay1, delay2); + return delayLessEqual(delay1, delay2); else - return fuzzyGreaterEqual(delay1, delay2); + return delayGreaterEqual(delay1, delay2); } bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); @@ -295,7 +282,7 @@ fuzzyGreater(const Delay &delay1, } bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, float delay2) { Sta *sta = Sta::sta(); @@ -304,7 +291,7 @@ fuzzyGreater(const Delay &delay1, } bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); @@ -313,7 +300,7 @@ fuzzyGreaterEqual(const Delay &delay1, } bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, float delay2) { Sta *sta = Sta::sta(); @@ -322,36 +309,25 @@ fuzzyGreaterEqual(const Delay &delay1, } bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreater(delay1, delay2); + return delayGreater(delay1, delay2); else - return fuzzyLess(delay1, delay2); + return delayLess(delay1, delay2); } bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreaterEqual(delay1, delay2); + return delayGreaterEqual(delay1, delay2); else - return fuzzyLessEqual(delay1, delay2); -} - -bool -fuzzyLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) -{ - if (min_max == MinMax::max()) - return fuzzyLess(delay1, delay2); - else - return fuzzyGreater(delay1, delay2); + return delayLessEqual(delay1, delay2); } float diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index 8b6f02ba..739676b8 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -24,6 +24,8 @@ #include "Fuzzy.hh" #include "Units.hh" #include "StaState.hh" +// temporary hack +#include "Sta.hh" // SSTA compilation. #if (SSTA == 2) @@ -187,30 +189,6 @@ Delay::operator==(const Delay &delay) const && sigma2_[late_index] == delay.sigma2_[early_index]; } -bool -Delay::operator>(const Delay &delay) const -{ - return mean_ + sqrt(sigma2_late_) > delay.mean_ + sqrt(delay.sigma2_late_); -} - -bool -Delay::operator>=(const Delay &delay) const -{ - return mean_ + sqrt(sigma2_late_) >= delay.mean_ + sqrt(delay.sigma2_late_); -} - -bool -Delay::operator<(const Delay &delay) const -{ - return mean_ - sqrt(sigma2_early_) < delay.mean_ - sqrt(delay.sigma2_early_); -} - -bool -Delay::operator<=(const Delay &delay) const -{ - return mean_ - sqrt(sigma2_early_) <= delay.mean_ - sqrt(delay.sigma2_early_); -} - //////////////////////////////////////////////////////////////// Delay @@ -234,12 +212,12 @@ delayIsInitValue(const Delay &delay, const MinMax *min_max) { return fuzzyEqual(delay.mean(), min_max->initValue()) - && delay.sigma2Early() == 0.0 - && delay.sigma2Late() == 0.0; + && fuzzyZero(delay.sigma2Early()) + && fuzzyZero(delay.sigma2Late()); } bool -fuzzyZero(const Delay &delay) +delayZero(const Delay &delay) { return fuzzyZero(delay.mean()) && fuzzyZero(delay.sigma2Early()) @@ -247,13 +225,13 @@ fuzzyZero(const Delay &delay) } bool -fuzzyInf(const Delay &delay) +delayInf(const Delay &delay) { return fuzzyInf(delay.mean()); } bool -fuzzyEqual(const Delay &delay1, +delayEqual(const Delay &delay1, const Delay &delay2) { return fuzzyEqual(delay1.mean(), delay2.mean()) @@ -262,7 +240,7 @@ fuzzyEqual(const Delay &delay1, } bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); @@ -271,7 +249,7 @@ fuzzyLess(const Delay &delay1, } bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, float delay2) { Sta *sta = Sta::sta(); @@ -280,7 +258,18 @@ fuzzyLess(const Delay &delay1, } bool -fuzzyLessEqual(const Delay &delay1, +delayLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return delayLess(delay1, delay2); + else + return delayGreater(delay1, delay2); +} + +bool +delayLessEqual(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); @@ -289,8 +278,8 @@ fuzzyLessEqual(const Delay &delay1, } bool -fuzzyLessEqual(const Delay &delay1, - float delay2) +delayLessEqual(const Delay &delay1, + float delay2) { Sta *sta = Sta::sta(); return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), @@ -298,18 +287,18 @@ fuzzyLessEqual(const Delay &delay1, } bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyLessEqual(delay1, delay2); + return delayLessEqual(delay1, delay2); else - return fuzzyGreaterEqual(delay1, delay2); + return delayGreaterEqual(delay1, delay2); } bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); @@ -318,7 +307,7 @@ fuzzyGreater(const Delay &delay1, } bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, float delay2) { Sta *sta = Sta::sta(); @@ -327,54 +316,43 @@ fuzzyGreater(const Delay &delay1, } bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2) { Sta *sta = Sta::sta(); return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, float delay2) { Sta *sta = Sta::sta(); - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + return delayGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), delay2); } bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreater(delay1, delay2); + return delayGreater(delay1, delay2); else - return fuzzyLess(delay1, delay2); + return delayLess(delay1, delay2); } bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max) { if (min_max == MinMax::max()) - return fuzzyGreaterEqual(delay1, delay2); + return delayGreaterEqual(delay1, delay2); else - return fuzzyLessEqual(delay1, delay2); -} - -bool -fuzzyLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) -{ - if (min_max == MinMax::max()) - return fuzzyLess(delay1, delay2); - else - return fuzzyGreater(delay1, delay2); + return delayLessEqual(delay1, delay2); } float diff --git a/include/sta/DelayFloat.hh b/include/sta/DelayFloat.hh index 70bd51f1..e895eae1 100644 --- a/include/sta/DelayFloat.hh +++ b/include/sta/DelayFloat.hh @@ -17,7 +17,6 @@ #pragma once #include "MinMax.hh" -#include "Fuzzy.hh" // Delay values defined as floats. @@ -32,6 +31,19 @@ const Delay delay_zero = 0.0; void initDelayConstants(); +const char * +delayAsString(const Delay &delay, + const StaState *sta); +const char * +delayAsString(const Delay &delay, + const StaState *sta, + int digits); +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta, + int digits); + inline Delay makeDelay(float delay, float, @@ -55,46 +67,61 @@ delayAsFloat(const Delay &delay) } // mean late+/early- sigma -float +inline float delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta); -float -delaySigma2(const Delay &delay, - const EarlyLate *early_late); -const char * -delayAsString(const Delay &delay, - const StaState *sta); -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits); -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); + const EarlyLate *, + const StaState *) +{ + return delay; +} + +inline float +delaySigma2(const Delay &, + const EarlyLate *) +{ + return 0.0; +} + const Delay & delayInitValue(const MinMax *min_max); bool delayIsInitValue(const Delay &delay, const MinMax *min_max); bool -fuzzyGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max); +delayZero(const Delay &delay); bool -fuzzyGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max); +delayInf(const Delay &delay); bool -fuzzyLess(const Delay &delay1, +delayEqual(const Delay &delay1, + const Delay &delay2); +bool +delayLess(const Delay &delay1, + const Delay &delay2); +bool +delayLess(const Delay &delay1, const Delay &delay2, const MinMax *min_max); bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, + const Delay &delay2); +bool +delayLessEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max); +bool +delayGreater(const Delay &delay1, + const Delay &delay2); +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); Delay delayRemove(const Delay &delay1, diff --git a/include/sta/DelayNormal1.hh b/include/sta/DelayNormal1.hh index 1b99f139..6a3bfb86 100644 --- a/include/sta/DelayNormal1.hh +++ b/include/sta/DelayNormal1.hh @@ -47,10 +47,6 @@ public: void operator-=(float delay); void operator-=(const Delay &delay); bool operator==(const Delay &delay) const; - bool operator>(const Delay &delay) const; - bool operator>=(const Delay &delay) const; - bool operator<(const Delay &delay) const; - bool operator<=(const Delay &delay) const; private: float mean_; @@ -63,6 +59,19 @@ const Delay delay_zero(0.0); void initDelayConstants(); +const char * +delayAsString(const Delay &delay, + const StaState *sta); +const char * +delayAsString(const Delay &delay, + const StaState *sta, + int digits); +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta, + int digits); + Delay makeDelay(float delay, float sigma_early, @@ -75,7 +84,10 @@ makeDelay2(float delay, float sigma_late); inline float -delayAsFloat(const Delay &delay) { return delay.mean(); } +delayAsFloat(const Delay &delay) +{ + return delay.mean(); +} // mean late+/early- sigma float @@ -85,56 +97,44 @@ delayAsFloat(const Delay &delay, float delaySigma2(const Delay &delay, const EarlyLate *early_late); -const char * -delayAsString(const Delay &delay, - const StaState *sta); -const char * -delayAsString(const Delay &delay, - const StaState *sta, - int digits); -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); const Delay & delayInitValue(const MinMax *min_max); bool delayIsInitValue(const Delay &delay, const MinMax *min_max); bool -fuzzyZero(const Delay &delay); +delayZero(const Delay &delay); bool -fuzzyInf(const Delay &delay); +delayInf(const Delay &delay); bool -fuzzyEqual(const Delay &delay1, +delayEqual(const Delay &delay1, const Delay &delay2); bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, const Delay &delay2); bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, const Delay &delay2, const MinMax *min_max); bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, const Delay &delay2); bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max); bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2); bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2); bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max); bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2, const MinMax *min_max); // delay1-delay2 subtracting sigma instead of addiing. diff --git a/include/sta/DelayNormal2.hh b/include/sta/DelayNormal2.hh index d39a5ec3..ab1c58c6 100644 --- a/include/sta/DelayNormal2.hh +++ b/include/sta/DelayNormal2.hh @@ -50,10 +50,6 @@ public: void operator-=(float delay); void operator-=(const Delay &delay); bool operator==(const Delay &delay) const; - bool operator>(const Delay &delay) const; - bool operator>=(const Delay &delay) const; - bool operator<(const Delay &delay) const; - bool operator<=(const Delay &delay) const; protected: static const int early_index = 0; @@ -110,38 +106,38 @@ bool delayIsInitValue(const Delay &delay, const MinMax *min_max); bool -fuzzyZero(const Delay &delay); +delayZero(const Delay &delay); bool -fuzzyInf(const Delay &delay); +delayInf(const Delay &delay); bool -fuzzyEqual(const Delay &delay1, +delayEqual(const Delay &delay1, const Delay &delay2); bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, const Delay &delay2); bool -fuzzyLess(const Delay &delay1, +delayLess(const Delay &delay1, const Delay &delay2, const MinMax *min_max); bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, const Delay &delay2); bool -fuzzyLessEqual(const Delay &delay1, +delayLessEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max); bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2); bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2); bool -fuzzyGreaterEqual(const Delay &delay1, +delayGreaterEqual(const Delay &delay1, const Delay &delay2, const MinMax *min_max); bool -fuzzyGreater(const Delay &delay1, +delayGreater(const Delay &delay1, const Delay &delay2, const MinMax *min_max); // delay1-delay2 subtracting sigma instead of addiing. diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 98f7dff6..34502129 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -809,7 +809,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, delay = graph_->arcDelay(edge, arc, arc_delay_index) + *value; else if (graph_->arcDelayAnnotated(edge, arc, arc_delay_index)) { ArcDelay prev_value = graph_->arcDelay(edge, arc, arc_delay_index); - if (fuzzyGreater(prev_value, delay, min_max)) + if (delayGreater(prev_value, delay, min_max)) delay = prev_value; } graph_->setArcDelay(edge, arc, arc_delay_index, delay); diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 4287776c..09b408ad 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -605,18 +605,18 @@ SdfWriter::writeEdgeCheck(Edge *edge, && arcs[clk_rf_index][RiseFall::fallIndex()] && arcs[clk_rf_index][RiseFall::riseIndex()] && arcs[clk_rf_index][RiseFall::fallIndex()] - && fuzzyEqual(graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::riseIndex()], - arc_delay_min_index_), - graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::fallIndex()], - arc_delay_min_index_)) - && fuzzyEqual(graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::riseIndex()], - arc_delay_max_index_), - graph_->arcDelay(edge, - arcs[clk_rf_index][RiseFall::fallIndex()], - arc_delay_max_index_))) + && delayEqual(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::riseIndex()], + arc_delay_min_index_), + graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::fallIndex()], + arc_delay_min_index_)) + && delayEqual(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::riseIndex()], + arc_delay_max_index_), + graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::fallIndex()], + arc_delay_max_index_))) // Rise/fall margins are the same, so no data edge specifier is required. writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], sdf_check, false, true); diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index d7020cc5..a3081697 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -107,7 +107,7 @@ void MaxSkewViolatorsVisititor::visit(MaxSkewCheck &check, const StaState *sta) { - if (fuzzyLess(check.slack(sta), 0.0)) + if (delayLess(check.slack(sta), 0.0)) checks_.push_back(new MaxSkewCheck(check)); } @@ -280,8 +280,8 @@ MaxSkewSlackLess::operator()(const MaxSkewCheck *check1, { Slack slack1 = check1->slack(sta_); Slack slack2 = check2->slack(sta_); - return slack1 < slack2 - || (fuzzyEqual(slack1, slack2) + return delayLess(slack1, slack2) + || (delayEqual(slack1, slack2) // Break ties based on constrained pin names. && sta_->network()->pinLess(check1->clkPin(sta_),check2->clkPin(sta_))); } diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index dd21636d..7e797c53 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -79,7 +79,7 @@ void MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check, StaState *sta) { - if (fuzzyLess(check.slack(sta), 0.0)) + if (delayLess(check.slack(sta), 0.0)) checks_.push_back(check.copy()); } @@ -231,9 +231,9 @@ MinPeriodSlackLess::operator()(const MinPeriodCheck *check1, Slack slack2 = check2->slack(sta_); const Pin *pin1 = check1->pin(); const Pin *pin2 = check2->pin(); - return fuzzyLess(slack1, slack2) + return delayLess(slack1, slack2) // Break ties based on pin and clock names. - || (fuzzyEqual(slack1, slack2) + || (delayEqual(slack1, slack2) && (sta_->network()->pinLess(pin1, pin2) || (pin1 == pin2 && ClockNameLess()(check1->clk(), diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index 385662c0..78d1dfa9 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -167,7 +167,7 @@ void MinPulseWidthViolatorsVisitor::visit(MinPulseWidthCheck &check, const StaState *sta) { - if (fuzzyLess(check.slack(sta), 0.0) + if (delayLess(check.slack(sta), 0.0) && (corner_ == nullptr || check.corner(sta) == corner_)) { MinPulseWidthCheck *copy = new MinPulseWidthCheck(check.openPath()); @@ -499,8 +499,8 @@ MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck *check1, Slack slack2 = check2->slack(sta_); const Pin *pin1 = check1->pin(sta_); const Pin *pin2 = check2->pin(sta_); - return slack1 < slack2 - || (fuzzyEqual(slack1, slack2) + return delayLess(slack1, slack2) + || (delayEqual(slack1, slack2) // Break ties for the sake of regression stability. && (sta_->network()->pinLess(pin1, pin2) || (pin1 == pin2 diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc index 8638eda3..d1d3388f 100644 --- a/search/ClkInfo.cc +++ b/search/ClkInfo.cc @@ -294,9 +294,9 @@ clkInfoCmp(const ClkInfo *clk_info1, const Arrival &insert1 = clk_info1->insertion(); const Arrival &insert2 = clk_info2->insertion(); - if (insert1 < insert2) + if (delayLess(insert1, insert2)) return -1; - if (insert1 > insert2) + if (delayGreater(insert1, insert2)) return 1; float latency1 = clk_info1->latency(); diff --git a/search/Genclks.cc b/search/Genclks.cc index 78da5608..5abdd31a 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -968,9 +968,9 @@ Genclks::recordSrcPaths(Clock *gclk) && (!has_edges || src_clk_rf == gclk->masterClkEdgeTr(rf)) && (src_path.isNull() - || fuzzyGreater(path->arrival(this), - src_path.arrival(this), - early_late))) { + || delayGreater(path->arrival(this), + src_path.arrival(this), + early_late))) { debugPrint4(debug_, "genclk", 2, " %s insertion %s %s %s\n", network_->pathName(gclk_pin), early_late->asString(), diff --git a/search/Latches.cc b/search/Latches.cc index d47b16a1..1a127567 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -98,7 +98,7 @@ Latches::latchRequired(const Path *data_path, network_->pathName(data_path->pin(this)), delayAsString(data_arrival, this), delayAsString(enable_arrival, this)); - if (data_arrival <= enable_arrival) { + if (delayLessEqual(data_arrival, enable_arrival)) { // Data arrives before latch opens. required = enable_arrival; borrow = 0.0; @@ -108,7 +108,7 @@ Latches::latchRequired(const Path *data_path, else { // Data arrives while latch is transparent. borrow = data_arrival - enable_arrival; - if (borrow <= max_borrow) + if (delayLessEqual(borrow, max_borrow)) required = data_arrival; else { borrow = max_borrow; @@ -332,7 +332,7 @@ Latches::latchOutArrival(Path *data_path, latchRequired(data_path, enable_path, &disable_path, path_ap, required, borrow, adjusted_data_arrival, time_given_to_startpoint); - if (borrow > 0.0) { + if (delayGreater(borrow, 0.0)) { // Latch is transparent when data arrives. arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, false, path_ap); diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 786a8af0..18d11cc7 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -1244,7 +1244,7 @@ PathEndLatchCheck::targetClkWidth(const StaState *sta) const if (enable_clk_info->isPulseClk()) return disable_arrival - enable_arrival; else { - if (enable_arrival > disable_arrival) { + if (delayGreater(enable_arrival, disable_arrival)) { float period = enable_clk_info->clock()->period(); disable_arrival += period; } @@ -1989,24 +1989,24 @@ PathEnd::cmpSlack(const PathEnd *path_end1, { Slack slack1 = path_end1->slack(sta); Slack slack2 = path_end2->slack(sta); - if (fuzzyZero(slack1) - && fuzzyZero(slack2) + if (delayZero(slack1) + && delayZero(slack2) && path_end1->isLatchCheck() && path_end2->isLatchCheck()) { Arrival borrow1 = path_end1->borrow(sta); Arrival borrow2 = path_end2->borrow(sta); // Latch slack is zero if there is borrowing so break ties // based on borrow time. - if (fuzzyEqual(borrow1, borrow2)) + if (delayEqual(borrow1, borrow2)) return 0; - else if (borrow1 > borrow2) + else if (delayGreater(borrow1, borrow2)) return -1; else return 1; } - else if (fuzzyEqual(slack1, slack2)) + else if (delayEqual(slack1, slack2)) return 0; - else if (slack1 < slack2) + else if (delayLess(slack1, slack2)) return -1; else return 1; @@ -2020,9 +2020,9 @@ PathEnd::cmpArrival(const PathEnd *path_end1, Arrival arrival1 = path_end1->dataArrivalTime(sta); Arrival arrival2 = path_end2->dataArrivalTime(sta); const MinMax *min_max = path_end1->minMax(sta); - if (fuzzyEqual(arrival1, arrival2)) + if (delayEqual(arrival1, arrival2)) return 0; - else if (fuzzyLess(arrival1, arrival2, min_max)) + else if (delayLess(arrival1, arrival2, min_max)) return -1; else return 1; diff --git a/search/PathEnum.cc b/search/PathEnum.cc index 0d650fa1..ce7abd00 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -348,7 +348,7 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, // Make the diverted path end to check slack with from_path crpr. makeDivertedPathEnd(from_path, arc, div_end, after_div_copy); // Only enumerate paths with greater slack. - if (fuzzyGreaterEqual(div_end->slack(sta_), path_end_slack_)) { + if (delayGreaterEqual(div_end->slack(sta_), path_end_slack_)) { reportDiversion(arc, from_path); path_enum_->makeDiversion(div_end, after_div_copy); } @@ -356,7 +356,7 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, delete div_end; } // Only enumerate slower/faster paths. - else if (fuzzyLessEqual(to_arrival, before_div_arrival_, min_max)) { + else if (delayLessEqual(to_arrival, before_div_arrival_, min_max)) { PathEnd *div_end; PathEnumed *after_div_copy; makeDivertedPathEnd(from_path, arc, div_end, after_div_copy); diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 20422f8d..d2856083 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -101,19 +101,19 @@ PathGroup::savable(PathEnd *path_end) // without crpr first because it is expensive to find. Slack slack = path_end->slackNoCrpr(sta_); if (!delayIsInitValue(slack, min_max_) - && fuzzyLessEqual(slack, threshold_) - && fuzzyLessEqual(slack, slack_max_)) { + && delayLessEqual(slack, threshold_) + && delayLessEqual(slack, slack_max_)) { // Now check with crpr. slack = path_end->slack(sta_); - savable = fuzzyLessEqual(slack, threshold_) - && fuzzyLessEqual(slack, slack_max_) - && fuzzyGreaterEqual(slack, slack_min_); + savable = delayLessEqual(slack, threshold_) + && delayLessEqual(slack, slack_max_) + && delayGreaterEqual(slack, slack_min_); } } else { const Arrival &arrival = path_end->dataArrivalTime(sta_); savable = !delayIsInitValue(arrival, min_max_) - && fuzzyGreaterEqual(arrival, threshold_, min_max_); + && delayGreaterEqual(arrival, threshold_, min_max_); } return savable; } diff --git a/search/PathVertex.cc b/search/PathVertex.cc index e870f659..d26b32ab 100644 --- a/search/PathVertex.cc +++ b/search/PathVertex.cc @@ -403,7 +403,7 @@ PrevPathVisitor::visitFromToPath(const Pin *, && path_ap_index == path_ap_index_ && (dcalc_tol_ > 0.0 ? std::abs(delayAsFloat(to_arrival - path_arrival_)) < dcalc_tol_ - : fuzzyEqual(to_arrival, path_arrival_)) + : delayEqual(to_arrival, path_arrival_)) && (tagMatch(to_tag, path_tag_, sta_) // If the filter exception became active searching from // from_path to to_path the tag includes the filter, but diff --git a/search/Power.cc b/search/Power.cc index ba5ecdd5..525aecde 100644 --- a/search/Power.cc +++ b/search/Power.cc @@ -597,7 +597,7 @@ Power::findInputInternalPower(const Pin *pin, for (auto rf : RiseFall::range()) { float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap->index())); - if (!fuzzyInf(slew)) { + if (!delayInf(slew)) { float table_energy = pwr->power(rf, pvt, slew, load_cap); energy += table_energy; tr_count++; @@ -735,7 +735,7 @@ Power::findOutputInternalPower(const Pin *to_pin, ? delayAsFloat(graph_->slew(from_vertex, from_rf, dcalc_ap->index())) : 0.0; - if (!fuzzyInf(slew)) { + if (!delayInf(slew)) { float table_energy = pwr->power(to_rf, pvt, slew, load_cap); energy += table_energy; tr_count++; diff --git a/search/Property.cc b/search/Property.cc index 177cb858..c06c0fe7 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -801,12 +801,12 @@ pinSlewProperty(const Pin *pin, Slew slew = min_max->initValue(); if (vertex) { Slew vertex_slew = sta->vertexSlew(vertex, rf, min_max); - if (fuzzyGreater(vertex_slew, slew, min_max)) + if (delayGreater(vertex_slew, slew, min_max)) slew = vertex_slew; } if (bidirect_drvr_vertex) { Slew vertex_slew = sta->vertexSlew(bidirect_drvr_vertex, rf, min_max); - if (fuzzyGreater(vertex_slew, slew, min_max)) + if (delayGreater(vertex_slew, slew, min_max)) slew = vertex_slew; } return PropertyValue(delayPropertyValue(slew, sta)); @@ -879,9 +879,9 @@ edgeDelayProperty(Edge *edge, ArcDelay arc_delay = sta->arcDelay(edge, arc, dcalc_ap); if (!delay_exists || ((min_max == MinMax::max() - && arc_delay > delay) + && delayGreater(arc_delay, delay)) || (min_max == MinMax::min() - && arc_delay < delay))) + && delayLess(arc_delay, delay)))) delay = arc_delay; } } diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 8a50e8cc..e5998078 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -578,7 +578,7 @@ ReportPath::reportFull(const PathEndLatchCheck *end, else reportTgtClk(end, result); - if (borrow >= 0.0) + if (delayGreaterEqual(borrow, 0.0)) reportLine("time borrowed from endpoint", borrow, req_time, early_late, result); else @@ -644,7 +644,7 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, if (tgt_clk_path->clkInfo(search_)->isPropagated()) { auto width_msg = stdstrPrint("%s nominal pulse width", tgt_clk_name.c_str()); reportLineTotal(width_msg.c_str(), nom_pulse_width, early_late, result); - if (!fuzzyZero(latency_diff)) + if (!delayZero(latency_diff)) reportLineTotalMinus("clock latency difference", latency_diff, early_late, result); } @@ -655,19 +655,19 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, ArcDelay margin = end->margin(this); reportLineTotalMinus("library setup time", margin, early_late, result); reportDashLineTotal(result); - if (!fuzzyZero(crpr_diff)) + if (!delayZero(crpr_diff)) reportLineTotalMinus("CRPR difference", crpr_diff, early_late, result); reportLineTotal("max time borrow", max_borrow, early_late, result); } - if (fuzzyGreater(borrow, delay_zero) + if (delayGreater(borrow, delay_zero) && (!fuzzyZero(open_uncertainty) - || !fuzzyZero(open_crpr))) { + || !delayZero(open_crpr))) { reportDashLineTotal(result); reportLineTotal("actual time borrow", borrow, early_late, result); if (!fuzzyZero(open_uncertainty)) reportLineTotal("open edge uncertainty", open_uncertainty, early_late, result); - if (!fuzzyZero(open_crpr)) + if (!delayZero(open_crpr)) reportLineTotal("open edge CRPR", open_crpr, early_late, result); reportDashLineTotal(result); reportLineTotal("time given to startpoint", time_given_to_startpoint, @@ -759,7 +759,7 @@ ReportPath::reportFull(const PathEndPathDelay *end, else { Arrival tgt_clk_delay = end->targetClkDelay(this); Arrival tgt_clk_arrival = delay + tgt_clk_delay; - if (!fuzzyZero(tgt_clk_delay)) + if (!delayZero(tgt_clk_delay)) reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)), tgt_clk_delay, tgt_clk_arrival, early_late, result); reportClkUncertainty(end, tgt_clk_arrival, result); @@ -2050,7 +2050,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, else if (clk_used_as_data) { reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late, result); - if (clk_insertion > 0.0) + if (delayGreater(clk_insertion, 0.0)) reportClkSrcLatency(clk_insertion, clk_time, early_late, result); if (reportClkPath()) reportPath1(path, expanded, true, time_offset, result); @@ -2067,7 +2067,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, } else { if (is_path_delay) { - if (clk_delay > 0.0) + if (delayGreater(clk_delay, 0.0)) reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_end_time, early_late, result); } @@ -2570,11 +2570,11 @@ ReportPath::reportPathJson(const Path *path, } result += " \"arrival\": "; - stringPrint(tmp, "%.3e", path->arrival(this)); + stringPrint(tmp, "%.3e", delayAsFloat(path->arrival(this))); result += tmp + ",\n"; result += " \"slew\": "; - stringPrint(tmp, "%.3e", path->slew(this)); + stringPrint(tmp, "%.3e", delayAsFloat(path->slew(this))); result += tmp + "\n"; result += " }"; @@ -2613,7 +2613,7 @@ ReportPath::reportPath1(const Path *path, } Arrival time = latch_enable_time + latch_time_given; Arrival incr = latch_time_given; - if (incr >= 0.0) + if (delayGreaterEqual(incr, 0.0)) reportLine("time given to startpoint", incr, time, early_late, result); else reportLine("time borrowed from startpoint", incr, time, diff --git a/search/Search.cc b/search/Search.cc index e585456b..0808f18c 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1194,7 +1194,7 @@ Search::arrivalsChanged(Vertex *vertex, bool arrival_exists2; tag_bldr->tagArrival(tag1, arrival2, arrival_exists2); if (!arrival_exists2 - || !fuzzyEqual(arrival1, arrival2)) + || !delayEqual(arrival1, arrival2)) return true; } return false; @@ -1238,7 +1238,7 @@ ArrivalVisitor::visitFromToPath(const Pin *, Tag *tag_match; tag_bldr_->tagMatchArrival(to_tag, tag_match, arrival, arrival_index); if (tag_match == nullptr - || fuzzyGreater(to_arrival, arrival, min_max)) { + || delayGreater(to_arrival, arrival, min_max)) { debugPrint5(debug, "search", 3, " %s + %s = %s %s %s\n", delayAsString(from_path->arrival(sta_), sta_), delayAsString(arc_delay, sta_), @@ -1258,7 +1258,7 @@ ArrivalVisitor::visitFromToPath(const Pin *, tag_bldr_no_crpr_->tagMatchArrival(to_tag, tag_match, arrival, arrival_index); if (tag_match == nullptr - || fuzzyGreater(to_arrival, arrival, min_max)) { + || delayGreater(to_arrival, arrival, min_max)) { tag_bldr_no_crpr_->setMatchArrival(to_tag, tag_match, to_arrival, arrival_index, &prev_path); @@ -1300,7 +1300,7 @@ ArrivalVisitor::pruneCrprArrivals() delayAsString(max_crpr, sta_), delayAsString(max_arrival_max_crpr, sta_)); Arrival arrival = tag_bldr_->arrival(arrival_index); - if (fuzzyGreater(max_arrival_max_crpr, arrival, min_max)) { + if (delayGreater(max_arrival_max_crpr, arrival, min_max)) { debugPrint1(debug, "search", 3, " pruned %s\n", tag->asString(sta_)); tag_bldr_->deleteArrival(tag); @@ -2207,7 +2207,7 @@ PathVisitor::visitFromPath(const Pin *from_pin, } else { arc_delay = search->deratedDelay(from_vertex, arc, edge, false, path_ap); - if (!fuzzyEqual(arc_delay, min_max->initValue())) { + if (!delayEqual(arc_delay, min_max->initValue())) { to_arrival = from_arrival + arc_delay; to_tag = search->thruTag(from_tag, edge, to_rf, min_max, path_ap); } @@ -3321,7 +3321,7 @@ RequiredCmp::requiredSet(int arrival_index, Required required, const MinMax *min_max) { - if (fuzzyGreater(required, requireds_[arrival_index], min_max)) { + if (delayGreater(required, requireds_[arrival_index], min_max)) { requireds_[arrival_index] = required; have_requireds_ = true; } @@ -3346,7 +3346,7 @@ RequiredCmp::requiredsSave(Vertex *vertex, Required req = requireds_[arrival_index]; if (prev_reqs) { Required prev_req = path->required(sta); - if (!fuzzyEqual(prev_req, req)) { + if (!delayEqual(prev_req, req)) { debugPrint2(debug, "search", 3, "required save %s -> %s\n", delayAsString(prev_req, sta), delayAsString(req, sta)); @@ -3640,7 +3640,7 @@ Search::totalNegativeSlack(const MinMax *min_max) for (Corner *corner : *corners_) { PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); Slack tns1 = tns_[path_ap_index]; - if (tns1 < tns) + if (delayLess(tns1, tns)) tns = tns1; } return tns; @@ -3735,7 +3735,7 @@ Search::tnsIncr(Vertex *vertex, Slack slack, PathAPIndex path_ap_index) { - if (fuzzyLess(slack, 0.0)) { + if (delayLess(slack, 0.0)) { debugPrint2(debug_, "tns", 3, "tns+ %s %s\n", delayAsString(slack, this), vertex->name(sdc_network_)); @@ -3754,7 +3754,7 @@ Search::tnsDecr(Vertex *vertex, bool found; tns_slacks_[path_ap_index].findKey(vertex, slack, found); if (found - && fuzzyLess(slack, 0.0)) { + && delayLess(slack, 0.0)) { debugPrint2(debug_, "tns", 3, "tns- %s %s\n", delayAsString(slack, this), vertex->name(sdc_network_)); @@ -3880,7 +3880,7 @@ FindEndSlackVisitor::visit(PathEnd *path_end) PathRef &path = path_end->pathRef(); PathAPIndex path_ap_index = path.pathAnalysisPtIndex(sta_); Slack slack = path_end->slack(sta_); - if (fuzzyLess(slack, slacks_[path_ap_index])) + if (delayLess(slack, slacks_[path_ap_index])) slacks_[path_ap_index] = slack; } } @@ -3907,7 +3907,7 @@ Search::wnsSlacks(Vertex *vertex, PathAPIndex path_ap_index = path->pathAnalysisPtIndex(this); const Slack path_slack = path->slack(this); if (!path->tag(this)->isFilter() - && fuzzyLess(path_slack, slacks[path_ap_index])) + && delayLess(path_slack, slacks[path_ap_index])) slacks[path_ap_index] = path_slack; } } diff --git a/search/Sta.cc b/search/Sta.cc index c18a9d9f..e2680e73 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2669,7 +2669,7 @@ Sta::vertexWorstArrivalPath(Vertex *vertex, PathVertex *path = path_iter.next(); Arrival arrival = path->arrival(this); if (!path->tag(this)->isGenClkSrcPath() - && fuzzyGreater(arrival, worst_arrival, min_max)) { + && delayGreater(arrival, worst_arrival, min_max)) { worst_arrival = arrival; worst_path.init(path); } @@ -2689,7 +2689,7 @@ Sta::vertexWorstArrivalPath(Vertex *vertex, Arrival arrival = path->arrival(this); if (path->minMax(this) == min_max && !path->tag(this)->isGenClkSrcPath() - && fuzzyGreater(arrival, worst_arrival, min_max)) { + && delayGreater(arrival, worst_arrival, min_max)) { worst_arrival = arrival; worst_path.init(path); } @@ -2709,7 +2709,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, PathVertex *path = path_iter.next(); Slack slack = path->slack(this); if (!path->tag(this)->isGenClkSrcPath() - && slack < min_slack) { + && delayLess(slack, min_slack)) { min_slack = slack; worst_path.init(path); } @@ -2730,7 +2730,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, if (path->minMax(this) == min_max && !path->tag(this)->isGenClkSrcPath()) { Slack slack = path->slack(this); - if (fuzzyLess(slack, min_slack)) { + if (delayLess(slack, min_slack)) { min_slack = slack; worst_path.init(path); } @@ -2764,7 +2764,7 @@ Sta::vertexArrival(Vertex *vertex, if ((clk_edge == clk_edge_wildcard || clk_info->clkEdge() == clk_edge) && !clk_info->isGenClkSrcPath() - && fuzzyGreater(path->arrival(this), arrival, min_max)) + && delayGreater(path->arrival(this), arrival, min_max)) arrival = path_arrival; } return arrival; @@ -2782,7 +2782,7 @@ Sta::vertexRequired(Vertex *vertex, const Path *path = path_iter.next(); if (path->minMax(this) == min_max) { const Required path_required = path->required(this); - if (fuzzyGreater(path_required, required, req_min_max)) + if (delayGreater(path_required, required, req_min_max)) required = path_required; } } @@ -2812,7 +2812,7 @@ Sta::vertexRequired(Vertex *vertex, const Required path_required = path->required(this); if ((clk_edge == clk_edge_wildcard || path->clkEdge(search_) == clk_edge) - && fuzzyGreater(path_required, required, min_max)) + && delayGreater(path_required, required, min_max)) required = path_required; } return required; @@ -2830,7 +2830,8 @@ Sta::netSlack(const Net *net, if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); Slack pin_slack = vertexSlack(vertex, min_max); - slack = min(slack, pin_slack); + if (delayLess(pin_slack, slack)) + slack = pin_slack; } } return slack; @@ -2846,8 +2847,11 @@ Sta::pinSlack(const Pin *pin, Slack slack = MinMax::min()->initValue(); if (vertex) slack = vertexSlack(vertex, min_max); - if (bidirect_drvr_vertex) - slack = min(slack, vertexSlack(bidirect_drvr_vertex, min_max)); + if (bidirect_drvr_vertex) { + Slack slack1 = vertexSlack(bidirect_drvr_vertex, min_max); + if (delayLess(slack1, slack)) + slack = slack1; + } return slack; } @@ -2862,8 +2866,11 @@ Sta::pinSlack(const Pin *pin, Slack slack = MinMax::min()->initValue(); if (vertex) slack = vertexSlack(vertex, rf, min_max); - if (bidirect_drvr_vertex) - slack = min(slack, vertexSlack(bidirect_drvr_vertex, rf, min_max)); + if (bidirect_drvr_vertex) { + Slack slack1 = vertexSlack(bidirect_drvr_vertex, rf, min_max); + if (delayLess(slack1, slack)) + slack = slack1; + } return slack; } @@ -2879,7 +2886,7 @@ Sta::vertexSlack(Vertex *vertex, Path *path = path_iter.next(); if (path->minMax(this) == min_max) { Slack path_slack = path->slack(this); - if (path_slack < slack) + if (delayLess(path_slack, slack)) slack = path_slack; } } @@ -2897,7 +2904,7 @@ Sta::vertexSlack(Vertex *vertex, while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack path_slack = path->slack(this); - if (path_slack < slack) + if (delayLess(path_slack, slack)) slack = path_slack; } return slack; @@ -2936,7 +2943,7 @@ Sta::vertexSlack1(Vertex *vertex, Slack path_slack = path->slack(this); if ((clk_edge == clk_edge_wildcard || path->clkEdge(search_) == clk_edge) - && path_slack < slack) + && delayLess(path_slack, slack)) slack = path_slack; } return slack; @@ -2958,7 +2965,7 @@ Sta::vertexSlacks(Vertex *vertex, Slack path_slack = path->slack(this); int rf_index = path->rfIndex(this); int mm_index = path->minMax(this)->index(); - if (path_slack < slacks[rf_index][mm_index]) + if (delayLess(path_slack, slacks[rf_index][mm_index])) slacks[rf_index][mm_index] = path_slack; } } @@ -3153,7 +3160,7 @@ Sta::vertexSlew(Vertex *vertex, Slew mm_slew = min_max->initValue(); for (DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); - if (fuzzyGreater(slew, mm_slew, min_max)) + if (delayGreater(slew, mm_slew, min_max)) mm_slew = slew; } return mm_slew; @@ -4775,7 +4782,7 @@ bool InstanceMaxSlewGreater::operator()(const Instance *inst1, const Instance *inst2) const { - return instMaxSlew(inst1) > instMaxSlew(inst2); + return delayGreater(instMaxSlew(inst1), instMaxSlew(inst2)); } Slew @@ -4792,7 +4799,7 @@ InstanceMaxSlewGreater::instMaxSlew(const Instance *inst) const for (RiseFall *rf : RiseFall::range()) { for (DcalcAnalysisPt *dcalc_ap : sta_->corners()->dcalcAnalysisPts()) { Slew slew = graph->slew(vertex, rf, dcalc_ap->index()); - if (slew > max_slew) + if (delayGreater(slew, max_slew)) max_slew = slew; } } diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index f3278e1d..704a6a14 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -48,7 +48,7 @@ WorstSlacks::worstSlack(const MinMax *min_max, Vertex *worst_vertex1; worst_slacks_[path_ap_index].worstSlack(path_ap_index, sta_, worst_slack1, worst_vertex1); - if (fuzzyLess(worst_slack1, worst_slack)) { + if (delayLess(worst_slack1, worst_slack)) { worst_slack = worst_slack1; worst_vertex = worst_vertex1; } @@ -159,10 +159,10 @@ WorstSlack::initQueue(PathAPIndex path_ap_index, while (end_iter.hasNext()) { Vertex *vertex = end_iter.next(); Slack slack = search->wnsSlack(vertex, path_ap_index); - if (!fuzzyEqual(slack, slack_init_)) { - if (fuzzyLess(slack, worst_slack_)) + if (!delayEqual(slack, slack_init_)) { + if (delayLess(slack, worst_slack_)) setWorstSlack(vertex, slack, sta); - if (fuzzyLessEqual(slack, slack_threshold_)) + if (delayLessEqual(slack, slack_threshold_)) queue_.insert(vertex); int queue_size = queue_.size(); if (queue_size >= max_queue_size_) @@ -206,7 +206,7 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index, while (queue_iter2.hasNext()) { Vertex *vertex = queue_iter2.next(); Slack slack = search->wnsSlack(vertex, path_ap_index); - if (fuzzyGreater(slack, slack_threshold_)) + if (delayGreater(slack, slack_threshold_)) break; queue_.insert(vertex); } @@ -231,7 +231,7 @@ WorstSlack::findWorstInQueue(PathAPIndex path_ap_index, while (queue_iter.hasNext()) { Vertex *vertex = queue_iter.next(); Slack slack = search->wnsSlack(vertex, path_ap_index); - if (slack < worst_slack_) + if (delayLess(slack, worst_slack_)) setWorstSlack(vertex, slack, sta); } } @@ -248,7 +248,7 @@ WorstSlack::checkQueue(PathAPIndex path_ap_index, VertexSet::Iterator end_iter(search->endpoints()); while (end_iter.hasNext()) { Vertex *end = end_iter.next(); - if (fuzzyLessEqual(search->wnsSlack(end, path_ap_index), + if (delayLessEqual(search->wnsSlack(end, path_ap_index), slack_threshold_)) ends.push_back(end); } @@ -261,7 +261,7 @@ WorstSlack::checkQueue(PathAPIndex path_ap_index, Vertex *end = end_iter2.next(); end_set.insert(end); if (!queue_.hasKey(end) - && fuzzyLessEqual(search->wnsSlack(end, path_ap_index), + && delayLessEqual(search->wnsSlack(end, path_ap_index), slack_threshold_)) report->print("WorstSlack queue missing %s %s < %s\n", end->name(network), @@ -294,14 +294,14 @@ WorstSlack::updateWorstSlack(Vertex *vertex, // threads. UniqueLock lock(lock_); if (worst_vertex_ - && fuzzyLess(slack, worst_slack_)) + && delayLess(slack, worst_slack_)) setWorstSlack(vertex, slack, sta); else if (vertex == worst_vertex_) // Mark worst slack as unknown (updated by findWorstSlack(). worst_vertex_ = nullptr; - if (!fuzzyEqual(slack, slack_init_) - && fuzzyLessEqual(slack, slack_threshold_)) { + if (!delayEqual(slack, slack_init_) + && delayLessEqual(slack, slack_threshold_)) { debugPrint2(debug, "wns", 3, "insert %s %s\n", vertex->name(network), delayAsString(slack, sta)); @@ -341,8 +341,8 @@ bool WnsSlackLess::operator()(Vertex *vertex1, Vertex *vertex2) { - return fuzzyLess(search_->wnsSlack(vertex1, path_ap_index_), - search_->wnsSlack(vertex2, path_ap_index_)); + return delayLess(search_->wnsSlack(vertex1, path_ap_index_), + search_->wnsSlack(vertex2, path_ap_index_)); } } // namespace From 7653096fe69937a524118c4930c4fe1c50a8c617 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 11 Jul 2020 17:43:30 -0700 Subject: [PATCH 70/70] Delay compare ops round2 --- dcalc/GraphDelayCalc1.cc | 6 +- graph/DelayFloat.cc | 24 ++- graph/DelayNormal1.cc | 297 +++++++++++++++++----------------- graph/DelayNormal2.cc | 64 ++++---- include/sta/DelayFloat.hh | 35 ++-- include/sta/DelayNormal1.hh | 41 +++-- include/sta/DelayNormal2.hh | 89 +++++----- include/sta/Search.hh | 3 +- sdf/SdfReader.cc | 2 +- search/CheckMaxSkews.cc | 4 +- search/CheckMinPeriods.cc | 4 +- search/CheckMinPulseWidths.cc | 4 +- search/ClkInfo.cc | 4 +- search/Genclks.cc | 3 +- search/Latches.cc | 6 +- search/PathEnd.cc | 8 +- search/PathEnum.cc | 4 +- search/PathGroup.cc | 12 +- search/Property.cc | 8 +- search/ReportPath.cc | 10 +- search/Search.cc | 27 ++-- search/Sta.cc | 34 ++-- search/WorstSlack.cc | 21 +-- 23 files changed, 379 insertions(+), 331 deletions(-) diff --git a/dcalc/GraphDelayCalc1.cc b/dcalc/GraphDelayCalc1.cc index 7f3ab44e..99d97673 100644 --- a/dcalc/GraphDelayCalc1.cc +++ b/dcalc/GraphDelayCalc1.cc @@ -1246,7 +1246,7 @@ GraphDelayCalc1::findArcDelay(LibertyCell *drvr_cell, // Merge slews. const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax()) + if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax(), this) && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)) graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { @@ -1446,7 +1446,7 @@ GraphDelayCalc1::annotateLoadDelays(Vertex *drvr_vertex, else { const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index); if (!merge - || delayGreater(load_slew, slew, slew_min_max)) + || delayGreater(load_slew, slew, slew_min_max, this)) graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); } } @@ -1459,7 +1459,7 @@ GraphDelayCalc1::annotateLoadDelays(Vertex *drvr_vertex, Delay wire_delay_extra = extra_delay + wire_delay; const MinMax *delay_min_max = dcalc_ap->delayMinMax(); if (!merge - || delayGreater(wire_delay_extra, delay, delay_min_max)) { + || delayGreater(wire_delay_extra, delay, delay_min_max, this)) { graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra); if (observer_) diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc index d4372543..9fda2611 100644 --- a/graph/DelayFloat.cc +++ b/graph/DelayFloat.cc @@ -94,7 +94,8 @@ delayEqual(const Delay &delay1, bool delayLess(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *) { return fuzzyLess(delay1, delay2); } @@ -102,7 +103,8 @@ delayLess(const Delay &delay1, bool delayLess(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyLess(delay1, delay2); @@ -112,7 +114,8 @@ delayLess(const Delay &delay1, bool delayLessEqual(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *) { return fuzzyLessEqual(delay1, delay2); } @@ -120,7 +123,8 @@ delayLessEqual(const Delay &delay1, bool delayLessEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyLessEqual(delay1, delay2); @@ -130,7 +134,8 @@ delayLessEqual(const Delay &delay1, bool delayGreater(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *) { return fuzzyGreater(delay1, delay2); } @@ -138,7 +143,8 @@ delayGreater(const Delay &delay1, bool delayGreater(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyGreater(delay1, delay2); @@ -148,7 +154,8 @@ delayGreater(const Delay &delay1, bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *) { return fuzzyGreaterEqual(delay1, delay2); } @@ -156,7 +163,8 @@ delayGreaterEqual(const Delay &delay1, bool delayGreaterEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyGreaterEqual(delay1, delay2); diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index fc70e885..3b2f5edb 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -24,8 +24,6 @@ #include "Fuzzy.hh" #include "Units.hh" #include "StaState.hh" -// temporary hack -#include "Sta.hh" // SSTA compilation. #if (SSTA == 1) @@ -185,151 +183,6 @@ makeDelay2(float delay, return Delay(delay, sigma2); } -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max) -{ - return fuzzyEqual(delay.mean(), min_max->initValue()) - && delay.sigma2() == 0.0; -} - -bool -delayZero(const Delay &delay) -{ - return fuzzyZero(delay.mean()) - && fuzzyZero(delay.sigma2()); -} - -bool -delayInf(const Delay &delay) -{ - return fuzzyInf(delay.mean()); -} - -bool -delayEqual(const Delay &delay1, - const Delay &delay2) -{ - return fuzzyEqual(delay1.mean(), delay2.mean()) - && fuzzyEqual(delay1.sigma2(), delay2.sigma2()); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); -} - -bool -delayLess(const Delay &delay1, - float delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) -{ - if (min_max == MinMax::max()) - return delayLess(delay1, delay2); - else - return delayGreater(delay1, delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); -} - -bool -delayLessEqual(const Delay &delay1, - float delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) -{ - if (min_max == MinMax::max()) - return delayLessEqual(delay1, delay2); - else - return delayGreaterEqual(delay1, delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -bool -delayGreater(const Delay &delay1, - float delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -bool -delayGreaterEqual(const Delay &delay1, - float delay2) -{ - Sta *sta = Sta::sta(); - return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) -{ - if (min_max == MinMax::max()) - return delayGreater(delay1, delay2); - else - return delayLess(delay1, delay2); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max) -{ - if (min_max == MinMax::max()) - return delayGreaterEqual(delay1, delay2); - else - return delayLessEqual(delay1, delay2); -} - float delayAsFloat(const Delay &delay, const EarlyLate *early_late, @@ -387,6 +240,156 @@ delayAsString(const Delay &delay, return sta->units()->timeUnit()->asString(mean_sigma, digits); } +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max) +{ + return fuzzyEqual(delay.mean(), min_max->initValue()) + && delay.sigma2() == 0.0; +} + +bool +delayZero(const Delay &delay) +{ + return fuzzyZero(delay.mean()) + && fuzzyZero(delay.sigma2()); +} + +bool +delayInf(const Delay &delay) +{ + return fuzzyInf(delay.mean()); +} + +bool +delayEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyEqual(delay1.mean(), delay2.mean()) + && fuzzyEqual(delay1.sigma2(), delay2.sigma2()); +} + +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +delayLess(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delay2); +} + +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return delayLess(delay1, delay2, sta); + else + return delayGreater(delay1, delay2, sta); +} + +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +delayLessEqual(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delay2); +} + +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return delayLessEqual(delay1, delay2, sta); + else + return delayGreaterEqual(delay1, delay2, sta); +} + +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) + +{ + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +bool +delayGreater(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delay2); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +bool +delayGreaterEqual(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delay2); +} + +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return delayGreater(delay1, delay2, sta); + else + return delayLess(delay1, delay2, sta); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return delayGreaterEqual(delay1, delay2, sta); + else + return delayLessEqual(delay1, delay2, sta); +} + Delay delayRemove(const Delay &delay1, const Delay &delay2) diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index 739676b8..883aa306 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -24,8 +24,6 @@ #include "Fuzzy.hh" #include "Units.hh" #include "StaState.hh" -// temporary hack -#include "Sta.hh" // SSTA compilation. #if (SSTA == 2) @@ -241,18 +239,18 @@ delayEqual(const Delay &delay1, bool delayLess(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), delayAsFloat(delay2, EarlyLate::early(), sta)); } bool delayLess(const Delay &delay1, - float delay2) + float delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), delay2); } @@ -260,28 +258,29 @@ delayLess(const Delay &delay1, bool delayLess(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) - return delayLess(delay1, delay2); + return delayLess(delay1, delay2, sta); else - return delayGreater(delay1, delay2); + return delayGreater(delay1, delay2, sta); } bool delayLessEqual(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), delayAsFloat(delay2, EarlyLate::early(), sta)); } bool delayLessEqual(const Delay &delay1, - float delay2) + float delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), delay2); } @@ -289,70 +288,73 @@ delayLessEqual(const Delay &delay1, bool delayLessEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) - return delayLessEqual(delay1, delay2); + return delayLessEqual(delay1, delay2, sta); else - return delayGreaterEqual(delay1, delay2); + return delayGreaterEqual(delay1, delay2, sta); } bool delayGreater(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreater(const Delay &delay1, - float delay2) + float delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2) + const Delay &delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreaterEqual(const Delay &delay1, - float delay2) + float delay2, + const StaState *sta) { - Sta *sta = Sta::sta(); - return delayGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), delay2); } bool delayGreater(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) - return delayGreater(delay1, delay2); + return delayGreater(delay1, delay2, sta); else - return delayLess(delay1, delay2); + return delayLess(delay1, delay2, sta); } bool delayGreaterEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max) + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) - return delayGreaterEqual(delay1, delay2); + return delayGreaterEqual(delay1, delay2, sta); else - return delayLessEqual(delay1, delay2); + return delayLessEqual(delay1, delay2, sta); } float diff --git a/include/sta/DelayFloat.hh b/include/sta/DelayFloat.hh index e895eae1..58987103 100644 --- a/include/sta/DelayFloat.hh +++ b/include/sta/DelayFloat.hh @@ -96,33 +96,42 @@ delayEqual(const Delay &delay1, const Delay &delay2); bool delayLess(const Delay &delay1, - const Delay &delay2); + const Delay &delay2, + const StaState *sta); bool delayLess(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2); + const MinMax *min_max, + const StaState *sta); bool delayLessEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); + const StaState *sta); bool -delayGreater(const Delay &delay1, - const Delay &delay2); +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayGreater(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2); + const StaState *sta); bool delayGreaterEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); + const StaState *sta); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +// delay1-delay2 subtracting sigma instead of addiing. Delay delayRemove(const Delay &delay1, const Delay &delay2); diff --git a/include/sta/DelayNormal1.hh b/include/sta/DelayNormal1.hh index 6a3bfb86..b25529c7 100644 --- a/include/sta/DelayNormal1.hh +++ b/include/sta/DelayNormal1.hh @@ -111,32 +111,41 @@ delayEqual(const Delay &delay1, const Delay &delay2); bool delayLess(const Delay &delay1, - const Delay &delay2); + const Delay &delay2, + const StaState *sta); bool delayLess(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2); + const MinMax *min_max, + const StaState *sta); bool delayLessEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); + const StaState *sta); bool -delayGreater(const Delay &delay1, - const Delay &delay2); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max); +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayGreater(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); + const StaState *sta); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); + // delay1-delay2 subtracting sigma instead of addiing. Delay delayRemove(const Delay &delay1, const Delay &delay2); diff --git a/include/sta/DelayNormal2.hh b/include/sta/DelayNormal2.hh index ab1c58c6..44f3d278 100644 --- a/include/sta/DelayNormal2.hh +++ b/include/sta/DelayNormal2.hh @@ -66,28 +66,6 @@ const Delay delay_zero(0.0); void initDelayConstants(); -Delay -makeDelay(float delay, - float sigma_early, - float sigma_late); - -Delay -makeDelay2(float delay, - // sigma^2 - float sigma_early, - float sigma_late); - -inline float -delayAsFloat(const Delay &delay) { return delay.mean(); } - -// mean late+/early- sigma -float -delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta); -float -delaySigma2(const Delay &delay, - const EarlyLate *early_late); const char * delayAsString(const Delay &delay, const StaState *sta); @@ -100,6 +78,32 @@ delayAsString(const Delay &delay, const EarlyLate *early_late, const StaState *sta, int digits); + +Delay +makeDelay(float delay, + float sigma_early, + float sigma_late); + +Delay +makeDelay2(float delay, + // sigma^2 + float sigma_early, + float sigma_late); + +inline float +delayAsFloat(const Delay &delay) +{ + return delay.mean(); +} + +// mean late+/early- sigma +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta); +float +delaySigma2(const Delay &delay, + const EarlyLate *early_late); const Delay & delayInitValue(const MinMax *min_max); bool @@ -114,32 +118,41 @@ delayEqual(const Delay &delay1, const Delay &delay2); bool delayLess(const Delay &delay1, - const Delay &delay2); + const Delay &delay2, + const StaState *sta); bool delayLess(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2); + const MinMax *min_max, + const StaState *sta); bool delayLessEqual(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); + const StaState *sta); bool -delayGreater(const Delay &delay1, - const Delay &delay2); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2); -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max); +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayGreater(const Delay &delay1, const Delay &delay2, - const MinMax *min_max); + const StaState *sta); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); + // delay1-delay2 subtracting sigma instead of addiing. Delay delayRemove(const Delay &delay1, const Delay &delay2); diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 09929e3c..18efcf20 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -752,7 +752,8 @@ public: const StaState *sta); void requiredSet(int arrival_index, Required required, - const MinMax *min_max); + const MinMax *min_max, + const StaState *sta); // Return true if the requireds changed. bool requiredsSave(Vertex *vertex, const StaState *sta); diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 34502129..e0f91d77 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -809,7 +809,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, delay = graph_->arcDelay(edge, arc, arc_delay_index) + *value; else if (graph_->arcDelayAnnotated(edge, arc, arc_delay_index)) { ArcDelay prev_value = graph_->arcDelay(edge, arc, arc_delay_index); - if (delayGreater(prev_value, delay, min_max)) + if (delayGreater(prev_value, delay, min_max, this)) delay = prev_value; } graph_->setArcDelay(edge, arc, arc_delay_index, delay); diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index a3081697..19583141 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -107,7 +107,7 @@ void MaxSkewViolatorsVisititor::visit(MaxSkewCheck &check, const StaState *sta) { - if (delayLess(check.slack(sta), 0.0)) + if (delayLess(check.slack(sta), 0.0, sta)) checks_.push_back(new MaxSkewCheck(check)); } @@ -280,7 +280,7 @@ MaxSkewSlackLess::operator()(const MaxSkewCheck *check1, { Slack slack1 = check1->slack(sta_); Slack slack2 = check2->slack(sta_); - return delayLess(slack1, slack2) + return delayLess(slack1, slack2, sta_) || (delayEqual(slack1, slack2) // Break ties based on constrained pin names. && sta_->network()->pinLess(check1->clkPin(sta_),check2->clkPin(sta_))); diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index 7e797c53..e4a47c70 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -79,7 +79,7 @@ void MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check, StaState *sta) { - if (delayLess(check.slack(sta), 0.0)) + if (delayLess(check.slack(sta), 0.0, sta)) checks_.push_back(check.copy()); } @@ -231,7 +231,7 @@ MinPeriodSlackLess::operator()(const MinPeriodCheck *check1, Slack slack2 = check2->slack(sta_); const Pin *pin1 = check1->pin(); const Pin *pin2 = check2->pin(); - return delayLess(slack1, slack2) + return delayLess(slack1, slack2, sta_) // Break ties based on pin and clock names. || (delayEqual(slack1, slack2) && (sta_->network()->pinLess(pin1, pin2) diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index 78d1dfa9..28fe6dec 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -167,7 +167,7 @@ void MinPulseWidthViolatorsVisitor::visit(MinPulseWidthCheck &check, const StaState *sta) { - if (delayLess(check.slack(sta), 0.0) + if (delayLess(check.slack(sta), 0.0, sta) && (corner_ == nullptr || check.corner(sta) == corner_)) { MinPulseWidthCheck *copy = new MinPulseWidthCheck(check.openPath()); @@ -499,7 +499,7 @@ MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck *check1, Slack slack2 = check2->slack(sta_); const Pin *pin1 = check1->pin(sta_); const Pin *pin2 = check2->pin(sta_); - return delayLess(slack1, slack2) + return delayLess(slack1, slack2, sta_) || (delayEqual(slack1, slack2) // Break ties for the sake of regression stability. && (sta_->network()->pinLess(pin1, pin2) diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc index d1d3388f..f8b0c5e0 100644 --- a/search/ClkInfo.cc +++ b/search/ClkInfo.cc @@ -294,9 +294,9 @@ clkInfoCmp(const ClkInfo *clk_info1, const Arrival &insert1 = clk_info1->insertion(); const Arrival &insert2 = clk_info2->insertion(); - if (delayLess(insert1, insert2)) + if (delayLess(insert1, insert2, sta)) return -1; - if (delayGreater(insert1, insert2)) + if (delayGreater(insert1, insert2, sta)) return 1; float latency1 = clk_info1->latency(); diff --git a/search/Genclks.cc b/search/Genclks.cc index 5abdd31a..9e89fd98 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -970,7 +970,8 @@ Genclks::recordSrcPaths(Clock *gclk) && (src_path.isNull() || delayGreater(path->arrival(this), src_path.arrival(this), - early_late))) { + early_late, + this))) { debugPrint4(debug_, "genclk", 2, " %s insertion %s %s %s\n", network_->pathName(gclk_pin), early_late->asString(), diff --git a/search/Latches.cc b/search/Latches.cc index 1a127567..687ecfec 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -98,7 +98,7 @@ Latches::latchRequired(const Path *data_path, network_->pathName(data_path->pin(this)), delayAsString(data_arrival, this), delayAsString(enable_arrival, this)); - if (delayLessEqual(data_arrival, enable_arrival)) { + if (delayLessEqual(data_arrival, enable_arrival, this)) { // Data arrives before latch opens. required = enable_arrival; borrow = 0.0; @@ -108,7 +108,7 @@ Latches::latchRequired(const Path *data_path, else { // Data arrives while latch is transparent. borrow = data_arrival - enable_arrival; - if (delayLessEqual(borrow, max_borrow)) + if (delayLessEqual(borrow, max_borrow, this)) required = data_arrival; else { borrow = max_borrow; @@ -332,7 +332,7 @@ Latches::latchOutArrival(Path *data_path, latchRequired(data_path, enable_path, &disable_path, path_ap, required, borrow, adjusted_data_arrival, time_given_to_startpoint); - if (delayGreater(borrow, 0.0)) { + if (delayGreater(borrow, 0.0, this)) { // Latch is transparent when data arrives. arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, false, path_ap); diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 18d11cc7..ff7a9b32 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -1244,7 +1244,7 @@ PathEndLatchCheck::targetClkWidth(const StaState *sta) const if (enable_clk_info->isPulseClk()) return disable_arrival - enable_arrival; else { - if (delayGreater(enable_arrival, disable_arrival)) { + if (delayGreater(enable_arrival, disable_arrival, sta)) { float period = enable_clk_info->clock()->period(); disable_arrival += period; } @@ -1999,14 +1999,14 @@ PathEnd::cmpSlack(const PathEnd *path_end1, // based on borrow time. if (delayEqual(borrow1, borrow2)) return 0; - else if (delayGreater(borrow1, borrow2)) + else if (delayGreater(borrow1, borrow2, sta)) return -1; else return 1; } else if (delayEqual(slack1, slack2)) return 0; - else if (delayLess(slack1, slack2)) + else if (delayLess(slack1, slack2, sta)) return -1; else return 1; @@ -2022,7 +2022,7 @@ PathEnd::cmpArrival(const PathEnd *path_end1, const MinMax *min_max = path_end1->minMax(sta); if (delayEqual(arrival1, arrival2)) return 0; - else if (delayLess(arrival1, arrival2, min_max)) + else if (delayLess(arrival1, arrival2, min_max, sta)) return -1; else return 1; diff --git a/search/PathEnum.cc b/search/PathEnum.cc index ce7abd00..7ff1f781 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -348,7 +348,7 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, // Make the diverted path end to check slack with from_path crpr. makeDivertedPathEnd(from_path, arc, div_end, after_div_copy); // Only enumerate paths with greater slack. - if (delayGreaterEqual(div_end->slack(sta_), path_end_slack_)) { + if (delayGreaterEqual(div_end->slack(sta_), path_end_slack_, sta_)) { reportDiversion(arc, from_path); path_enum_->makeDiversion(div_end, after_div_copy); } @@ -356,7 +356,7 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, delete div_end; } // Only enumerate slower/faster paths. - else if (delayLessEqual(to_arrival, before_div_arrival_, min_max)) { + else if (delayLessEqual(to_arrival, before_div_arrival_, min_max, sta_)) { PathEnd *div_end; PathEnumed *after_div_copy; makeDivertedPathEnd(from_path, arc, div_end, after_div_copy); diff --git a/search/PathGroup.cc b/search/PathGroup.cc index d2856083..0afe3ab2 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -101,19 +101,19 @@ PathGroup::savable(PathEnd *path_end) // without crpr first because it is expensive to find. Slack slack = path_end->slackNoCrpr(sta_); if (!delayIsInitValue(slack, min_max_) - && delayLessEqual(slack, threshold_) - && delayLessEqual(slack, slack_max_)) { + && delayLessEqual(slack, threshold_, sta_) + && delayLessEqual(slack, slack_max_, sta_)) { // Now check with crpr. slack = path_end->slack(sta_); - savable = delayLessEqual(slack, threshold_) - && delayLessEqual(slack, slack_max_) - && delayGreaterEqual(slack, slack_min_); + savable = delayLessEqual(slack, threshold_, sta_) + && delayLessEqual(slack, slack_max_, sta_) + && delayGreaterEqual(slack, slack_min_, sta_); } } else { const Arrival &arrival = path_end->dataArrivalTime(sta_); savable = !delayIsInitValue(arrival, min_max_) - && delayGreaterEqual(arrival, threshold_, min_max_); + && delayGreaterEqual(arrival, threshold_, min_max_, sta_); } return savable; } diff --git a/search/Property.cc b/search/Property.cc index c06c0fe7..f77e0889 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -801,12 +801,12 @@ pinSlewProperty(const Pin *pin, Slew slew = min_max->initValue(); if (vertex) { Slew vertex_slew = sta->vertexSlew(vertex, rf, min_max); - if (delayGreater(vertex_slew, slew, min_max)) + if (delayGreater(vertex_slew, slew, min_max, sta)) slew = vertex_slew; } if (bidirect_drvr_vertex) { Slew vertex_slew = sta->vertexSlew(bidirect_drvr_vertex, rf, min_max); - if (delayGreater(vertex_slew, slew, min_max)) + if (delayGreater(vertex_slew, slew, min_max, sta)) slew = vertex_slew; } return PropertyValue(delayPropertyValue(slew, sta)); @@ -879,9 +879,9 @@ edgeDelayProperty(Edge *edge, ArcDelay arc_delay = sta->arcDelay(edge, arc, dcalc_ap); if (!delay_exists || ((min_max == MinMax::max() - && delayGreater(arc_delay, delay)) + && delayGreater(arc_delay, delay, sta)) || (min_max == MinMax::min() - && delayLess(arc_delay, delay)))) + && delayLess(arc_delay, delay, sta)))) delay = arc_delay; } } diff --git a/search/ReportPath.cc b/search/ReportPath.cc index e5998078..2a9187d9 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -578,7 +578,7 @@ ReportPath::reportFull(const PathEndLatchCheck *end, else reportTgtClk(end, result); - if (delayGreaterEqual(borrow, 0.0)) + if (delayGreaterEqual(borrow, 0.0, this)) reportLine("time borrowed from endpoint", borrow, req_time, early_late, result); else @@ -659,7 +659,7 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, reportLineTotalMinus("CRPR difference", crpr_diff, early_late, result); reportLineTotal("max time borrow", max_borrow, early_late, result); } - if (delayGreater(borrow, delay_zero) + if (delayGreater(borrow, delay_zero, this) && (!fuzzyZero(open_uncertainty) || !delayZero(open_crpr))) { reportDashLineTotal(result); @@ -2050,7 +2050,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, else if (clk_used_as_data) { reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late, result); - if (delayGreater(clk_insertion, 0.0)) + if (delayGreater(clk_insertion, 0.0, this)) reportClkSrcLatency(clk_insertion, clk_time, early_late, result); if (reportClkPath()) reportPath1(path, expanded, true, time_offset, result); @@ -2067,7 +2067,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, } else { if (is_path_delay) { - if (delayGreater(clk_delay, 0.0)) + if (delayGreater(clk_delay, 0.0, this)) reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_end_time, early_late, result); } @@ -2613,7 +2613,7 @@ ReportPath::reportPath1(const Path *path, } Arrival time = latch_enable_time + latch_time_given; Arrival incr = latch_time_given; - if (delayGreaterEqual(incr, 0.0)) + if (delayGreaterEqual(incr, 0.0, this)) reportLine("time given to startpoint", incr, time, early_late, result); else reportLine("time borrowed from startpoint", incr, time, diff --git a/search/Search.cc b/search/Search.cc index 0808f18c..48c7afe1 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1238,7 +1238,7 @@ ArrivalVisitor::visitFromToPath(const Pin *, Tag *tag_match; tag_bldr_->tagMatchArrival(to_tag, tag_match, arrival, arrival_index); if (tag_match == nullptr - || delayGreater(to_arrival, arrival, min_max)) { + || delayGreater(to_arrival, arrival, min_max, sta_)) { debugPrint5(debug, "search", 3, " %s + %s = %s %s %s\n", delayAsString(from_path->arrival(sta_), sta_), delayAsString(arc_delay, sta_), @@ -1258,7 +1258,7 @@ ArrivalVisitor::visitFromToPath(const Pin *, tag_bldr_no_crpr_->tagMatchArrival(to_tag, tag_match, arrival, arrival_index); if (tag_match == nullptr - || delayGreater(to_arrival, arrival, min_max)) { + || delayGreater(to_arrival, arrival, min_max, sta_)) { tag_bldr_no_crpr_->setMatchArrival(to_tag, tag_match, to_arrival, arrival_index, &prev_path); @@ -1300,7 +1300,7 @@ ArrivalVisitor::pruneCrprArrivals() delayAsString(max_crpr, sta_), delayAsString(max_arrival_max_crpr, sta_)); Arrival arrival = tag_bldr_->arrival(arrival_index); - if (delayGreater(max_arrival_max_crpr, arrival, min_max)) { + if (delayGreater(max_arrival_max_crpr, arrival, min_max, sta_)) { debugPrint1(debug, "search", 3, " pruned %s\n", tag->asString(sta_)); tag_bldr_->deleteArrival(tag); @@ -3254,7 +3254,7 @@ FindEndRequiredVisitor::visit(PathEnd *path_end) bool arrival_exists; path.arrivalIndex(arrival_index, arrival_exists); Required required = path_end->requiredTime(sta_); - required_cmp_->requiredSet(arrival_index, required, req_min); + required_cmp_->requiredSet(arrival_index, required, req_min, sta_); } } @@ -3319,9 +3319,10 @@ RequiredCmp::requiredsInit(Vertex *vertex, void RequiredCmp::requiredSet(int arrival_index, Required required, - const MinMax *min_max) + const MinMax *min_max, + const StaState *sta) { - if (delayGreater(required, requireds_[arrival_index], min_max)) { + if (delayGreater(required, requireds_[arrival_index], min_max, sta)) { requireds_[arrival_index] = required; have_requireds_ = true; } @@ -3463,7 +3464,7 @@ RequiredVisitor::visitFromToPath(const Pin *, delayAsString(from_required, sta_), min_max == MinMax::max() ? "<" : ">", delayAsString(required_cmp_->required(arrival_index), sta_)); - required_cmp_->requiredSet(arrival_index, from_required, req_min); + required_cmp_->requiredSet(arrival_index, from_required, req_min, sta_); } else { if (sta_->search()->crprApproxMissingRequireds()) { @@ -3487,7 +3488,7 @@ RequiredVisitor::visitFromToPath(const Pin *, min_max == MinMax::max() ? "<" : ">", delayAsString(required_cmp_->required(arrival_index), sta_)); - required_cmp_->requiredSet(arrival_index, from_required, req_min); + required_cmp_->requiredSet(arrival_index, from_required, req_min, sta_); break; } } @@ -3640,7 +3641,7 @@ Search::totalNegativeSlack(const MinMax *min_max) for (Corner *corner : *corners_) { PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); Slack tns1 = tns_[path_ap_index]; - if (delayLess(tns1, tns)) + if (delayLess(tns1, tns, this)) tns = tns1; } return tns; @@ -3735,7 +3736,7 @@ Search::tnsIncr(Vertex *vertex, Slack slack, PathAPIndex path_ap_index) { - if (delayLess(slack, 0.0)) { + if (delayLess(slack, 0.0, this)) { debugPrint2(debug_, "tns", 3, "tns+ %s %s\n", delayAsString(slack, this), vertex->name(sdc_network_)); @@ -3754,7 +3755,7 @@ Search::tnsDecr(Vertex *vertex, bool found; tns_slacks_[path_ap_index].findKey(vertex, slack, found); if (found - && delayLess(slack, 0.0)) { + && delayLess(slack, 0.0, this)) { debugPrint2(debug_, "tns", 3, "tns- %s %s\n", delayAsString(slack, this), vertex->name(sdc_network_)); @@ -3880,7 +3881,7 @@ FindEndSlackVisitor::visit(PathEnd *path_end) PathRef &path = path_end->pathRef(); PathAPIndex path_ap_index = path.pathAnalysisPtIndex(sta_); Slack slack = path_end->slack(sta_); - if (delayLess(slack, slacks_[path_ap_index])) + if (delayLess(slack, slacks_[path_ap_index], sta_)) slacks_[path_ap_index] = slack; } } @@ -3907,7 +3908,7 @@ Search::wnsSlacks(Vertex *vertex, PathAPIndex path_ap_index = path->pathAnalysisPtIndex(this); const Slack path_slack = path->slack(this); if (!path->tag(this)->isFilter() - && delayLess(path_slack, slacks[path_ap_index])) + && delayLess(path_slack, slacks[path_ap_index], this)) slacks[path_ap_index] = path_slack; } } diff --git a/search/Sta.cc b/search/Sta.cc index e2680e73..cbd8f649 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2669,7 +2669,7 @@ Sta::vertexWorstArrivalPath(Vertex *vertex, PathVertex *path = path_iter.next(); Arrival arrival = path->arrival(this); if (!path->tag(this)->isGenClkSrcPath() - && delayGreater(arrival, worst_arrival, min_max)) { + && delayGreater(arrival, worst_arrival, min_max, this)) { worst_arrival = arrival; worst_path.init(path); } @@ -2689,7 +2689,7 @@ Sta::vertexWorstArrivalPath(Vertex *vertex, Arrival arrival = path->arrival(this); if (path->minMax(this) == min_max && !path->tag(this)->isGenClkSrcPath() - && delayGreater(arrival, worst_arrival, min_max)) { + && delayGreater(arrival, worst_arrival, min_max, this)) { worst_arrival = arrival; worst_path.init(path); } @@ -2709,7 +2709,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, PathVertex *path = path_iter.next(); Slack slack = path->slack(this); if (!path->tag(this)->isGenClkSrcPath() - && delayLess(slack, min_slack)) { + && delayLess(slack, min_slack, this)) { min_slack = slack; worst_path.init(path); } @@ -2730,7 +2730,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, if (path->minMax(this) == min_max && !path->tag(this)->isGenClkSrcPath()) { Slack slack = path->slack(this); - if (delayLess(slack, min_slack)) { + if (delayLess(slack, min_slack, this)) { min_slack = slack; worst_path.init(path); } @@ -2764,7 +2764,7 @@ Sta::vertexArrival(Vertex *vertex, if ((clk_edge == clk_edge_wildcard || clk_info->clkEdge() == clk_edge) && !clk_info->isGenClkSrcPath() - && delayGreater(path->arrival(this), arrival, min_max)) + && delayGreater(path->arrival(this), arrival, min_max, this)) arrival = path_arrival; } return arrival; @@ -2782,7 +2782,7 @@ Sta::vertexRequired(Vertex *vertex, const Path *path = path_iter.next(); if (path->minMax(this) == min_max) { const Required path_required = path->required(this); - if (delayGreater(path_required, required, req_min_max)) + if (delayGreater(path_required, required, req_min_max, this)) required = path_required; } } @@ -2812,7 +2812,7 @@ Sta::vertexRequired(Vertex *vertex, const Required path_required = path->required(this); if ((clk_edge == clk_edge_wildcard || path->clkEdge(search_) == clk_edge) - && delayGreater(path_required, required, min_max)) + && delayGreater(path_required, required, min_max, this)) required = path_required; } return required; @@ -2830,7 +2830,7 @@ Sta::netSlack(const Net *net, if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); Slack pin_slack = vertexSlack(vertex, min_max); - if (delayLess(pin_slack, slack)) + if (delayLess(pin_slack, slack, this)) slack = pin_slack; } } @@ -2849,7 +2849,7 @@ Sta::pinSlack(const Pin *pin, slack = vertexSlack(vertex, min_max); if (bidirect_drvr_vertex) { Slack slack1 = vertexSlack(bidirect_drvr_vertex, min_max); - if (delayLess(slack1, slack)) + if (delayLess(slack1, slack, this)) slack = slack1; } return slack; @@ -2868,7 +2868,7 @@ Sta::pinSlack(const Pin *pin, slack = vertexSlack(vertex, rf, min_max); if (bidirect_drvr_vertex) { Slack slack1 = vertexSlack(bidirect_drvr_vertex, rf, min_max); - if (delayLess(slack1, slack)) + if (delayLess(slack1, slack, this)) slack = slack1; } return slack; @@ -2886,7 +2886,7 @@ Sta::vertexSlack(Vertex *vertex, Path *path = path_iter.next(); if (path->minMax(this) == min_max) { Slack path_slack = path->slack(this); - if (delayLess(path_slack, slack)) + if (delayLess(path_slack, slack, this)) slack = path_slack; } } @@ -2904,7 +2904,7 @@ Sta::vertexSlack(Vertex *vertex, while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack path_slack = path->slack(this); - if (delayLess(path_slack, slack)) + if (delayLess(path_slack, slack, this)) slack = path_slack; } return slack; @@ -2943,7 +2943,7 @@ Sta::vertexSlack1(Vertex *vertex, Slack path_slack = path->slack(this); if ((clk_edge == clk_edge_wildcard || path->clkEdge(search_) == clk_edge) - && delayLess(path_slack, slack)) + && delayLess(path_slack, slack, this)) slack = path_slack; } return slack; @@ -2965,7 +2965,7 @@ Sta::vertexSlacks(Vertex *vertex, Slack path_slack = path->slack(this); int rf_index = path->rfIndex(this); int mm_index = path->minMax(this)->index(); - if (delayLess(path_slack, slacks[rf_index][mm_index])) + if (delayLess(path_slack, slacks[rf_index][mm_index], this)) slacks[rf_index][mm_index] = path_slack; } } @@ -3160,7 +3160,7 @@ Sta::vertexSlew(Vertex *vertex, Slew mm_slew = min_max->initValue(); for (DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); - if (delayGreater(slew, mm_slew, min_max)) + if (delayGreater(slew, mm_slew, min_max, this)) mm_slew = slew; } return mm_slew; @@ -4782,7 +4782,7 @@ bool InstanceMaxSlewGreater::operator()(const Instance *inst1, const Instance *inst2) const { - return delayGreater(instMaxSlew(inst1), instMaxSlew(inst2)); + return delayGreater(instMaxSlew(inst1), instMaxSlew(inst2), sta_); } Slew @@ -4799,7 +4799,7 @@ InstanceMaxSlewGreater::instMaxSlew(const Instance *inst) const for (RiseFall *rf : RiseFall::range()) { for (DcalcAnalysisPt *dcalc_ap : sta_->corners()->dcalcAnalysisPts()) { Slew slew = graph->slew(vertex, rf, dcalc_ap->index()); - if (delayGreater(slew, max_slew)) + if (delayGreater(slew, max_slew, sta_)) max_slew = slew; } } diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index 704a6a14..f9c0893a 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -48,7 +48,7 @@ WorstSlacks::worstSlack(const MinMax *min_max, Vertex *worst_vertex1; worst_slacks_[path_ap_index].worstSlack(path_ap_index, sta_, worst_slack1, worst_vertex1); - if (delayLess(worst_slack1, worst_slack)) { + if (delayLess(worst_slack1, worst_slack, sta_)) { worst_slack = worst_slack1; worst_vertex = worst_vertex1; } @@ -160,9 +160,9 @@ WorstSlack::initQueue(PathAPIndex path_ap_index, Vertex *vertex = end_iter.next(); Slack slack = search->wnsSlack(vertex, path_ap_index); if (!delayEqual(slack, slack_init_)) { - if (delayLess(slack, worst_slack_)) + if (delayLess(slack, worst_slack_, sta)) setWorstSlack(vertex, slack, sta); - if (delayLessEqual(slack, slack_threshold_)) + if (delayLessEqual(slack, slack_threshold_, sta)) queue_.insert(vertex); int queue_size = queue_.size(); if (queue_size >= max_queue_size_) @@ -206,7 +206,7 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index, while (queue_iter2.hasNext()) { Vertex *vertex = queue_iter2.next(); Slack slack = search->wnsSlack(vertex, path_ap_index); - if (delayGreater(slack, slack_threshold_)) + if (delayGreater(slack, slack_threshold_, sta)) break; queue_.insert(vertex); } @@ -231,7 +231,7 @@ WorstSlack::findWorstInQueue(PathAPIndex path_ap_index, while (queue_iter.hasNext()) { Vertex *vertex = queue_iter.next(); Slack slack = search->wnsSlack(vertex, path_ap_index); - if (delayLess(slack, worst_slack_)) + if (delayLess(slack, worst_slack_, sta)) setWorstSlack(vertex, slack, sta); } } @@ -249,7 +249,7 @@ WorstSlack::checkQueue(PathAPIndex path_ap_index, while (end_iter.hasNext()) { Vertex *end = end_iter.next(); if (delayLessEqual(search->wnsSlack(end, path_ap_index), - slack_threshold_)) + slack_threshold_, sta)) ends.push_back(end); } WnsSlackLess slack_less(path_ap_index, sta); @@ -262,7 +262,7 @@ WorstSlack::checkQueue(PathAPIndex path_ap_index, end_set.insert(end); if (!queue_.hasKey(end) && delayLessEqual(search->wnsSlack(end, path_ap_index), - slack_threshold_)) + slack_threshold_, sta)) report->print("WorstSlack queue missing %s %s < %s\n", end->name(network), delayAsString(search->wnsSlack(end, path_ap_index), sta), @@ -294,14 +294,14 @@ WorstSlack::updateWorstSlack(Vertex *vertex, // threads. UniqueLock lock(lock_); if (worst_vertex_ - && delayLess(slack, worst_slack_)) + && delayLess(slack, worst_slack_, sta)) setWorstSlack(vertex, slack, sta); else if (vertex == worst_vertex_) // Mark worst slack as unknown (updated by findWorstSlack(). worst_vertex_ = nullptr; if (!delayEqual(slack, slack_init_) - && delayLessEqual(slack, slack_threshold_)) { + && delayLessEqual(slack, slack_threshold_, sta)) { debugPrint2(debug, "wns", 3, "insert %s %s\n", vertex->name(network), delayAsString(slack, sta)); @@ -342,7 +342,8 @@ WnsSlackLess::operator()(Vertex *vertex1, Vertex *vertex2) { return delayLess(search_->wnsSlack(vertex1, path_ap_index_), - search_->wnsSlack(vertex2, path_ap_index_)); + search_->wnsSlack(vertex2, path_ap_index_), + search_); } } // namespace