From d6e7b4256c202023e892feb38a06bf8cc554d69e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 13 Mar 2026 14:06:35 -0700 Subject: [PATCH 01/14] lvf squish Signed-off-by: James Cherry --- CMakeLists.txt | 19 +- dcalc/ArcDcalcWaveforms.cc | 3 +- dcalc/ArcDelayCalc.cc | 12 +- dcalc/ArnoldiDelayCalc.cc | 33 +- dcalc/CcsCeffDelayCalc.cc | 36 +- dcalc/CcsCeffDelayCalc.hh | 20 +- dcalc/Delay.cc | 515 ++ dcalc/DelayCalc.tcl | 25 +- dcalc/DelayCalcBase.cc | 27 +- dcalc/DelayCalcBase.hh | 10 +- dcalc/DelayNormal.cc | 231 + dcalc/DelayScalar.cc | 206 + dcalc/DelaySkewNormal.cc | 292 + dcalc/DmpCeff.cc | 87 +- dcalc/DmpCeff.hh | 8 +- dcalc/DmpDelayCalc.cc | 40 +- dcalc/GraphDelayCalc.cc | 38 +- dcalc/LumpedCapDelayCalc.cc | 34 +- dcalc/LumpedCapDelayCalc.hh | 4 +- dcalc/ParallelDelayCalc.cc | 20 +- dcalc/PrimaDelayCalc.cc | 15 +- doc/ApiChanges.txt | 2 + doc/ChangeLog.txt | 128 + doc/OpenSTA.fodt | 7081 +++++++++++---------- doc/OpenSTA.pdf | Bin 1403976 -> 1419896 bytes doc/StaApi.txt | 22 - graph/DelayFloat.cc | 199 - graph/DelayNormal1.cc | 483 -- graph/DelayNormal2.cc | 517 -- graph/Graph.cc | 126 +- graph/Graph.i | 41 +- graph/Graph.tcl | 54 +- include/sta/ArcDelayCalc.hh | 12 +- include/sta/Delay.hh | 325 +- include/sta/DelayFloat.hh | 152 - include/sta/DelayNormal.hh | 90 + include/sta/DelayNormal1.hh | 203 - include/sta/DelayNormal2.hh | 214 - include/sta/DelayScalar.hh | 90 + include/sta/DelaySkewNormal.hh | 98 + include/sta/Graph.hh | 34 +- include/sta/GraphDelayCalc.hh | 4 +- include/sta/InternalPower.hh | 70 +- include/sta/Liberty.hh | 2 +- include/sta/LinearModel.hh | 22 +- include/sta/Path.hh | 14 +- include/sta/PathEnd.hh | 2 +- graph/Delay.cc => include/sta/PocvMode.hh | 16 +- include/sta/Sta.hh | 26 +- include/sta/StaState.hh | 5 +- include/sta/TableModel.hh | 100 +- include/sta/TimingModel.hh | 24 +- include/sta/Variables.hh | 12 +- liberty/InternalPower.cc | 30 +- liberty/Liberty.cc | 2 +- liberty/LibertyReader.cc | 219 +- liberty/LibertyReaderPvt.hh | 34 +- liberty/LibertyWriter.cc | 5 +- liberty/LinearModel.cc | 26 +- liberty/TableModel.cc | 556 +- liberty/TimingArc.cc | 5 +- power/Power.cc | 15 +- sdc/Variables.cc | 25 +- sdf/SdfReader.cc | 4 +- sdf/SdfWriter.cc | 75 +- search/CheckMaxSkews.cc | 8 +- search/CheckMaxSkews.hh | 2 +- search/CheckMinPeriods.cc | 2 +- search/CheckMinPulseWidths.cc | 19 +- search/ClkDelays.hh | 4 +- search/ClkInfo.cc | 7 +- search/ClkLatency.cc | 39 +- search/ClkSkew.cc | 76 +- search/ClkSkew.hh | 8 +- search/Crpr.cc | 22 +- search/Latches.cc | 49 +- search/MakeTimingModel.cc | 48 +- search/Path.cc | 16 +- search/PathEnd.cc | 174 +- search/PathEnum.cc | 16 +- search/PocvMode.cc | 48 + search/Property.cc | 2 +- search/ReportPath.cc | 315 +- search/ReportPath.hh | 54 +- search/Search.cc | 71 +- search/Search.i | 179 +- search/Search.tcl | 46 +- search/Sta.cc | 105 +- search/StaState.cc | 4 +- search/WorstSlack.cc | 10 +- spice/WriteSpice.cc | 2 +- tcl/StaTclTypes.i | 28 +- tcl/TclTypeHelpers.cc | 2 +- tcl/Variables.tcl | 40 +- util/StaConfig.hh.cmake | 2 - verilog/VerilogReader.cc | 2 +- 96 files changed, 7580 insertions(+), 6659 deletions(-) create mode 100644 dcalc/Delay.cc create mode 100644 dcalc/DelayNormal.cc create mode 100644 dcalc/DelayScalar.cc create mode 100644 dcalc/DelaySkewNormal.cc delete mode 100644 graph/DelayFloat.cc delete mode 100644 graph/DelayNormal1.cc delete mode 100644 graph/DelayNormal2.cc delete mode 100644 include/sta/DelayFloat.hh create mode 100644 include/sta/DelayNormal.hh delete mode 100644 include/sta/DelayNormal1.hh delete mode 100644 include/sta/DelayNormal2.hh create mode 100644 include/sta/DelayScalar.hh create mode 100644 include/sta/DelaySkewNormal.hh rename graph/Delay.cc => include/sta/PocvMode.hh (81%) create mode 100644 search/PocvMode.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 99a94864..055927fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14) cmake_policy(SET CMP0086 NEW) endif() -project(STA VERSION 3.0.0 +project(STA VERSION 3.0.1 LANGUAGES CXX ) @@ -82,13 +82,17 @@ endif() set(STA_SOURCE app/StaMain.cc - dcalc/ArcDelayCalc.cc dcalc/ArcDcalcWaveforms.cc + dcalc/ArcDelayCalc.cc dcalc/ArnoldiDelayCalc.cc dcalc/ArnoldiReduce.cc dcalc/CcsCeffDelayCalc.cc + dcalc/Delay.cc dcalc/DelayCalc.cc dcalc/DelayCalcBase.cc + dcalc/DelayNormal.cc + dcalc/DelayScalar.cc + dcalc/DelaySkewNormal.cc dcalc/DmpCeff.cc dcalc/DmpDelayCalc.cc dcalc/FindRoot.cc @@ -99,9 +103,6 @@ set(STA_SOURCE dcalc/PrimaDelayCalc.cc dcalc/UnitDelayCalc.cc - graph/DelayFloat.cc - graph/DelayNormal1.cc - graph/DelayNormal2.cc graph/Graph.cc graph/GraphCmp.cc @@ -199,6 +200,7 @@ set(STA_SOURCE search/PathEnum.cc search/PathExpanded.cc search/PathGroup.cc + search/PocvMode.cc search/Property.cc search/ReportPath.cc search/Search.cc @@ -403,11 +405,6 @@ find_package(Eigen3 REQUIRED) include(cmake/FindCUDD.cmake) -if("${SSTA}" STREQUAL "") - set(SSTA 0) -endif() -message(STATUS "SSTA: ${SSTA}") - # configure a header file to pass some of the CMake settings configure_file(${STA_HOME}/util/StaConfig.hh.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/sta/StaConfig.hh @@ -554,7 +551,7 @@ endif() # common to gcc/clang set(CXX_FLAGS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls - -Wformat-security -Werror=misleading-indentation) + -Wformat-security -Werror=misleading-indentation -Wundef) if(ENABLE_TSAN) message(STATUS "Thread sanitizer: ${ENABLE_TSAN}") diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 4c0fe95b..6c1e7560 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -61,7 +61,8 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, library->supplyVoltage("VDD", vdd, vdd_exists); if (!vdd_exists) report->error(1751, "VDD not defined in library %s", library->name()); - Waveform in_waveform = driver_waveform->waveform(delayAsFloat(in_slew)); + float slew1 = delayAsFloat(in_slew, min_max, sta); + Waveform in_waveform = driver_waveform->waveform(slew1); // Delay time axis. FloatSeq time_values; for (float time : in_waveform.axis1()->values()) diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index a17ab9a9..54143b28 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -257,18 +257,18 @@ ArcDcalcResult::ArcDcalcResult(size_t load_count) : } void -ArcDcalcResult::setGateDelay(ArcDelay gate_delay) +ArcDcalcResult::setGateDelay(const ArcDelay &gate_delay) { gate_delay_ = gate_delay; } void -ArcDcalcResult::setDrvrSlew(Slew drvr_slew) +ArcDcalcResult::setDrvrSlew(const Slew &drvr_slew) { drvr_slew_ = drvr_slew; } -ArcDelay +const ArcDelay & ArcDcalcResult::wireDelay(size_t load_idx) const { return wire_delays_[load_idx]; @@ -276,7 +276,7 @@ ArcDcalcResult::wireDelay(size_t load_idx) const void ArcDcalcResult::setWireDelay(size_t load_idx, - ArcDelay wire_delay) + const ArcDelay &wire_delay) { wire_delays_[load_idx] = wire_delay; } @@ -288,7 +288,7 @@ ArcDcalcResult::setLoadCount(size_t load_count) load_slews_.resize(load_count); } -Slew +const Slew & ArcDcalcResult::loadSlew(size_t load_idx) const { return load_slews_[load_idx]; @@ -296,7 +296,7 @@ ArcDcalcResult::loadSlew(size_t load_idx) const void ArcDcalcResult::setLoadSlew(size_t load_idx, - Slew load_slew) + const Slew &load_slew) { load_slews_[load_idx] = load_slew; } diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 970c91f1..2b44188e 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -237,7 +237,6 @@ private: ArnoldiReduce *reduce_; delay_work *delay_work_; std::vector unsaved_parasitics_; - bool pocv_enabled_; }; ArcDelayCalc * @@ -397,7 +396,6 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin, ConcreteParasitic *cparasitic = reinterpret_cast(const_cast(parasitic)); rcmodel_ = dynamic_cast(cparasitic); - pocv_enabled_ = variables_->pocvEnabled(); GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && rcmodel_) { const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); @@ -453,8 +451,8 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, auto load_idx_itr = load_pin_index_map.find(load_pin); if (load_idx_itr != load_pin_index_map.end()) { size_t load_idx = load_idx_itr->second; - ArcDelay wire_delay = _delayV[i] - _delayV[0]; - Slew load_slew = _slewV[i]; + double wire_delay = _delayV[i] - _delayV[0]; + double load_slew = _slewV[i]; thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); dcalc_result.setWireDelay(load_idx, wire_delay); dcalc_result.setLoadSlew(load_idx, load_slew); @@ -1325,9 +1323,8 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D, float c1; double tlohi,r; c1 = ctot; - ArcDelay d1; - Slew s1; - tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1); + float d1, s1; + tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1); tlohi = slew_derate*delayAsFloat(s1); r = tlohi/(c_log*c1); if (rdelay>0.0 && r > rdelay) @@ -1346,9 +1343,8 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D, double c_log = con->vlg; double c_smin = con->smin; double tlohi,smin,s; - ArcDelay d1; - Slew s1; - tab->table->gateDelay(tab->pvt, tab->in_slew, c, pocv_enabled_, d1, s1); + float d1, s1; + tab->table->gateDelay(tab->pvt, tab->in_slew, c, d1, s1); tlohi = slew_derate*delayAsFloat(s1); smin = r*c*c_smin; // c_smin = ra_hinv((1-vhi)/vhi-log(vhi)) + log(vhi); if (c_log*r*c >= tlohi) { @@ -1378,10 +1374,9 @@ ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab, float c2 = 0.5*c1; if (c1==c2) return 0.0; - ArcDelay d1, d2; - Slew s1, s2; - tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1); - tab->table->gateDelay(tab->pvt, tab->in_slew, c2, pocv_enabled_, d2, s2); + float d1, d2, s1, s2; + tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1); + tab->table->gateDelay(tab->pvt, tab->in_slew, c2, d2, s2); double dt50 = delayAsFloat(d1)-delayAsFloat(d2); if (dt50 <= 0.0) return 0.0; @@ -1402,8 +1397,7 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, double vlo = con->vlo; double ctot = mod->ctot; double ceff,tlohi,t50_sy,r,s,t50_sr,rdelay; - ArcDelay df; - Slew sf; + float df, sf; debugPrint(debug_, "arnoldi", 1, "ctot=%s", units_->capacitanceUnit()->asString(ctot)); @@ -1432,7 +1426,7 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, units_->timeUnit()->asString(s)); thix = ra_solve_for_t(p,s,vhi); tlox = ra_solve_for_t(p,s,vlo); - tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, pocv_enabled_, df, sf); + tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, df, sf); debugPrint(debug_, "arnoldi", 1, "table slew (in_slew %s ctot %s) = %s", units_->timeUnit()->asString(tab->in_slew), units_->capacitanceUnit()->asString(ctot), @@ -1443,8 +1437,7 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, units_->timeUnit()->asString(tlox-thix)); } ceff = ctot; - tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_, - df, sf); + tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, df, sf); t50_sy = delayAsFloat(df); t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5); @@ -1485,7 +1478,7 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, units_->timeUnit()->asString(ceff_time), units_->capacitanceUnit()->asString(ceff)); - tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_, df, sf); + tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, df, sf); t50_sy = delayAsFloat(df); t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5); for (j=0;jn;j++) { diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 95156145..31762fd0 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -113,14 +113,12 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; drvr_cell->ensureVoltageWaveforms(scenes_); - in_slew_ = delayAsFloat(in_slew); output_waveforms_ = output_waveforms; ref_time_ = output_waveforms_->referenceTime(in_slew_); debugPrint(debug_, "ccs_dcalc", 1, "%s %s", drvr_cell->name(), drvr_rf_->shortName()); - ArcDelay gate_delay; - Slew drvr_slew; + double gate_delay, drvr_slew; gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew); return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map); } @@ -133,8 +131,8 @@ void CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, const RiseFall *rf, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) + double &gate_delay, + double &drvr_slew) { initRegions(drvr_library, rf); findCsmWaveform(); @@ -145,7 +143,7 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, "gate_delay %s drvr_slew %s (initial)", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); - float prev_drvr_slew = delayAsFloat(drvr_slew); + float prev_drvr_slew = drvr_slew; constexpr int max_iterations = 5; for (int iter = 0; iter < max_iterations; iter++) { debugPrint(debug_, "ccs_dcalc", 2, "iteration %d", iter); @@ -185,9 +183,9 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, "gate_delay %s drvr_slew %s", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); - if (std::abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew) + if (std::abs(drvr_slew - prev_drvr_slew) < .01 * prev_drvr_slew) break; - prev_drvr_slew = delayAsFloat(drvr_slew); + prev_drvr_slew = drvr_slew; } } @@ -309,8 +307,8 @@ CcsCeffDelayCalc::findCsmWaveform() ArcDcalcResult CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay &gate_delay, - Slew &drvr_slew, + double &gate_delay, + double &drvr_slew, const LoadPinIndexMap &load_pin_index_map) { ArcDcalcResult dcalc_result(load_pin_index_map.size()); @@ -322,8 +320,7 @@ CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library, dcalc_result.setDrvrSlew(drvr_slew); for (const auto &[load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay; - Slew load_slew; + double wire_delay, load_slew; loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew); dcalc_result.setWireDelay(load_idx, wire_delay); dcalc_result.setLoadSlew(load_idx, load_slew); @@ -335,13 +332,14 @@ void CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - Slew &drvr_slew, + double &drvr_slew, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { wire_delay = 0.0; load_slew = drvr_slew; + bool elmore_exists = false; float elmore = 0.0; if (parasitic_ @@ -351,7 +349,7 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, if (elmore_exists && (elmore == 0.0 // Elmore delay is small compared to driver slew. - || elmore < delayAsFloat(drvr_slew) * 1e-3)) { + || elmore < drvr_slew * 1e-3)) { wire_delay = elmore; load_slew = drvr_slew; } @@ -363,11 +361,11 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, void CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, - Slew &drvr_slew, + double &drvr_slew, float elmore, // Return values. - ArcDelay &delay, - Slew &slew) + double &delay, + double &slew) { for (size_t i = 0; i <= region_count_; i++) { region_ramp_times_[i] = region_times_[i]; diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 83a72911..53a103bb 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -73,29 +73,29 @@ protected: void gateDelaySlew(const LibertyLibrary *drvr_library, const RiseFall *rf, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew); + double &gate_delay, + double &drvr_slew); void initRegions(const LibertyLibrary *drvr_library, const RiseFall *rf); void findCsmWaveform(); ArcDcalcResult makeResult(const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay &gate_delay, - Slew &drvr_slew, + double &gate_delay, + double &drvr_slew, const LoadPinIndexMap &load_pin_index_map); void loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - Slew &drvr_slew, + double &drvr_slew, // Return values. - ArcDelay &wire_delay, - Slew &load_slew); + double &wire_delay, + double &load_slew); void loadDelaySlew(const Pin *load_pin, - Slew &drvr_slew, + double &drvr_slew, float elmore, // Return values. - ArcDelay &delay, - Slew &slew); + double &delay, + double &slew); double findVlTime(double v, double elmore); bool makeWaveformPreamble(const Pin *in_pin, diff --git a/dcalc/Delay.cc b/dcalc/Delay.cc new file mode 100644 index 00000000..7154e3d8 --- /dev/null +++ b/dcalc/Delay.cc @@ -0,0 +1,515 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "Delay.hh" + +#include + +#include "StaConfig.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "StaState.hh" +#include "Variables.hh" + +namespace sta { + +static Delay delay_init_values[MinMax::index_count]; + +void +initDelayConstants() +{ + delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); + delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); +} + +Delay::Delay() : + values_{0.0, 0.0, 0.0, 0.0} +{ +} + +Delay::Delay(float mean) : + values_{mean, 0.0, 0.0, 0.0} +{ +} + +Delay::Delay(float mean, + float std_dev2) : + values_{mean, 0.0, std_dev2, 0.0} +{ +} + +Delay::Delay(float mean, + float mean_shift, + float std_dev2, + float skewness) : + values_{mean, mean_shift, std_dev2, skewness} +{ +} + +void +Delay::operator=(float delay) +{ + values_[0] = delay; + values_[1] = 0.0; + values_[2] = 0.0; + values_[3] = 0.0; +} + +void +Delay::setValues(float mean, + float mean_shift, + float std_dev2, + float skewnes) +{ + values_[0] = mean; + values_[1] = mean_shift; + values_[2] = std_dev2; + values_[3] = skewnes; +} + +void +Delay::setMean(float mean) +{ + values_[0] = mean; +} + +void +Delay::setMeanShift(float mean_shift) +{ + values_[1] = mean_shift; +} + +float +Delay::stdDev() const +{ + float std_dev2 = values_[2]; + if (std_dev2 < 0.0) + // std_dev is negative for crpr to offset std_dev in the common + // clock path. + return -std::sqrt(-std_dev2); + else + return std::sqrt(std_dev2); +} + +void +Delay::setStdDev(float std_dev) +{ + values_[2] = square(std_dev); +} + +void +Delay::setSkewness(float skewness) +{ + values_[3] = skewness; +} + +//////////////////////////////////////////////////////////////// + +DelayDbl::DelayDbl() : + values_{0.0, 0.0, 0.0, 0.0} +{ +} + +DelayDbl::DelayDbl(double mean) : + values_{mean, 0.0, 0.0, 0.0} +{ +} + +void +DelayDbl::setMean(double mean) +{ + values_[0] = mean; +} + +double +DelayDbl::stdDev() const +{ + float std_dev2 = values_[2]; + if (std_dev2 < 0.0) + // std_dev is negative for crpr to offset std_dev in the common + // clock path. + return -std::sqrt(-std_dev2); + else + return std::sqrt(std_dev2); +} + +void +DelayDbl::setValues(double mean, + double mean_shift, + double std_dev2, + double skewnes) +{ + values_[0] = mean; + values_[1] = mean_shift; + values_[2] = std_dev2; + values_[3] = skewnes; +} + +void +DelayDbl::operator=(double delay) +{ + values_[0] = delay; + values_[1] = 0.0; + values_[2] = 0.0; + values_[3] = 0.0; +} + +//////////////////////////////////////////////////////////////// + +Delay +makeDelay(float mean, + float mean_shift, + float std_dev, + float skewness) +{ + return Delay(mean, mean_shift, square(std_dev), skewness); +} + +Delay +makeDelay(float mean, + float std_dev) +{ + return Delay(mean, 0.0, square(std_dev), 0.0); +} + +Delay +makeDelay2(float mean, + float std_dev) +{ + return Delay(mean, 0.0, std_dev, 0.0); +} + +void +delaySetMean(Delay &delay, + float mean) +{ + delay.setMean(mean); +} + +//////////////////////////////////////////////////////////////// + +Delay +delayDblAsDelay(DelayDbl &delay) +{ + return Delay(delay.mean(), delay.meanShift(), delay.stdDev2(), delay.skewness()); +} + +const char * +delayAsString(const Delay &delay, + const StaState *sta) +{ + return delayAsString(delay, EarlyLate::late(), + sta->units()->timeUnit()->digits(), sta); +} + +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return delayAsString(delay, early_late, sta->units()->timeUnit()->digits(), sta); +} + +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + int digits, + const StaState *sta) +{ + const Unit *unit = sta->units()->timeUnit(); + float mean_std_dev = delayAsFloat(delay, early_late, sta); + return unit->asString(mean_std_dev, digits); +} + +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + bool report_variance, + int digits, + const StaState *sta) +{ + if (report_variance) + return sta->delayOps()->asStringVariance(delay, digits, sta); + else + return delayAsString(delay, early_late, digits, sta); +} + +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return sta->delayOps()->asFloat(delay, early_late, sta); +} + +float +delayAsFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return sta->delayOps()->asFloat(delay, early_late, sta); +} + +float +delayAsFloat(const Delay &delay) +{ + return delay.mean(); +} + +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.mean(), min_max->initValue()); +} + +bool +delayZero(const Delay &delay, + const StaState *sta) +{ + return sta->delayOps()->isZero(delay); +} + +bool +delayInf(const Delay &delay, + const StaState *sta) +{ + return sta->delayOps()->isInf(delay); +} + +bool +delayEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->equal(delay1, delay2, sta); +} + +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->less(delay1, delay2, sta); +} + +bool +delayLess(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) +{ + return sta->delayOps()->less(delay1, delay2, sta); +} + +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->less(delay1, delay2, sta); + else + return sta->delayOps()->greater(delay1, delay2, sta); +} + +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->lessEqual(delay1, delay2, sta); +} + +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->lessEqual(delay1, delay2, sta); + else + return sta->delayOps()->greaterEqual(delay1, delay2, sta); +} + +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->greater(delay1, delay2, sta); +} + +bool +delayGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->greater(delay1, delay2, sta); + else + return sta->delayOps()->less(delay1, delay2, sta); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->greaterEqual(delay1, delay2, sta); +} + +bool +delayGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) +{ + if (min_max == MinMax::max()) + return sta->delayOps()->greaterEqual(delay1, delay2, sta); + else + return sta->delayOps()->lessEqual(delay1, delay2, sta); +} + +Delay +delayRemove(const Delay &delay1, + const Delay &delay2) +{ + return makeDelay2(delay1.mean() - delay2.mean(), + delay1.stdDev2() - delay2.stdDev2()); +} + +Delay +delaySum(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->sum(delay1, delay2); +} + +Delay +delaySum(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return sta->delayOps()->sum(delay1, delay2); +} + +Delay +delayDiff(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->diff(delay1, delay2); +} + +Delay +delayDiff(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return sta->delayOps()->diff(delay1, delay2); +} + +Delay +delayDiff(float delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->diff(delay1, delay2); +} + +void +delayIncr(Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->incr(delay1, delay2); +} + +void +delayIncr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->incr(delay1, delay2); +} + +void +delayIncr(Delay &delay1, + float delay2, + const StaState *sta) +{ + sta->delayOps()->incr(delay1, delay2); +} + +void +delayDecr(Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->decr(delay1, delay2); +} + +void +delayDecr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta) +{ + sta->delayOps()->decr(delay1, delay2); +} + +Delay +delayProduct(const Delay &delay1, + float delay2, + const StaState *sta) +{ + return sta->delayOps()->product(delay1, delay2); +} + +Delay +delayDiv(float delay1, + const Delay &delay2, + const StaState *sta) +{ + return sta->delayOps()->div(delay1, delay2); +} + +float +delayStdDev2(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) +{ + return sta->delayOps()->stdDev2(delay, early_late); +} + +} // namespace diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl index 967039aa..7d15b5a9 100644 --- a/dcalc/DelayCalc.tcl +++ b/dcalc/DelayCalc.tcl @@ -135,8 +135,6 @@ proc set_delay_calculator { alg } { } } -define_cmd_args "set_pocv_sigma_factor" { factor } - ################################################################ define_cmd_args "set_assigned_delay" \ @@ -382,22 +380,31 @@ proc set_assigned_transition { args } { ################################################################ -define_cmd_args "report_slews" {[-scenes scenes] pin} +define_cmd_args "report_slews" {[-scenes scenes] [-digits digits]\ + [-report_variance] pin} proc report_slews { args } { global sta_report_default_digits - parse_key_args "report_slews" args keys {-corner -scenes} flags {} + parse_key_args "report_slews" args keys {-corner -scenes -digits} \ + flags {-report_variance} check_argc_eq1 "report_slews" $args set scenes [parse_scenes_or_all keys] set pin [get_port_pin_error "pin" [lindex $args 0]] - set digits $sta_report_default_digits + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + set report_variance [info exists flags(-report_variance)] + foreach vertex [$pin vertices] { - set rise_min [format_time [$vertex slew_scenes rise $scenes min] $digits] - set rise_max [format_time [$vertex slew_scenes rise $scenes max] $digits] - set fall_min [format_time [$vertex slew_scenes fall $scenes min] $digits] - set fall_max [format_time [$vertex slew_scenes fall $scenes max] $digits] + set rise_min [$vertex slew_scenes_string rise $scenes min $report_variance $digits] + set rise_max [$vertex slew_scenes_string rise $scenes max $report_variance $digits] + set fall_min [$vertex slew_scenes_string fall $scenes min $report_variance $digits] + set fall_max [$vertex slew_scenes_string fall $scenes max $report_variance $digits] report_line "[vertex_path_name $vertex] [rise_short_name] $rise_min:$rise_max [fall_short_name] $fall_min:$fall_max" } } diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index bc5b77e0..25429d68 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -81,10 +81,10 @@ DelayCalcBase::finishDrvrPin() void DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin, const RiseFall *rf, - Slew drvr_slew, + double drvr_slew, float elmore, - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { LibertyLibrary *load_library = thresholdLibrary(load_pin); @@ -107,8 +107,8 @@ void DelayCalcBase::thresholdAdjust(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay &load_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { LibertyLibrary *load_library = thresholdLibrary(load_pin); if (load_library @@ -118,11 +118,11 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin, float load_vth = load_library->inputThreshold(rf); float drvr_slew_delta = drvr_library->slewUpperThreshold(rf) - drvr_library->slewLowerThreshold(rf); - float load_delay_delta = + float wire_delay_delta = delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta); - load_delay += (rf == RiseFall::rise()) - ? load_delay_delta - : -load_delay_delta; + wire_delay += (rf == RiseFall::rise()) + ? wire_delay_delta + : -wire_delay_delta; float load_slew_delta = load_library->slewUpperThreshold(rf) - load_library->slewLowerThreshold(rf); float drvr_slew_derate = drvr_library->slewDerateFromLibrary(); @@ -162,9 +162,8 @@ DelayCalcBase::checkDelay(const Pin *check_pin, float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); return model->checkDelay(pinPvt(check_pin, scene, min_max), - from_slew1, to_slew1, - related_out_cap, - variables_->pocvEnabled()); + from_slew1, to_slew1, related_out_cap, + min_max, variables_->pocvMode()); } else return delay_zero; @@ -187,8 +186,8 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin, float to_slew1 = delayAsFloat(to_slew); return model->reportCheckDelay(pinPvt(check_pin, scene, min_max), from_slew1, from_slew_annotation, - to_slew1, related_out_cap, false, - digits); + to_slew1, related_out_cap, min_max, + PocvMode::scalar, digits); } return ""; } diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index 6d641c7e..faaa4358 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -72,16 +72,16 @@ protected: void thresholdAdjust(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay &load_delay, - Slew &load_slew); + double &load_delay, + double &load_slew); // Helper function for input ports driving dspf parasitic. void dspfWireDelaySlew(const Pin *load_pin, const RiseFall *rf, - Slew drvr_slew, + double drvr_slew, float elmore, // Return values. - ArcDelay &wire_delay, - Slew &load_slew); + double &wire_delay, + double &load_slew); const Pvt *pinPvt(const Pin *pin, const Scene *scene, const MinMax *min_max); diff --git a/dcalc/DelayNormal.cc b/dcalc/DelayNormal.cc new file mode 100644 index 00000000..31f03a52 --- /dev/null +++ b/dcalc/DelayNormal.cc @@ -0,0 +1,231 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "DelayNormal.hh" + +#include // sqrt + +#include "Error.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "StaState.hh" +#include "Variables.hh" + +namespace sta { + +float +DelayOpsNormal::stdDev2(const Delay &delay, + const EarlyLate *) const +{ + return delay.stdDev2(); +} + +float +DelayOpsNormal::asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + float quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() - delay.stdDev() * quantile; + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.stdDev() * quantile; +} + +double +DelayOpsNormal::asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + double quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() - delay.stdDev() * quantile; + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.stdDev() * quantile; +} + +bool +DelayOpsNormal::isZero(const Delay &delay) const +{ + return fuzzyZero(delay.mean()) + && fuzzyZero(delay.stdDev2()); +} + +bool +DelayOpsNormal::isInf(const Delay &delay) const +{ + return fuzzyInf(delay.mean()); +} + +bool +DelayOpsNormal::equal(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyEqual(delay1.mean(), delay2.mean()) + && fuzzyEqual(delay1.stdDev2(), delay2.stdDev2()); +} + +bool +DelayOpsNormal::less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsNormal::less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsNormal::lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsNormal::greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +bool +DelayOpsNormal::greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +Delay +DelayOpsNormal::sum(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() + delay2.mean(), + delay1.stdDev2() + delay2.stdDev2()); +} + +Delay +DelayOpsNormal::sum(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() + delay2, + delay1.stdDev2()); +} + +Delay +DelayOpsNormal::diff(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() - delay2.mean(), + delay1.stdDev2() + delay2.stdDev2()); +} + +Delay +DelayOpsNormal::diff(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() - delay2, + delay1.stdDev2()); +} + +Delay +DelayOpsNormal::diff(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 - delay2.mean(), + delay2.stdDev2()); +} + +void +DelayOpsNormal::incr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), 0.0, + delay1.stdDev2() + delay2.stdDev2(), 0.0); +} + +void +DelayOpsNormal::incr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), 0.0, + delay1.stdDev2() + delay2.stdDev2(), 0.0); +} + +void +DelayOpsNormal::decr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +void +DelayOpsNormal::decr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +Delay +DelayOpsNormal::product(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() * delay2, + delay1.stdDev2() * square(delay2)); +} + +Delay +DelayOpsNormal::div(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 / delay2.mean()); +} + +const char * +DelayOpsNormal::asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const +{ + const Unit *unit = sta->units()->timeUnit(); + return stringPrintTmp("%s[%s]", + unit->asString(delay.mean(), digits), + unit->asString(delay.stdDev(), digits)); +} + +} // namespace diff --git a/dcalc/DelayScalar.cc b/dcalc/DelayScalar.cc new file mode 100644 index 00000000..69346355 --- /dev/null +++ b/dcalc/DelayScalar.cc @@ -0,0 +1,206 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +// Delay as floats, non-SSTA. + +#include "DelayScalar.hh" + +#include "Fuzzy.hh" +#include "Units.hh" +#include "StaState.hh" + +namespace sta { + +float +DelayOpsScalar::stdDev2(const Delay &, + const EarlyLate *) const +{ + return 0.0; +} + +float +DelayOpsScalar::asFloat(const Delay &delay, + const EarlyLate *, + const StaState *) const +{ + return delay.mean(); +} + +double +DelayOpsScalar::asFloat(const DelayDbl &delay, + const EarlyLate *, + const StaState *) const +{ + return delay.mean(); +} + +bool +DelayOpsScalar::isZero(const Delay &delay) const +{ + return fuzzyZero(delay.mean()); +} + +bool +DelayOpsScalar::isInf(const Delay &delay) const +{ + return fuzzyInf(delay.mean()); +} + +bool +DelayOpsScalar::equal(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyEqual(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::less(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *) const +{ + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyLessEqual(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::greater(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyGreater(delay1.mean(), delay2.mean()); +} + +bool +DelayOpsScalar::greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); +} + +Delay +DelayOpsScalar::sum(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() + delay2.mean()); +} + +Delay +DelayOpsScalar::sum(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() + delay2); +} + +Delay +DelayOpsScalar::diff(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() - delay2.mean()); +} + +Delay +DelayOpsScalar::diff(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() - delay2); +} + +Delay +DelayOpsScalar::diff(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 - delay2.mean()); +} + + +void +DelayOpsScalar::incr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() + delay2.mean()); +} + +void +DelayOpsScalar::incr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() + delay2.mean()); +} + +void +DelayOpsScalar::decr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +void +DelayOpsScalar::decr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setMean(delay1.mean() - delay2.mean()); +} + +Delay +DelayOpsScalar::product(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() * delay2); +} + +Delay +DelayOpsScalar::div(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 / delay2.mean()); +} + +const char * +DelayOpsScalar::asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const +{ + const Unit *unit = sta->units()->timeUnit(); + return unit->asString(delay.mean(), digits); +} + +} // namespace + diff --git a/dcalc/DelaySkewNormal.cc b/dcalc/DelaySkewNormal.cc new file mode 100644 index 00000000..82bdfbe4 --- /dev/null +++ b/dcalc/DelaySkewNormal.cc @@ -0,0 +1,292 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "DelaySkewNormal.hh" + +#include // sqrt + +#include "Error.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "StaState.hh" +#include "Variables.hh" + +namespace sta { + +float +DelayOpsSkewNormal::stdDev2(const Delay &delay, + const EarlyLate *) const +{ + return delay.stdDev2(); +} + +float +DelayOpsSkewNormal::asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + // LVF: mean + mean_shift + sigma * sigma_factor with skewness consideration. + float quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() + delay.meanShift() + - delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.meanShift() + + delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); +} + +double +DelayOpsSkewNormal::asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const +{ + // LVF: mean + mean_shift + sigma * sigma_factor with skewness consideration. + double quantile = sta->variables()->pocvQuantile(); + if (early_late == EarlyLate::early()) + return delay.mean() + delay.meanShift() + - delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); + else // (early_late == EarlyLate::late()) + return delay.mean() + delay.meanShift() + + delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0); +} + +bool +DelayOpsSkewNormal::isZero(const Delay &delay) const +{ + return fuzzyZero(delay.mean()) + && fuzzyZero(delay.meanShift()) + && fuzzyZero(delay.stdDev2()) + && fuzzyZero(delay.skewness()); +} + +bool +DelayOpsSkewNormal::isInf(const Delay &delay) const +{ + return fuzzyInf(delay.mean()); +} + +bool +DelayOpsSkewNormal::equal(const Delay &delay1, + const Delay &delay2, + const StaState *) const +{ + return fuzzyEqual(delay1.mean(), delay2.mean()) + && fuzzyEqual(delay1.meanShift(), delay2.meanShift()) + && fuzzyEqual(delay1.stdDev2(), delay2.stdDev2()) + && fuzzyEqual(delay1.skewness(), delay2.skewness()); +} + +bool +DelayOpsSkewNormal::less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsSkewNormal::less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const +{ + return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsSkewNormal::lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), + delayAsFloat(delay2, EarlyLate::early(), sta)); +} + +bool +DelayOpsSkewNormal::greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +bool +DelayOpsSkewNormal::greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const +{ + return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), + delayAsFloat(delay2, EarlyLate::late(), sta)); +} + +Delay +DelayOpsSkewNormal::sum(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() + delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +float +DelayOpsSkewNormal::skewnessSum(const Delay &delay1, + const Delay &delay2) const +{ + return skewnessSum(delay1.stdDev(), delay1.skewness(), + delay2.stdDev(), delay2.skewness()); +} + +// Helper function to compute combined skewness from std dev and skewness values. +double +DelayOpsSkewNormal::skewnessSum(double std_dev1, + double skewness1, + double std_dev2, + double skewness2) const +{ + double std_dev_sum = square(std_dev1) + square(std_dev2); + if (std_dev_sum == 0.0) + return 0.0; + else { + // Un-normalize the skews so they are third moments so they can be added. + double skew = (skewness1 * cube(std_dev1) + skewness2 * cube(std_dev2)) + // std_dev_sum^(3/2) + / (std_dev_sum * std::sqrt(std_dev_sum)); + return skew; + } +} + +Delay +DelayOpsSkewNormal::sum(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() + delay2, + delay1.meanShift(), + delay1.stdDev2(), + delay1.skewness()); +} + +Delay +DelayOpsSkewNormal::diff(const Delay &delay1, + const Delay &delay2) const +{ + return Delay(delay1.mean() - delay2.mean(), + delay1.meanShift() - delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +Delay +DelayOpsSkewNormal::diff(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() - delay2, + delay1.meanShift(), + delay1.stdDev2(), + delay1.skewness()); +} + +Delay +DelayOpsSkewNormal::diff(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 - delay2.mean(), + delay2.meanShift(), + delay2.stdDev2(), + delay2.skewness()); +} + +void +DelayOpsSkewNormal::incr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +void +DelayOpsSkewNormal::incr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() + delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1.stdDev(), delay1.skewness())); +} + +void +DelayOpsSkewNormal::decr(Delay &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() - delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1, delay2)); +} + +void +DelayOpsSkewNormal::decr(DelayDbl &delay1, + const Delay &delay2) const +{ + delay1.setValues(delay1.mean() - delay2.mean(), + delay1.meanShift() + delay2.meanShift(), + delay1.stdDev2() + delay2.stdDev2(), + skewnessSum(delay1.stdDev(), delay1.skewness())); +} + +Delay +DelayOpsSkewNormal::product(const Delay &delay1, + float delay2) const +{ + return Delay(delay1.mean() * delay2, + delay1.meanShift() * delay2, + delay1.stdDev2() * square(delay2), + delay1.skewness() * cube(delay2)); +} + +Delay +DelayOpsSkewNormal::div(float delay1, + const Delay &delay2) const +{ + return Delay(delay1 / delay2.mean()); +} + +const char * +DelayOpsSkewNormal::asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const +{ + const Unit *unit = sta->units()->timeUnit(); + return stringPrintTmp("%s[%s,%s,%s]", + unit->asString(delay.mean(), digits), + unit->asString(delay.meanShift(), digits), + unit->asString(delay.stdDev(), digits), + sta->units()->scalarUnit()->asString(delay.skewness(), digits)); +} + +} // namespace diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 4619bc97..48114895 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -90,8 +90,7 @@ gateModelRd(const LibertyCell *cell, double in_slew, double c2, double c1, - const Pvt *pvt, - bool pocv_enabled); + const Pvt *pvt); static void newtonRaphson(const int max_iter, double x[], @@ -143,8 +142,8 @@ public: virtual void loadDelaySlew(const Pin *load_pin, double elmore, // Return values. - ArcDelay &delay, - Slew &slew); + double &delay, + double &slew); double ceff() { return ceff_; } // Given x_ as a vector of input parameters, fill fvec_ with the @@ -348,13 +347,10 @@ DmpAlg::gateCapDelaySlew(double ceff, double &delay, double &slew) { - ArcDelay model_delay; - Slew model_slew; - gate_model_->gateDelay(pvt_, in_slew_, ceff, - variables_->pocvEnabled(), - model_delay, model_slew); - delay = delayAsFloat(model_delay); - slew = delayAsFloat(model_slew); + float model_delay, model_slew; + gate_model_->gateDelay(pvt_, in_slew_, ceff, model_delay, model_slew); + delay = model_delay; + slew = model_slew; } void @@ -543,8 +539,8 @@ DmpAlg::showVo() void DmpAlg::loadDelaySlew(const Pin *, double elmore, - ArcDelay &delay, - Slew &slew) + double &delay, + double &slew) { if (!driver_valid_ || elmore == 0.0 @@ -700,8 +696,8 @@ public: void loadDelaySlew(const Pin *, double elmore, // Return values. - ArcDelay &delay, - Slew &slew) override; + double &delay, + double &slew) override; void evalDmpEqns() override; double voCrossingUpperBound() override; @@ -753,8 +749,8 @@ DmpCap::gateDelaySlew(// Return values. void DmpCap::loadDelaySlew(const Pin *, double elmore, - ArcDelay &delay, - Slew &slew) + double &delay, + double &slew) { delay = elmore; slew = drvr_slew_; @@ -1498,21 +1494,36 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, parasitics_->piModel(parasitic, c2, rpi, c1); if (std::isnan(c2) || std::isnan(c1) || std::isnan(rpi)) report_->error(1040, "parasitic Pi model has NaNs."); - setCeffAlgorithm(drvr_library, drvr_cell, pinPvt(drvr_pin, scene, min_max), + const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); + setCeffAlgorithm(drvr_library, drvr_cell, pvt, table_model, rf, in_slew1, c2, rpi, c1); double gate_delay, drvr_slew; gateDelaySlew(gate_delay, drvr_slew); + + // Fill in pocv parameters. + double ceff = dmp_alg_->ceff(); + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + if (variables_->pocvEnabled()) + table_model->gateDelayPocv(pvt, in_slew1, ceff, min_max, + variables_->pocvMode(), + gate_delay2, drvr_slew2); ArcDcalcResult dcalc_result(load_pin_index_map.size()); - dcalc_result.setGateDelay(gate_delay); - dcalc_result.setDrvrSlew(drvr_slew); + dcalc_result.setGateDelay(gate_delay2); + dcalc_result.setDrvrSlew(drvr_slew2); for (const auto &[load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay; - Slew load_slew; + double wire_delay; + double load_slew; loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic, wire_delay, load_slew); - dcalc_result.setWireDelay(load_idx, wire_delay); - dcalc_result.setLoadSlew(load_idx, load_slew); + // Copy pocv params from driver. + ArcDelay wire_delay2(gate_delay2); + Slew load_slew2(drvr_slew2); + delaySetMean(wire_delay2, wire_delay); + delaySetMean(load_slew2, load_slew); + dcalc_result.setWireDelay(load_idx, wire_delay2); + dcalc_result.setLoadSlew(load_idx, load_slew2); } return dcalc_result; } @@ -1543,8 +1554,7 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, { double rd = 0.0; if (gate_model) { - rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, - pvt, variables_->pocvEnabled()); + rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, pvt); // Zero Rd means the table is constant and thus independent of load cap. if (rd < 1e-2 // Rpi is small compared to Rd, which makes the load capacitive. @@ -1612,14 +1622,12 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, else c_eff = load_cap; if (model) { - const Unit *time_unit = units->timeUnit(); float in_slew1 = delayAsFloat(in_slew); result += model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), - in_slew1, c_eff, - variables_->pocvEnabled(), digits); + in_slew1, c_eff, min_max, + variables_->pocvMode(), digits); result += "Driver waveform slew = "; - float drvr_slew = delayAsFloat(dcalc_result.drvrSlew()); - result += time_unit->asString(drvr_slew, digits); + result += delayAsString(dcalc_result.drvrSlew(), min_max, digits, this); result += '\n'; } return result; @@ -1632,18 +1640,15 @@ gateModelRd(const LibertyCell *cell, double in_slew, double c2, double c1, - const Pvt *pvt, - bool pocv_enabled) + const Pvt *pvt) { float cap1 = c1 + c2; float cap2 = cap1 + 1e-15; - ArcDelay d1, d2; - Slew s1, s2; - gate_model->gateDelay(pvt, in_slew, cap1, pocv_enabled, d1, s1); - gate_model->gateDelay(pvt, in_slew, cap2, pocv_enabled, d2, s2); + float d1, d2, s1, s2; + gate_model->gateDelay(pvt, in_slew, cap1, d1, s1); + gate_model->gateDelay(pvt, in_slew, cap2, d2, s2); double vth = cell->libertyLibrary()->outputThreshold(rf); - float rd = -std::log(vth) * std::abs(delayAsFloat(d1) - delayAsFloat(d2)) - / (cap2 - cap1); + float rd = -std::log(vth) * std::abs(d1 - d2) / (cap2 - cap1); return rd; } @@ -1658,8 +1663,8 @@ DmpCeffDelayCalc::gateDelaySlew(// Return values. void DmpCeffDelayCalc::loadDelaySlewElmore(const Pin *load_pin, double elmore, - ArcDelay &delay, - Slew &slew) + double &delay, + double &slew) { if (dmp_alg_) dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew); diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index d1931517..4d3b7fd5 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -69,15 +69,15 @@ protected: const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) = 0; + double &wire_delay, + double &load_slew) = 0; void gateDelaySlew(// Return values. double &delay, double &slew); void loadDelaySlewElmore(const Pin *load_pin, double elmore, - ArcDelay &delay, - Slew &slew); + double &delay, + double &slew); // Select the appropriate special case Dartu/Menezes/Pileggi algorithm. void setCeffAlgorithm(const LibertyLibrary *library, const LibertyCell *cell, diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index e0a350d2..aece357e 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -59,8 +59,8 @@ protected: const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) override; + double &wire_delay, + double &load_slew) override; }; ArcDelayCalc * @@ -93,8 +93,8 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *, ArcDcalcResult dcalc_result(load_pin_index_map.size()); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); for (auto [load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay = 0.0; - Slew load_slew = in_slew; + double wire_delay = 0.0; + double load_slew = in_slew; bool elmore_exists = false; float elmore = 0.0; if (parasitic) @@ -116,8 +116,8 @@ DmpCeffElmoreDelayCalc::loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { wire_delay = 0.0; load_slew = drvr_slew; @@ -143,14 +143,14 @@ public: Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, - const MinMax *min_max) override; + const MinMax *min_max) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, const Scene *scene, - const MinMax *min_max) override; + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -158,7 +158,7 @@ public: const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, const Scene *scene, - const MinMax *min_max) override; + const MinMax *min_max) override; private: void loadDelaySlew(const Pin *load_pin, @@ -167,14 +167,14 @@ private: const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) override; + double &wire_delay, + double &load_slew) override; void loadDelay(double drvr_slew, Parasitic *pole_residue, double p1, double k1, - ArcDelay &wire_delay, - Slew &load_slew); + double &wire_delay, + double &load_slew); float loadDelay(double vth, double p1, double p2, @@ -267,8 +267,8 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *, { const Parasitics *parasitics = scene->parasitics(min_max); ArcDcalcResult dcalc_result(load_pin_index_map.size()); - ArcDelay wire_delay = 0.0; - Slew load_slew = in_slew; + double wire_delay = 0.0; + double load_slew = in_slew; LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); for (const auto [load_pin, load_idx] : load_pin_index_map) { if (parasitics->isPiPoleResidue(parasitic)) { @@ -323,8 +323,8 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, const Parasitic *parasitic, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { parasitic_is_pole_residue_ = parasitics_->isPiPoleResidue(parasitic); // Should handle PiElmore parasitic. @@ -362,12 +362,12 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew, double p1, double k1, // Return values. - ArcDelay &wire_delay, - Slew &load_slew) + double &wire_delay, + double &load_slew) { ComplexFloat pole2, residue2; parasitics_->poleResidue(pole_residue, 1, pole2, residue2); - if (!delayZero(drvr_slew) + if (!delayZero(drvr_slew, this) && pole2.imag() == 0.0 && residue2.imag() == 0.0) { double p2 = pole2.real(); diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index e33067d2..10c64e93 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -646,17 +646,17 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, ArcDcalcResult intrinsic_result = arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, load_pin_index_map, scene, min_max); - ArcDelay intrinsic_delay = intrinsic_result.gateDelay(); + const ArcDelay &intrinsic_delay = intrinsic_result.gateDelay(); ArcDcalcResult gate_result = arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), load_cap, parasitic, load_pin_index_map, scene, min_max); - ArcDelay gate_delay = gate_result.gateDelay(); - Slew gate_slew = gate_result.drvrSlew(); + const ArcDelay &gate_delay = gate_result.gateDelay(); + const Slew &gate_slew = gate_result.drvrSlew(); - ArcDelay load_delay = gate_delay - intrinsic_delay; + const ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this); debugPrint(debug_, "delay_calc", 3, " gate delay = %s intrinsic = %s slew = %s", delayAsString(gate_delay, this), @@ -729,9 +729,8 @@ GraphDelayCalc::loadSlews(LoadPinIndexMap &load_pin_index_map) Vertex *load_vertex = graph_->pinLoadVertex(pin); SlewSeq &slews = load_slews[index];; slews.resize(slew_count); - Slew *vertex_slews = load_vertex->slews(); for (size_t i = 0; i < slew_count; i++) - slews[i] = vertex_slews[i]; + slews[i] = graph_->slew(load_vertex, i); } return load_slews; } @@ -744,9 +743,9 @@ GraphDelayCalc::loadSlewsChanged(DrvrLoadSlews &load_slews_prev, for (auto const [pin, index] : load_pin_index_map) { Vertex *load_vertex = graph_->pinLoadVertex(pin); SlewSeq &slews_prev = load_slews_prev[index];; - const Slew *slews = load_vertex->slews(); for (size_t i = 0; i < slew_count; i++) { - if (!delayEqual(slews[i], slews_prev[i])) + const Slew &slew = graph_->slew(load_vertex, i); + if (!delayEqual(slew, slews_prev[i], this)) return true; } } @@ -915,19 +914,18 @@ GraphDelayCalc::initLoadSlews(Vertex *drvr_vertex) Edge *wire_edge = edge_iter.next(); if (wire_edge->isWire()) { Vertex *load_vertex = wire_edge->to(graph_); - for (Scene *scene : scenes_) { for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); Slew slew_init_value(min_max->initValue()); - for (const RiseFall *rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { if (!load_vertex->slewAnnotated(rf, min_max)) - graph_->setSlew(load_vertex, rf, ap_index, slew_init_value); + graph_->setSlew(load_vertex, rf, ap_index, slew_init_value); + } } } } } - } } bool @@ -965,12 +963,12 @@ GraphDelayCalc::initRootSlews(Vertex *vertex) for (Scene *scene : scenes_) { for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - for (const RiseFall *rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { if (!vertex->slewAnnotated(rf, min_max)) - graph_->setSlew(vertex, rf, ap_index, default_slew); + graph_->setSlew(vertex, rf, ap_index, default_slew); + } } } - } } void @@ -1195,8 +1193,8 @@ GraphDelayCalc::annotateDelaysSlews(Edge *edge, bool GraphDelayCalc::annotateDelaySlew(Edge *edge, const TimingArc *arc, - ArcDelay &gate_delay, - Slew &gate_slew, + const ArcDelay &gate_delay, + const Slew &gate_slew, const Scene *scene, const MinMax *min_max) { @@ -1258,8 +1256,8 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, Vertex *load_vertex = wire_edge->to(graph_); Pin *load_pin = load_vertex->pin(); size_t load_idx = load_pin_index_map[load_pin]; - ArcDelay wire_delay = dcalc_result.wireDelay(load_idx); - Slew load_slew = dcalc_result.loadSlew(load_idx); + const ArcDelay &wire_delay = dcalc_result.wireDelay(load_idx); + const Slew &load_slew = dcalc_result.loadSlew(load_idx); debugPrint(debug_, "delay_calc", 3, " %s load delay = %s slew = %s", load_vertex->to_string(this).c_str(), @@ -1287,7 +1285,7 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, // annotate the same wire edges so they must be combined // rather than set. const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, ap_index); - Delay wire_delay_extra = extra_delay + wire_delay; + Delay wire_delay_extra = delaySum(extra_delay, wire_delay, this); if (!merge || delayGreater(wire_delay_extra, delay, min_max, this)) { graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra); diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 4c4a1251..07a68e5b 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -118,7 +118,7 @@ LumpedCapDelayCalc::inputPortDelay(const Pin *, const MinMax *) { const LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); - return makeResult(drvr_library,rf, 0.0, in_slew, load_pin_index_map); + return makeResult(drvr_library,rf, delay_zero, in_slew, load_pin_index_map); } ArcDcalcResult @@ -139,16 +139,22 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, const RiseFall *rf = arc->toEdge()->asRiseFall(); const LibertyLibrary *drvr_library = arc->to()->libertyLibrary(); if (model) { - ArcDelay gate_delay; - Slew drvr_slew; + float gate_delay, drvr_slew; float in_slew1 = delayAsFloat(in_slew); // NaNs cause seg faults during table lookup. - if (std::isnan(load_cap) || std::isnan(delayAsFloat(in_slew))) + if (std::isnan(load_cap) || std::isnan(in_slew.mean())) report_->error(1350, "gate delay input variable is NaN"); - model->gateDelay(pinPvt(drvr_pin, scene, min_max), in_slew1, load_cap, - variables_->pocvEnabled(), - gate_delay, drvr_slew); - return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map); + const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); + model->gateDelay(pvt, in_slew1, load_cap, gate_delay, drvr_slew); + + // Fill in pocv parameters. + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + if (variables_->pocvEnabled()) + model->gateDelayPocv(pvt, in_slew1, load_cap, min_max, variables_->pocvMode(), + gate_delay2, drvr_slew2); + + return makeResult(drvr_library, rf, gate_delay2, drvr_slew2, load_pin_index_map); } else return makeResult(drvr_library, rf, delay_zero, delay_zero, load_pin_index_map); @@ -157,17 +163,18 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, ArcDcalcResult LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay gate_delay, - Slew drvr_slew, + const ArcDelay &gate_delay, + const Slew &drvr_slew, const LoadPinIndexMap &load_pin_index_map) { ArcDcalcResult dcalc_result(load_pin_index_map.size()); dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); + double drvr_slew1 = delayAsFloat(drvr_slew); for (const auto [load_pin, load_idx] : load_pin_index_map) { - ArcDelay wire_delay = 0.0; - Slew load_slew = drvr_slew; + double wire_delay = 0.0; + double load_slew = drvr_slew1; thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); dcalc_result.setWireDelay(load_idx, wire_delay); dcalc_result.setLoadSlew(load_idx, load_slew); @@ -190,7 +197,8 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin, if (model) { float in_slew1 = delayAsFloat(in_slew); return model->reportGateDelay(pinPvt(check_pin, scene, min_max), - in_slew1, load_cap, false, digits); + in_slew1, load_cap, min_max, + PocvMode::scalar, digits); } return ""; } diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index ac631f3a..752edea9 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -74,8 +74,8 @@ public: protected: ArcDcalcResult makeResult(const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay gate_delay, - Slew drvr_slew, + const ArcDelay &gate_delay, + const Slew &drvr_slew, const LoadPinIndexMap &load_pin_index_map); using ArcDelayCalc::reduceParasitic; diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index 512ea9f5..38b71c32 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -88,13 +88,13 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, load_pin_index_map, scene, min_max); ArcDelay gate_delay = gate_result.gateDelay(); Slew drvr_slew = gate_result.drvrSlew(); - ArcDelay load_delay = gate_delay - intrinsic_delay; + ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this); load_delays[drvr_idx] = load_delay; - if (!delayZero(load_delay)) - load_delay_sum += 1.0 / load_delay; - if (!delayZero(drvr_slew)) - slew_sum += 1.0 / drvr_slew; + if (!delayZero(load_delay, this)) + delayIncr(load_delay_sum, delayDiv(1.0, load_delay, this), this); + if (!delayZero(drvr_slew, this)) + delayIncr(slew_sum, delayDiv(1.0, drvr_slew, this), this); dcalc_result.setLoadCount(load_pin_index_map.size()); for (const auto &[load_pin, load_idx] : load_pin_index_map) { @@ -103,14 +103,16 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, } } - ArcDelay gate_load_delay = delayZero(load_delay_sum) + ArcDelay gate_load_delay = delayZero(load_delay_sum, this) ? delay_zero - : 1.0 / load_delay_sum; - ArcDelay drvr_slew = delayZero(slew_sum) ? delay_zero : 1.0 / slew_sum; + : delayDiv(1.0, load_delay_sum, this); + ArcDelay drvr_slew = delayZero(slew_sum, this) + ? delay_zero + : delayDiv(1.0, slew_sum, this); for (size_t drvr_idx = 0; drvr_idx < drvr_count; drvr_idx++) { ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; - dcalc_result.setGateDelay(intrinsic_delays[drvr_idx] + gate_load_delay); + dcalc_result.setGateDelay(delaySum(intrinsic_delays[drvr_idx], gate_load_delay, this)); dcalc_result.setDrvrSlew(drvr_slew); } return dcalc_results; diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index b8b5c85c..90e31ed3 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -166,8 +166,8 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin, for (auto load_pin_index : load_pin_index_map) { const Pin *load_pin = load_pin_index.first; size_t load_idx = load_pin_index.second; - ArcDelay wire_delay = 0.0; - Slew load_slew = in_slew; + double wire_delay = 0.0; + double load_slew = in_slew; bool elmore_exists = false; float elmore = 0.0; if (pi_elmore) @@ -722,8 +722,8 @@ PrimaDelayCalc::dcalcResults() size_t drvr_node = pin_node_map_[drvr_pin]; ThresholdTimes &drvr_times = threshold_times_[drvr_node]; float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlewFlt()); - ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time; - Slew drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); + double gate_delay = drvr_times[threshold_vth] - ref_time; + double drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); debugPrint(debug_, "ccs_dcalc", 2, @@ -739,8 +739,8 @@ PrimaDelayCalc::dcalcResults() size_t load_node = pin_node_map_[load_pin]; ThresholdTimes &wire_times = threshold_times_[load_node]; ThresholdTimes &drvr_times = threshold_times_[drvr_node]; - ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth]; - Slew load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]); + double wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth]; + double load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]); debugPrint(debug_, "ccs_dcalc", 2, "load %s %s delay %s slew %s", network_->pathName(load_pin), @@ -920,7 +920,8 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, if (model) { float in_slew1 = delayAsFloat(in_slew); return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), - in_slew1, load_cap, false, digits); + in_slew1, load_cap, min_max, + PocvMode::scalar, digits); } return ""; } diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index 23c9e11f..0ac6caa8 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -62,6 +62,8 @@ The Vector/Map/Set/UnorderedSet classes have been removed and replaced by the std containers. The member functions are now templated functions found in ContainerHelpers.hh. +The Graph slew_rf_count option is no longer supported. + Release 2.6.2 2025/03/30 ------------------------ diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 8d695f63..1f1871b9 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -3,6 +3,134 @@ OpenSTA Timing Analyzer Release Notes This file summarizes user visible changes for each release. +Release 3.0.1 2026/03/12 +------------------------ + +Statistical timing (SSTA) with Liberty LVF (Liberty Variation Format) +models is now supported. Statistical timing uses a probaility +distribution to represent a delay or slew ranther than a single +number. + +Normal and skew normal probability distributions are supported. + +SSTA is enabled with the sta_pocv_mode variaable. + + set sta_pocv_mode scalar|normal|skew_normal + +scalar mode is for non-SSTA analysis +normal mode uses gaussian normal distributions +skew_normal'mode is for skew normal LVF moment based distributions + +The target quantile of a delay probability distribution (confidence level) is +set with the sta_pocv_quantile variable. + + sta_pocv_quantile + +The default value is 3 standard deviations, or sigma. + +Use the variance field with report_checks or report_check_types to see +distribution parameters in timing reports. + +A command file for analyzing a design with statisical timing with an +LVF library is shown below. + +read_liberty lvf_library.lib.gz +read_verilog design.v +link_design top +create_clock -period 50 clk +set_input_delay -clock clk 1 {in1 in2} +set sta_pocv_mode skew_normal +report_checks -fields {slew variation input_pin variation} -digits 3 + +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Slew Delay Variation Time Description +--------------------------------------------------------------------------- + 0.000 0.000 0.000 clock clk (rise edge) + 0.000 0.000 clock network delay (ideal) + 0.000 0.000 0.000 ^ r2/CK (FDPQ1) + 12.026 mean + 0.017 mean_shift + 0.366 std_dev + 0.000 skewness + 4.648 12.409 12.409 v r2/Q (FFQ1) + 4.648 0.000 12.409 v u1/A (BUF1) + 6.084 mean + 0.007 mean_shift + 0.188 std_dev + 0.000 skewness + 2.513 6.137 18.546 v u1/X (BUF1) + 2.513 0.000 18.546 v u2/A2 (AN21) + 6.447 mean + 0.008 mean_shift + 0.191 std_dev + 0.000 skewness + 2.565 6.497 25.043 v u2/X (AN21) + 2.565 0.000 25.043 v r3/D (FFQ1) + 25.043 data arrival time + + 0.000 50.000 50.000 clock clk (rise edge) + 0.000 50.000 clock network delay (ideal) + 0.000 50.000 clock reconvergence pessimism + 50.000 ^ r3/CK (FFQ1) + -9.376 40.624 library setup time + 40.624 data required time +--------------------------------------------------------------------------- + 40.624 data required time + -25.043 data arrival time +--------------------------------------------------------------------------- + 15.581 slack (MET) + + +The following commands now support a -report_variance arggument. + + report_arrival [-report_variance] + report_required [-report_variance] + report_slack [-report_variance] + report_slews [-report_variance] + report_edges [-report_variance] + +The following commands now support a -digits option. + + report_edges [-digits digits] + report_slews [-digits digits] + +The standard deviation for normal distributions is specified with the +following liberty timing groups. + + ocv_sigma_cell_rise + ocv_sigma_cell_fall + ocv_sigma_rise_transition + ocv_sigma_fall_transition + ocv_sigma_rise_constraint + ocv_sigma_fall_constraint + +LVF skew normal distributions are specified with liberty groups below. + + ocv_std_dev_cell_rise + ocv_std_dev_cell_fall + ocv_mean_shift_cell_rise + ocv_mean_shift_cell_fall + ocv_skewness_cell_rise + ocv_skewness_cell_fall + + ocv_std_dev_rise_transition + ocv_std_dev_fall_transition + ocv_skewness_rise_transition + ocv_skewness_fall_transition + ocv_mean_shift_rise_transition + ocv_mean_shift_fall_transition + + ocv_std_dev_rise_constraint + ocv_std_dev_fall_constraint + ocv_skewness_rise_constraint + ocv_skewness_fall_constraint + ocv_mean_shift_rise_constraint + ocv_mean_shift_fall_constraint + 2026/02/24 ---------- diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index fb9ea3b6..56bfdfc2 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,24 +1,24 @@ - Parallax STA documentationJames Cherry5192025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-07T17:12:46.349252000P123DT1H24M11SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse + Parallax STA documentationJames Cherry5272025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-13T08:23:12.816774000P123DT2H11M52SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse - 1488348 - 1956 - 19290 - 17736 + 138335 + 12 + 21890 + 20512 true false view2 - 17619 - 1497110 - 1956 - 1488348 - 21244 - 1506082 + 5793 + 99330 + 12 + 138335 + 21900 + 158845 0 1 false @@ -89,7 +89,7 @@ false true false - 26953533 + 27526206 0 false @@ -198,7 +198,7 @@ - + @@ -4029,6 +4029,21 @@ + + + + + + + + + + + + + + + @@ -4175,267 +4190,289 @@ - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + + - - + + + - - + + + - + - + - - + - - + + - - + - - + + - - + + - - + + - - + + - + - - + + - + - - + + - - + - - + + - + + - - + + - + - + - - + - + - + - + - + - + + - + - + + - - + + + - + - - + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4444,864 +4481,883 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - + + - - + + + - - + - + - + - - - + + - + - - + + + - - + + - - + + - + - + - + - - - - - - - - + + + + + + + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - + - - - + + + - - + + + - - + - + - - - + + - - + + - - + + - - - + + - - - + + - - + + - - + + - + + - + - - + + + - - + + - - + + + - + - + - - + + - - + + + - - + - - - - + - + + + + - - + + - - + + - - + + - + - - - - - + - - + + - + - + + + + - - + + - - + + + - - + - - + + - + - - + + + - + - - - - - + - - + + + + + + - - + + - - + + - + - - + + + - + - - - + + - - + + - + - - + + + - - - + + - - + + - - + - - + - - + + + - + - - + + - + - + - - + + - + - - + + + - - + + + - - + + + - + - - - + + - - - + + - - + + - + - + - - - - + - - + + - - - + + - - - + + + + + - + - + - + - + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - + - - + + - - + + - - - + + - - - + + - - + + - - + - - - + + + - + - - + + + - + - - + + - + - - + - + - + - + - - + + + - - + + + - - + + + - + - - - + + - + - - - + + - + - + - - - - - - - + - + - + - + - - - + + - - + + + + + + + + + - + - + - - + + - + - - - - + - - + + + + + - + - - - - - - - + - + - + + + + + + + + - + + + + + - + - - + + + - - - - - + + - + + - - + + - + - + - - - + + - + + - - - - + - - - + + + - - - + + - - - + + + + + - - + - - - + + - - + + - + - - + + + - - + + - - + - + - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + - + - + - + @@ -5345,1044 +5401,1077 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6518,85 +6607,101 @@ Variables85 - Command Line Arguments + Command Line Arguments The command line arguments for sta are shown below. sta -help show help and exit -version show version and exit -no_init do not read ~/.sta -no_splash do not print the splash message -threads count|max use count threads -exit exit after reading cmd_file cmd_file source cmd_file When OpenSTA starts up, commands are first read from the user initialization file ~/.sta if it exists. If a TCL command file cmd_file is specified on the command line, commands are read from the file and executed before entering an interactive TCL command interpreter. If -exit is specified the application exits after reading cmd_file. Use the TCL exit command to exit the application. The –threads option specifies how many parallel threads to use. Use –threads max to use one thread per processor. - Example Command Scripts + Example Command Scripts To read a design into OpenSTA use the read_liberty command to read Liberty library files. Next, read hierarchical structural Verilog files with the read_verilog command. The link_design command links the Verilog to the Liberty timing cells. Any number of Liberty and Verilog files can be read before linking the design. Delays used for timing analysis are calculated using the Liberty timing models. If no parasitics are read only the pin capacitances of the timing models are used in delay calculation. Use the read_spef command to read parasitics from an extractor, or read_sdf to use delays calculated by an external delay calculator. Timing constraints can be entered as TCL commands or read using the read_sdc command. The units used by OpenSTA for all command arguments and reports are taken from the first Liberty file that is read. Use the set_cmd_units command to override the default units. Use the report_units command to see the ccmmand units. - Timing Analysis using SDF + Timing Analysis using SDF A sample command file that reads a library and a Verilog netlist and reports timing checks is shown below. read_liberty example1_slow.libread_verilog example1.vlink_design topread_sdf example1.sdfcreate_clock -name clk -period 10 {clk1 clk2 clk3}set_input_delay -clock clk 0 {in1 in2}report_checks This example can be found in examples/sdf_delays.tcl. - Timing Analysis with Multiple Process Corners + Timing Analysis with Multiple Process Corners An example command script using three process corners and +/-10% min/max derating is shown below. read_liberty nangate45_slow.lib.gzread_liberty nangate45_typ.lib.gzread_liberty nangate45_fast.lib.gzread_verilog example1.link_design topset_timing_derate -early 0.9set_timing_derate -late 1.1create_clock -name clk -period 10 {clk1 clk2 clk3}set_input_delay -clock clk 0 {in1 in2}define_scene ss -liberty nangate45_slowdefine_scene tt -liberty nangate45_typdefine_scene ff -liberty nangate45_fast# report all scenesreport_checks -path_delay min_max# report typical scenereport_checks -scene tt This example can be found in examples/multi_corner.tcl. Other examples can be found in the examples directory. - Timing Analysis with Multiple Corners and Modes + Timing Analysis with Multiple Corners and Modes OpenSTA supports multi-corner, multi-mode analysis. Each corner/mode combination is called a “scene”. The SDC constraints in each mode describe a different operating mode, such as mission mode or scan mode. Each corner has min/max Liberty libraries and SPEF parasitics. A mode named “default” is initially created for SDC commands. It is deleted when a mode is defined with set_mode or read_sdc -mode. Similartly, a named “default” is initially created that is deleted when define_scene is used to define a scene. An example command script using two process corners two modes is shown below. read_liberty asap7_small_ff.lib.gzread_liberty asap7_small_ss.lib.gzread_verilog reg1_asap7.vlink_design topread_sdc -mode mode1 mcmm2_mode1.sdcread_sdc -mode mode2 mcmm2_mode2.sdcread_spef -name reg1_ff reg1_asap7.spefread_spef -name reg1_ss reg1_asap7_ss.spefdefine_scene scene1 -mode mode1 -liberty asap7_small_ff -spef reg1_ffdefine_scene scene2 -mode mode2 -liberty asap7_small_ss -spef reg1_ssreport_checks -scenes scene1report_checks -scenes scene2report_checks -group_path_count 4 This example can be found in examples/mcmm3.tcl.In the example show above the SDC for the modes is in separate files. Alternatively, the SDC can be defined in the command file using the set_mode command between SDC command groups. set_mode mode1create_clock -name m1_clk -period 1000 {clk1 clk2 clk3}set_input_delay -clock m1_clk 100 {in1 in2}set_mode mode2create_clock -name m2_clk -period 500 {clk1 clk3}set_output_delay -clock m2_clk 100 out - Power Analysis - OpenSTA also supports static power analysis with the report_power command. Probabalistic switching activities are propagated from the input ports to determine switching activities for internal pins. - read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefset_power_activity -input -activity 0.1set_power_activity -input_port reset -activity 0report_power - In this example the activity for all inputs is set to 0.1, and then the activity for the reset signal is set to zero because it does not switch during steady state operation. - Group Internal Switching Leakage Total Power Power Power Power (Watts)----------------------------------------------------------------Sequential 3.27e-04 7.87e-05 2.96e-10 4.06e-04 36.4%Combinational 2.34e-04 3.10e-04 6.95e-10 5.43e-04 48.7%Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 15.0%Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%----------------------------------------------------------------Total 6.07e-04 5.09e-04 1.01e-09 1.12e-03 100.0% 54.4% 45.6% 0.0% - This example can be found in examples/power.tcl. - Gate level simulation results can be used to get a more accurate power estimate. For example, the Icarus verilog simulator can be used to run the the test bench examples/gcd_tb.v for the gcd design in the previous example. + Statistical Timing Analysis + OpenSTA also supports statistical timing .anallysis with Liberty Variation Format (LVF) libraries. Statistical timing uses a probaility distribution to represent a delay or slew ranther than a single number. + Normal and skew normal probability distributions are supported. SSTA is enabled with the sta_pocv_mode variaable. + set sta_pocv_mode scalar|normal|skew_normalscalar mode is for non-SSTA analysisnormal mode uses gaussian normal distributionsskew_normal mode is for skew normal LVF moment based distributions + The target quantile of a delay probability distribution (confidence level) is set with the sta_pocv_quantile variable. + set sta_pocv_quantile <float> + The default value is 3 standard deviations, or sigma. + Use the variance field with the report_checks and report_check_types commands to see distribution parameters in timing reports. + A command file for analyzing a design with statisical timing is shown below. + read_liberty lvf_library.lib.gzread_verilog design.vlink_design topcreate_clock -period 50 clkset_input_delay -clock clk 1 {in1 in2}set sta_pocv_mode skew_normalreport_checks -fields {slew variation input_pin variation} -digits 3 + Startpoint: r2 (rising edge-triggered flip-flop clocked by clk)Endpoint: r3 (rising edge-triggered flip-flop clocked by clk)Path Group: clkPath Type: max Slew Delay Variation Time Description--------------------------------------------------------------------------- 0.000 0.000 0.000 clock clk (rise edge) 0.000 0.000 clock network delay (ideal) 0.000 0.000 0.000 ^ r2/CK (FDPQ1) 12.026 mean 0.017 mean_shift 0.366 std_dev 0.000 skewness 4.648 12.409 12.409 v r2/Q (FFQ1) 4.648 0.000 12.409 v u1/A (BUF1) 6.084 mean 0.007 mean_shift 0.188 std_dev 0.000 skewness 2.513 6.137 18.546 v u1/X (BUF1) 2.513 0.000 18.546 v u2/A2 (AN21) 6.447 mean 0.008 mean_shift 0.191 std_dev 0.000 skewness 2.565 6.497 25.043 v u2/X (AN21) 2.565 0.000 25.043 v r3/D (FFQ1) 25.043 data arrival time 0.000 50.000 50.000 clock clk (rise edge) 0.000 50.000 clock network delay (ideal) 0.000 50.000 clock reconvergence pessimism 50.000 ^ r3/CK (FFQ1) -9.376 40.624 library setup time 40.624 data required time--------------------------------------------------------------------------- 40.624 data required time -25.043 data arrival time--------------------------------------------------------------------------- 15.581 slack (MET) + The standard deviation for normal distributions is specified with the following liberty timing groups. + ocv_sigma_cell_riseocv_sigma_cell_fallocv_sigma_rise_transitionocv_sigma_fall_transitionocv_sigma_rise_constraintocv_sigma_fall_constraint + LVF skew normal distributions are specified with liberty groups below. + ocv_std_dev_cell_riseocv_std_dev_cell_fallocv_mean_shift_cell_riseocv_mean_shift_cell_fallocv_skewness_cell_riseocv_skewness_cell_fallocv_std_dev_rise_transitionocv_std_dev_fall_transitionocv_skewness_rise_transitionocv_skewness_fall_transitionocv_mean_shift_rise_transitionocv_mean_shift_fall_transitionocv_std_dev_rise_constraintocv_std_dev_fall_constraintocv_skewness_rise_constraintocv_skewness_fall_constraintocv_mean_shift_rise_constraintocv_mean_shift_fall_constraint + + Power Analysis + OpenSTA also supports static power analysis with the report_power command. Probabalistic switching activities are propagated from the input ports to determine switching activities for internal pins. + read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefset_power_activity -input -activity 0.1set_power_activity -input_port reset -activity 0report_power + In this example the activity for all inputs is set to 0.1, and then the activity for the reset signal is set to zero because it does not switch during steady state operation. + Group Internal Switching Leakage Total Power Power Power Power (Watts)----------------------------------------------------------------Sequential 3.27e-04 7.87e-05 2.96e-10 4.06e-04 36.4%Combinational 2.34e-04 3.10e-04 6.95e-10 5.43e-04 48.7%Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 15.0%Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%----------------------------------------------------------------Total 6.07e-04 5.09e-04 1.01e-09 1.12e-03 100.0% 54.4% 45.6% 0.0% + This example can be found in examples/power.tcl. + Gate level simulation results can be used to get a more accurate power estimate. For example, the Icarus verilog simulator can be used to run the the test bench examples/gcd_tb.v for the gcd design in the previous example. iverilog -o gcd_tb gcd_tb.vvvp gcd_tb - The test bench writes the VCD (Value Change Data) file gcd_sky130hd.vcd which can then be read with the read_vcd command. - read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefread_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd.gzreport_power - This example can be found in examples/power_vcd.tcl. - Note that in this simple example design simulation based activities does not significantly change the results. - TCL Interpreter + The test bench writes the VCD (Value Change Data) file gcd_sky130hd.vcd which can then be read with the read_vcd command. + read_liberty sky130hd_tt.libread_verilog gcd_sky130hd.vlink_design gcdread_sdc gcd_sky130hd.sdcread_spef gcd_sky130hd.spefread_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd.gzreport_power + This example can be found in examples/power_vcd.tcl. + Note that in this simple example design simulation based activities does not significantly change the results. + TCL Interpreter Keyword arguments to commands may be abbreviated. For example, report_checks -unique is equivalent to the following command. - report_checks -unique_paths_to_endpoint - The help command lists matching commands and their arguments. - > help report*report_annotated_check [-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period] [-max_skew] [-max_lines liness] [-list_annotated]group_path_count [-list_not_annotated] [-constant_arcs]report_annotated_delay [-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines liness] [-list_annotated] [-list_not_annotated] [-constant_arcs]report_arrival pinreport_check_types [-violators] [-verbose] [-scene scene] [-format slack_only|end] [-max_delay] [-min_delay] [-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] [-net net] [-digits digits [-no_line_splits] [> filename] [>> filename]report_checks [-from from_list|-rise_from from_list|-fall_from from_list] [-through through_list|-rise_through through_list|-fall_through through_list] [-to to_list|-rise_to to_list|-fall_to to_list] [-unconstrained] [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max] [-scene scene] [-group_path_count path_count] [-endpoint_path_count path_count] [-unique_paths_to_endpoint] [-slack_max slack_max] [-slack_min slack_min] [-sort_by_slack] [-path_group group_name] [-format full|full_clock|full_clock_expanded|short|end|summary]... - Many reporting commands support redirection of the output to a file much like a Unix shell. - report_checks -to out1 > path.logreport_checks -to out2 >> path.log - Debugging Timing - Here are some guidelines for debugging your design if static timing does not report any paths, or does not report the expected paths. - Debugging timing problems generally involves using the following commands to follow the propagation of arrival times from a known arrival downstream to understand why the arrival times are not propagating: - report_edgesreport_arrivalsreport_net - report_edges -from can be used to walk forward and report_edges -to to walk backward in the netlist/timing graph. report_arrivals shows the min/max rise/fall arrival times with respect to each clock that has a path to the pin. report_net shows connections to a net across hierarchy levels. + report_checks -unique_paths_to_endpoint + The help command lists matching commands and their arguments. + > help report*report_annotated_check [-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period] [-max_skew] [-max_lines liness] [-list_annotated]group_path_count [-list_not_annotated] [-constant_arcs]report_annotated_delay [-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines liness] [-list_annotated] [-list_not_annotated] [-constant_arcs]report_arrival pinreport_check_types [-violators] [-verbose] [-scene scene] [-format slack_only|end] [-max_delay] [-min_delay] [-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] [-net net] [-digits digits [-no_line_splits] [> filename] [>> filename]report_checks [-from from_list|-rise_from from_list|-fall_from from_list] [-through through_list|-rise_through through_list|-fall_through through_list] [-to to_list|-rise_to to_list|-fall_to to_list] [-unconstrained] [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max] [-scene scene] [-group_path_count path_count] [-endpoint_path_count path_count] [-unique_paths_to_endpoint] [-slack_max slack_max] [-slack_min slack_min] [-sort_by_slack] [-path_group group_name] [-format full|full_clock|full_clock_expanded|short|end|summary]... + Many reporting commands support redirection of the output to a file much like a Unix shell. + report_checks -to out1 > path.logreport_checks -to out2 >> path.log + Debugging Timing + Here are some guidelines for debugging your design if static timing does not report any paths, or does not report the expected paths. + Debugging timing problems generally involves using the following commands to follow the propagation of arrival times from a known arrival downstream to understand why the arrival times are not propagating: + report_edgesreport_arrivalsreport_net + report_edges -from can be used to walk forward and report_edges -to to walk backward in the netlist/timing graph. report_arrivals shows the min/max rise/fall arrival times with respect to each clock that has a path to the pin. report_net shows connections to a net across hierarchy levels. No paths found - The report_checks command only reports paths that are constrained by timing checks or SDC commands such as set_output_delay. If the design has only combinational logic (no registers or latches), there are no timing checks, so no paths are reported. Use the -unconstrained option to report_checks to see unconstrained paths. + The report_checks command only reports paths that are constrained by timing checks or SDC commands such as set_output_delay. If the design has only combinational logic (no registers or latches), there are no timing checks, so no paths are reported. Use the -unconstrained option to report_checks to see unconstrained paths. % report_checks -unconstrained - If the design is sequential (has registers or latches) and no paths are reported, it is likely that there is a problem with the clock propagation. Check the timing at an register in the design with the report_arrivals command. - % report_arrivals r1/CP (clk ^) r 0.00:0.00 f INF:-INF (clk v) r INF:-INF f 5.00:5.00 - In this example the rising edge of the clock "clk" causes the rising arrival min:max time at 0.00, and the falling edge arrives at 5.00. Since the rising edge of the clock causes the rising edge of the register clock pin, the clock path is positive unate. - The clock path should be positive or negative unate. Something is probably wrong with the clock network if it is non-unate. A non-unate clock path will report arrivals similar to the foillowing: + If the design is sequential (has registers or latches) and no paths are reported, it is likely that there is a problem with the clock propagation. Check the timing at an register in the design with the report_arrivals command. + % report_arrivals r1/CP (clk ^) r 0.00:0.00 f INF:-INF (clk v) r INF:-INF f 5.00:5.00 + In this example the rising edge of the clock "clk" causes the rising arrival min:max time at 0.00, and the falling edge arrives at 5.00. Since the rising edge of the clock causes the rising edge of the register clock pin, the clock path is positive unate. + The clock path should be positive or negative unate. Something is probably wrong with the clock network if it is non-unate. A non-unate clock path will report arrivals similar to the foillowing: % report_arrivals r1/CP (clk ^) r 0.00:0.00 f 0.00:0.00 (clk v) r 5.00:5.00 f 5.00:5.00 - Notice that each clock edge causes both rise and fall arrivals at the register clock pin. - If there are no paths to the register clock pin, nothing is printed. Use the report_edges -to command to find the gate driving the clock pin. - % report_edges -to r1/CPi1/ZN -> CP wire ^ -> ^ 0.00:0.00 v -> v 0.00:0.00 - This shows that the gate/pin i1/ZN is driving the clock pin. The report_edges -to commond can be used to walk backward or forward through the netlist one gate/net at a time. By checking the arrivals with the report_arrival command you can determine where the path is broken. - No path reported an endpoint - In order for a timing check to be reported, there must be an arrival time at the data pin (the constrained pin) as well as the timing check clock pin. If report_checks -to a register input does not report any paths, check that the input is constrained by a timing check with report_edges -to. + Notice that each clock edge causes both rise and fall arrivals at the register clock pin. + If there are no paths to the register clock pin, nothing is printed. Use the report_edges -to command to find the gate driving the clock pin. + % report_edges -to r1/CPi1/ZN -> CP wire ^ -> ^ 0.00:0.00 v -> v 0.00:0.00 + This shows that the gate/pin i1/ZN is driving the clock pin. The report_edges -to commond can be used to walk backward or forward through the netlist one gate/net at a time. By checking the arrivals with the report_arrival command you can determine where the path is broken. + No path reported an endpoint + In order for a timing check to be reported, there must be an arrival time at the data pin (the constrained pin) as well as the timing check clock pin. If report_checks -to a register input does not report any paths, check that the input is constrained by a timing check with report_edges -to. % report_edges -to r1/DCP -> D hold ^ -> ^ -0.04:-0.04 ^ -> v -0.03:-0.03CP -> D setup ^ -> ^ 0.09:0.0 ^ -> v 0.08:0.08in1 -> D wire ^ -> ^ 0.00:0.00 v -> v 0.00:0.00 - This reports the setup and hold checks for the D pin of r1. - Next, check the arrival times at the D and CP pins of the register with report_arrivals. + This reports the setup and hold checks for the D pin of r1. + Next, check the arrival times at the D and CP pins of the register with report_arrivals. % report_arrivals r1/D (clk1 ^) r 1.00:1.00 f 1.00:1.00% report_arrivals r1/CP (clk1 ^) r 0.00:0.00 f INF:-INF (clk1 v) r INF:-INF f 5.00:5.00 - If there are no arrivals on an input port of the design, use the set_input_delay command to specify the arrival times on the port. - Commands + If there are no arrivals on an input port of the design, use the set_input_delay command to specify the arrival times on the port. + Commands - all_clocks + all_clocks - + @@ -6606,16 +6711,15 @@ - all_inputs + all_inputs - [-no_clocks] + [-no_clocks] - - -no_clocks + -no_clocks Exclude inputs defined as clock sources. @@ -6628,10 +6732,10 @@ - all_outputs + all_outputs - + @@ -6639,95 +6743,95 @@ + - all_registers + all_registers - [-clock clock_names][-cells | -data_pins | -clock_pins | -async_pins | ‑output_pins][-level_sensitive][-edge_triggered] + [-clock clock_names][-cells | -data_pins | -clock_pins | -async_pins | ‑output_pins][-level_sensitive][-edge_triggered] - -clock clock_names + -clock clock_names - A list of clock names. Only registers clocked by these clocks are returned. + A list of clock names. Only registers clocked by these clocks are returned. - -cells + -cells - Return a list of register instances. + Return a list of register instances. - -data_pins + -data_pins - Return the register data pins. + Return the register data pins. - -clock_pins + -clock_pins - Return the register clock pins. + Return the register clock pins. - -async_pins + -async_pins - Return the register set/clear pins. + Return the register set/clear pins. - -output_pins + -output_pins - Return the register output pins. + Return the register output pins. - -level_sensitive + -level_sensitive - Return level-sensitive latches. + Return level-sensitive latches. - -edge_triggered + -edge_triggered - Return edge-triggered registers. + Return edge-triggered registers. - The all_registers command returns a list of register instances or register pins in the design. Options allow the list of registers to be restricted in various ways. The -clock keyword restrcts the registers to those that are clocked by a set of clocks. The -cells option returns the list of registers or latches (the default). The -‑data_pins, -clock_pins, -async_pins and -output_pins options cause all_registers to return a list of register pins rather than instances. + The all_registers command returns a list of register instances or register pins in the design. Options allow the list of registers to be restricted in various ways. The -clock keyword restrcts the registers to those that are clocked by a set of clocks. The -cells option returns the list of registers or latches (the default). The -‑data_pins, -clock_pins, -async_pins and -output_pins options cause all_registers to return a list of register pins rather than instances. - - check_setup + check_setup - [-verbose][-unconstrained_endpoints][-multiple_clock][-no_clock][-no_input_delay][-loops][-generated_clocks][> filename][>> filename] + [-verbose][-unconstrained_endpoints][-multiple_clock][-no_clock][-no_input_delay][-loops][-generated_clocks][> filename][>> filename] - -verbose + -verbose Show offending objects rather than just error counts. @@ -6735,7 +6839,7 @@ - -unconstrained_endpoints + -unconstrained_endpoints Check path endpoints for timing constraints (timing check or set_output_delay). @@ -6743,15 +6847,16 @@ - -multiple_clock + -multiple_clock Check register/latch clock pins for multiple clocks. + - -no_clock + -no_clock Check register/latch clock pins for a clock. @@ -6759,7 +6864,7 @@ - -no_input_delay + -no_input_delay Check for inputs that do not have a set_input_delay command. @@ -6767,7 +6872,7 @@ - -loops + -loops Check for combinational logic loops. @@ -6775,28 +6880,28 @@ - -generated_clocks + -generated_clocks Check that generated clock source pins have been defined as clocks. - The check_setup command performs sanity checks on the design. Individual checks can be performed with the keywords. If no check keywords are specified all checks are performed. Checks that fail are reported as warnings. If no checks fail nothing is reported. The command returns 1 if there are no warnings for use in scripts. + The check_setup command performs sanity checks on the design. Individual checks can be performed with the keywords. If no check keywords are specified all checks are performed. Checks that fail are reported as warnings. If no checks fail nothing is reported. The command returns 1 if there are no warnings for use in scripts. - connect_pin + connect_pin - netport|pin + netport|pin - net + net A net to add connections to. @@ -6804,7 +6909,7 @@ - port + port A port to connect to net. @@ -6812,29 +6917,28 @@ - Pin + Pin A pin to connect to net. - The connect_pin command connects a port or instance pin to a net. + The connect_pin command connects a port or instance pin to a net. - - create_clock + create_clock - -period period[-name clock_name][-waveform edge_list][-add][pin_list] + -period period[-name clock_name][-waveform edge_list][-add][pin_list] - -period period + -period period The clock period. @@ -6842,7 +6946,7 @@ - -name clock_name + -name clock_name The name of the clock. @@ -6850,7 +6954,7 @@ - -waveform edge_list + -waveform edge_list A list of edge rise and fall time. @@ -6858,15 +6962,15 @@ - -add + -add - Add this clock to the clocks on pin_list. + Add this clock to the clocks on pin_list. - pin_list + pin_list A list of pins driven by the clock. @@ -6874,28 +6978,28 @@ The create_clock command defines the waveform of a clock used by the design. - If no pin_list is specified the clock is virtual. A virtual clock can be refered to by name in input arrival and departure time commands but is not attached to any pins in the design. - If no clock name is specified the name of the first pin is used as the clock name. + If no pin_list is specified the clock is virtual. A virtual clock can be refered to by name in input arrival and departure time commands but is not attached to any pins in the design. + If no clock name is specified the name of the first pin is used as the clock name. If a wavform is not specified the clock rises at zero and falls at half the clock period. The waveform is a list with time the clock rises as the first element and the time it falls as the second element. If a clock is already defined on a pin the clock is redefined using the new clock parameters. If multiple clocks drive the same pin, use the -add option to prevent the existing definition from being overwritten. The following command creates a clock with a period of 10 time units that rises at time 0 and falls at 5 time units on the pin named clk1. create_clock -period 10 clk1 The following command creates a clock with a period of 10 time units that is high at time zero, falls at time 2 and rises at time 8. The clock drives three pins named clk1, clk2, and clk3. - create_clock -period 10 -waveform {8 2} -name clk {clk1 clk2 clk3} + create_clock -period 10 -waveform {8 2} -name clk {clk1 clk2 clk3} - create_generated_clock + create_generated_clock - [-name clock_name]-source master_pin[-master_clock master_clock][-divide_by divisor][-multiply_by multiplier][-duty_cycle duty_cycle][-invert][-edges edge_list][-edge_shift shift_list][-add]pin_list + [-name clock_name]-source master_pin[-master_clock master_clock][-divide_by divisor][-multiply_by multiplier][-duty_cycle duty_cycle][-invert][-edges edge_list][-edge_shift shift_list][-add]pin_list - -name clock_name + -name clock_name The name of the generated clock. @@ -6903,39 +7007,39 @@ - -source master_pin + -source master_pin - A pin or port in the fanout of the master clock that is the source of the generated clock. + A pin or port in the fanout of the master clock that is the source of the generated clock. - -master_clock master_clock + -master_clock master_clock - Use -master_clock to specify which source clock to use when multiple clocks are present on master_pin. + Use -master_clock to specify which source clock to use when multiple clocks are present on master_pin. - -divide_by divisor + -divide_by divisor - Divide the master clock period by divisor. + Divide the master clock period by divisor. - -multiply_by multiplier + -multiply_by multiplier - Multiply the master clock period by multiplier. + Multiply the master clock period by multiplier. - -duty_cycle duty_cycle + -duty_cycle duty_cycle The percent of the period that the generated clock is high (between 0 and 100). @@ -6943,23 +7047,24 @@ - -invert + -invert Invert the master clock. + - -edges edge_list + -edges edge_list - List of master clock edges to use in the generated clock. Edges are numbered from 1. edge_list must be 3 edges long. + List of master clock edges to use in the generated clock. Edges are numbered from 1. edge_list must be 3 edges long. - -edge_shift shift_list + -edge_shift shift_list Not supported. @@ -6967,46 +7072,46 @@ - -add + -add - Add this clock to the existing clocks on pin_list. + Add this clock to the existing clocks on pin_list. - pin_list + pin_list - A list of pins driven by the generated clock. + A list of pins driven by the generated clock. The create_generated_clock command is used to generate a clock from an existing clock definition. It is used to model clock generation circuits such as clock dividers and phase locked loops. The -divide_by, -multiply_by and -edges arguments are mutually exclusive. - The -multiply_by option is used to generate a higher frequency clock from the source clock. The period of the generated clock is divided by multiplier. The clock multiplier must be a positive integer. If a duty cycle is specified the generated clock rises at zero and falls at period * duty_cycle / 100. If no duty cycle is specified the source clock edge times are divided by multiplier. + The -multiply_by option is used to generate a higher frequency clock from the source clock. The period of the generated clock is divided by multiplier. The clock multiplier must be a positive integer. If a duty cycle is specified the generated clock rises at zero and falls at period * duty_cycle / 100. If no duty cycle is specified the source clock edge times are divided by multiplier. The -divide_by option is used to generate a lower frequency clock from the source clock. The clock divisor must be a positive integer. If the clock divisor is a power of two the source clock period is multiplied by divisor, the clock rise time is the same as the source clock, and the clock fall edge is one half period later. If the clock divisor is not a power of two the source clock waveform edge times are multiplied by divisor. The -edges option forms the generated clock waveform by selecting edges from the source clock waveform. If the -invert option is specified the waveform derived above is inverted. If a clock is already defined on a pin the clock is redefined using the new clock parameters. If multiple clocks drive the same pin, use the -add option to prevent the existing definition from being overwritten. - In the example show below generates a clock named gclk1 on register output pin r1/Q by dividing it by four. + In the example show below generates a clock named gclk1 on register output pin r1/Q by dividing it by four. create_clock -period 10 -waveform {1 8} clk1create_generated_clock -name gclk1 -source clk1 -divide_by 4 r1/Q The generated clock has a period of 40, rises at time 1 and falls at time 21. In the example shown below the duty cycle is used to define the derived clock waveform. create_generated_clock -name gclk1 -source clk1 -duty_cycle 50 \ -multiply_by 2 r1/Q The generated clock has a period of 5, rises at time .5 and falls at time 3. In the example shown below the first, third and fifth source clock edges are used to define the derived clock waveform. - create_generated_clock -name gclk1 -source clk1 -edges {1 3 5} r1/Q + create_generated_clock -name gclk1 -source clk1 -edges {1 3 5} r1/Q The generated clock has a period of 20, rises at time 1 and falls at time 11. - create_voltage_area + create_voltage_area - [-name name][-coordinate coordinates][-guard_band_x guard_x][-guard_band_y guard_y]cells + [-name name][-coordinate coordinates][-guard_band_x guard_x][-guard_band_y guard_y]cells @@ -7016,90 +7121,91 @@ - current_design + current_design - [design] + [design] - + - current_instance + current_instance - [instance] + [instance] - instance + instance - Not supported. + Not supported. - + - define_scene + define_scene - -mode mode_name -liberty liberty_files|-liberty_min liberty_min_files -liberty_max liberty_max_files-spef spef_file| -spef_min spef_min_file -spef_max spef_max_file + -mode mode_name -liberty liberty_files|-liberty_min liberty_min_files -liberty_max liberty_max_files-spef spef_file| -spef_min spef_min_file -spef_max spef_max_file - mode_name + mode_name - The SDC mode to use. + The SDC mode to use. - liberty_files + liberty_files - List of Liberty files to use. + List of Liberty files to use. - spef_file + spef_file - The SPEF parasitics file to use. + The SPEF parasitics file to use. - The define_scene command defines a scene for a mode (SDC), liberty files and spef parasitics. Define scenes after reading Liberty libraries and SPEF parasitics.Liberty files are specifiec with the name of the liberty library or the filename of the liberty file. If a filename is used, it must be the same as the filename used to read the library with read_liberty.. - Use get_scenes to find defined scenes. + The define_scene command defines a scene for a mode (SDC), liberty files and spef parasitics. Define scenes after reading Liberty libraries and SPEF parasitics.Liberty files are specifiec with the name of the liberty library or the filename of the liberty file. If a filename is used, it must be the same as the filename used to read the library with read_liberty.. + Use get_scenes to find defined scenes. + - delete_clock + delete_clock - [-all] clocks + [-all] clocks - clocks + clocks - A list of clocks to remove. + A list of clocks to remove. @@ -7109,26 +7215,26 @@ - delete_from_list + delete_from_list - list objects + list objects - list + list - A list of objects. + A list of objects. - objects + objects - A list of objects to delete from list. + A list of objects to delete from list. @@ -7136,21 +7242,20 @@ - - delete_generated_clock + delete_generated_clock - [-all] clocks + [-all] clocks - clocks + clocks - A list of generated clocks to remove. + A list of generated clocks to remove. @@ -7160,18 +7265,18 @@ - delete_instance + delete_instance - instance + instance - instance + instance - Instance to delete. + Instance to delete. @@ -7181,18 +7286,18 @@ - delete_net + delete_net - net + net - net + net - Net to delete. + Net to delete. @@ -7202,23 +7307,23 @@ - disconnect_pin + disconnect_pin - netport | pin | -all + netport | pin | -all - net + net - The net to disconnect pins from. + The net to disconnect pins from. - port + port A port to connect to net. @@ -7226,18 +7331,19 @@ - pin + pin A pin to connect to net. + - -all + -all - Disconnect all pins from the net. + Disconnect all pins from the net. @@ -7247,10 +7353,10 @@ - elapsed_run_time + elapsed_run_time - + @@ -7258,99 +7364,98 @@ - - find_timing_paths + find_timing_paths - [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-scene scene][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups] + [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-scene scene][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups] - -from from_list + -from from_list - Return paths from a list of clocks, instances, ports, register clock pins, or latch data pins. + Return paths from a list of clocks, instances, ports, register clock pins, or latch data pins. - -rise_from from_list + -rise_from from_list - Return paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. + Return paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. - -fall_from from_list + -fall_from from_list - Return paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. + Return paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. - -through through_list + -through through_list - Return paths through a list of instances, pins or nets. + Return paths through a list of instances, pins or nets. - -rise_through through_list + -rise_through through_list - Return rising paths through a list of instances, pins or nets. - - - - - -fall_through through_list - - - Return falling paths through a list of instances, pins or nets. - - - - - -to to_list - - - Return paths to a list of clocks, instances, ports or pins. - - - - - -rise_to to_list - - - Return rising paths to a list of clocks, instances, ports or pins. - - - - - -fall_to to_list - - - Return falling paths to a list of clocks, instances, ports or pins. - - - - - -unconstrained - - - Report unconstrained paths also. + Return rising paths through a list of instances, pins or nets. - -path_delay min + -fall_through through_list + + + Return falling paths through a list of instances, pins or nets. + + + + + -to to_list + + + Return paths to a list of clocks, instances, ports or pins. + + + + + -rise_to to_list + + + Return rising paths to a list of clocks, instances, ports or pins. + + + + + -fall_to to_list + + + Return falling paths to a list of clocks, instances, ports or pins. + + + + + -unconstrained + + + Report unconstrained paths also. + + + + + -path_delay min Return min path (hold) checks. @@ -7358,7 +7463,7 @@ - -path_delay min_rise + -path_delay min_rise Return min path (hold) checks for rising endpoints. @@ -7366,7 +7471,7 @@ - -path_delay min_fall + -path_delay min_fall Return min path (hold) checks for falling endpoints. @@ -7374,7 +7479,7 @@ - -path_delay max + -path_delay max Return max path (setup) checks. @@ -7382,7 +7487,7 @@ - -path_delay max_rise + -path_delay max_rise Return max path (setup) checks for rising endpoints. @@ -7390,7 +7495,7 @@ - -path_delay max_fall + -path_delay max_fall Return max path (setup) checks for falling endpoints. @@ -7398,7 +7503,7 @@ - -path_delay min_max + -path_delay min_max Return max and max path (setup and hold) checks. @@ -7406,23 +7511,23 @@ - -group_path_count path_count + -group_path_count path_count - The number of paths to return in each path group. + The number of paths to return in each path group. - -endpoint_path_count endpoint_path_count + -endpoint_path_count endpoint_path_count - The number of paths to return for each endpoint. + The number of paths to return for each endpoint. - ‑unique_paths_to_endpoint + ‑unique_paths_to_endpoint Return multiple paths to an endpoint that traverse different pins without showing multiple paths with different rise/fall transitions. @@ -7430,7 +7535,7 @@ - -scene scene + -scene scene Return paths for one process corner. @@ -7438,109 +7543,108 @@ - -slack_max max_slack + -slack_max max_slack - Return paths with slack less than max_slack. + Return paths with slack less than max_slack. - -slack_min min_slack + -slack_min min_slack - Return paths with slack greater than min_slack. + Return paths with slack greater than min_slack. - -sort_by_slack + -sort_by_slack - Sort paths by slack rather than slack within path groups. + Sort paths by slack rather than slack within path groups. - -path_group groups + -path_group groups - Return paths in path groups. Paths in all groups are returned if this option is not specified. + Return paths in path groups. Paths in all groups are returned if this option is not specified. - The find_timing_paths command returns a list of path objects for scripting. Use the get_property function to access properties of the paths. + The find_timing_paths command returns a list of path objects for scripting. Use the get_property function to access properties of the paths. - - get_cells + get_cells - [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -hierarchical + -hierarchical - Searches hierarchy levels below the current instance for matches. + Searches hierarchy levels below the current instance for matches. - -hsc separator + -hsc separator - Character to use to separate hierarchical instance names in patterns. + Character to use to separate hierarchical instance names in patterns. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects - The name of a pin or net, a list of pins returned by get_pins, or a list of nets returned by get_nets. The –hierarchical option cannot be used with ‑of_objects. + The name of a pin or net, a list of pins returned by get_pins, or a list of nets returned by get_nets. The –hierarchical option cannot be used with ‑of_objects. - patterns + patterns A list of instance name patterns. @@ -7553,48 +7657,48 @@ - get_clocks + get_clocks - [-regexp][-nocase][-filter expr][-quiet]patterns + [-regexp][-nocase][-filter expr][-quiet]patterns - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of clock name patterns. @@ -7607,15 +7711,15 @@ - get_fanin + get_fanin - -to sink_list[-flat][-only_cells][-startpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] + -to sink_list[-flat][-only_cells][-startpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] - -to sink_list + -to sink_list List of pins, ports, or nets to find the fanin of. For nets, the fanin of driver pins on the nets are returned. @@ -7623,15 +7727,15 @@ - -flat + -flat - With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. + With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. - -only_cells + -only_cells Return the instances connected to the pins in the fanin. @@ -7639,7 +7743,7 @@ - -startpoints_only + -startpoints_only Only return pins that are startpoints. @@ -7647,7 +7751,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7655,7 +7759,7 @@ - -pin_levels pin_count + -pin_levels pin_count Only return pins within pin_count pin traversals. @@ -7663,26 +7767,26 @@ - -trace_arcs timing + -trace_arcs timing - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs enabled + -trace_arcs enabled - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs all + -trace_arcs all - Trace through all arcs, including disabled ones. + Trace through all arcs, including disabled ones. @@ -7693,15 +7797,15 @@ - get_fanout + get_fanout - -from source_list[-flat][-only_cells][-endpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] + -from source_list[-flat][-only_cells][-endpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] - -from source_list + -from source_list List of pins, ports, or nets to find the fanout of. For nets, the fanout of load pins on the nets are returned. @@ -7709,15 +7813,15 @@ - -flat + -flat - With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. + With –flat pins in the fanin at any hierarchy level are returned. Without ‑flat only pins at the same hierarchy level as the sinks are returned. - -only_cells + -only_cells Return the instances connected to the pins in the fanout. @@ -7725,7 +7829,7 @@ - -endpoints_only + -endpoints_only Only return pins that are endpoints. @@ -7733,7 +7837,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7741,7 +7845,7 @@ - -pin_levels pin_count + -pin_levels pin_count Only return pins within pin_count pin traversals. @@ -7749,26 +7853,26 @@ - -trace_arcs timing + -trace_arcs timing - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs enabled + -trace_arcs enabled - Only trace through timing arcs that are not disabled. + Only trace through timing arcs that are not disabled. - -trace_arcs all + -trace_arcs all - Trace through all arcs, including disabled ones. + Trace through all arcs, including disabled ones. @@ -7778,85 +7882,85 @@ - get_full_name + get_full_name - object + object - object + object - A library, cell, port, instance, pin or timing arc object. + A library, cell, port, instance, pin or timing arc object. - Return the name of object. Equivalent to [get_property object full_name]. + Return the name of object. Equivalent to [get_property object full_name]. - get_lib_cells + get_lib_cells - [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns + [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns - -of_objects objects + -of_objects objects - A list of instance objects. + A list of instance objects. - -hsc separator + -hsc separator - Character that separates the library name and cell name in patterns. Defaults to ‘/’. + Character that separates the library name and cell name in patterns. Defaults to ‘/’. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of library cell name patterns of the form library_name/cell_name. @@ -7869,64 +7973,64 @@ - get_lib_pins + get_lib_pins - [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns + [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns - -of_objects objects + -of_objects objects - A list of library cell objects. + A list of library cell objects. - -hsc separator + -hsc separator - Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. + Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of library port name patterns of the form library_name/cell_name/port_name. @@ -7939,117 +8043,117 @@ - get_libs + get_libs - [-filter expr][-regexp][-nocase][-quiet]patterns + [-filter expr][-regexp][-nocase][-quiet]patterns - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - patterns + patterns A list of library name patterns. - The get_libs command returns a list of clocks that match patterns. + The get_libs command returns a list of clocks that match patterns. - get_nets + get_nets - [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -hierarchical + -hierarchical - Searches hierarchy levels below the current instance for matches. + Searches hierarchy levels below the current instance for matches. - -hsc separator + -hsc separator - Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. + Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects The name of a pin or instance, a list of pins returned by get_pins, or a list of instances returned by get_cells. The –hierarchical option cannot be used with –of_objects. @@ -8057,7 +8161,7 @@ - patterns + patterns A list of net name patterns. @@ -8070,15 +8174,15 @@ - get_name + get_name - object + object - object + object A library, cell, port, instance, pin or timing arc object. @@ -8092,63 +8196,63 @@ - get_pins + get_pins - [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -hierarchical + -hierarchical - Searches hierarchy levels below the current instance for matches. + Searches hierarchy levels below the current instance for matches. - -hsc separator + -hsc separator - Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. + Character that separates the library name, cell name and port name in pattern. Defaults to ‘/’. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects - The name of a net or instance, a list of nets returned by get_nets, or a list of instances returned by get_cells. The –hierarchical option cannot be used with –of_objects. + The name of a net or instance, a list of nets returned by get_nets, or a list of instances returned by get_cells. The –hierarchical option cannot be used with –of_objects. - patterns + patterns A list of pin name patterns. @@ -8156,63 +8260,63 @@ The get_pins command returns a list of all instance pins that match patterns. - A useful idiom to find the driver pin for a net is the following. - get_pins -of_objects [get_net net_name] -filter “direction==output” + A useful idiom to find the driver pin for a net is the following. + get_pins -of_objects [get_net net_name] -filter “direction==output” - get_ports + get_ports - [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - -regexp + -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. - -nocase + -nocase - Ignore case when matching. Only valid with –regexp. + Ignore case when matching. Only valid with –regexp. - -quiet + -quiet - Do not warn if no matches are found. + Do not warn if no matches are found. - -of_objects objects + -of_objects objects - The name of net or a list of nets returned by get_nets. + The name of net or a list of nets returned by get_nets. - patterns + patterns A list of port name patterns. @@ -8225,31 +8329,31 @@ - get_property + get_property - [-object_type object_type]objectproperty + [-object_type object_type]objectproperty - -object_type object_type + -object_type object_type - The type of object when it is specified as a name.cell|pin|net|port|clock|library|library_cell|library_pin|timing_arc + The type of object when it is specified as a name.cell|pin|net|port|clock|library|library_cell|library_pin|timing_arc - object + object - An object returned by get_cells, get_pins, get_nets, get_ports, get_clocks, get_libs, get_lib_cells, get_lib_pins, or get_timing_arcs, or object name. ‑object_type is required if object is a name. + An object returned by get_cells, get_pins, get_nets, get_ports, get_clocks, get_libs, get_lib_cells, get_lib_pins, or get_timing_arcs, or object name. ‑object_type is required if object is a name. - property + property A property name. @@ -8257,120 +8361,120 @@ The properties for different objects types are shown below. - cell (SDC lib_cell) - base_namefilenamefull_namelibraryname - clock - full_nameis_generatedis_propagatedis_virtualnameperiodsources - edge - delay_max_falldelay_min_falldelay_max_risedelay_min_risefull_namefrom_pinsenseto_pin - instance (SDC cell) - cellfull_nameis_bufferis_clock_gateis_hierarchicalis_inverteris_macrois_memoryliberty_cellnameref_name - liberty_cell (SDC lib_cell) - areabase_namedont_usefilenamefull_nameis_bufferis_inverteris_memorylibraryname - liberty_port (SDC lib_pin) - capacitancedirectiondrive_resistancedrive_resistance_max_falldrive_resistance_max_risedrive_resistance_min_falldrive_resistance_min_risefull_nameintrinsic_delayintrinsic_delay_max_fallintrinsic_delay_max_riseintrinsic_delay_min_fallintrinsic_delay_min_riseis_register_clocklib_cellname - library - filename (Liberty library only)namefull_name - net - full_namename - path (PathEnd) + cell (SDC lib_cell) + base_namefilenamefull_namelibraryname + clock + full_nameis_generatedis_propagatedis_virtualnameperiodsources + edge + delay_max_falldelay_min_falldelay_max_risedelay_min_risefull_namefrom_pinsenseto_pin + instance (SDC cell) + cellfull_nameis_bufferis_clock_gateis_hierarchicalis_inverteris_macrois_memoryliberty_cellnameref_name + liberty_cell (SDC lib_cell) + areabase_namedont_usefilenamefull_nameis_bufferis_inverteris_memorylibraryname + liberty_port (SDC lib_pin) + capacitancedirectiondrive_resistancedrive_resistance_max_falldrive_resistance_max_risedrive_resistance_min_falldrive_resistance_min_risefull_nameintrinsic_delayintrinsic_delay_max_fallintrinsic_delay_max_riseintrinsic_delay_min_fallintrinsic_delay_min_riseis_register_clocklib_cellname + library + filename (Liberty library only)namefull_name + net + full_namename + path (PathEnd) endpointendpoint_clockendpoint_clock_pinslackstartpointstartpoint_clockpoints - pin - activity (activity in transitions per second, duty cycle, origin)origin is one ofglobalset_power_activity -globalinputset_power_activity -inputuserset_power_activity -input_ports -pinsvcdread_vcdsaifread_saifpropagatedpropagated from upstream activitiesclockSDC create_clock or create_generated_clockconstantconstant pins propagated from verilog tie high/low, set_case_analysis, set_logic_one/zero/dc - slew_max_fallslew_max_riseslew_min_fallslew_min_riseclocksclock_domainsdirectionfull_nameis_hierarchicalis_portis_register_clocklib_pin_namenameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise - port - activityslew_max_fallslew_max_riseslew_min_fallslew_min_risedirectionfull_nameliberty_portnameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise - point (PathRef) - arrivalpinrequiredslack + pin + activity (activity in transitions per second, duty cycle, origin)origin is one ofglobalset_power_activity -globalinputset_power_activity -inputuserset_power_activity -input_ports -pinsvcdread_vcdsaifread_saifpropagatedpropagated from upstream activitiesclockSDC create_clock or create_generated_clockconstantconstant pins propagated from verilog tie high/low, set_case_analysis, set_logic_one/zero/dc + slew_max_fallslew_max_riseslew_min_fallslew_min_riseclocksclock_domainsdirectionfull_nameis_hierarchicalis_portis_register_clocklib_pin_namenameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise + port + activityslew_max_fallslew_max_riseslew_min_fallslew_min_risedirectionfull_nameliberty_portnameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise + point (PathRef) + arrivalpinrequiredslack - get_scenes + get_scenes - [-mode mode_name]scene_name + [-mode mode_name]scene_name - mode_name + mode_name - Get the scenes for mode_name. + Get the scenes for mode_name. - scene_name + scene_name - A scene name pattern. + A scene name pattern. - The get_scenes command is used to find the scenes matching a pattern or that use an SDC mode. + The get_scenes command is used to find the scenes matching a pattern or that use an SDC mode. - get_timing_edges + get_timing_edges - [-from from_pins][-to to_pins][-of_objects objects][-filter expr][patterns] + [-from from_pins][-to to_pins][-of_objects objects][-filter expr][patterns] - -from from_pin + -from from_pin - A list of pins. + A list of pins. - -to to_pin + -to to_pin - A list of pins. + A list of pins. - -of_objects objects + -of_objects objects - A list of instances or library cells. The –from and -to options cannot be used with –of_objects. + A list of instances or library cells. The –from and -to options cannot be used with –of_objects. - -filter expr + -filter expr - A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. + A filter expression of the form property==value”where property is a property supported by the get_property command. See the section “Filter Expressions” for additional forms. - The get_timing_edges command returns a list of timing edges (arcs) to, from or between pins. The result can be passed to get_property or set_disable_timing. + The get_timing_edges command returns a list of timing edges (arcs) to, from or between pins. The result can be passed to get_property or set_disable_timing. - group_path + group_path - -name group_name[-weight weight][-critical_range range][-from from_list |-rise_from from_list |-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-default] + -name group_name[-weight weight][-critical_range range][-from from_list |-rise_from from_list |-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-default] - -name group_name + -name group_name The name of the path group. @@ -8378,7 +8482,7 @@ - -weight weight + -weight weight Not supported. @@ -8386,7 +8490,7 @@ - -critical_range range + -critical_range range Not supported. @@ -8394,82 +8498,82 @@ - -from from_list + -from from_list - Group paths from a list of clocks, instances, ports, register clock pins, or latch data pins. + Group paths from a list of clocks, instances, ports, register clock pins, or latch data pins. - -rise_from from_list + -rise_from from_list - Group paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. + Group paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. - -fall_from from_list + -fall_from from_list - Group paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. + Group paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. - -through through_list + -through through_list - Group paths through a list of instances, pins or nets. + Group paths through a list of instances, pins or nets. - -rise_through through_list + -rise_through through_list - Group rising paths through a list of instances, pins or nets. + Group rising paths through a list of instances, pins or nets. - -fall_through through_list + -fall_through through_list - Group falling paths through a list of instances, pins or nets. + Group falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Group paths to a list of clocks, instances, ports or pins. + Group paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Group rising paths to a list of clocks, instances, ports or pins. + Group rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Group falling paths to a list of clocks, instances, port-s or pins. + Group falling paths to a list of clocks, instances, port-s or pins. - -default + -default - Restore the paths in the path group -from/-to/-through/-to to their default path group. + Restore the paths in the path group -from/-to/-through/-to to their default path group. @@ -8479,84 +8583,84 @@ - include + include - [-echo|-e][-verbose|-v]filename[> log_filename][>> log_filename] + [-echo|-e][-verbose|-v]filename[> log_filename][>> log_filename] - -echo|-e + -echo|-e - Print each command before evaluating it. + Print each command before evaluating it. - -verbose|-v + -verbose|-v - Print each command before evaluating it as well as the result it returns. + Print each command before evaluating it as well as the result it returns. - filename + filename - The name of the file containing commands to read. + The name of the file containing commands to read. - > log_filename + > log_filename - Redirect command output to log_filename. + Redirect command output to log_filename. - >> log_filename + >> log_filename - Redirect command output and append log_filename. + Redirect command output and append log_filename. - Read STA/SDC/Tcl commands from filename. - The include command stops and reports any errors encountered while reading a file unless sta_continue_on_error is 1. + Read STA/SDC/Tcl commands from filename. + The include command stops and reports any errors encountered while reading a file unless sta_continue_on_error is 1. - link_design + link_design - [-no_black_boxes][cell_name] + [-no_black_boxes][cell_name] - -no_black_boxes + -no_black_boxes - Do not make empty “black box” cells for instances that reference undefined cells. + Do not make empty “black box” cells for instances that reference undefined cells. - cell_name + cell_name - The top level module/cell name of the design hierarchy to link. + The top level module/cell name of the design hierarchy to link. - Link (elaborate, flatten) the the top level cell cell_name. The design must be linked after reading netlist and library files. The default value of cell_name is the current design. + Link (elaborate, flatten) the the top level cell cell_name. The design must be linked after reading netlist and library files. The default value of cell_name is the current design. The linker creates empty "block box" cells for instances the reference undefined cells when the variable link_create_black_boxes is true. When link_create_black_boxes is false an error is reported and the link fails. The link_design command returns 1 if the link succeeds and 0 if it fails. @@ -8564,15 +8668,15 @@ - make_instance + make_instance - inst_pathlib_cell + inst_pathlib_cell - inst_path + inst_path A hierarchical instance name. @@ -8581,28 +8685,28 @@ - lib_cell + lib_cell The library cell of the new instance. - The make_instance command makes an instance of library cell lib_cell. + The make_instance command makes an instance of library cell lib_cell. - make_net + make_net - net_name_list + net_name_list - net_name_list + net_name_list A list of net names. @@ -8615,47 +8719,47 @@ - read_liberty + read_liberty - [-corner corner][-min][-max][-infer_latches]filename + [-corner corner][-min][-max][-infer_latches]filename - -corner corner + -corner corner - Use the library for process corner corner delay calculation. + Use the library for process corner corner delay calculation. - -min + -min - Use library for min delay calculation. + Use library for min delay calculation. - -max + -max - Use library for max delay calculation. + Use library for max delay calculation. - filename + filename - The liberty file name to read. + The liberty file name to read. The read_liberty command reads a Liberty format library file. The first library that is read sets the units used by SDC/TCL commands and reporting. The include_file attribute is supported. - Some Liberty libraries do not include latch groups for cells that are describe transparent latches. In that situation the -infer_latches command flag can be used to infer the latches. The timing arcs required for a latch to be inferred should look like the following: + Some Liberty libraries do not include latch groups for cells that are describe transparent latches. In that situation the -infer_latches command flag can be used to infer the latches. The timing arcs required for a latch to be inferred should look like the following: cell (infered_latch) { pin(D) { direction : input ; timing () { related_pin : "E" ; timing_type : setup_falling ; } timing () { related_pin : "E" ; timing_type : hold_falling ; } } pin(E) { direction : input; } pin(Q) { direction : output ; timing () { related_pin : "D" ; } timing () { related_pin : "E" ; timing_type : rising_edge ; } }} In this example a positive level-sensitive latch is inferred. Files compressed with gzip are automatically uncompressed. @@ -8664,69 +8768,69 @@ - read_saif + read_saif - [-scope scope]filename + [-scope scope]filename - scope + scope - The SAIF scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. + The SAIF scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. - filename + filename - The name of the SAIF file to read. + The name of the SAIF file to read. - The read_saif command reads a SAIF (Switching Activity Interchange Format) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. + The read_saif command reads a SAIF (Switching Activity Interchange Format) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. - read_sdc + read_sdc - [-mode mode_name][-echo]filename + [-mode mode_name][-echo]filename - mode_name + mode_name - Mode for the SDC commands in the file. + Mode for the SDC commands in the file. - -echo + -echo - Print each command before evaluating it. + Print each command before evaluating it. - filename + filename - SDC command file. + SDC command file. - Read SDC commands from filename. - If the mode does not exist it is created. Multiple SDC files can append commands to a mode by using the -mode_name argument for each one. If no -mode arguement is is used the commands are added to the current mode. + Read SDC commands from filename. + If the mode does not exist it is created. Multiple SDC files can append commands to a mode by using the -mode_name argument for each one. If no -mode arguement is is used the commands are added to the current mode. The read_sdc command stops and reports any errors encountered while reading a file unless sta_continue_on_error is 1. Files compressed with gzip are automatically uncompressed. @@ -8734,23 +8838,23 @@ - read_sdf + read_sdf - [-scene scene][-unescaped_dividers]filename + [-scene scene][-unescaped_dividers]filename - scene + scene - Scene delays to annotate. + Scene delays to annotate. - -unescaped_dividers + -unescaped_dividers With this option path names in the SDF do not have to escape hierarchy dividers when the path name is escaped. For example, the escaped Verilog name "\inst1/inst2 " can be referenced as "inst1/inst2". The correct SDF name is "inst1\/inst2", since the divider does not represent a change in hierarchy in this case. @@ -8758,138 +8862,138 @@ - filename + filename - The name of the SDF file to read. + The name of the SDF file to read. - Read SDF delays from a file. The min and max values in the SDF tuples are used to annotate the delays for corner. The typical values in the SDF tuples are ignored. If multiple scenes are defined -scene must be specified. SDC annotation for mcmm analysis must follow the scene definitions. + Read SDF delays from a file. The min and max values in the SDF tuples are used to annotate the delays for corner. The typical values in the SDF tuples are ignored. If multiple scenes are defined -scene must be specified. SDC annotation for mcmm analysis must follow the scene definitions. Files compressed with gzip are automatically uncompressed. INCREMENT is supported as an alias for INCREMENTAL. - The following SDF statements are not supported. + The following SDF statements are not supported. PORTINSTANCE wildcards - read_spef + read_spef - [-name name][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce][-path path]filename + [-name name][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce][-path path]filename - name + name - The name of the SPEF parasitics to use for defining scenes. The default is the base name of filename. + The name of the SPEF parasitics to use for defining scenes. The default is the base name of filename. - path + path - Hierarchical block instance path to annotate with parasitics. + Hierarchical block instance path to annotate with parasitics. - ‑keep_capacitive_coupling + ‑keep_capacitive_coupling - Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. + Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. - ‑coupling_reduction_factorfactor + ‑coupling_reduction_factorfactor - Factor to multiply coupling capacitance by when reducing parasitic networks. The default value is 1.0. + Factor to multiply coupling capacitance by when reducing parasitic networks. The default value is 1.0. - filename + filename - The name of the parasitics file to read. + The name of the parasitics file to read. - The read_spef command reads a file of net parasitics in SPEF format. Use the report_parasitic_annotation command to check for nets that are not annotated. + The read_spef command reads a file of net parasitics in SPEF format. Use the report_parasitic_annotation command to check for nets that are not annotated. Files compressed with gzip are automatically uncompressed. - Separate min/max parasitics can be annotated for each scene mode/corner. - read_spef -name min spef1read_spef -name max spef2define_scene -mode mode1 -spef_min min -spef_max max - Coupling capacitors are multiplied by the –coupling_reduction_factor when a parasitic network is reduced. + Separate min/max parasitics can be annotated for each scene mode/corner. + read_spef -name min spef1read_spef -name max spef2define_scene -mode mode1 -spef_min min -spef_max max + Coupling capacitors are multiplied by the –coupling_reduction_factor when a parasitic network is reduced. The following SPEF constructs are ignored. *DESIGN_FLOW (all values are ignored)*S slews*D driving cell*I pin capacitances (library cell capacitances are used instead)*Q r_net load poles*K r_net load residues - If the SPEF file contains triplet values the first value is used. - Parasitic networks (DSPEF) can be annotated on hierarchical blocks using the -path argument to specify the instance path to the block. Parasitic networks in the higher level netlist are stitched together at the hierarchical pins of the blocks. + If the SPEF file contains triplet values the first value is used. + Parasitic networks (DSPEF) can be annotated on hierarchical blocks using the -path argument to specify the instance path to the block. Parasitic networks in the higher level netlist are stitched together at the hierarchical pins of the blocks. - read_vcd + read_vcd - [-scope scope][-mode mode_name]filename + [-scope scope][-mode mode_name]filename - scope + scope - The VCD scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. + The VCD scope of the current design to extract simulation data. Typically the test bench name and design under test instance name. Scope levels are separated with ‘/’. - mode_name + mode_name - Mode to annotate activities. + Mode to annotate activities. - filename + filename - The name of the VCD file to read. + The name of the VCD file to read. - The read_vcd command reads a VCD (Value Change Dump) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. + The read_vcd command reads a VCD (Value Change Dump) file from a Verilog simulation and extracts pin activities and duty cycles for use in power estimation. Files compressed with gzip are supported. Annotated activities are propagated to the fanout of the annotated pins. - read_verilog + read_verilog - filename + filename - filename + filename - The name of the verilog file to read. + The name of the verilog file to read. - The read_verilog command reads a gate level verilog netlist. After all verilog netlist and Liberty libraries are read the design must be linked with the link_design command. - Verilog 2001 module port declaratations are supported. An example is shown below. + The read_verilog command reads a gate level verilog netlist. After all verilog netlist and Liberty libraries are read the design must be linked with the link_design command. + Verilog 2001 module port declaratations are supported. An example is shown below. module top (input in1, in2, clk1, clk2, clk3, output out); Files compressed with gzip are automatically uncompressed. @@ -8897,707 +9001,715 @@ - replace_cell + replace_cell - instance_listreplacement_cell + instance_listreplacement_cell - instance_list + instance_list - A list of instances to swap the cell. + A list of instances to swap the cell. - replacement_cell + replacement_cell - The replacement lib cell. + The replacement lib cell. - The replace_cell command changes the cell of an instance. The replacement cell must have the same port list (number, name, and order) as the instance's existing cell for the replacement to be successful. + The replace_cell command changes the cell of an instance. The replacement cell must have the same port list (number, name, and order) as the instance's existing cell for the replacement to be successful. - replace_activity_annotation + replace_activity_annotation - [-report_unannotated][-report_annotated] + [-report_unannotated][-report_annotated] - -report_unannotated + -report_unannotated - Report unannotated pins. + Report unannotated pins. - -report_unannotated + -report_unannotated - Report annotated pins. + Report annotated pins. - Report a summary of pins that are annotated by read_vcd, read_saif or set_power_activity. Sequential internal pins and hierarchical pins are ignored. + Report a summary of pins that are annotated by read_vcd, read_saif or set_power_activity. Sequential internal pins and hierarchical pins are ignored. - report_annotated_check + report_annotated_check - [-setup][-hold][-recovery][-removal][-nochange][-width][-period][-max_skew][-max_line lines][-report_annotated][-report_unannotated][-constant_arcs] + [-setup][-hold][-recovery][-removal][-nochange][-width][-period][-max_skew][-max_line lines][-report_annotated][-report_unannotated][-constant_arcs] - -setup + -setup - Report annotated setup checks. + Report annotated setup checks. - -hold + -hold - Report annotated hold checks. + Report annotated hold checks. - -recovery + -recovery - Report annotated recovery checks. + Report annotated recovery checks. - -removal + -removal - Report annotated removal checks. + Report annotated removal checks. - -nochange + -nochange - Report annotated nochange checks. + Report annotated nochange checks. - -width + -width - Report annotated width checks. + Report annotated width checks. - -period + -period - Report annotated period checks. + Report annotated period checks. - -max_skew + -max_skew - Report annotated max skew checks. + Report annotated max skew checks. - -max_line lines + -max_line lines - Maximum number of lines listed by the report_annotated and ‑report_unannotated options. + Maximum number of lines listed by the report_annotated and ‑report_unannotated options. - -report_annotated + -report_annotated - Report annotated timing arcs. + Report annotated timing arcs. - -report_unannotated + -report_unannotated - Report unannotated timing arcs. + Report unannotated timing arcs. - -constant_arcs + -constant_arcs - Report separate annotation counts for arcs disabled by logic constants (set_logic_one, set_logic_zero). + Report separate annotation counts for arcs disabled by logic constants (set_logic_one, set_logic_zero). - The report_annotated_check command reports a summary of SDF timing check annotation. The -report_annotated and report_annotated options can be used to list arcs that are annotated or not annotated. + The report_annotated_check command reports a summary of SDF timing check annotation. The -report_annotated and report_annotated options can be used to list arcs that are annotated or not annotated. - report_annotated_delay + report_annotated_delay - [-cell][-net][-from_in_ports][-to_out_ports][-max_lines lines][-report_annotated][-report_unannotated][-constant_arcs] + [-cell][-net][-from_in_ports][-to_out_ports][-max_lines lines][-report_annotated][-report_unannotated][-constant_arcs] - -cell + -cell - Report annotated cell delays. + Report annotated cell delays. - -net + -net - Report annotated internal net delays. + Report annotated internal net delays. - -from_in_ports + -from_in_ports - Report annotated delays from input ports. + Report annotated delays from input ports. - -to_out_ports + -to_out_ports - Report annotated delays to output ports. + Report annotated delays to output ports. - -max_lines lines + -max_lines lines - Maximum number of lines listed by the report_annotated and ‑report_unannotated options. + Maximum number of lines listed by the report_annotated and ‑report_unannotated options. - -report_annotated + -report_annotated - Report annotated timing arcs. + Report annotated timing arcs. - -report_unannotated + -report_unannotated - Report unannotated timing arcs. + Report unannotated timing arcs. - -constant_arcs + -constant_arcs Report separate annotation counts for arcs disabled by logic constants (set_logic_one, set_logic_zero). - The report_annotated_delay command reports a summary of SDF delay annotation. Without the ‑from_in_ports and –to_out_ports options arcs to and from top level ports are not reported. The ‑report_annotated and report_unannotated options can be used to list arcs that are annotated or not annotated. + The report_annotated_delay command reports a summary of SDF delay annotation. Without the ‑from_in_ports and –to_out_ports options arcs to and from top level ports are not reported. The ‑report_annotated and report_unannotated options can be used to list arcs that are annotated or not annotated. - report_checks + report_checks - [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-unique_edges_to_endpoint][-scenes scenes][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups][-format end|full|short|summary |full_clock|full_clock_expanded |json][-fields fields][-digits digits][-no_line_split][> filename][>> filename] + [-from from_list |-rise_from from_list |-fall_from from_list][-through through_list |-rise_through through_list |-fall_through through_list][-to to_list |-rise_to to_list |-fall_to to_list][-unconstrained][-path_delay min|min_rise|min_fall |max|max_rise|max_fall |min_max][-group_path_count path_count][-endpoint_path_count endpoint_path_count][-unique_paths_to_endpoint][-unique_edges_to_endpoint][-scenes scenes][-slack_max max_slack][-slack_min min_slack][-sort_by_slack][-path_group groups][-format end|full|short|summary |full_clock|full_clock_expanded |json][-fields fields][-digits digits][-no_line_split][> filename][>> filename] - -from from_list + -from from_list - Report paths from a list of clocks, instances, ports, register clock pins, or latch data pins. + Report paths from a list of clocks, instances, ports, register clock pins, or latch data pins. - -rise_from from_list + -rise_from from_list - Report paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. + Report paths from the rising edge of clocks, instances, ports, register clock pins, or latch data pins. - -fall_from from_list + -fall_from from_list - Report paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. + Report paths from the falling edge of clocks, instances, ports, register clock pins, or latch data pins. - -through through_list + -through through_list - Report paths through a list of instances, pins or nets. + Report paths through a list of instances, pins or nets. - -rise_through through_list + -rise_through through_list - Report rising paths through a list of instances, pins or nets. + Report rising paths through a list of instances, pins or nets. - -fall_through through_list + -fall_through through_list - Report falling paths through a list of instances, pins or nets. + Report falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Report paths to a list of clocks, instances, ports or pins. + Report paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Report rising paths to a list of clocks, instances, ports or pins. + Report rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Report falling paths to a list of clocks, instances, ports or pins. + Report falling paths to a list of clocks, instances, ports or pins. - -unconstrained + -unconstrained - Report unconstrained paths also. The unconstrained path group is not reported without this option. + Report unconstrained paths also. The unconstrained path group is not reported without this option. - -path_delay min + -path_delay min - Report min path (hold) checks. + Report min path (hold) checks. - -path_delay min_rise + -path_delay min_rise - Report min path (hold) checks for rising endpoints. + Report min path (hold) checks for rising endpoints. - -path_delay min_fall + -path_delay min_fall - Report min path (hold) checks for falling endpoints. + Report min path (hold) checks for falling endpoints. - -path_delay max + -path_delay max - Report max path (setup) checks. + Report max path (setup) checks. - -path_delay max_rise + -path_delay max_rise - Report max path (setup) checks for rising endpoints. + Report max path (setup) checks for rising endpoints. - -path_delay max_fall + -path_delay max_fall - Report max path (setup) checks for falling endpoints. + Report max path (setup) checks for falling endpoints. - -path_delay min_max + -path_delay min_max - Report max and max path (setup and hold) checks. + Report max and max path (setup and hold) checks. - -group_path_count path_count + -group_path_count path_count - The number of paths to report in each path group. The default is 1. + The number of paths to report in each path group. The default is 1. - -endpoint_path_count endpoint_path_count + -endpoint_path_count endpoint_path_count - The number of paths to report for each endpoint. The default is 1. + The number of paths to report for each endpoint. The default is 1. - ‑unique_paths_to_endpoint + ‑unique_paths_to_endpoint - When multiple paths to an endpoint are specified with ‑endpoint_path_count, many of the paths may differ only in the rise/fall edges of the pins in the paths. With this option only the worst path through the set of pins is reported. + When multiple paths to an endpoint are specified with ‑endpoint_path_count, many of the paths may differ only in the rise/fall edges of the pins in the paths. With this option only the worst path through the set of pins is reported. - ‑unique_edges_to_endpoint + ‑unique_edges_to_endpoint - When multiple paths to an endpoint are specified with ‑endpoint_path_count, conditional timing arcs result in paths that through the same pins and rise/fall edges. With this option only the worst path through the set of pins and rise/fall edges is reported. + When multiple paths to an endpoint are specified with ‑endpoint_path_count, conditional timing arcs result in paths that through the same pins and rise/fall edges. With this option only the worst path through the set of pins and rise/fall edges is reported. - scenes + scenes - Report paths for one process corner. The default is to report paths for all process corners. + Report paths for one process corner. The default is to report paths for all process corners. - max_slack + max_slack - Only report paths with less slack than max_slack. + Only report paths with less slack than max_slack. - min_slack + min_slack - Only report paths with more slack than min_slack. + Only report paths with more slack than min_slack. - -sort_by_slack + -sort_by_slack - Sort paths by slack rather than slack grouped by path group. + Sort paths by slack rather than slack grouped by path group. - groups + groups - List of path groups to report. The default is to report all path groups. + List of path groups to report. The default is to report all path groups. - -format end + -format end - Report path ends in one line with delay, required time and slack. + Report path ends in one line with delay, required time and slack. - -format full + -format full - Report path start and end points and the path. This is the default path type. + Report path start and end points and the path. This is the default path type. - -format full_clock + -format full_clock - Report path start and end points, the path, and the source and and target clock paths. + Report path start and end points, the path, and the source and and target clock paths. - -format full_clock_expanded + -format full_clock_expanded - Report path start and end points, the path, and the source and and target clock paths. If the clock is generated and propagated, the path from the clock source pin is also reported. + Report path start and end points, the path, and the source and and target clock paths. If the clock is generated and propagated, the path from the clock source pin is also reported. - -format short + -format short - Report only path start and end points. + Report only path start and end points. - -format summary + -format summary - Report only path ends with delay. + Report only path ends with delay. - -format json + -format json - Report in json format. -fields is ignored. + Report in json format. -fields is ignored. - fields + fields - List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance - digits + digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - -no_line_splits + -no_line_splits - Do not split long lines into multiple lines. + Do not split long lines into multiple lines. - The report_checks command reports paths in the design. Paths are reported in groups by capture clock, unclocked path delays, gated clocks and unconstrained. - See set_false_path for a description of allowed from_list, through_list and to_list objects. + The report_checks command reports paths in the design. Paths are reported in groups by capture clock, unclocked path delays, gated clocks and unconstrained. + See set_false_path for a description of allowed from_list, through_list and to_list objects. - report_check_types + report_check_types - [-scenes scenes][-violators][-verbose][-format slack_only|end][-max_delay][-min_delay][-recovery][-removal][-clock_gating_setup][-clock_gating_hold][-max_slew][-min_slew][-min_pulse_width][-min_period][-digits digits][-no_split_lines][> filename][>> filename] + [-scenes scenes][-violators][-verbose][-fields fields][-format slack_only|end][-max_delay][-min_delay][-recovery][-removal][-clock_gating_setup][-clock_gating_hold][-max_slew][-min_slew][-min_pulse_width][-min_period][-digits digits][-no_split_lines][> filename][>> filename] - scenes + scenes - Report checks for some scens. The default value is all scenes. + Report checks for some scenes. The default value is all scenes. - -violators + -violators - Report all violated timing and design rule constraints. + Report all violated timing and design rule constraints. - -verbose + -verbose - Use a verbose output format. + Use a verbose output format. - -format slack_only + -format slack_only - Report the minimum slack for each timing check. + Report the minimum slack for each timing check. - -format end + -format end - Report the endpoint for each check. + Report the endpoint for each check. - -max_delay + fields - Report setup and max delay path delay constraints. + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance - -min_delay + -max_delay - Report hold and min delay path delay constraints. + Report setup and max delay path delay constraints. - -recovery + -min_delay - Report asynchronous recovery checks. + Report hold and min delay path delay constraints. - -removal + -recovery - Report asynchronous removal checks. + Report asynchronous recovery checks. - -clock_gating_setup + -removal - Report gated clock enable setup checks. + Report asynchronous removal checks. - -clock_gating_hold + -clock_gating_setup - Report gated clock hold setup checks. + Report gated clock enable setup checks. - -max_slew + -clock_gating_hold - Report max transition design rule checks. + Report gated clock hold setup checks. - -max_skew + -max_slew - Report max skew design rule checks. + Report max transition design rule checks. - -min_pulse_width + -max_skew - Report min pulse width design rule checks. + Report max skew design rule checks. - -min_period + -min_pulse_width - Report min period design rule checks. + Report min pulse width design rule checks. - -min_slew + -min_period - Report min slew design rule checks. + Report min period design rule checks. - -digits digits + -min_slew - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + Report min slew design rule checks. - -no_split_lines + -digits digits - Do not split long lines into multiple lines. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + + + + + -no_split_lines + + + Do not split long lines into multiple lines. @@ -9607,93 +9719,92 @@ - report_clock_latency + report_clock_latency - [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] - clocks + clocks - The clocks to report. The default value is all c + The clocks to report. The default value is all c - scenes + scenes - Report clocks for scenes. The default value is all clocks in scenes modes. + Report clocks for scenes. The default value is all clocks in scenes modes. - -include_internal_latency + -include_internal_latency - Include internal clock latency from liberty min/max_clock_tree_path timing groups. + Include internal clock latency from liberty min/max_clock_tree_path timing groups. - digits + digits - The number of digits to report for delays. + The number of digits to report for delays. - Report the clock network latency. + Report the clock network latency. - report_clock_min_period + report_clock_min_period - [-clocks clocks][-scenes scenes][-include_port_paths] + [-clocks clocks][-scenes scenes][-include_port_paths] - clocks + clocks - The clocks to report. + The clocks to report. - -include_port_paths + -include_port_paths - Include paths from input port and to output ports. + Include paths from input port and to output ports. - Report the minimum period and maximum frequency for clocks. If the -clocks argument is not specified all clocks are reported. The minimum period is determined by examining the smallest slack paths between registers the rising edges of the clock or between falling edges of the clock. Paths between different clocks, different clock edges of the same clock, level sensitive latches, or paths constrained by set_multicycle_path, set_max_path are not considered. + Report the minimum period and maximum frequency for clocks. If the -clocks argument is not specified all clocks are reported. The minimum period is determined by examining the smallest slack paths between registers the rising edges of the clock or between falling edges of the clock. Paths between different clocks, different clock edges of the same clock, level sensitive latches, or paths constrained by set_multicycle_path, set_max_path are not considered. - - report_clock_properties + report_clock_properties - [clock_names] + [clock_names] - clock_names + clock_names - List of clock names to report. + List of clock names to report. @@ -9703,120 +9814,120 @@ - report_clock_skew + report_clock_skew - [-setup|-hold][-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + [-setup|-hold][-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] - -setup + -setup - Report skew for setup checks. + Report skew for setup checks. - -hold + -hold - Report skew for hold checks. + Report skew for hold checks. - clocks + clocks - The clocks to report. The default value is all clocks in scenes modes. + The clocks to report. The default value is all clocks in scenes modes. - scenes + scenes - Report clocks for scenes. The default value is all scenes. + Report clocks for scenes. The default value is all scenes. - -include_internal_latency + -include_internal_latency - Include internal clock latency from liberty min/max_clock_tree_path timing groups. + Include internal clock latency from liberty min/max_clock_tree_path timing groups. - -digits digits + -digits digits - The number of digits to report for delays. + The number of digits to report for delays. - Report the maximum difference in clock arrival between every source and target register that has a path between the source and target registers. + Report the maximum difference in clock arrival between every source and target register that has a path between the source and target registers. - report_dcalc + report_dcalc - [-from from_pin][-to to_pin][-scene scene][-min][-max][-digits digits][> filename][>> filename] + [-from from_pin][-to to_pin][-scene scene][-min][-max][-digits digits][> filename][>> filename] - from_pin + from_pin - Report delay calculations for timing arcs from instance input pin from_pin. + Report delay calculations for timing arcs from instance input pin from_pin. - to_pin + to_pin - Report delay calculations for timing arcs to instance output pin to_pin. - - - - - scene - - - Report paths for process scene. The -scene keyword is required if more than one process corner is defined. + Report delay calculations for timing arcs to instance output pin to_pin. - -min + scene - Report delay calculation for min delays. + Report paths for process scene. The -scene keyword is required if more than one process corner is defined. - -max + -min - Report delay calculation for max delays. + Report delay calculation for min delays. - -digits digits + -max - The number of digits after the decimal point to report. The default is sta_report_default_digits. + Report delay calculation for max delays. + + + + + -digits digits + + + The number of digits after the decimal point to report. The default is sta_report_default_digits. @@ -9826,61 +9937,78 @@ - report_disabled_edges + report_disabled_edges - + The report_disabled_edges command reports disabled timing arcs along with the reason they are disabled. Each disabled timing arc is reported as the instance name along with the from and to ports of the arc. The disable reason is shown next. Arcs that are disabled with set_disable_timing are reported with constraint as the reason. Arcs that are disabled by constants are reported with constant as the reason along with the constant instance pin and value. Arcs that are disabled to break combinational feedback loops are reported with loop as the reason. - > report_disabled_edgesu1 A B constant B=0 + > report_disabled_edgesu1 A B constant B=0 - report_edges + report_edges - [-from from_pin][-to to_pin] + [-from from_pin][-to to_pin][-report_variation][-digits digits] - -from from_pin + from_pin - Report edges/timing arcs from pin from_pin. + Report edges/timing arcs from pin from_pin. - -to to_pin + to_pin - Report edges/timing arcs to pin to_pin. + Report edges/timing arcs to pin to_pin. + + + + + -report_variation + + + + + + + + digits + + + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - Report the edges/timing arcs and their delays in the timing graph from/to/between pins. + Report the edges/timing arcs and their delays in the timing graph from/to/between pins. + - report_instance + report_instance - instance_path[> filename][>> filename] + instance_path[> filename][>> filename] - instance_path + instance_path - Hierarchical path to an instance. + Hierarchical path to an instance. @@ -9890,262 +10018,280 @@ - report_lib_cell + report_lib_cell - cell_name[> filename][>> filename] + cell_name[> filename][>> filename] - - cell_name + cell_name - The name of a library cell. + The name of a library cell. - Describe the liberty library cell cell_name. + Describe the liberty library cell cell_name. - report_net + report_net - [-digits digits]net_path[> filename][>> filename] + [-digits digits]net_path[> filename][>> filename] - -digits digits + digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - net_path + net_path - Hierarchical path to a net. + Hierarchical path to a net. - Report the connections and capacitance of a net. + Report the connections and capacitance of a net. - report_parasitic_annotation + report_parasitic_annotation - [-report_unannotated][> filename][>> filename] + [-report_unannotated][> filename][>> filename] - -report_unannotated + -report_unannotated - Report unannotated and partially annotated nets. + Report unannotated and partially annotated nets. - Report SPEF parasitic annotation completeness. + Report SPEF parasitic annotation completeness. - report_power + report_power - [-instances instances][-highest_power_instances count][-digits digits][> filename][>> filename] + [-instances instances][-highest_power_instances count][-digits digits][> filename][>> filename] + + + + + + -instances instances + + + Report the power for each instance of instances. If the instance is hierarchical the total power for the instances inside the hierarchical instance is reported. - -instances instances + -highest_power_instances count - Report the power for each instance of instances. If the instance is hierarchical the total power for the instances inside the hierarchical instance is reported. + Report the power for the count highest power instances. - -highest_power_instances count + -digits digits - Report the power for the count highest power instances. - - - - - -digits digits - - - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - The report_power command uses static power analysis based on propagated or annotated pin activities in the circuit using Liberty power models. The internal, switching, leakage and total power are reported. Design power is reported separately for combinational, sequential, macro and pad groups. Power values are reported in watts. - The read_vcd or read_saif commands can be used to read activities from a file based on simulation. If no simulation activities are available, the set_power_activity command should be used to set the activity of input ports or pins in the design. The default input activity and duty for inputs are 0.1 and 0.5 respectively. The activities are propagated from annotated input ports or pins through gates and used in the power calculations. + The report_power command uses static power analysis based on propagated or annotated pin activities in the circuit using Liberty power models. The internal, switching, leakage and total power are reported. Design power is reported separately for combinational, sequential, macro and pad groups. Power values are reported in watts. + The read_vcd or read_saif commands can be used to read activities from a file based on simulation. If no simulation activities are available, the set_power_activity command should be used to set the activity of input ports or pins in the design. The default input activity and duty for inputs are 0.1 and 0.5 respectively. The activities are propagated from annotated input ports or pins through gates and used in the power calculations. Group Internal Switching Leakage Total Power Power Power Power----------------------------------------------------------------Sequential 3.29e-06 3.41e-08 2.37e-07 3.56e-06 92.4%Combinational 1.86e-07 3.31e-08 7.51e-08 2.94e-07 7.6%Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%---------------------------------------------------------------Total 3.48e-06 6.72e-08 3.12e-07 3.86e-06 100.0% 90.2% 1.7% 8.1% - report_slews + report_slews - [-scenes scenes]pin + [-scenes scenes][-report_variation][-digits digits]pin - scenes + scenes - Report slews for process for scenes process corners.. + Report slews for process for scenes process corners. - pin + -report_variation - + Report SSTA distribution parameters. + + + + + -digits digits + + + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + + + + + pin + + + - Report the slews at pin + Report the slews at pin + - report_tns + report_tns - [-min][-max][-digits digits] + [-min][-max][-digits digits] - -max + -max - Report the total max/setup slack. + Report the total max/setup slack. - -min + -min - Report the total min/hold slack. + Report the total min/hold slack. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - Report the total negative slack. + Report the total negative slack. - report_units + report_units - + - Report the units used for command arguments and reporting. - report_units time 1ns capacitance 1pF resistance 1kohm voltage 1v current 1A power 1pW distance 1um + Report the units used for command arguments and reporting. + report_units time 1ns capacitance 1pF resistance 1kohm voltage 1v current 1A power 1pW distance 1um - report_wns + report_wns - [-min][-max][-digits digits] + [-min][-max][-digits digits] - -max + -max - Report the worst max/setup slack. + Report the worst max/setup slack. - -min + -min - Report the worst min/hold slack. + Report the worst min/hold slack. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - Report the worst negative slack. If the worst slack is positive, zero is reported. + Report the worst negative slack. If the worst slack is positive, zero is reported. - report_worst_slack + report_worst_slack - [-min][-max][-digits digits] + [-min][-max][-digits digits] + + + + + + -max + + + Report the worst max/setup slack. - -max + -min - Report the worst max/setup slack. + Report the worst min/hold slack. - -min + -digits digits - Report the worst min/hold slack. - - - - - -digits digits - - - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. @@ -10153,117 +10299,117 @@ + + + set_assigned_check + + + -setup|-hold|-recovery|-removal[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin + + + + + -setup + + + Annotate setup timing checks. + + + + + -hold + + + Annotate hold timing checks. + + + + + -recovery + + + Annotate recovery timing checks. + + + + + -removal + + + Annotate removal timing checks. + + + + + -rise + + + Annotate rising delays. + + + + + -fall + + + Annotate falling delays. + + + + + scene + + + The name of a scene. The -scene keyword is required if more than one scene is defined. + + + + + -min + + + Annotate the minimum value of the process corner. + + + + + -max + + + Annotate the maximum value of the process corner. + + + + + from_pins + + + A list of pins for the clock. + + + + + to_pins + + + A list of pins for the data. + + - - set_assigned_check + + -clock rise|fall - - -setup|-hold|-recovery|-removal[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin + + The timing check clock pin transition. - -setup + margin - Annotate setup timing checks. - - - - - -hold - - - Annotate hold timing checks. - - - - - -recovery - - - Annotate recovery timing checks. - - - - - -removal - - - Annotate removal timing checks. - - - - - -rise - - - Annotate rising delays. - - - - - -fall - - - Annotate falling delays. - - - - - scene - - - The name of a scene. The -scene keyword is required if more than one scene is defined. - - - - - -min - - - Annotate the minimum value of the process corner. - - - - - -max - - - Annotate the maximum value of the process corner. - - - - - from_pins - - - A list of pins for the clock. - - - - - to_pins - - - A list of pins for the data. - - - - - -clock rise|fall - - - The timing check clock pin transition. - - - - - margin - - - The timing check margin. + The timing check margin. @@ -10271,93 +10417,92 @@ - - set_assigned_delay + set_assigned_delay - -cell|-net[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins]delay + -cell|-net[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins]delay - -cell + -cell - Annotate the delays between two pins on an instance. + Annotate the delays between two pins on an instance. - -net + -net - Annotate the delays between two pins on a net. + Annotate the delays between two pins on a net. - -rise + -rise - Annotate the rising delays. + Annotate the rising delays. - -fall + -fall - Annotate the falling delays. + Annotate the falling delays. - scene + scene - The name of a scene. The -scene keyword is required if more than one scene is defined. + The name of a scene. The -scene keyword is required if more than one scene is defined. - -min + -min - Annotate the minimum delays. + Annotate the minimum delays. - -max + -max - Annotate the maximum delays. + Annotate the maximum delays. - from_pins + from_pins - A list of pins. + A list of pins. - to_pins + to_pins - A list of pins. + A list of pins. - delay + delay - The delay between from_pins and to_pins. + The delay between from_pins and to_pins. @@ -10365,69 +10510,69 @@ - - - set_assigned_transition - - - [-rise][-fall][-scene scene][-min][-max]slewpin_list - - - - - -rise - - - Annotate the rising transition. - - - - -fall + + set_assigned_transition - - Annotate the falling transition. + + [-rise][-fall][-scene scene][-min][-max]slewpin_list - scene + -rise - Annotate delays for scene. + Annotate the rising transition. - -min + -fall - Annotate the minimum transition time. + Annotate the falling transition. - -max + scene - Annotate the maximum transition time. + Annotate delays for scene. - slew + -min - The pin transition time. + Annotate the minimum transition time. - pin_list + -max - A list of pins. + Annotate the maximum transition time. + + + + + slew + + + The pin transition time. + + + + + pin_list + + + A list of pins. @@ -10437,89 +10582,89 @@ - set_case_analysis + set_case_analysis - 0|1|zero|one|rise|rising|fall|fallingport_or_pin_list + 0|1|zero|one|rise|rising|fall|fallingport_or_pin_list - port_or_pin_list + port_or_pin_list - A list of ports or pins. + A list of ports or pins. The set_case_analysis command sets the signal on a port or pin to a constant logic value. No paths are propagated from constant pins. Constant values set with the set_case_analysis command are propagated through downstream gates. - Conditional timing arcs with mode groups are controlled by logic values on the instance pins. + Conditional timing arcs with mode groups are controlled by logic values on the instance pins. - set_clock_gating_check + set_clock_gating_check - [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] - - - - - -setup setup_time - - - Clock enable setup margin. - - - - - -hold hold_time - - - Clock enable hold margin. - - - - - -rise - - - The setup/hold margin is for the rising edge of the clock enable. - - - - - -fall - - - The setup/hold margin is for the falling edge of the clock enable. - - - - - -high - - - The gating clock is active high (pin and instance objects only). + [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] - -low + -setup setup_time - The gating clock is active low (pin and instance objects only). + Clock enable setup margin. - objects + -hold hold_time - A list of clocks, instances, pins or ports. + Clock enable hold margin. + + + + + -rise + + + The setup/hold margin is for the rising edge of the clock enable. + + + + + -fall + + + The setup/hold margin is for the falling edge of the clock enable. + + + + + -high + + + The gating clock is active high (pin and instance objects only). + + + + + -low + + + The gating clock is active low (pin and instance objects only). + + + + + objects + + + A list of clocks, instances, pins or ports. @@ -10533,197 +10678,197 @@ - set_clock_groups + set_clock_groups - [-name name][-logically_exclusive][-physically_exclusive][-asynchronous][-allow_paths]-group clocks + [-name name][-logically_exclusive][-physically_exclusive][-asynchronous][-allow_paths]-group clocks - -name name + -name name - The clock group name. + The clock group name. - -logically_exclusive + -logically_exclusive - The clocks in different groups do not interact logically but can be physically present on the same chip. Paths between clock groups are considered for noise analysis. + The clocks in different groups do not interact logically but can be physically present on the same chip. Paths between clock groups are considered for noise analysis. + + + + + + -physically_exclusive + + + The clocks in different groups cannot be present at the same time on a chip. Paths between clock groups are not considered for noise analysis. - -physically_exclusive + -asynchronous - The clocks in different groups cannot be present at the same time on a chip. Paths between clock groups are not considered for noise analysis. + The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. - -asynchronous + -allow_paths - The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. - - - - - -allow_paths - - - + - clocks + clocks - A list of clocks in the group. + A list of clocks in the group. - The set_clock_groups command is used to define groups of clocks that interact with each other. Clocks in different groups do not interact and paths between them are not reported. Use a –group argument for each clock group. + The set_clock_groups command is used to define groups of clocks that interact with each other. Clocks in different groups do not interact and paths between them are not reported. Use a –group argument for each clock group. - set_clock_latency + set_clock_latency - [-source][-clock clock][-rise][-fall][-min][-max]delayobjects + [-source][-clock clock][-rise][-fall][-min][-max]delayobjects - -source + -source - The latency is at the clock source. + The latency is at the clock source. - -clock clock + -clock clock - If multiple clocks are defined at a pin this use this option to specify the latency for a specific clock. + If multiple clocks are defined at a pin this use this option to specify the latency for a specific clock. - -rise + -rise - The latency is for the rising edge of the clock. + The latency is for the rising edge of the clock. - -fall + -fall - The latency is for the falling edge of the clock. + The latency is for the falling edge of the clock. - -min + -min - delay is the minimum latency. + delay is the minimum latency. - -max + -max - delay is the maximum latency. + delay is the maximum latency. - delay + delay - Clock source or insertion delay. + Clock source or insertion delay. - objects + objects - A list of clocks, pins or ports. + A list of clocks, pins or ports. - The set_clock_latency command describes expected delays of the clock tree when anxsalyzing a design using ideal clocks. Use the -source option to specify latency at the clock source, also known as insertion delay. Source latency is delay in the clock tree that is external to the design or a clock tree internal to an instance that implements a complex logic function.set_clock_latency removes propagated clock properties for the clocks and pins objects. + The set_clock_latency command describes expected delays of the clock tree when anxsalyzing a design using ideal clocks. Use the -source option to specify latency at the clock source, also known as insertion delay. Source latency is delay in the clock tree that is external to the design or a clock tree internal to an instance that implements a complex logic function.set_clock_latency removes propagated clock properties for the clocks and pins objects. - set_clock_transition + set_clock_transition - [-rise][-fall][-min][-max]transitionclocks + [-rise][-fall][-min][-max]transitionclocks - -rise + -rise - Set the transition time for the rising edge of the clock. - - - - - - -fall - - - Set the transition time for the falling edge of the clock. + Set the transition time for the rising edge of the clock. - -min + -fall - Set the min transition time. + Set the transition time for the falling edge of the clock. - -max + -min - Set the min transition time. + Set the min transition time. - transition + -max - Clock transition time (slew). + Set the min transition time. - clocks + transition - A list of clocks. + Clock transition time (slew). + + + + + clocks + + + A list of clocks. @@ -10733,154 +10878,155 @@ - set_clock_uncertainty + set_clock_uncertainty - [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold]uncertainty[objects] + [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold]uncertainty[objects] - -from from_clock + -from from_clock - Inter-clock uncertainty source clock. + Inter-clock uncertainty source clock. - -to to_clock + -to to_clock - Inter-clock uncertainty target clock. + Inter-clock uncertainty target clock. - -rise + -rise - Inter-clock target clock rise edge, alternative to ‑rise_to.Inter-clock target clock rise edge, alternative to ‑rise_to. + Inter-clock target clock rise edge, alternative to ‑rise_to.Inter-clock target clock rise edge, alternative to ‑rise_to. - -fall + -fall - Inter-clock target clock rise edge, alternative to ‑fall_to. + Inter-clock target clock rise edge, alternative to ‑fall_to. - -setup + -setup - uncertainty is for setup checks. + uncertainty is for setup checks. + + + + + + -hold + + + uncertainty is for hold checks. - -hold + uncertainty - uncertainty is for hold checks. + Clock uncertainty. - uncertainty + objects - Clock uncertainty. - - - - - objects - - - A list of clocks, ports or pins. + A list of clocks, ports or pins. - The set_clock_uncertainty command specifies the uncertainty or jitter in a clock. The uncertainty for a clock can be specified on its source pin or port, or the clock itself. - set_clock_uncertainty .1 [get_clock clk1] - Inter-clock uncertainty between the source and target clocks of timing checks is specified with the ‑from|‑rise_from|-fall_from andto|‑rise_to|-fall_to arguments . - set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] .1 - The following commands are equivalent. - set_clock_uncertainty -from [get_clock clk1] -rise_to [get_clocks clk2] .1set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] -rise .1 + The set_clock_uncertainty command specifies the uncertainty or jitter in a clock. The uncertainty for a clock can be specified on its source pin or port, or the clock itself. + set_clock_uncertainty .1 [get_clock clk1] + Inter-clock uncertainty between the source and target clocks of timing checks is specified with the ‑from|‑rise_from|-fall_from andto|‑rise_to|-fall_to arguments . + set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] .1 + The following commands are equivalent. + set_clock_uncertainty -from [get_clock clk1] -rise_to [get_clocks clk2] .1set_clock_uncertainty -from [get_clock clk1] -to [get_clocks clk2] -rise .1 - set_cmd_units + set_cmd_units - [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] + [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] - -capacitance cap_unit + -capacitance cap_unit - The capacitance scale factor followed by 'f'. + The capacitance scale factor followed by 'f'. - -resistance res_unit + -resistance res_unit - The resistance scale factor followed by 'ohm'. + The resistance scale factor followed by 'ohm'. - -time time_unit + -time time_unit - The time scale factor followed by 's'. + The time scale factor followed by 's'. - -voltage voltage_unit + -voltage voltage_unit - The voltage scale factor followed by 'v'. + The voltage scale factor followed by 'v'. - -current current_unit + -current current_unit - The current scale factor followed by 'A'. + The current scale factor followed by 'A'. - -power power_unit + -power power_unit - The power scale factor followed by 'w'. + The power scale factor followed by 'w'. - -distance distance_unit + -distance distance_unit - The distance scale factor followed by 'm'. + The distance scale factor followed by 'm'. - The set_cmd_units command is used to change the units used by the STA command interpreter when parsing commands and reporting results. The default units are the units specified in the first Liberty library file that is read. + The set_cmd_units command is used to change the units used by the STA command interpreter when parsing commands and reporting results. The default units are the units specified in the first Liberty library file that is read. Units are specified as a scale factor followed by a unit name. The scale factors are as follows. - M 1E+6k 1E+3m 1E-3u 1E-6n 1E-9p 1E-12f 1E-15 + M 1E+6k 1E+3m 1E-3u 1E-6n 1E-9p 1E-12f 1E-15 An example of the set_units command is shown below. set_cmd_units -time ns -capacitance pF -current mA -voltage V -resistance kOhm -distance um @@ -10888,58 +11034,58 @@ - set_data_check + set_data_check - [-from|-rise_from|-fall_from from_pin][-to|-rise_to|-fall_to to_pin][-setup][-hold][-clock clock]margin + [-from|-rise_from|-fall_from from_pin][-to|-rise_to|-fall_to to_pin][-setup][-hold][-clock clock]margin - -from from_pin + -from from_pin - A pin used as the timing check reference. + A pin used as the timing check reference. - -to to_pin + -to to_pin - A pin that the setup/hold check is applied to. + A pin that the setup/hold check is applied to. - -setup + -setup - Add a setup timing check. + Add a setup timing check. - -hold + -hold - Add a hold timing check. + Add a hold timing check. - -clock clock + -clock clock - The setup/hold check clock. + The setup/hold check clock. - margin + margin - The setup or hold time margin. + The setup or hold time margin. @@ -10949,123 +11095,121 @@ - set_disable_inferred_clock_gating + set_disable_inferred_clock_gating - objects + objects - objects + objects - A list of clock gating instances, clock gating pins, or clock enable pins. + A list of clock gating instances, clock gating pins, or clock enable pins. - The set_disable_inferred_clock_gating command disables clock gating checks on a clock gating instance, clock gating pin, or clock gating enable pin. + The set_disable_inferred_clock_gating command disables clock gating checks on a clock gating instance, clock gating pin, or clock gating enable pin. - - set_disable_timing + set_disable_timing - [-from from_port][-to to_port]objects + [-from from_port][-to to_port]objects - -from from_port + -from from_port - + - -to to_port + -to to_port - + - objects + objects - A list of instances, ports, pins, cells, cell/port, or library/cell/port. + A list of instances, ports, pins, cells, cell/port, or library/cell/port. The set_disable_timing command is used to disable paths though pins in the design. There are many different forms of the command depending on the objects specified in objects. - All timing paths though an instance are disabled when objects contains an instance. Timing checks in the instance are not disabled. + All timing paths though an instance are disabled when objects contains an instance. Timing checks in the instance are not disabled. set_disable_timing u2 The -from and -to options can be used to restrict the disabled path to those from, to or between specific pins on the instance. set_disable_timing -from A u2set_disable_timing -to Z u2set_disable_timing -from A -to Z u2 A list of top level ports or instance pins can also be disabled. set_disable_timing u2/Zset_disable_timing in1 Timing paths though all instances of a library cell in the design can be disabled by naming the cell using a hierarchy separator between the library and cell name. Paths from or to a cell port can be disabled with the -from and -to options or a port name after library and cell names. - set_disable_timing liberty1/snl_bufx2set_disable_timing -from A liberty1/snl_bufxset_disable_timing -to Z liberty1/snl_bufxset_disable_timing liberty1/snl_bufx2/A + set_disable_timing liberty1/snl_bufx2set_disable_timing -from A liberty1/snl_bufxset_disable_timing -to Z liberty1/snl_bufxset_disable_timing liberty1/snl_bufx2/A - set_drive + set_drive - [-rise][-fall][-max][-min]resistanceports - - - - - - -rise - - - Set the drive rise resistance. + [-rise][-fall][-max][-min]resistanceports - -fall + -rise - Set the drive fall resistance. + Set the drive rise resistance. - -max + -fall - Set the maximum resistance. + Set the drive fall resistance. - -min + -max - Set the minimum resistance. + Set the maximum resistance. - resistance + -min - The external drive resistance. + Set the minimum resistance. - ports + resistance + + + The external drive resistance. + + + + + ports A list of ports. @@ -11078,96 +11222,96 @@ - set_driving_cell + set_driving_cell - [-lib_cell cell_name][-library library][-rise][-fall][-min][-max][-pin pin][-from_pin from_pin][-input_transition_rise trans_rise][-input_transition_fall trans_fall]ports + [-lib_cell cell_name][-library library][-rise][-fall][-min][-max][-pin pin][-from_pin from_pin][-input_transition_rise trans_rise][-input_transition_fall trans_fall]ports - -lib_cell cell_name + -lib_cell cell_name - The driving cell. + The driving cell. - -library library + -library library - The driving cell library. + The driving cell library. - -rise + -rise - Set the driving cell for a rising edge. - - - - - -fall - - - Set the driving cell for a falling edge. - - - - - -max - - - Set the driving cell for max delays. - - - - - -min - - - Set the driving cell for min delays. - - - - - -pin pin - - - The output port of the driving cell. - - - - - -from_pin from_pin - - - Use timing arcs from from_pin to the output pin. + Set the driving cell for a rising edge. - -input_transition_rise trans_rise + -fall - The transition time for a rising input at from_pin. + Set the driving cell for a falling edge. - -input_transition_fall trans_fall + -max - The transition time for a falling input at from_pin. + Set the driving cell for max delays. - ports + -min + + + Set the driving cell for min delays. + + + + + -pin pin + + + The output port of the driving cell. + + + + + -from_pin from_pin + + + Use timing arcs from from_pin to the output pin. + + + + + -input_transition_rise trans_rise + + + The transition time for a rising input at from_pin. + + + + + -input_transition_fall trans_fall + + + The transition time for a falling input at from_pin. + + + + + ports A list of ports. @@ -11180,47 +11324,48 @@ - set_false_path + set_false_path - [-setup][-hold][-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path] + [-setup][-hold][-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path] - -setup + -setup - Apply to setup checks. + Apply to setup checks. - -hold + -hold - Apply to hold checks. + Apply to hold checks. - -rise + -rise - Apply to rising path edges. + Apply to rising path edges. - -fall + -fall - Apply to falling path edges. + Apply to falling path edges. + - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -11228,7 +11373,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11236,7 +11381,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11244,7 +11389,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11252,7 +11397,7 @@ The set_false_path command disables timing along a path from, through and to a group of design objects. - Objects in from_list can be clocks, register/latch instances, or register/latch clock pins. The -rise_from and -fall_from keywords restrict the false paths to a specific clock edge. + Objects in from_list can be clocks, register/latch instances, or register/latch clock pins. The -rise_from and -fall_from keywords restrict the false paths to a specific clock edge. Objects in through_list can be nets, instances, instance pins, or hierarchical pins,. The -rise_through and -fall_through keywords restrict the false paths to a specific path edge that traverses through the object. Objects in to_list can be clocks, register/latch instances, or register/latch clock pins. The -rise_to and -fall_to keywords restrict the false paths to a specific transition at the path end. @@ -11260,10 +11405,10 @@ - set_fanout_load + set_fanout_load - fanoutport_list + fanoutport_list @@ -11273,18 +11418,18 @@ - set_hierarchy_separator + set_hierarchy_separator - separator + separator - separator + separator - Character used to separate hierarchical names. + Character used to separate hierarchical names. @@ -11294,10 +11439,10 @@ - set_ideal_latency + set_ideal_latency - [-rise] [-fall] [-min] [-max] delay objects + [-rise] [-fall] [-min] [-max] delay objects @@ -11307,10 +11452,10 @@ - set_ideal_network + set_ideal_network - [-no_propagation] objects + [-no_propagation] objects @@ -11320,187 +11465,186 @@ - set_ideal_transition + set_ideal_transition - [-rise] [-fall] [-min] [-max] transition_time objects + [-rise] [-fall] [-min] [-max] transition_time objects - The set_ideal_transition command is parsed but ignored. + The set_ideal_transition command is parsed but ignored. - - set_input_delay + set_input_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list - -rise + -rise - Set the arrival time for the rising edge of the input. + Set the arrival time for the rising edge of the input. - -fall + -fall - Set the arrival time for the falling edge of the input. + Set the arrival time for the falling edge of the input. - -max + -max - Set the maximum arrival time. + Set the maximum arrival time. - -min + -min - Set the minimum arrival time. + Set the minimum arrival time. - -clock clock + -clock clock - The arrival time is from clock. + The arrival time is from clock. - -clock_fall + -clock_fall - The arrival time is from the falling edge of clock. + The arrival time is from the falling edge of clock. - -reference_pin ref_pin + -reference_pin ref_pin - The arrival time is with respect to the clock that arrives at ref_pin. + The arrival time is with respect to the clock that arrives at ref_pin. - -source_latency_included + -source_latency_included - D no add the clock source latency (insertion delay) to the delay value. + D no add the clock source latency (insertion delay) to the delay value. - -network_latency_included + -network_latency_included - Do not add the clock latency to the delay value when the clock is ideal. + Do not add the clock latency to the delay value when the clock is ideal. - -add_delay + -add_delay - Add this arrival to any existing arrivals. + Add this arrival to any existing arrivals. - delay + delay - The arrival time after clock. + The arrival time after clock. - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. - The set_input_delay command is used to specify the arrival time of an input signal. - The following command sets the min, max, rise and fall times on the in1 input port 1.0 time units after the rising edge of clk1. - set_input_delay -clock clk1 1.0 [get_ports in1] - Use multiple commands with the -add_delay option to specify separate arrival times for min, max, rise and fall times or multiple clocks. For example, the following specifies separate arrival times with respect to clocks clk1 and clk2. - set_input_delay -clock clk1 1.0 [get_ports in1]set_input_delay -add_delay -clock clk2 2.0 [get_ports in1] - The –reference_pin option is used to specify an arrival time with respect to the arrival on a pin in the clock network. For propagated clocks, the input arrival time is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, input arrival time is relative to the reference pin clock source latency. With the -clock_fall flag the arrival time is relative to the falling transition at the reference pin. If no clocks arrive at the reference pin the set_input_delay command is ignored. If no -clock is specified the arrival time is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. - Paths from inputs that do not have an arrival time defined by set_input_delay are not reported. Set the sta_input_port_default_clock variable to 1 to report paths from inputs without a set_input_delay. + The set_input_delay command is used to specify the arrival time of an input signal. + The following command sets the min, max, rise and fall times on the in1 input port 1.0 time units after the rising edge of clk1. + set_input_delay -clock clk1 1.0 [get_ports in1] + Use multiple commands with the -add_delay option to specify separate arrival times for min, max, rise and fall times or multiple clocks. For example, the following specifies separate arrival times with respect to clocks clk1 and clk2. + set_input_delay -clock clk1 1.0 [get_ports in1]set_input_delay -add_delay -clock clk2 2.0 [get_ports in1] + The –reference_pin option is used to specify an arrival time with respect to the arrival on a pin in the clock network. For propagated clocks, the input arrival time is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, input arrival time is relative to the reference pin clock source latency. With the -clock_fall flag the arrival time is relative to the falling transition at the reference pin. If no clocks arrive at the reference pin the set_input_delay command is ignored. If no -clock is specified the arrival time is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. + Paths from inputs that do not have an arrival time defined by set_input_delay are not reported. Set the sta_input_port_default_clock variable to 1 to report paths from inputs without a set_input_delay. - set_input_transition + set_input_transition - [-rise][-fall][-max][-min]transitionport_list + [-rise][-fall][-max][-min]transitionport_list - -rise + -rise - Set the rising edge transition. + Set the rising edge transition. - -fall + -fall - Set the falling edge transition. + Set the falling edge transition. - -max + -max - Set the minimum transition time. + Set the minimum transition time. - -min + -min - Set the maximum transition time. + Set the maximum transition time. - transition + transition - The transition time (slew). + The transition time (slew). - port_list + port_list - A list of ports. + A list of ports. @@ -11510,10 +11654,10 @@ - set_level_shifter_strategy + set_level_shifter_strategy - [-rule rule_type] + [-rule rule_type] @@ -11524,10 +11668,10 @@ - set_level_shifter_threshold + set_level_shifter_threshold - [-voltage voltage] + [-voltage voltage] @@ -11537,55 +11681,55 @@ - set_load + set_load - [-rise][-fall][-max][-min][-subtract_pin_load][-pin_load][-wire_load]capacitanceobjects + [-rise][-fall][-max][-min][-subtract_pin_load][-pin_load][-wire_load]capacitanceobjects - -rise + -rise - Set the external port rising capacitance (ports only). + Set the external port rising capacitance (ports only). - -fall + -fall - Set the external port falling capacitance (ports only). + Set the external port falling capacitance (ports only). - -max + -max - Set the max capacitance. + Set the max capacitance. - -min + -min - Set the min capacitance. + Set the min capacitance. - -subtract_pin_load + -subtract_pin_load - Subtract the capacitance of all instance pins connected to the net from capacitance (nets only). If the resulting capacitance is negative, zero is used. Pin capacitances are ignored by delay calculation when this option is used. + Subtract the capacitance of all instance pins connected to the net from capacitance (nets only). If the resulting capacitance is negative, zero is used. Pin capacitances are ignored by delay calculation when this option is used. - -pin_load + -pin_load capacitance is external instance pin capacitance (ports only). @@ -11593,7 +11737,7 @@ - -wire_load + -wire_load capacitance is external wire capacitance (ports only). @@ -11601,7 +11745,7 @@ - capacitance + capacitance The capacitance, in library capacitance units. @@ -11609,34 +11753,34 @@ - objects + objects A list of nets or ports. - The set_load command annotates wire capacitance on a net or external capacitance on a port. There are four different uses for the set_load commanc: - set_load -wire_load port external port wire capacitanceset_load -pin_load port external port pin capacitanceset_load port same as -pin_loadset_load net net wire capacitance - External port capacitance can be annotated separately with the -pin_load and ‑wire_load options. Without the -pin_load and -wire_load options pin capacitance is annotated. - When annotating net wire capacitance with the -subtract_pin_load option the capacitance of all instance pins connected to the net is subtracted from capacitance. Setting the capacitance on a net overrides SPEF parasitics for delay calculation. + The set_load command annotates wire capacitance on a net or external capacitance on a port. There are four different uses for the set_load commanc: + set_load -wire_load port external port wire capacitanceset_load -pin_load port external port pin capacitanceset_load port same as -pin_loadset_load net net wire capacitance + External port capacitance can be annotated separately with the -pin_load and ‑wire_load options. Without the -pin_load and -wire_load options pin capacitance is annotated. + When annotating net wire capacitance with the -subtract_pin_load option the capacitance of all instance pins connected to the net is subtracted from capacitance. Setting the capacitance on a net overrides SPEF parasitics for delay calculation. - set_logic_dc + set_logic_dc - port_list + port_list - port_pin_list + port_pin_list - List of ports or pins. + List of ports or pins. @@ -11646,60 +11790,60 @@ - set_logic_one + set_logic_one - port_list + port_list - port_pin_list + port_pin_list - List of ports or pins. + List of ports or pins. - Set a port or pin to a constant logic one value. No paths are propagated from constant pins. Constant values set with the set_logic_one command are not propagated through downstream gates. + Set a port or pin to a constant logic one value. No paths are propagated from constant pins. Constant values set with the set_logic_one command are not propagated through downstream gates. - set_logic_zero + set_logic_zero - port_list + port_list - port_pin_list + port_pin_list - List of ports or pins. + List of ports or pins. - Set a port or pin to a constant logic zero value. No paths are propagated from constant pins. Constant values set with the set_logic_zero command are not propagated through downstream gates. + Set a port or pin to a constant logic zero value. No paths are propagated from constant pins. Constant values set with the set_logic_zero command are not propagated through downstream gates. - set_max_area + set_max_area - area + area - area + area - + @@ -11709,27 +11853,27 @@ - set_max_capacitance + set_max_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance - + - objects + objects - List of ports or cells. + List of ports or cells. @@ -11739,31 +11883,31 @@ - set_max_delay + set_max_delay - [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay + [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay - -rise + -rise - Set max delay for rising paths. + Set max delay for rising paths. - -fall + -fall - Set max delay for falling paths. + Set max delay for falling paths. - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11771,7 +11915,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11779,7 +11923,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11787,7 +11931,7 @@ - -ignore_clock_latency + -ignore_clock_latency Ignore clock latency at the source and target registers. @@ -11795,15 +11939,15 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -11811,7 +11955,7 @@ - delay + delay The maximum delay. @@ -11825,10 +11969,10 @@ - set_max_dynamic_power + set_max_dynamic_power - power [unit] + power [unit] @@ -11838,26 +11982,26 @@ - set_max_fanout + set_max_fanout - fanoutobjects + fanoutobjects - fanout + fanout - + - objects + objects - List of ports or cells. + List of ports or cells. @@ -11867,10 +12011,10 @@ - set_max_leakage_power + set_max_leakage_power - power [unit] + power [unit] @@ -11880,92 +12024,92 @@ - set_max_time_borrow + set_max_time_borrow - delayobjects + delayobjects - delay + delay - The maximum time the latches can borrow. + The maximum time the latches can borrow. - objects + objects - List of clocks, instances or pins. + List of clocks, instances or pins. - The set_max_time_borrow command specifies the maximum amount of time that latches can borrow. Time borrowing is the time that a data input to a transparent latch arrives after the latch opens. + The set_max_time_borrow command specifies the maximum amount of time that latches can borrow. Time borrowing is the time that a data input to a transparent latch arrives after the latch opens. - set_max_transition + set_max_transition - [-data_path][-clock_path][-rise][-fall]transitionobjects + [-data_path][-clock_path][-rise][-fall]transitionobjects - -data_path + -data_path - Set the max slew for data paths. + Set the max slew for data paths. - -clock_path + -clock_path - Set the max slew for clock paths. + Set the max slew for clock paths. - -rise + -rise - Set the max slew for rising paths. + Set the max slew for rising paths. - -fall + -fall - Set the max slew for falling paths. + Set the max slew for falling paths. - transition + transition - The maximum slew/transition time. + The maximum slew/transition time. - objects + objects - List of clocks, ports or designs. + List of clocks, ports or designs. - The set_max_transition command is specifies the maximum transition time (slew) design rule checked by the report_check_types –max_transition command. + The set_max_transition command is specifies the maximum transition time (slew) design rule checked by the report_check_types –max_transition command. If specified for a design, the default maximum transition is set for the design. If specified for a clock, the maximum transition is applied to all pins in the clock domain. The –clock_path option restricts the maximum transition to clocks in clock paths. The -data_path option restricts the maximum transition to clocks data paths. The –clock_path, -data_path, -rise and –fall options only apply to clock objects. @@ -11973,26 +12117,26 @@ - set_min_capacitance + set_min_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance - Minimum capacitance. + Minimum capacitance. - objects + objects - List of ports or cells. + List of ports or cells. @@ -12002,32 +12146,32 @@ - set_min_delay + set_min_delay - [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay + [-rise][-fall][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-ignore_clock_latency][-probe][-reset_path]delay - -rise + -rise - Set min delay for rising paths. + Set min delay for rising paths. - -fall + -fall - Set min delay for falling paths. + Set min delay for falling paths. - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12035,7 +12179,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12043,7 +12187,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12051,7 +12195,7 @@ - -ignore_clock_latency + -ignore_clock_latency Ignore clock latency at the source and target registers. @@ -12059,15 +12203,15 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -12075,10 +12219,10 @@ - delay + delay - The minimum delay. + The minimum delay. @@ -12089,42 +12233,42 @@ - set_min_pulse_width + set_min_pulse_width - [-high][-low]min_widthobjects + [-high][-low]min_widthobjects - -high + -high - Set the minimum high pulse width. + Set the minimum high pulse width. - -low + -low - Set the minimum low pulse width. + Set the minimum low pulse width. - min_width + min_width - + - objects + objects - List of pins, instances or clocks. + List of pins, instances or clocks. @@ -12134,61 +12278,61 @@ - set_mode + set_mode - mode_name + mode_name - The the mode for SDC c ommands in the TCL interpreter. If mode mode_name does not exist, it is created. When modes are created the default mode is deleted. + The the mode for SDC c ommands in the TCL interpreter. If mode mode_name does not exist, it is created. When modes are created the default mode is deleted. - set_multicycle_path + set_multicycle_path - [-setup][-hold][-rise][-fall][-start][-end][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path]path_multiplier + [-setup][-hold][-rise][-fall][-start][-end][-from from_list][-rise_from from_list][-fall_from from_list][-through through_list][-rise_through through_list][-fall_through through_list][-to to_list][-rise_to to_list][-fall_to to_list][-reset_path]path_multiplier - -setup + -setup - Set cycle count for setup checks. + Set cycle count for setup checks. - -hold + -hold - Set cycle count for hold checks. + Set cycle count for hold checks. - -rise + -rise - Set cycle count for rising path edges. + Set cycle count for rising path edges. - -fall + -fall - Set cycle count for falling path edges. + Set cycle count for falling path edges. - -start + -start Multiply the source clock period by period_multiplier. @@ -12196,7 +12340,7 @@ - -end + -end Multiply the target clock period by period_multiplier. @@ -12204,7 +12348,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12212,7 +12356,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12220,7 +12364,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12228,7 +12372,7 @@ - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -12236,7 +12380,7 @@ - path_multiplier + path_multiplier The number of clock periods to add to the path required time. @@ -12250,82 +12394,82 @@ - set_operating_conditions + set_operating_conditions - [-analysis_type single|bc_wc|on_chip_variation][-library lib][condition][-min min_condition][-max max_condition][-min_library min_lib][-max_library max_lib] + [-analysis_type single|bc_wc|on_chip_variation][-library lib][condition][-min min_condition][-max max_condition][-min_library min_lib][-max_library max_lib] - -analysis_type single + -analysis_type single - Use one operating condition for min and max paths. + Use one operating condition for min and max paths. - -analysis_type bc_wc + -analysis_type bc_wc - Best case, worst case analysis. Setup checks use max_condition for clock and data paths. Hold checks use the min_condition for clock and data paths. + Best case, worst case analysis. Setup checks use max_condition for clock and data paths. Hold checks use the min_condition for clock and data paths. - ‑analysis_type on_chip_variation + ‑analysis_type on_chip_variation - The min and max operating conditions represent variations on the chip that can occur simultaneously. Setup checks use max_condition for data paths and min_condition for clock paths. Hold checks use min_condition for data paths and max_condition for clock paths. This is the default analysis type. + The min and max operating conditions represent variations on the chip that can occur simultaneously. Setup checks use max_condition for data paths and min_condition for clock paths. Hold checks use min_condition for data paths and max_condition for clock paths. This is the default analysis type. - -library lib + -library lib - The name of the library that contains condition. + The name of the library that contains condition. - condition + condition - The operating condition for analysis type single. + The operating condition for analysis type single. - -min min_condition + -min min_condition - The operating condition to use for min paths and hold checks. + The operating condition to use for min paths and hold checks. - -max max_condition + -max max_condition - The operating condition to use for max paths and setup checks. + The operating condition to use for max paths and setup checks. - -min_library min_lib + -min_library min_lib - The name of the library that contains min_condition. + The name of the library that contains min_condition. - -max_library max_lib + -max_library max_lib - The name of the library that contains max_condition. + The name of the library that contains max_condition. @@ -12335,237 +12479,237 @@ - set_output_delay + set_output_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall][-reference_pin ref_pin][-source_latency_included][-network_latency_included][-add_delay]delayport_pin_list - -rise + -rise - Set the output delay for the rising edge of the input. + Set the output delay for the rising edge of the input. - -fall + -fall - Set the output delay for the falling edge of the input. + Set the output delay for the falling edge of the input. - -max + -max - Set the maximum output delay. + Set the maximum output delay. - -min + -min - Set the minimum output delay. + Set the minimum output delay. - -clock clock + -clock clock - The external check is to clock. The default clock edge is rising. + The external check is to clock. The default clock edge is rising. - -clock_fall + -clock_fall - The external check is to the falling edge of clock. + The external check is to the falling edge of clock. - -reference_pin ref_pin + -reference_pin ref_pin - The external check is clocked by the clock that arrives at ref_pin. + The external check is clocked by the clock that arrives at ref_pin. - -add_delay + -add_delay - Add this output delay to any existing output delays. + Add this output delay to any existing output delays. - delay + delay - The external delay to the check clocked by clock. + The external delay to the check clocked by clock. - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. - The set_output_delay command is used to specify the external delay to a setup/hold check on an output port or internal pin that is clocked by clock. Unless the -add_delay option is specified any existing output delays are replaced. - The –reference_pin option is used to specify a timing check with respect to the arrival on a pin in the clock network. For propagated clocks, the timing check is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, the timing check is relative to the reference pin clock source latency. With the -clock_fall flag the timing check is relative to the falling edge of the reference pin. If no clocks arrive at the reference pin the set_output_delay command is ignored. If no -clock is specified the timing check is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. + The set_output_delay command is used to specify the external delay to a setup/hold check on an output port or internal pin that is clocked by clock. Unless the -add_delay option is specified any existing output delays are replaced. + The –reference_pin option is used to specify a timing check with respect to the arrival on a pin in the clock network. For propagated clocks, the timing check is relative to the clock arrival time at the reference pin (the clock source latency and network latency from the clock source to the reference pin). For ideal clocks, the timing check is relative to the reference pin clock source latency. With the -clock_fall flag the timing check is relative to the falling edge of the reference pin. If no clocks arrive at the reference pin the set_output_delay command is ignored. If no -clock is specified the timing check is with respect to all clocks that arrive at the reference pin. The -source_latency_included and -network_latency_included options cannot be used with -reference_pin. - set_port_fanout_number + set_port_fanout_number - [-min][-max]fanoutports + [-min][-max]fanoutports - -min + -min - Set the min fanout. + Set the min fanout. - -max + -max - Set the max fanout. + Set the max fanout. - fanout + fanout - The external fanout of the ports. + The external fanout of the ports. - port_list + port_list - A list of ports. + A list of ports. - Set the external fanout for ports. + Set the external fanout for ports. - set_power_activity + set_power_activity - [-global][-input][-input_ports ports][-pins pins][-activity activity | -density density][-duty duty][-clock clock] + [-global][-input][-input_ports ports][-pins pins][-activity activity | -density density][-duty duty][-clock clock] - -global + -global - Set the activity/duty for all non-clock pins. + Set the activity/duty for all non-clock pins. - -input + -input - Set the default input port activity/duty. + Set the default input port activity/duty. - -input_ports input_ports + -input_ports input_ports - Set the input port activity/duty. + Set the input port activity/duty. - -pins pins + -pins pins - Set the pin activity/duty. + Set the pin activity/duty. - -activity activity + -activity activity - The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. + The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. - -density density + -density density - Transitions per library time unit. + Transitions per library time unit. - -duty duty + -duty duty - The duty, or probability the signal is high (0 <= duty <= 1.0). Defaults to 0.5. + The duty, or probability the signal is high (0 <= duty <= 1.0). Defaults to 0.5. - -clock clock + -clock clock - The clock to use for the period with -activity. This option is ignored if -density is used. + The clock to use for the period with -activity. This option is ignored if -density is used. - The set_power_activity command is used to set the activity and duty used for power analysis globally or for input ports or pins in the design. - The default input activity for inputs is 0.1 transitions per minimum clock period if a clock is defined or 0.0 if there are no clocks defined. The default input duty is 0.5. This is equivalent to the following command: - set_power_activity -input -activity 0.1 -duty 0.5 + The set_power_activity command is used to set the activity and duty used for power analysis globally or for input ports or pins in the design. + The default input activity for inputs is 0.1 transitions per minimum clock period if a clock is defined or 0.0 if there are no clocks defined. The default input duty is 0.5. This is equivalent to the following command: + set_power_activity -input -activity 0.1 -duty 0.5 - set_propagated_clock + set_propagated_clock - objects + objects - objects + objects - A list of clocks, ports or pins. + A list of clocks, ports or pins. @@ -12576,59 +12720,59 @@ - set_pvt + set_pvt - [-min][-max][-process process][-voltage voltage] - [-temperature temperature]instances + [-min][-max][-process process][-voltage voltage] + [-temperature temperature]instances - -min + -min - Set the PVT values for max delays. + Set the PVT values for max delays. - -max + -max - Set the PVT values for min delays. + Set the PVT values for min delays. - -process process + -process process - A process value (float). + A process value (float). - -voltage voltage + -voltage voltage - A voltage value (float). + A voltage value (float). - -temperature temperature + -temperature temperature - A temperature value (float). + A temperature value (float). - instances + instances - A list instances. + A list instances. @@ -12638,177 +12782,177 @@ - set_sense + set_sense - [-type clock|data][-positive][-negative][-pulse pulse_type][-stop_propagation][-clock clocks]pins + [-type clock|data][-positive][-negative][-pulse pulse_type][-stop_propagation][-clock clocks]pins - -type clock + -type clock - Set the sense for clock paths. + Set the sense for clock paths. - -type data + -type data - Set the sense for data paths (not supported). + Set the sense for data paths (not supported). - -positive + -positive - The clock sense is positive unate. + The clock sense is positive unate. - -negative + -negative - The clock sense is negative unate. + The clock sense is negative unate. - -pulse pulse_type + -pulse pulse_type - rise_triggered_high_pulserise_triggered_low_pulsefall_triggered_high_pulsefall_triggered_low_pulseNot supported. + rise_triggered_high_pulserise_triggered_low_pulsefall_triggered_high_pulsefall_triggered_low_pulseNot supported. - -stop_propagation + -stop_propagation - Stop propagating clocks at pins. + Stop propagating clocks at pins. - clocks + clocks - A list of clocks to apply the sense. + A list of clocks to apply the sense. - pins + pins - A list of pins. + A list of pins. - The set_sense command is used to modify the propagation of a clock signal. The clock sense is set with the ‑positive and –negative flags. Use the –stop_propagation flag to stop the clock from propagating beyond a pin. The –positive, -negative, -stop_propagation, and –pulse options are mutually exclusive. If the –clock option is not used the command applies to all clocks that traverse pins. The –pulse option is currently not supported. + The set_sense command is used to modify the propagation of a clock signal. The clock sense is set with the ‑positive and –negative flags. Use the –stop_propagation flag to stop the clock from propagating beyond a pin. The –positive, -negative, -stop_propagation, and –pulse options are mutually exclusive. If the –clock option is not used the command applies to all clocks that traverse pins. The –pulse option is currently not supported. - set_timing_derate + set_timing_derate - [-rise][-fall][-early][-late][-clock][-data][-net_delay][-cell_delay][-cell_check]derate[objects] + [-rise][-fall][-early][-late][-clock][-data][-net_delay][-cell_delay][-cell_check]derate[objects] - -rise + -rise - Set the derating for rising delays. + Set the derating for rising delays. - -fall + -fall - Set the derating for falling delays. + Set the derating for falling delays. - -early + -early - Derate early (min) paths. + Derate early (min) paths. - -late + -late - Derate late (max) paths. + Derate late (max) paths. - -clock + -clock - Derate paths in the clock network. + Derate paths in the clock network. - -data + -data - Derate data paths. + Derate data paths. - -net_delay + -net_delay - Derate net (interconnect) delays. + Derate net (interconnect) delays. - -cell_delay + -cell_delay - Derate cell delays. + Derate cell delays. - -cell_check + -cell_check - Derate cell timing check margins. + Derate cell timing check margins. - derate + derate - The derating factor to apply to delays. + The derating factor to apply to delays. - objects + objects - A list of instances, library cells, or nets. + A list of instances, library cells, or nets. @@ -12819,42 +12963,42 @@ - set_resistance + set_resistance - [-max][-min]resistancenets + [-max][-min]resistancenets - -min + -min - The resistance for minimum path delay calculation. + The resistance for minimum path delay calculation. - -max + -max - The resistance for maximum path delay calculation. + The resistance for maximum path delay calculation. - resistance + resistance - The net resistance. + The net resistance. - nets + nets - A list of nets. + A list of nets. @@ -12864,76 +13008,76 @@ - set_units + set_units - [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] + [-capacitance cap_unit][-resistance res_unit][-time time_unit][-voltage voltage_unit][-current current_unit][-power power_unit][-distance distance_unit] - -capacitance cap_unit + -capacitance cap_unit - The capacitance scale factor followed by 'f'. + The capacitance scale factor followed by 'f'. - -resistance res_unit + -resistance res_unit - The resistance scale factor followed by 'ohm'. + The resistance scale factor followed by 'ohm'. - -time time_unit + -time time_unit - The time scale factor followed by 's'. + The time scale factor followed by 's'. - -voltage voltage_unit + -voltage voltage_unit - The voltage scale factor followed by 'v'. + The voltage scale factor followed by 'v'. - -current current_unit + -current current_unit - The current scale factor followed by 'A'. + The current scale factor followed by 'A'. - -power power_unit + -power power_unit - The power scale factor followed by 'w'. + The power scale factor followed by 'w'. - The set_units command is used to check the units used by the STA command interpreter when parsing commands and reporting results. If the current units differ from the set_unit value a warning is printed. Use the set_cmd_units command to change the command units. + The set_units command is used to check the units used by the STA command interpreter when parsing commands and reporting results. If the current units differ from the set_unit value a warning is printed. Use the set_cmd_units command to change the command units. Units are specified as a scale factor followed by a unit name. The scale factors are as follows. - M 1E+6k 1E+3m 1E-3u 1E-6n 1E-9p 1E-12f 1E-15 + M 1E+6k 1E+3m 1E-3u 1E-6n 1E-9p 1E-12f 1E-15 An example of the set_units command is shown below. - set_units -time ns -capacitance pF -current mA -voltage V -resistance kOhm + set_units -time ns -capacitance pF -current mA -voltage V -resistance kOhm - set_wire_load_min_block_size + set_wire_load_min_block_size - size + size @@ -12943,34 +13087,34 @@ - set_wire_load_mode + set_wire_load_mode - top|enclosed|segmented + top|enclosed|segmented - top + top - + - enclosed + enclosed - + - segmented + segmented - + @@ -12980,51 +13124,51 @@ - set_wire_load_model + set_wire_load_model - -name model_name[-library library][-max][-min][objects] + -name model_name[-library library][-max][-min][objects] - -name model_name + -name model_name - The name of a wire load model. + The name of a wire load model. - -library library + -library library - Library to look for model_name. + Library to look for model_name. - -max + -max - The wire load model is for maximum path delays. + The wire load model is for maximum path delays. - -min + -min - The wire load model is for minimum path delays. + The wire load model is for minimum path delays. - objects + objects - Not supported. + Not supported. @@ -13034,50 +13178,50 @@ - set_wire_load_selection_group + set_wire_load_selection_group - [-library library][-max][-min]group_name[objects] + [-library library][-max][-min]group_name[objects] - library + library - Library to look for group_name. + Library to look for group_name. - -max + -max - The wire load selection is for maximum path delays. + The wire load selection is for maximum path delays. - -min + -min - The wire load selection is for minimum path delays. + The wire load selection is for minimum path delays. - group_name + group_name - A wire load selection group name. + A wire load selection group name. - objects + objects - Not supported. + Not supported. @@ -13087,39 +13231,39 @@ - suppress_msg + suppress_msg - msg_ids + msg_ids - msg_ids + msg_ids - A list of error/warning message IDs to suppress. + A list of error/warning message IDs to suppress. - The suppress_msg command suppresses specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. + The suppress_msg command suppresses specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. - unset_case_analysis + unset_case_analysis - port_or_pin_list + port_or_pin_list - port_or_pin_list + port_or_pin_list - A list of ports or pins. + A list of ports or pins. @@ -13129,26 +13273,26 @@ - unset_clock_latency + unset_clock_latency - [-source]objects + [-source]objects - -source + -source - Specifies source clock latency (clock insertion delay). + Specifies source clock latency (clock insertion delay). - objects + objects - A list of clocks, pins or ports. + A list of clocks, pins or ports. @@ -13158,18 +13302,18 @@ - unset_clock_transition + unset_clock_transition - clocks + clocks - clocks + clocks - A list of clocks. + A list of clocks. @@ -13179,74 +13323,74 @@ - unset_clock_uncertainty + unset_clock_uncertainty - [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold][objects] + [-from|-rise_from|-fall_from from_clock][-to|-rise_to|-fall_to to_clock][-rise][-fall][-setup][-hold][objects] - -from from_clock + -from from_clock - + - -to to_clock + -to to_clock - + - -rise + -rise - The uncertainty is for the rising edge of the clock. + The uncertainty is for the rising edge of the clock. - -fall + -fall - The uncertainty is for the falling edge of the clock. + The uncertainty is for the falling edge of the clock. - -setup + -setup - uncertainty is the setup check uncertainty. + uncertainty is the setup check uncertainty. - -hold + -hold - uncertainty is the hold uncertainty. + uncertainty is the hold uncertainty. - uncertainty + uncertainty - Clock uncertainty. + Clock uncertainty. - objects + objects - A list of clocks, ports or pins. + A list of clocks, ports or pins. @@ -13256,50 +13400,50 @@ - unset_data_check + unset_data_check - [-from|-rise_from|-fall_from from_object][-to|-rise_to|-fall_to to_object][-setup][-hold][-clock clock] + [-from|-rise_from|-fall_from from_object][-to|-rise_to|-fall_to to_object][-setup][-hold][-clock clock] - -from from_object + -from from_object - A pin used as the timing check reference. + A pin used as the timing check reference. - -to to_object + -to to_object - A pin that the setup/hold check is applied to. + A pin that the setup/hold check is applied to. - -setup + -setup - Add a setup timing check. + Add a setup timing check. - -hold + -hold - Add a hold timing check. + Add a hold timing check. - clock + clock - The setup/hold check clock. + The setup/hold check clock. @@ -13309,52 +13453,52 @@ - unset_disable_inferred_clock_gating + unset_disable_inferred_clock_gating - objects + objects - objects + objects - A list of clock gating instances, clock gating pins, or clock enable pins. + A list of clock gating instances, clock gating pins, or clock enable pins. - The unset_disable_inferred_clock_gating command removes a previous set_disable_inferred_clock_gating command. + The unset_disable_inferred_clock_gating command removes a previous set_disable_inferred_clock_gating command. - unset_disable_timing + unset_disable_timing - [-from from_port][-to to_port]objects + [-from from_port][-to to_port]objects - from_port + from_port - + - to_port + to_port - + - objects + objects A list of instances, ports, pins, cells or [library/]cell/port. @@ -13368,66 +13512,66 @@ - unset_input_delay + unset_input_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list - -rise + -rise - Unset the arrival time for the rising edge of the input. + Unset the arrival time for the rising edge of the input. - -fall + -fall - Unset the arrival time for the falling edge of the input. + Unset the arrival time for the falling edge of the input. - -max + -max - Unset the minimum arrival time. + Unset the minimum arrival time. - -min + -min - Unset the maximum arrival time. + Unset the maximum arrival time. - clock + clock - Unset the arrival time from clock. + Unset the arrival time from clock. - -clock_fall + -clock_fall - Unset the arrival time from the falling edge of clock + Unset the arrival time from the falling edge of clock - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. @@ -13437,67 +13581,67 @@ - unset_output_delay + unset_output_delay - [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list + [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list - -rise + -rise - This is the arrival time for the rising edge of the input. + This is the arrival time for the rising edge of the input. - -fall + -fall - This is the arrival time for the falling edge of the input. + This is the arrival time for the falling edge of the input. - -max + -max - This is the minimum arrival time. + This is the minimum arrival time. - -min + -min - This is the maximum arrival time. + This is the maximum arrival time. - clock + clock - The arrival time is from this clock. + The arrival time is from this clock. - -clock_fall + -clock_fall - The arrival time is from the falling edge of clock + The arrival time is from the falling edge of clock - pin_port_list + pin_port_list - A list of pins or ports. + A list of pins or ports. @@ -13507,47 +13651,47 @@ - unset_path_exceptions + unset_path_exceptions - [-setup][-hold][-rise][-fall][-from|-rise_from|-fall_from from][-through|-rise_through|-fall_through through][-to|-rise_to|-fall_to to] + [-setup][-hold][-rise][-fall][-from|-rise_from|-fall_from from][-through|-rise_through|-fall_through through][-to|-rise_to|-fall_to to] - -setup + -setup - Unset path exceptions for setup checks. + Unset path exceptions for setup checks. - -hold + -hold - Unset path exceptions for hold checks. + Unset path exceptions for hold checks. - -rise + -rise - Unset path exceptions for rising path edges. + Unset path exceptions for rising path edges. - -fall + -fall - Unset path exceptions for falling path edges. + Unset path exceptions for falling path edges. - -from from + -from from A list of clocks, instances, ports or pins. @@ -13555,7 +13699,7 @@ - -through through + -through through A list of instances, pins or nets. @@ -13563,7 +13707,7 @@ - -to to + -to to A list of clocks, instances, ports or pins. @@ -13571,75 +13715,75 @@ The unset_path_exceptions command removes any matching set_false_path, set_multicycle_path, set_max_delay, and set_min_delay exceptions. - + - unset_power_activity + unset_power_activity - [-global][-input][-input_ports ports][-pins pins] + [-global][-input][-input_ports ports][-pins pins] - -global + -global - Set the activity/duty for all non-clock pins. + Set the activity/duty for all non-clock pins. - -input + -input - Set the default input port activity/duty. + Set the default input port activity/duty. - -input_ports input_ports + -input_ports input_ports - Set the input port activity/duty. + Set the input port activity/duty. - -pins pins + -pins pins - Set the pin activity/duty. + Set the pin activity/duty. - -activity activity + -activity activity - The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. + The activity, or number of transitions per clock cycle. If clock is not specified the clock with the minimum period is used. If no clocks are defined an error is reported. - The unset_power_activity_command is used to undo the effects of the set_power_activity command. + The unset_power_activity_command is used to undo the effects of the set_power_activity command. - unset_propagated_clock + unset_propagated_clock - objects + objects - objects + objects A list of clocks, ports or pins. @@ -13652,44 +13796,44 @@ - unset_timing_derate + unset_timing_derate - + - Remove all derating factors set with the set_timing_derate command. + Remove all derating factors set with the set_timing_derate command. - unsuppress_msg + unsuppress_msg - msg_ids + msg_ids - msg_ids + msg_ids - A list of error/warning message IDs to unsuppress. + A list of error/warning message IDs to unsuppress. - The unsuppress_msg command removes suppressions for the specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. + The unsuppress_msg command removes suppressions for the specified error/warning messages by ID. The list of message IDs can be found in doc/messages.txt. - user_run_time + user_run_time - + @@ -13699,147 +13843,147 @@ - with_output_to_variable + with_output_to_variable - var { commands } + var { commands } - var + var - The name of a variable to save the output of commands to. + The name of a variable to save the output of commands to. - commands + commands - TCL commands that the output will be redirected from. + TCL commands that the output will be redirected from. - The with_output_to_variable command redirects the output of TCL commands to a variable. + The with_output_to_variable command redirects the output of TCL commands to a variable. - write_path_spice + write_path_spice - -path_args path_args-spice_directory spice_directory-lib_subckt_file lib_subckts_file-model_file model_file-power power-ground ground[-simulator hspice|ngspice|xyce] + -path_args path_args-spice_directory spice_directory-lib_subckt_file lib_subckts_file-model_file model_file-power power-ground ground[-simulator hspice|ngspice|xyce] - path_args + path_args - -from|-through|-to arguments as in report_checks. + -from|-through|-to arguments as in report_checks. - spice_directory + spice_directory - Directory for spice to write output files. + Directory for spice to write output files. - lib_subckts_file + lib_subckts_file - Cell transistor level subckts. + Cell transistor level subckts. - model_file + model_file - Transistor model definitions .included by spice_file. + Transistor model definitions .included by spice_file. - power + power - Voltage supply name in voltage_map of the default liberty library. + Voltage supply name in voltage_map of the default liberty library. - ground + ground - Ground supply name in voltage_map of the default liberty library. + Ground supply name in voltage_map of the default liberty library. - -simulator + -simulator - Simulator that will read the spice netlist. + Simulator that will read the spice netlist. The write_path_spice command writes a spice netlist for timing paths. Use path_args to specify -from/-through/-to as arguments to the find_timing_paths command. For each path, a spice netlist and the subckts referenced by the path are written in spice_directory. The spice netlist is written in path_<id>.sp and subckt file is path_<id>.subckt. The spice netlists used by the path are written to subckt_file, which spice_file .includes. The device models used by the spice subckt netlists in model_file are also .included in spice_file. Power and ground names are specified with the -power and -ground arguments. The spice netlist includes a piecewise linear voltage source at the input and .measure statement for each gate delay and pin slew. - Example command: - write_path_spice -path_args {-from "in0" -to "out1" -unconstrained} \ -spice_directory $result_dir \ -lib_subckt_file "write_spice1.subckt" \ -model_file "write_spice1.models" \ -power VDD -ground VSS - When the simulator is hspice, .measure statements will be added to the spice netlist. - When the simulator is Xyce, the .print statement selects the CSV format and writes the waveform data to a file name path_<id>.csv so the results can be used by gnuplot. + Example command: + write_path_spice -path_args {-from "in0" -to "out1" -unconstrained} \ -spice_directory $result_dir \ -lib_subckt_file "write_spice1.subckt" \ -model_file "write_spice1.models" \ -power VDD -ground VSS + When the simulator is hspice, .measure statements will be added to the spice netlist. + When the simulator is Xyce, the .print statement selects the CSV format and writes the waveform data to a file name path_<id>.csv so the results can be used by gnuplot. - write_sdc + write_sdc - [-digits digits][-gzip][-no_timestamp]filename + [-digits digits][-gzip][-no_timestamp]filename - digits + digits - The number of digits after the decimal point to report. The default is 4. + The number of digits after the decimal point to report. The default is 4. - -gzip + -gzip - Compress the SDC with gzip. + Compress the SDC with gzip. - -no_timestamp + -no_timestamp - Do not include a time and date in the SDC file. + Do not include a time and date in the SDC file. - filename + filename - The name of the file to write the constraints to. + The name of the file to write the constraints to. @@ -13849,242 +13993,242 @@ - write_sdf + write_sdf - [-scene scene][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename + [-scene scene][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename - scene + scene - Write delays for scene. + Write delays for scene. - -divider + -divider - Divider to use between hierarchy levels in pin and instance names. + Divider to use between hierarchy levels in pin and instance names. - -include_typ + -include_typ - Include a 'typ' value in the SDF triple that is the average of min and max delays to satisfy some Verilog simulators that require three values in the delay triples. + Include a 'typ' value in the SDF triple that is the average of min and max delays to satisfy some Verilog simulators that require three values in the delay triples. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default is 4. + The number of digits after the decimal point to report. The default is 4. - -gzip + -gzip - Compress the SDF using gzip. + Compress the SDF using gzip. - -no_timestamp + -no_timestamp - Do not write a DATE statement. + Do not write a DATE statement. - -no_version + -no_version - Do not write a VERSION statement. + Do not write a VERSION statement. - filename + filename - The SDF filename to write. + The SDF filename to write. - Write the delay calculation delays for the design in SDF format to filename. If -corner is not specified the min/max delays are across all corners. With -corner the min/max delays for corner are written. The SDF TIMESCALE is same as the time_unit in the first liberty file read. + Write the delay calculation delays for the design in SDF format to filename. If -corner is not specified the min/max delays are across all corners. With -corner the min/max delays for corner are written. The SDF TIMESCALE is same as the time_unit in the first liberty file read. - write_timing_model + write_timing_model - [-library_name lib_name][-cell_name cell_name] - [-scene scene]filename + [-library_name lib_name][-cell_name cell_name] + [-scene scene]filename - lib_name + lib_name - The name to use for the liberty library. Defaults to cell_name. + The name to use for the liberty library. Defaults to cell_name. - cell_name + cell_name - The name to use for the liberty cell. Defaults to the top level module name. + The name to use for the liberty cell. Defaults to the top level module name. - scene + scene - The scene to use for extracting the model. + The scene to use for extracting the model. - filename + filename - Filename for the liberty timing model. + Filename for the liberty timing model. - The write_timing_model command constructs a liberty timing model for the current design and writes it to filename. cell_name defaults to the cell name of the top level block in the design. - The SDC used to extract the block should include the clock definitions. If the block contains a clock network set_propagated_clock should be used so the clock delays are included in the timing model. The following SDC commands are ignored when building the timing model. - set_input_delayset_output_delayset_loadset_timing_derate - Using set_input_transition with the slew from the block context will be used will improve the match between the timing model and the block netlist. Paths defined on clocks that are defined on internal pins are ignored because the model has no way to include the clock definition. + The write_timing_model command constructs a liberty timing model for the current design and writes it to filename. cell_name defaults to the cell name of the top level block in the design. + The SDC used to extract the block should include the clock definitions. If the block contains a clock network set_propagated_clock should be used so the clock delays are included in the timing model. The following SDC commands are ignored when building the timing model. + set_input_delayset_output_delayset_loadset_timing_derate + Using set_input_transition with the slew from the block context will be used will improve the match between the timing model and the block netlist. Paths defined on clocks that are defined on internal pins are ignored because the model has no way to include the clock definition. The resulting timing model can be used in a hierarchical timing flow as a replacement for the block to speed up timing analysis. This hierarchical timing methodology does not handle timing exceptions that originate or terminate inside the block. The timing model includes: - combinational paths between inputs and outputssetup and hold timing constraints on inputsclock to output timing paths - Resistance of long wires on inputs and outputs of the block cannot be modeled in Liberty. To reduce inaccuracies from wire resistance in technologies with resistive wires place buffers on inputs and ouputs. + combinational paths between inputs and outputssetup and hold timing constraints on inputsclock to output timing paths + Resistance of long wires on inputs and outputs of the block cannot be modeled in Liberty. To reduce inaccuracies from wire resistance in technologies with resistive wires place buffers on inputs and ouputs. The extracted timing model setup/hold checks are scalar (no input slew dependence). Delay timing arcs are load dependent but do not include input slew dependency. - write_verilog + write_verilog - [-include_pwr_gnd][-remove_cells lib_cells]filename + [-include_pwr_gnd][-remove_cells lib_cells]filename - -include_pwr_gnd + -include_pwr_gnd - Include power and ground pins on instances. + Include power and ground pins on instances. - -remove_cells lib_cells + -remove_cells lib_cells - Liberty cells to remove from the Verilog netlist. Use get_lib_cells, a list of cells names, or a cell name with wildcards. + Liberty cells to remove from the Verilog netlist. Use get_lib_cells, a list of cells names, or a cell name with wildcards. - filename + filename - Filename for the liberty library. + Filename for the liberty library. - The write_verilog command writes a Verilog netlist to filename. Use -sort to sort the instances so the results are reproducible across operating systems. Use -remove_cells to remove instances of lib_cells from the netlist. - Filter Expressions - The get_cells, get_pins, get_ports and get_timing_edges functions support filtering the returned objects by property values. Supported filter expressions are shown below. + The write_verilog command writes a Verilog netlist to filename. Use -sort to sort the instances so the results are reproducible across operating systems. Use -remove_cells to remove instances of lib_cells from the netlist. + Filter Expressions + The get_cells, get_pins, get_ports and get_timing_edges functions support filtering the returned objects by property values. Supported filter expressions are shown below. - property + property - Return objects with property value equal to 1. + Return objects with property value equal to 1. - property==value + property==value - Return objects with property value equal to value. + Return objects with property value equal to value. - property=~pattern + property=~pattern - Return objects with property value that matches pattern. + Return objects with property value that matches pattern. - property!=value + property!=value - Return objects with property value not equal to value. + Return objects with property value not equal to value. - property!~value + property!~value - Return objects with property value that does not match pattern. + Return objects with property value that does not match pattern. - expr1&&expr2 + expr1&&expr2 - Return objects with expr1 and expr2. expr1 and expr2 are one of the first three property value forms shown above. + Return objects with expr1 and expr2. expr1 and expr2 are one of the first three property value forms shown above. - expr1||expr2 + expr1||expr2 - Return objects with expr1 or expr2. expr1 and expr2 are one of the first three property value forms shown above. + Return objects with expr1 or expr2. expr1 and expr2 are one of the first three property value forms shown above. - Where property is a property supported by the get_property command. Note that if there are spaces in the expression it must be enclosed in quotes so that it is a single argument. - Variables + Where property is a property supported by the get_property command. Note that if there are spaces in the expression it must be enclosed in quotes so that it is a single argument. + Variables - hierarchy_separator + hierarchy_separator - Any character. + Any character. @@ -14094,37 +14238,37 @@ - sta_continue_on_error + sta_continue_on_error - 0|1 + 0|1 - The include and read_sdc commands stop and report any errors encountered while reading a file unless sta_continue_on_error is 1. The default value is 0. + The include and read_sdc commands stop and report any errors encountered while reading a file unless sta_continue_on_error is 1. The default value is 0. - sta_crpr_mode + sta_crpr_mode - same_pin|same_transition + same_pin|same_transition - When the data and clock paths of a timing check overlap (see sta_crpr_enabled), pessimism is removed independent of whether of the path rise/fall transitions. When sta_crpr_mode is same_transition, the pessimism is only removed if the path rise/fall transitions are the same. The default value is same_pin. + When the data and clock paths of a timing check overlap (see sta_crpr_enabled), pessimism is removed independent of whether of the path rise/fall transitions. When sta_crpr_mode is same_transition, the pessimism is only removed if the path rise/fall transitions are the same. The default value is same_pin. - sta_cond_default_arcs_enabled + sta_cond_default_arcs_enabled - 0|1 + 0|1 @@ -14134,10 +14278,10 @@ - sta_crpr_enabled + sta_crpr_enabled - 0|1 + 0|1 @@ -14147,10 +14291,10 @@ - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking - 0|1 + 0|1 @@ -14160,23 +14304,23 @@ - sta_gated_clock_checks_enabled + sta_gated_clock_checks_enabled - 0|1 + 0|1 - When sta_gated_clock_checks_enabled is 1, clock gating setup and hold timing checks are checked. The default value is 1. + When sta_gated_clock_checks_enabled is 1, clock gating setup and hold timing checks are checked. The default value is 1. - sta_input_port_default_clock + sta_input_port_default_clock - 0|1 + 0|1 @@ -14186,10 +14330,10 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled - 0|1 + 0|1 @@ -14199,27 +14343,40 @@ - sta_pocv_enabled + sta_pocv_mode - 0|1 + scalar|normal|skew_normal - Enable parametric on chip variation using statistical timing analysis. The default value is 0. + Enable parametric on chip variation using statistical timing analysis. The default value is scalar. + + + + + + sta_pocv_quartile + + + quartile + + + + The target quantile of a delay probability distribution (confidence level).The default value is 3 standard deviations, or sigma. - sta_propagate_all_clocks + sta_propagate_all_clocks - 0|1 + 0|1 - All clocks defined after sta_propagate_all_clocks is set to 1 are propagated. If it is set before any clocks are defined it has the same effect as + All clocks defined after sta_propagate_all_clocks is set to 1 are propagated. If it is set before any clocks are defined it has the same effect as set_propagated_clock [all_clocks] After all clocks have been defined. The default value is 0. @@ -14227,36 +14384,36 @@ - sta_propagate_gated_clock_enable + sta_propagate_gated_clock_enable - 0|1 + 0|1 - When set to 1, paths of gated clock enables are propagated through the clock gating instances. If the gated clock controls sequential elements setting sta_propagate_gated_clock_enable to 0 prevents spurious paths from the clock enable. The default value is 1. + When set to 1, paths of gated clock enables are propagated through the clock gating instances. If the gated clock controls sequential elements setting sta_propagate_gated_clock_enable to 0 prevents spurious paths from the clock enable. The default value is 1. - sta_recovery_removal_checks_enabled + sta_recovery_removal_checks_enabled - 0|1 + 0|1 - When sta_recovery_removal_checks_enabled is 0, recovery and removal timing checks are disabled. The default value is 1. + When sta_recovery_removal_checks_enabled is 0, recovery and removal timing checks are disabled. The default value is 1. - sta_report_default_digits + sta_report_default_digits - integer + integer @@ -14266,10 +14423,10 @@ - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled - 0|1 + 0|1 @@ -14299,186 +14456,186 @@ - Alphabetical Index + Alphabetical Index - all_clocks7 - all_inputs7 - all_outputs8 - all_registers8 - check_setup9 - Command Line Arguments1 - Commands7 - connect_pin9 - create_generated_clock11 - create_voltage_area12 - current_design12 - current_instance13 - define_scene13 - delete_clock13 - delete_from_list13 - delete_generated_clock14 - delete_instance14 - delete_net14 - disconnect_pin14 - elapsed_run_time14 - Example Command Scripts1 - Filter Expressions84 - find_timing_paths15 - get_cells17 - get_clocks17 - get_fanin18 - get_fanout19 - get_full_name19 - get_lib_pins20 - get_libs21 - get_name22 - get_nets22 - get_pins23 - get_ports23 - get_property24 - get_scenes28 - get_timing_edges28 - group_path29 - hierarchy_separator85 - include30 - link_design30 - make_instance30 - make_net31 - Power Analysis3 - read_liberty31 - read_saif32 - read_sdc33 - read_sdf33 - read_spef34 - read_vcd35 - read_verilog35 - redirection5 - replace_activity_annotation36 - replace_cell35 - report_annotated_check36 - report_annotated_delay37 - report_check_types41 - report_checks38 - report_clock_latency42 - report_clock_min_period42 - report_clock_properties43 - report_clock_skew43 - report_dcalc43 - report_disabled_edges44 - report_edges44 - report_instance44 - report_lib_cell44 - report_net45 - report_parasitic_annotation45 - report_power45 - report_slews46 - report_tns46 - report_units46 - report_wns47 - report_worst_slack47 - set_assigned_check48 - set_assigned_delay49 - set_assigned_transition49 - set_case_analysis50 - set_clock_gating_check50 - set_clock_groups51 - set_clock_latency52 - set_clock_transition52 - set_clock_uncertainty53 - set_cmd_units54 - set_data_check55 - set_disable_inferred_clock_gating55 - set_disable_timing55 - set_drive56 - set_driving_cell57 - set_false_path58 - set_fanout_load59 - set_hierarchy_separator59 - set_ideal_latency59 - set_ideal_network59 - set_ideal_transition59 - set_input_delay59 - set_input_transition61 - set_level_shifter_strategy61 - set_level_shifter_threshold61 - set_load61 - set_logic_dc62 - set_logic_one62 - set_logic_zero63 - set_max_area63 - set_max_capacitance63 - set_max_delay63 - set_max_dynamic_power64 - set_max_fanout64 - set_max_leakage_power64 - set_max_time_borrow64 - set_max_transition65 - set_min_capacitance65 - set_min_delay66 - set_min_pulse_width67 - set_mode67 - set_multicycle_path67 - set_operating_conditions68 - set_output_delay69 - set_port_fanout_number70 - set_power_activity70 - set_propagated_clock71 - set_pvt71 - set_resistance73 - set_sense72 - set_timing_derate73 - set_units74 - set_wire_load_min_block_size75 - set_wire_load_mode75 - set_wire_load_model75 - set_wire_load_selection_group75 - SPEF34 - sta_cond_default_arcs_enabled85 - sta_continue_on_error85 - sta_crpr_enabled85 - sta_crpr_mode85 - sta_dynamic_loop_breaking85 - sta_gated_clock_checks_enabled85 - sta_input_port_default_clock86 - sta_internal_bidirect_instance_paths_enabled86 - sta_pocv_enabled86 - sta_preset_clear_arcs_enabled87 - sta_propagate_all_clocks86 - sta_propagate_gated_clock_enable86 - sta_recovery_removal_checks_enabled86 - sta_report_default_digits86 - suppress_msg76 - TCL Interpreter5 - Timing Analysis using SDF2 - Timing Analysis with Multiple Corners and Modes3 - Timing Analysis with Multiple Process Corners2 - unset_case_analysis76 - unset_clock_latency76 - unset_clock_transition76 - unset_clock_uncertainty77 - unset_data_check77 - unset_disable_inferred_clock_gating78 - unset_disable_timing78 - unset_input_delay78 - unset_output_delay79 - unset_path_exceptions79 - unset_power_activity80 - unset_propagated_clock80 - unset_timing_derate80 - unsuppress_msg81 - user_run_time81 - Variables85 - verilog netlist35 - with_output_to_variable81 - write_path_spice81 - write_sdc82 - write_sdf82 - write_timing_model83 - write_verilog84 + all_clocks7 + all_inputs7 + all_outputs8 + all_registers8 + check_setup9 + Command Line Arguments1 + Commands7 + connect_pin9 + create_generated_clock11 + create_voltage_area12 + current_design12 + current_instance13 + define_scene13 + delete_clock13 + delete_from_list13 + delete_generated_clock14 + delete_instance14 + delete_net14 + disconnect_pin14 + elapsed_run_time14 + Example Command Scripts1 + Filter Expressions84 + find_timing_paths15 + get_cells17 + get_clocks17 + get_fanin18 + get_fanout19 + get_full_name19 + get_lib_pins20 + get_libs21 + get_name22 + get_nets22 + get_pins23 + get_ports23 + get_property24 + get_scenes28 + get_timing_edges28 + group_path29 + hierarchy_separator85 + include30 + link_design30 + make_instance30 + make_net31 + Power Analysis3 + read_liberty31 + read_saif32 + read_sdc33 + read_sdf33 + read_spef34 + read_vcd35 + read_verilog35 + redirection5 + replace_activity_annotation36 + replace_cell35 + report_annotated_check36 + report_annotated_delay37 + report_check_types41 + report_checks38 + report_clock_latency42 + report_clock_min_period42 + report_clock_properties43 + report_clock_skew43 + report_dcalc43 + report_disabled_edges44 + report_edges44 + report_instance44 + report_lib_cell44 + report_net45 + report_parasitic_annotation45 + report_power45 + report_slews46 + report_tns46 + report_units46 + report_wns47 + report_worst_slack47 + set_assigned_check48 + set_assigned_delay49 + set_assigned_transition49 + set_case_analysis50 + set_clock_gating_check50 + set_clock_groups51 + set_clock_latency52 + set_clock_transition52 + set_clock_uncertainty53 + set_cmd_units54 + set_data_check55 + set_disable_inferred_clock_gating55 + set_disable_timing55 + set_drive56 + set_driving_cell57 + set_false_path58 + set_fanout_load59 + set_hierarchy_separator59 + set_ideal_latency59 + set_ideal_network59 + set_ideal_transition59 + set_input_delay59 + set_input_transition61 + set_level_shifter_strategy61 + set_level_shifter_threshold61 + set_load61 + set_logic_dc62 + set_logic_one62 + set_logic_zero63 + set_max_area63 + set_max_capacitance63 + set_max_delay63 + set_max_dynamic_power64 + set_max_fanout64 + set_max_leakage_power64 + set_max_time_borrow64 + set_max_transition65 + set_min_capacitance65 + set_min_delay66 + set_min_pulse_width67 + set_mode67 + set_multicycle_path67 + set_operating_conditions68 + set_output_delay69 + set_port_fanout_number70 + set_power_activity70 + set_propagated_clock71 + set_pvt71 + set_resistance73 + set_sense72 + set_timing_derate73 + set_units74 + set_wire_load_min_block_size75 + set_wire_load_mode75 + set_wire_load_model75 + set_wire_load_selection_group75 + SPEF34 + sta_cond_default_arcs_enabled85 + sta_continue_on_error85 + sta_crpr_enabled85 + sta_crpr_mode85 + sta_dynamic_loop_breaking85 + sta_gated_clock_checks_enabled85 + sta_input_port_default_clock86 + sta_internal_bidirect_instance_paths_enabled86 + sta_pocv_enabled86 + sta_preset_clear_arcs_enabled87 + sta_propagate_all_clocks86 + sta_propagate_gated_clock_enable86 + sta_recovery_removal_checks_enabled86 + sta_report_default_digits86 + suppress_msg76 + TCL Interpreter5 + Timing Analysis using SDF2 + Timing Analysis with Multiple Corners and Modes3 + Timing Analysis with Multiple Process Corners2 + unset_case_analysis76 + unset_clock_latency76 + unset_clock_transition76 + unset_clock_uncertainty77 + unset_data_check77 + unset_disable_inferred_clock_gating78 + unset_disable_timing78 + unset_input_delay78 + unset_output_delay79 + unset_path_exceptions79 + unset_power_activity80 + unset_propagated_clock80 + unset_timing_derate80 + unsuppress_msg81 + user_run_time81 + Variables85 + verilog netlist35 + with_output_to_variable81 + write_path_spice81 + write_sdc82 + write_sdf82 + write_timing_model83 + write_verilog84 - - Version 3.0.0, Mar 7, 2026Copyright (c) 2026, Parallax Software, Inc. + + Version 3.0.0, Mar 7, 2026Copyright (c) 2026, 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 <https://www.gnu.org/licenses/>. diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf index 77ebd937428ddf6b36c2e23426bf24752da895ab..5cfe27894ec91f1fedbeb0e26bd26516ad34f4cd 100644 GIT binary patch literal 1419896 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 z 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

%;lb+P{ur&aFZ9xTf1 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 z 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%0A8hk_fC7d(`pQupM6Hr2* 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 h)4ROBFK)Xw(5RC@?cRq-eb2Nwi?1mJ*= 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%0D$ z>_7cnftFiWL7~T-*hV0Y=eR2X={@kiZ|snFW)b10Lm_Foh2?W0^J?=1KkbmcuyqO@ zly;2W(v1Ey3mH~RqfI;PKQyC50=H#^4FkBTmvxf}IyNVaW@1-?#D# zX4&|<4-F=mlAn4qKVP8bk5mqakGBEoaWlf7EmUgZPpCs#j zy352a-o=(rVeK1?SUJvPxxY(bA%!NIQ=w@&c6yG;-fgkd16d=a<*aBT4S{4lS*=** z?;e8phaAkW7HB!l<1-7HPVRCKS;$UL&i~&s{!u$x{<~o#M4VG7 zZW*js?>92DZd~?M=wpp!tobPvA9Yb2EJ)2C`VNbUrv*P{!{mNI9@T0PZ1nvc`Yz2f z|ITRkLikGDD-h~4nh5jpm_WMEAfoMQ*9m%xco9blT#vJbA~D1pf}fgs#ESusvc_H1R!!{indHmrj;! zEI~@Ec!4w>Bn*>VRIk=kRx254`6&a9F02jhj3&#L2V1yubvuc?1k!yac9InBJ)T4Z z0_iT=WQZX-SCT{o0_n9m6?#b$6?hkG9fd|pOmd`-S;(ziGL7X^AuVSo<<$@>r;99h zEOA^MGt+IcX*rAioE1%W-MgeCr;x9*@lHQw7}%BAQ@QHV$a#Gsl)Ow z1!aN}@1C=kM@Kopxoe)Us>|dqj=vNX3N!q3+ z>VBk_mJi7G6TurBKc}#5moF;d6YzPT@4at>N6WMAV+tE(P*^KU%dKdkupxoMHg^j7 zyu~;&ROR!dkgs~x^KuL=H&>&u*ZHDCvp^J5YZkQJL_;ABwnhU;#(Y3BomU~yTMf#Q@Y5H!+-wq!X^_xDH;|@{n8{G4Hl9y~<{C-%38j{X8pY|x3n`>VyX*i7 zzCKoHYEJr06tHW2%(JBd6tL;yD5S3z3-(u?D5TTuftbYjX;B6(hf)*xd4^=>N1fp7 zfTW895)2*?x06*r>SXSHGN573SA8-72{r}<%zq&yowgGX+U#_iO%)>)KlNV1vcIlc zXuSIv!EPQ%>V&m-E=oKb4mCk(v3ZFlt%QvyCX}Cg5T91)1!yA_J%wOnMFNuXeO{rl z)0rY^x#fd%p;=C%P5Ej6>XQrA^6Qmb8&0faW^ojnjL_lq0)*?gIf<5Ar2W!DUsvDf z$rK7rg|QkDX_?-?N`WzdNjos1Ql0a0mgFdeD*Mh%9YI3)u~-o{8Y;vc73%#%O9(gS>y;~>-3o0g$T1&2DTSkYt17*S&O~|0LkYO z2lM6`zidkhVU?Pmf2Z$OF%t;fg#Xb(P!ORlald@tCq?3QvA<<}7lj}$hZ^te^~{e# zX1?uOP}y2+3i&+eOJZ^PY5(ez3)ONecl;UA(COln3K@LlIUeh)>Co~4$=dXVk)J~G zDct8UIu#n>_t|ee(YLi6(-}+L`{X_nUP5g)Rp9<{FJY@0xb^+v-m2IPi;9E%b}K# zU@XP?DWuOkdcACYkA9!PT?$QVNrTGX4CW&VB~15y9+OsqF}F63LJ*!iM2vE5?M83x zY(Sdylg$%@^+r!n#!AMUuvlXKyWNDvV$yD&5epiS)Hw>hY_X|PXF>4x3BNiOGMdmq z3}>VePCGwapyi*f!@HoLS7)s#EoZS~k^|D4;UR)cqH5uZy)_dbi|s!PkX3I>94!wB zBMf`vO}lNsJ%k|g;2zvIKea{*NE2Tt^2Fn&ZE6(upBm3Gli_F3?Ziw5q?a;@GtZFq z6D{w8Fv?H3NOECP$j4u$Ku23yifQjg|wXQdxGSrwtC>Ro$P$WU__B`}q*GK|*>jQO>nSlk?H zr$YM5_DHBI4oJOA6U7RNl=hh)GQI4Hn4EZ6H{TqEpedJ~#G@j*g|N0eJx)hCBy7~S&hwO9i z0x@rqJwFo+eJA}TQg3gV=MYwGSp3v%vh7ohIf|%Qqk!}>ogf)%uI^VqZFQiGr6W$# z%>6xV(&?m?Hdq45Xg;N|v0p|U9-HtmAX($wkURC}+xK~|^8HJ$!HL5H(!IZI1cC2U zwElUo8%X2g1nF5M@`zkOP3W%{X!&RB@Q%S3)VXeXAK_kT!g>i|oeI6F@>DK~j289K za`;AkFz++U&|t8FN_IOBW!lrP6%~!M=nTk3v*K zFFC~%{>1a$y(jYQI*?4Jogjf^G&#-_BE?S`R?h5X1?8uZ$Wdpnyb4Tsf8*k!uvcy( ze#zPY5~g@61gBw>Nixcl@cZXqqrjNIZiCQwkC}V%hWT@0LjsVE94Txhdnz>B=3S3% zDJi7#R>0k>Z5Ozvrp{5=2$Y}F`$b_}O6u%|g-lv*-pmy~pC5$`zHdU*$>8_GuDO8$ z^Vmf!a}?53TOrOrrQM64Haej4w%92!&;hBwZtI*Oy%AAEY@|;ih-+sTAPaG{+-C=c zH275t)$;2NwwQu`BzSLa6OdqeFSKq6_v1wwwA=>^kG*7GeR=>1=8r;#(HoC6YN^xO z9U!YyS`H-kI}|s)UI`Gx)w#!<0z9lV!u-(f#@Kd-lAoV_T6mist37c#x$rwp})5-RVIR|?f~5SPu6SMvBN18Y_18M6Ph zkNLJh%c17j?|?L+C&&#*W)X@%L7Kq?!uplsm2aB#K2`|9#=>|1^94rSryGUd;(62V zKIT9QnR!d!sQogBK7)W{gxhIC%YD0}kd~Xn-^YGNp|Nv<^x6=Ox$f1}xh>$1HXv`m z669aBX!>9nytmWBcX5+M$!tL9W7e4`=RE<^?d zk}kum6BR}TL@#Z}fPs`2ktp;UCkBkXjhRfl{s2iAE!6`_Uq@k0hL-!{he9}=UKBzQ zt=FP*a1kDRf;1nF35+<7^!`-}jQOjy!+MQ&{#&;k9QF-{(3Rl1HG!z&o1$Wp1JV?Q zgGdol5npM0?{OyB2rI#0JU-TxaVn&_R>n}L*&Pi9Kj`KG7_c+8+|nfvc^Np;qe{(1vYj_UsLY$ zf*yt7Cq$gwwfvL`<{aQ+){7eodmkw$#w-ju{nh@69#Z7?6~fvoKx(BdzE7&z`xvgy zkbDzzn_+*m57yTMYYQ#$SiLq$bPJ>ji;;EZkZfD5 zXgTDV}4)xrJZQqAm#|TB|O$RYTF^UYeue3md zx=x0`08=Nt=YC)QNC?U4N>Ex?K0z8$2@#`CZ|Q`H1@iV>=%L1#1wED0nHr6+#E#vc z3h6s%_UWe#;`pfLV~*xLG_2rOc0itn?dc>% zEMdnjEj_g|9Y}W?gWwsm|Fjo+4?N|D^%Nq0%F*1mLLQ#oAtBjBf!&`98|I;>(i-3A zr96(v3^a6_KrepEXu^wPKjWuf$XQ@Cul+=)zvQR?kG33XH_35HWZ(NOj|$#DwRZko z$nuApV^~CzS$nCT8x9p7g)B@Q1f$? z0*P)zpvvbt^q(WAp6aZ2X!)qqLp?!y-yV?21A#!hOIiFJHvXlB^qt$>b8d58;PK_- zr``hzy5pxE(u?$*Q{X5>k@37qyUaWsK9BDKX|{BNWME;giLB$*g5IU4!WsrGN3WI; zJ0KZY*kqDaBJ4J!N>CfmN2leS{?8k~q~#D+LqJPdX)KInOla2r83kGnbIntLxOw%! z5?{t*FV%1!14JtCS;f$JpUj37nobi!hO<~BH)cE_jogkU!)0`bVh$cgT92XBn={WluR(I{?g_@ ze(JrIe26@D5oG3qJt1#Z=gT0BdGz&V?f9$z)-4Bzty_jbm+m_uf4C!N{UTL#R!4^3 z?3YX8w%g8yjj(`hOOgUEtbO(4j=7N|g^k%#*cO}HVj67u07%;DT>)exH45u1T5cXj zVWV9NX}Q%`XGlIj>ZJDrvanMvr=8wZXUK-1GbHos{l*3P6$mX~{FIrtPJuexB3%_) z=zjRNg&CYh7wTxaB~A*NX|u_Pyj@6P|0#1nAnA06Y^;=)``SbypTAo8-5a(m53?r) zBZW-ws}vZ`S81n4cssEF5nJvhi7^tzNYiDft;7wu<#7tV2V#7D;90$;(4x?rvxDy5 z0_lb1mA9^wz0|RGw46NQ3Q15}He56i>+Q#EEWN&b<6Z{-s1(_QJ6+G=g)?`j^e6^ebBp?5h z0!aBKpVz=%8Ai3-gr0{Xd1omo=zAiNEZZj))&kIS8!?VTOpWA$YD9fW;ms$0-iws5 zK)z~vx+rY2KP|WHPodFrDl|bP9uQ?*BP!2F0%_tq3fs=1<<^-|=(Qk4+(s>@Lb}Mo z(m{W3Ntl(sTI>#lmXDt@!5k5>C{kx{`I8IPaw_))`UKg3`uPIeqs#^mdFPTs+HJX> zxP+##ymLuDu#iaSJOPsFwK)IK5qb&+a>hv;oJG9(aMhcm-zY3ury@%pYkf3GzyjspM`l>)=NxHZw z1PM7t@|-0K>HM_O7%hj!hy`eDG$V&D%lz<=G>jB7tjI=qH1{`qAw0hGDDwGh5c+=Y zr;mK~DL?&8%Rxc*K=L~BE?sci;yaMg%2vR~JK}pH*MKy~O28UQ{uUlih13bf<1n+K zt_!q$K$@`dOX)i8_(Vp&eD$$HFG}0%XgM^8-M1V3yh1t+OKt;TwY>k-h{7nFE;9HC zJeWcIr#60I1WDz{IhabS4lPH{iEZ!<$t)trpDJ%ZLAzzoctBy_U510AlKy3o<|45< z(nar^Qz0{N)ijS8rqk?`SRo%yg*9|$p8LU3=m@xnM8KU3X*uFV%#qlWn)7kn+HiV; z>_7Da#B4|ySTE9N6!35Vq_29}lJN2&FH};XY0Df!m|D;LRA|Bn zh1(im6?$!AWqWuC5hVpBkbItlI1zBv%|LTce8@rk5bhW&8|^h^G=#~74)Rpw8In$q zIvMH#$&_=%A1#NK9$mDO;?zaNfTPadas-3pr;R(F#~e}ksIz|_0)2?^@bd*){`AT$ z_s1#+E$xZQay27Q`b{8>-dGDsFWO%{Mv;<~le{9DpW51s-F0mk{O!X6WA}p`%bEgV z^kR2TLLgB$Jchy&h==vI*-`@WFyr>Ndkj6bU5hRcNWRMXd-|#A@&w5oj?Xi~4RX_R zvw=MNNT-2>uoA3?p*h32D6Fq&IRudaIw0NPQ`Y9G{bwGYvNo}gv8Q_QNqh*TTb_`& z`RNxH-h8EY&clc-o2-ol$@Vfmg~DRE<9*pCfTz&hQ{=)}@Tk*7oXn2!oF??d|3Qvr zAmN8p&B+>@9=o^c;3>l3Q}|pGGs3wt&`@gItUyyV2v522$BM;I&9T0uP%Z28&~EHw z@XaQ^&nVDxcynHnJVQd4A@sZ%NtAM^Fr4pzw5QNRqVy;Wkeydae6umJ@c5eaQL5G5%IW(Yz83=*5!=lY^UxcR zrny+U9`eE(g-kftjo7SkD`{C|76=S@?I#9y{1hyQdY;2d6K_XMdFH-xHCoOAn#egI z!A4kDQX!H7djBaCY!fO!H6+OqdDgkdlc&1yl4+7nEFF+0tP`Y17!wzD($1!d#LYgM z$k@8@o7s2`45SdF7lsAlp8s{Pr!+gr}9>^k>L6 zhcjevC13SXgvgbiwgTS&143WF6tdxqb~3Qmv*8{V#P#V0B#3LZIFFrY%q_+9wE4D^ zD6GLV<|aN0S%3qQP7g>LY`%iH86-4cA(Q*+f|i@XP{=~IYzLUr(RLk4O z`&c375E5i`gNejKGVYls77NI#gA09gBMObo6J*22@7}QSAfvqDMIjx0l>&okf~C;d ziFE}?&-`6$d+`O#WcpM>-fwirgaRbf3yI`auSedSUsp&9Y3C@UufE>nZkX|eAR5A$ zLp`TD`Md=V?jIT0rx$4XqHHsWc8TXCkjo?ojVXG_Lk5obc1=PiZw1( zHQ7)aJ3OAYq3Kj;0!i*bZdE_(g?k`4ie{MH%j*z3M#r z(i04O<2jN*ntFHuq#=Fd~+>q8FR}tkBL5_kgtwH`s$;Q zO#B(oYXw?0wDrd}D{>0Ld zeQ0$`%WW*8uvgwzkTouSD@u_dr3GUkS>s<)pzlxkoRIVl7YWVbF^R@kH1UrF_I90( zuyA~DEjtd}`8=}HeJCUO;CPFo_Z9(V-A;H$9zdeb!VWFBKusZ)!#oIH>CZzM(S=hv zV}7krE&p7dx1U>QEmO7J>-CijU!mouIpSd&bz6a!2ZXsFkYGM?Aps=$Jd}{F*v5zY zeEn1{_dYrOlwo8CCNK#NU|PN?Y^23LDdja)gXL^i+_8IDVdMDTHYBoS3<9Va^Ha`s z93ZPQBR(LZut9ncgE!oJrcig77S>Scmg|-zC713D6za&3( z7kLVcV2H-)RiQB!>j9P1*S1RuoQR}|)wjE7MFu~muk6wU=R77jhe8_6uE6qA?a*@0 z`q&SEq;jOm6C`~dbyB&n#`&CQwH2!6TL{Z^K(d0N%mfrs zXZs1-EloGUBEfsxH-MzwoF=Hl8r`AYgbxu=(zKhfA0TV_8DA(Yss2!ycV%w0TZ1Ra z{?lIQH9pm83ghCM&wKD(9Rt~aY64A&5xqBEo(ervu7&kEp%kI-t3u;6#s@0zkj4*J zwG1PLZF|si_6e7+u5%PJ)W|{!dE%>#(1qv%5)Jj38g7_3PD}ldZCO` zI+I>7RljZlTzss-ta@|`oMAoU0}^~4kfyo3E=3nX+)*c#k+XwM+AX&ZodBhLl}+Cf zQ;a!s{Z%1^mHn5ri+q&@h)Uuy`>hMKd_dBBHeO<%XfL}W_r$zP#ZQ?<vdIe(< z0@AzdC`6)rx+pXfVu8WlmJzm(G>}X%n<0;EJwD|;iIoOOQ_EBCj!!vilDCmMYlH-` zFek2+bM z1CqHPkPJR#{1izinG-QFrsdmIrJUl5I?P15Tz4BPfkW48GQ67 zu^SPJ*^KEqJUs>^e#$H&WhHMTKV|L_-w(*~Q>Jr3vIK3Cc05v646|AZag<)XB>5@%A7T1k=OuI*D;7WXS6v|DB{EE2|Kh0VnVt$wG|&m zG9b;klIsyja2iE)Lc;hdxHt+6%hhtwovo0^&G=~_Y6@ZXG0G9AoG~ATj5#vQOD-;_ zLWY`?A}J?nFe?#(=jAA5%sEzK+~cbtJlifNL1c%}%2CLevm;+N-8@Dnb9h7&Zt_!TBS%;a zoBWia9)%1wm#fa#2{v-e%DZ$xGWf8em%R|ClPsT)N#%JiWX$a<#!ngQQ3#&Hs*?AU z${BokR`P!GQ)nX!%tL%X>Krp^co$FVS;!fo&fCcq(MfJEsey%nN*c z6+9n>jJeJI)X7l00B0!^7vk5x+MArLZ9`1zK*A`dr9RTP4a*8R}8UP}??0os7BVZy+1bIu|n37EAbP zA8HDlSON}@LdM(&0zU=MMtO`)#= zTxx>nmX3g|!Gq_%7@P|kb4yVClrbNLjJYol)XA8)LbaSRABBv$PdMuALrtL%HoWaJ z=04AWWXye@0SUtUJOh%!_jv{+gYSb4NCw~M8IaI(pJzZa_&(TxWbl2k0m+#AV0%jH zRSKbvtpGOqJUbUM)IQ<(DMLL98EPMF)Cr!qLbaTs9)%3G4>s!TLrtO2GqV2&&wZW& z37-2r0}?#{Kr-e&pnzn| zeLw*T;`)FBlEL>$1tf#-0}4n6-v<c{H|}Iq^0RGziVv`kREfKLxA*{ z-{m~(0->Ba&rXFNY9ef@(?dNKdZ=+bp-$6A!f}D@Lro#)*}J2Vq2`2(`wGgVj5+7o zyEZWJQ=|7($e44m#i51D8GH`5LBcTdg^nD4({J> z6MMt@Z=-(0x^E+S!+Q5^o^{>^@rL#7uWMnqGwV&Q=yniMyyL~4Qd?4dPx}8Z-jlPp zix1>1?BXLSdRFmwzy9@`58wa(hyV0{y@B9q6Ujej2z#a#Fi%3kr!=h zXyhQ-7CKQG$?4Tuo^W{G$frG2-{xg!2yJWNU1E5N;NDgM|Di6B;AC}zC(yQ}L_>EW zTBLVLrSmTDZ{Um5Ek~t-=S?FzLh)Vi&9_Wz>xO9tsNX!w$E`!=tAPlFp_oUyw!wubDJr8rT*z9I^%eGa)NJlU=mw{zkBn|U;OZ= z?|*vxa(}b`7FnG4-=BZ~{SQBV^LKyxPe0Xe{P%zO{y%^7KmG9I??3$b+iK|B-?20Q z;ium|z57+&ilCTZfhNLEepOZ~>=FF)cl7lAPanSj>5qT*SAY3ef5kP0={@rI@i+Y+ z{N(X3`HzrCl3#y&{}+^b4SvDMOtN6qA-aLA8T_rZCG~Cn%D?;R$3OkUPk-~>kBmceu9f-XITM{r`K-BB8Qm>Y+vZsR21oVZ`lfJI^UNH<>msI16Z^rhSo$$bxZmy47Il32G;f!d z_@v8eeq?F);WxX@=t*n*D>k-9e@mF_BJ}yAe}4Q`areT{fF+;rdO)XZ{4}sQitG!ytChVvCCfI?E6~ZH2yuVq=#W=nk z7i;ndE}lOd7e$zBenr%Uhu(;fnNuIESz);RuZq!YT6ee8D)#>Q(`w#B2+7Spe7T-h z?>%_!Hf+|VcuJO~$~CnLzh1E7GvP%~;W67?HPc&pBQ&>nw{xo#>&xf1CbKUYN5>WU z)|iX87XPd{aGLG>G5n^;aPiUNF^!9-yUc5o^fE6Jhx`gYqnUksJF_}WKYwPuF}JB4 zGkbSEv)W8WXth!`a;CRtn3JRa7H4I~;hH*py1=b$IJPb z=m@pUUHW)C|6eA!-OhiL<2?U5;Is782 z45y;=p|{E3ngdR%OTb)XFAnhvM|e#jueBQo) z{j`5g(EPFPIROwD{^rLYe*dR`_^@|U2TYBi8sWcc^u9(@+b4IQA|v3{IZvUGzL7Eq z5HE*re{06|@18mFAy^K!|M1h@WE)^S?}1^1x*6;re)#@3-~IIAU;pOc-C=>@ds0H7 z;duVH-#-1TZ@>Ll|K_wd{|^-N=G$7e=Qj+a|Hs9((trHp+XX|-!ARe(-`;+|!RjA% zO|SnQFoH)Q{S)ln9g4qF^_*bXaBz`=?gnhRcn#QkJNgDj99(I#Z(t~}%bs%!ds;ER z1GWOeZ(+FCXng2D*s!DCN?JHzE6w_Vk-ARb@>G~$%RoH7T|PPkw!GPHVK`%`Jsdp@ zTOpX^TY`z^Snzc)j5lCa@1-nhqrK-+{|*=mmH|T>T9@>jdMHav@ikz}ggRhk22s5g zH8Se0prrv@o;?G$o=YAu7|ZMfP1G$tvzO4Ls~#4DiF@e2G@G}5yyF#Q!B(uY%4zFjWixApFp8EwE;INX4tM4jysP&d~X9dRi_$G31c!IlH)VI#S?5fMvQtZy}%g;pIk8sXIOFXGWXrW z$jhv`@#>dhE0p*AEs6Wq(L7+hwRqbH^~f+M7+&gvndd;drFFjTgVeLyza*9vp2;cY zdMjw_+#a|>KYQB;C)#o)IoIR#G_QL{^n})ptRbQek~wHBEV<}~cwNl{ouY8M1-hKa z2gAX9emf6f&t(oe*DF&&nYYfcnp^4VZ(+~N+Hr=_-g29|g<%xcd`WYox#9Y(HGf)x zHlw|j_h`VD=j(v2wC-mZb6cKuXBbX1%a8U32EQ+Z$r(oA=!$+HFzsJ9`Lb!B>)~`G z{#+-D=?|Ax(KC;Y3s*b>F1o^=qOF~1Vlyl=^7&iVW0~^KFmR8^e_M~t@je>~ zyqC;MImU_SG(_-SvE|F=B4#<$w5^x-prH$%(nd;2ZtwA$nKMAv(N z0V7OA?X3{E(Z}+09P^QFg#yJU(uQ8uOM_)T?xiHt+5bbX4s0qza1Yp?{Thj(2{Utj+rCRxLUy?x9y>Q zQG1&!+Nj56d-lP#*59tTBFDD~UHW#V?;7=18o65-kt*6-PwNGYQE%Dm2aEu`+3zzd z2*FlZ^!Rp#%nsP{85ppYQ|%T;9xLIVPlbb*cNehr-0)~`J#IQ+Jg<2>5BSE4DLvQY zK@WuQ+j=NveDAr#m3y?uCR(9#=k`cqu`)N@z(~q5Wj*RGci5YHB=#<^hXF%9W0>yj z?@=vH`okkd^6q>5;9PIT8Qu2F5jFP=$|#*PT#7GXD@*%mj}&Ed9=wY_af)Da(eIPa zBZS8bn9ny3IQLP=pJRbKg!{XdxcGJ+DAR-&62u6nDBrYhd^-0kIZkx1eb08&_}(w? z`P;F)Cw=3b2X|+Ug@<|F-g@Hhwmn|)6As9JJ$VGqOOh>k8`(G@DTmtQE-CzN@9Nx+ zh17e()t2ya)LSW22F$#UL@&kx_f?G#5l-Vn6*<LL-nBXA=*wD zsdLI7e|&4X1h>EPuDq$oi`ddV6pIj##T9bGFeH>$+-T4DBa-Y(7j)0%evX-hKjPW= z92y2%<{H6D%CGNsET{$M8nLscE)6zho`bese27=IrL#npR{r**-xYs&3*&mJF>^uE z__)*w21TonyJy`OxK_;jB{!=(>7>!5mDBj`_&Cc10|gp0@3xDE*yh4L1nu@#Ak%HX zJh(phG+JBZl=EZKAFnw{z9&PF>68avMQhuG%C}=djPRI!=JAfP_8ZBD{4HtkwLf@~ zMYtlZiq@ASdCs?9juwmvS$&Hc!m#xY&Fy?qp$PW`!)ttWE4<+GE1W)AM(1@~w|Bws zSt73`d8;+PCtgpPw2MdZfN`#_48rI6))`vyJ-0B{T=U@k&>n>42nU=WT3-?m2*z8X zng=?CF=oWIm1gfeK7?oMH*i9`=woFuzWtVXc8!JMsb501MOU0XbMM#7f5O4bvw02= zR?hi?X5C3pjY#H4eJ}pP6K`{Uk$fx{^K78x*b)=Rn)B_m=0U0}$s0V=Ci$5hQpU^O zdP?aA_xQF8r^TA)dcZYwMxlO@%>lP-(JAk2Q~=d&zijhKyX9U#X`H-Ux=Z3FQO@?G zJ=^J!xO5KUULoBa_J6lmUIw!dZm}BQ`;{sCb}R^)u4h{toJo6?9jN;jIm0Bgk~K`{ zZv`W$U$XdVEw?wZZpXYmrFH{@?TW9}8w*-XfQIpS%EaIHRL)Hwa1GBF{gvi{kFWMQ zHZRc-qK{~U1Xx;2x)5GSRi!@2l&P_xo11*7UgFSL;IXPl;EBe^)n)Q4d~akAsMjO> zt?lO=$?_qn7a26)=L`IChtOEa*rN5ttyB4p2Fy+v@_2gkzP`%t)eB1wRC|G*-r`v>1j z9zv6$v!YZAa}VNVr?Y}kGV!o7`HLnI;wF#4ax8h5M0t`oD#PvMlO($p-^FXy^voIM zvG5wx?V4jKw|tMJEIAC<6v>^iXW49!a&#A>p67;cPUGXK6>V^s35LsrzGWJQy)Q5D zTe{j3s1F!au6irW(x~?&eox*%5zP(p-mrJxN1)z)ym)-rfYdK}ptNW3 zcbt2WE2n52*Idc0nwAo8RmsG@Ckq zu|kO7mmj&-oaYbb9>nS*IRXzL(+2vrNmpc`lzf0omi9CzT%A3*rAeR7TVT3Zkb+V) z$qA}^6v->6-w4mX%H~k5N&Z7sZ<>UiPJVzlrg1>J@p-18k}>^ZpP73fH*U}Oex;eb zUGpved%%2_VVV_>Aa~~U>A;f{&zoBsFYiwAI<#=QPjdno_n<{{!(^jA>ad;tvI(cW zf$B?l172^F?gnkF`4CJ@bPBL+1-D?|D%-M^q5QNI^-K-Rgz$7ydpTY1wSsMq=NOw7gnmSbV!04ERoejqZ4 z7o&-k?g6PpvNu#EJt45UM)d302hyGD{2-@?>@MVu(7JJ&OTNcjN<5p?tlHD4ZFP2Y z3lQDowJsh(u2%6$$Zwv>+$tO=g8Qk?1=PyYRgvpe`W!qpWK%*#p}j>iSe=b%jJ$65 zwnshVLLj>5?lSowVxoMWp3%ekTkGL!>#IGKs`|DRykpPc(kuRls$TLbDip5=sny-b z%J+J^w@?~Qet>pz@)u0jD@EMxw`^67nKNLVGw`eRjQRQ7;!Tss!eCeO%5yzVed|W> zZ_=59@3i*^TShp*r*iTpPFnGHPJP)WnTPiR?tf#xI7U57uEUSQdl15(X9~RC>vrKk zPC8ua6v)&onGj8yWIec*bdgYo?gnUwbk;tTnRl)^9-5MsIkqGV;DO;Yg;YOtjksFr zF7%AuWt@W~%JKc2EW=Z0M7{NF(G6YUh9_Q!N2O$CZpoqpT;sLA+=O(8<*3#9hUQgg zMTND^bDC#7CF@~fk^bNX$7bOL5>k5?F;g7-qFpfRxuFK0dxmhjc?W|#``p3{&v?PD zV4mHmcSRfArbSnr0LBZv*yFSO!b)DWXPFx9y=e_MOX2jwX2Wii?hqBS?kT7lbkAce z$mYf9B$ps^8CM)V!U1=Z$zMpiEE$Q&8|m;!k8IjN5v)G&VV?XC5zcFLq5Fb6o1W|P zJkCKl?Bt!e1zBDrL9yq{-9WU)g;hM~31?#UYrP|pGP+;F>g1b(^k*7E1W~^TA>yZG z!50j|()eJq^De|~L1zjPXF8{G)is?G?l)usjG-6%_d;%mJy)K;Gmqt4$-BVp4@}Ck z3z zNWap@0+S6Am|{?YtvA<%7p(x;)!knAqt+=hOmp8|Jxz*|0 zi#@|VML2NmD`SzLAh4C&^|oIw)WQ|6jRP-y%NCjA!+fdvVk_7BBJ{hSd~xuK#Dz_L ztXwNMW5xy7d!K8q`&j8f$F~F!jy}*t&a<1b=-x=a(DhpLXm8~n9k7-0b--43{sEJ2 z4uM}bQncB!?=u$Zsw$u9ylViHJ`or<+ld3*A}1XXo?=+-kohgI4^Q3%7oR#o=CPi= zzQw^E+JCjDcrdwTQA)fO#jLfRSX*r8) zEYcq^X4xTd#a8~8a?hT2KVXVouXMKBzm=On^WeUvwPd=gXI&6|5ZOEDhSQ>8*aId% zz#`{)z)B`x!!)L|hm?@UGd@{bOWtM_%yAgy8hJ`Z9Te@YH?K#%^&0yPjJTqCk1FpH(A zDrEVYBS8BcFPW)}tgPGG(|oG^OH99E6`VF`5)Lu-6PK^>nZu$wM11l_c>}a@n_tcr``vr48>0alOF(MF<&EWXUev+jm(*E@c<_1M0m zdmI6#e1L>L{T&Gcm%SDkN}jooFs2I697pQcx#egNwmsV;WKZ}bpNz-6-V(c=FA7xg zb`-|qC8$s58CCJ(q65SYw08$;;x}U{b~SNDXqxWRh^*7rOM8kJhm)+A-A5nN z4fCz+27D_UA28+O0w$krBInFINywwK5mmkCflxTt7Y;V8c_Jshep@nRmynEGA%zyLSs8CPBLgG$LZ>6t7s(;fw0WGmuK z;YgZ&@KDF3ajrAcZF19?aYeMZ9z4ILt9y*IqYvU&r`?oOOZgrulv{TaT%C~PlU!rLtGAY@2-o*+^dDaxgH$0k})f!VbT?X&c(OZAKYV` zn6%5GJZ)KcBVFLxeo*DL*@oSBEz^f&GDhG)0lB^pJ!C1;}HzjG|yGkj>Z9EqUaAjm*$Jj zT4TXy*7cCI=R8oo8fKejx%z3p!S2-uS2fquS<9{4b{9mS=`%q+`RNzwYM*l=OE199 zUUw!WNAV#v>*7NdyFT%P`aov@CP?j7)WDNxvnh37C^=%DWsJ`EBVr0A)8M%v-h@=A zy9TXkPgConJ?j*Tl}x%P(nC5jq&n$=QIAVThl6QfVse#U3asdiA_`4%ETZl7(Ik_U z{QxQqP4mDCIrktM3fXpuq&00IxKFyrMBp*w8{m34GLAj?z#@5k7+q&QPMXO}V8zpC z5%!GtjK0OwS1`+>L|1q$)!_V=32Q8~?aeUR&*bZHM2STx6i%~~)V`ZYez3gMnB ztZ0qE3C$NfgucbDp?cWBHMeqd)tG%Q5MkoIh1{+_a7LUmVWo4^_+WpAU7QP?DHDGc z<~sX$BnzzT*{*?Wp6+>wG&5EP#kFD&QA#N;5Sa2d7EE#z5}|bZkJu7456o6`&Dk>2 z$0C~P9$#r3=AP#Ep}kPsQuR3Y)h}1)xkji8w7yt5JwC33lg2CNd&m}?P*{*|>95Yv zDeK{+YCQw7f}St>oJo`HeDNkC5Trk?SaZ=3+*ml^vN7g?#4mqQV7}Ml1tmQQQnB`a zxq*5t2pqS#LM&FyR7Hk+z6etq^Zj~_^+q3tnOHocV#qZX(4zGvv`V-~r>}2uIWbJR z%$SF5y+m#6Jjdfnx<0}%ypPy~!XL;V^mmV|uI~0oR~P+S&gM;I@pi%jb;sr?^S&ey zS9sxysC|ikNHob&p}9G}f^BrcBk&Pcd)#|87IZxG{3Yv_?zMQWoBv_{6}?>Oo7lbD zAEo~DSP)?5Uf`}V$4A_@&V8gh^E%k6@XY;4{2AN(#8o|{K5>sYq&{A-q64JM#amB&jVrPAerM=b8!w12nj0bW9*g2IINTJo3=AeQXb3Md(J8Uv;t_;LiAMm_9h+?~S)Q10(KD_y z+HX(zP>HU}S~SPZSs}cjzY?BtxtsLNic5b$ytw!a-m&tna-ZVolk&i1fh;&+HB*KU>`>jCh2}8ZBdK@ao z0fV03UhsBUyYwK$m1`gII;wP9PcRAf%O(_ELA)9Z*AIRBtQaD+qV}034qWF9$C2a` zqR=Gsb7hh~2kWotiWHI>AHhpnbAs4J8@PoVhBMP#4@|-nSD?V_ODM{$M`)z>2S=^$ zd7J>^b+~6wy%Z9hVk1#mT2{u>O6OpuiJ3H6@!gY05Vx%}o+WZU?k;l=5~D5MCTd&p zAtX`xyjRSq+PmYBAM1gXsQX}<%jO>Bx-Yo|O`7-+QJOk?keaj?u&#O@uxE{r_Ys9t zRFxRFr>RtFjRUEzhKq8-Xz=Vw#`(xsNU|dyD^=yye=DBWYWOUD? z2lGT!!)^~@VDv$J_eCF;`H3r+jRmWV_BnCoqBYcxlFce!eBufe=={LjOlLf3(HV-H zf@qSfr0gGfP)QCW{#@%z47u(zXh3xjz}rl+K&954w1N6cI%Kk1$*x;5^K;!WwTZ70 zYktwk1x}rBCa?u~+uI5Jcx?xCSlh(NAY0WXLO+G{8SV6<|&J?pCd-TK=g1h=ad8qbK zp9=S-sGjXz_-UaH)7=!YQ~nmX1xdz4s`Gy1txM4mVx89fQF(EQ+CjZI$BcqQ{E9FZ z*TdjAzvXZg{N2nFXfv z5}4%4a$T5wtzh%K1g3i^FyjSLVCqZCWy0&m88v8)_7o?~x6=Cnldb@m;{0*_&^w;E zbSR#LZaxz}dl+=+s>j znDPn%lZ*sR{(AL9#yo#3+x0v{`Bwg6z;r+7Tj|z;5qv%72G^MUnR>EO6>RE)fU#ZG zo_Xv$kTus5%DBL@?D2I}OgRyleD{GpOnC^mFFa7k__%&dn`=F7GW8q4*dY2=_a1gM z(XX=}fh?jSj>buUL~hP|H_j@`p$km0pym2D@20@K2f1xc8H9Rxs?ITUPnl~8wM_Yx z^ZY^wfR1eJU)U9nU$YPEmbfH)FL3qK*;wJB!YK}Wng@Pl!gD3inPcIOqwzWC3gbg5 zt#9={4i*vVZOhAd-UEOs=TBMp#bXg^#Ou)VPx+n;iS$xsozc0#^J+R9%M>UaaE}sB ziF-Fp`5EX}x!VYkkgkd`TV5lz!<4hBr#CWyVa=NBfm^O<7p1Sp%+q$FA#5UQ4K8Mj`_-Kj zUZ?nEV2CE8kBa{l3<+Oj;aMorAD3N!t2l>x9b0%t%$B@csY>P*N#kf9#9uf*rY#!HhbqH&KDK@z!mc)`{HfAax>StN_@QcFGtV7DPGOW#lTqPGg19c z`I(0n$iOkj!ZB?a2dnBSP8}C||WqxPBi0KQdDB4Dz!b0L;r1~A#E_}2QhdRlR=FCm5E z*<1~#t*E?~BsUS`E*@K{Aw>tEK>gB*VaoZ)++^!O@>Wbg<5PaAIw>UkFb&CY66E9}Srd?bsrcA>$OsC~+b<0b@6_2e>7$TO|ob(Z}uSeB;t+`2g5F z-JH8eh47lc0^JQ-twbv{hKQM`T1lc+IqA{hOa+sJ5-OTb0H<=`~F#rD$S%s@6MofX8m`^>|SOtkTG)i287 zffrn6b>>xg_Z%NuYUx=k;i1;(Tdg^f0KTiia-@I0h_~lk`J0x)2@*Hzk9_g(H<{|PCPSR ztuJaC)q`BL9wlU|J>}oU7@!z(URdChp1D`R`B;?|H zsG#S!n140qa`IO_>zXT`UHgq~B-~?G6Yl-4AP@fe{)WV0oN|ZIy735)=HVPEc=cJw zgUFzBs~qfvQ=FOH@5{WKa=z&dt+Q^wh+qc>l(-M%~)=Y1NOb%z+= zrANZZtaU?joOki^RuOF=N{Po}=oHVPHQ9>TsyZ7{T}#g4<|2N;%};y-sYJGkWyqO$ zMn^Vj93zJ0PLvAbf2cu>KhBi77f|NvJV%_={zWk*ojJ-m&kd1b(gqho*}PB;%$N%p znDXS88cF-IE=`lhxe!jDN}~O|2RY%T13<`>+yqSbo-%h&-c$#_aLOBplfN(#=^fcx zb6+xM?<1J8WXxjZ^XvhE(jO4Lr8BQKp~j5+xW|m(IcXB*l5oI|o4gb0+4}>REb)?y zvy4yOM+NB3u^_^!JxrC8$8waJp0Uvj2l#HOj~DBKG544zxmSqpF&PSf1Wp*H{5g0% z*!GFO$-E9xPkX_68&TZY{)@$0XCv|P=6|?&Y0QLD7*{A(<{F`c5?yhP(i-vbjp&)H z(8MXxGx`>fcda=Rm*jTLe&S8U(QD6e@er17sY1ji zp1CG_z8G1A7krNOEoO~L<9K(9#+j|oM&j*tMpf+pT;Geg*irN2m^A{Ngv*K2{& z-H;+mG8|8RxDPH@bAO=06Fq$$ZH@OhaY@cS0 zbgm*}NWGXt~JQ5wC2Bt_ffsOt}&PL zaO_nwKS|~P<=YON7w9MJ|sjc|%kIC(4Hv-rS`<<)Q}Wvkdp$iD&9LMuN1By3-NLBi>n0tczYQe7rbjq{rk$ zmV8CJFYj{#afMTw)0*4PUeU19xnbdxe1-K~IvGqXlGiGpeXcpN>y!UMfAVz!rniJJ z65D?bEvxJ<>}c^dyl$kshO=w0qPdaWUT>36oU)WUqd0vvH(W$q57(4Qr>HE&Cy|;Y z=MZu$*^HQZ$@jQmXulC1DjNo(kN7EMFBz_4=I1^q>`8VFZV=+*6<0pb8M1FkX23$P z`){Ryn6yg>mhOXiJBnx5m0j!RIQq(j?)Am3S@R{-WAb9Q+msLB#d^mHzRMwS%Xgs` z`vtK=+HXX$2&Ztr*&f8KxI19VF96Iw4p{e%KkgOd{NP)XJ>Sj)m~saqSSWrFn0?t{ zVV4=7#D{p=$$Oy^dChs?HX?dKC=?wa$cmmTF@)N?*dJ^K^$UYh zdoWqy{=)wX4?dkUSUGi%BGg0j23D{sACSO>lW@)h6SL;#`186+O2#CIpLsD?gRvL* z7DLYL7ZF%v;a$eb4^W5c+^QJ&xt4{h*#{AJTDOV=_qWPDhH}z=GRR?~=MuvvzpprT z^+9O3=7w5o;tIF4i*K#l1kICY*J~nkpBEpR_(Sn;Ijq8a=6dj+x_EnqzD|1PCM4cY z#FY3rqUc3?<|SC7wU4lf7=QSZOxlIx=$=B{yYvybYDj;;HQ8&4ZCmqoJ~E68_6_9? z_=`R+<^d}fyb2t#<8(i$*$u4h7_lJ`KR?zA@oYrpi?R4D zLmHBNk2cYJri5UP&psXGSJ4@YIPUSmP{(}BL?C&Rmte)m(T?lBk2UI|58u^ZI0i-c zn2*N2f}JlNXz{Qazg5|9y;?|?i$7mO7mHdRB(g;(VldDz{Im* zB)V$=lbi!gdNWLNim!msq>rVZ{Cvxt=P?tVru9W5tMw==>BK=9+9!;yul7)V34diY zoZoT^h@LTaj5CUb)Y%A(*Q{oHrTh|%Xx|CL4&iyo$B=>G*)qR{-)nBfy=!i4Bh^ER z;xTjcQ6F3wCX77q!ZR0n&CPG*mRG?%d$@=zS5BFPr+opKa_G{Y?vlWCE&zin$2{tt zbm8Dde#3k#yErh@Bwq0Iu8WCTGA7@00hwc|2fQZ?GWD3{lTRO{X`J7ZH-4^1sdp!> z;cYR`eazj8d%`U4y-=ZolQ!y_qIo#q2<_>e0bt(0Xxye;$N0P#Dz1FeILFb{EAy@5 zO@YY=5}5Wc@&T6OTRh`wpnX)?mFHauCjg=@ZolOsseHb`h&od}`E${pa+m_sSZGgq ztbxh)1kCaaOQ|y+O~$kZa?6wN8)H#EX<+i*2PS!v_T(EyJ>@q8CS5au$hs%fo_wBw zDc>+K<&y@cm_1<9fdZ2bfU{9=9Wg$|0stczbgnth725k4M74i0y3TWf+sxD-&O=R&dg8(Bi@id78W%6^eZN};4&>KNA4orQEGXCKe90a$?Y}rjSdN92O#Mr_&X{+W zB1!!|P5&^yl`mbn=Xk!Q1eon{PZ6%L+iQ)u;~3`i4aruxN8>T|AdeN&F!=`F0`vYx zJx+@;=1MVWnBKVNTg%G0n$9?XBrfH&119@)!E{DpBAB*!>M2h-F!_+8-IaeaFuirj zx8Bphbatbb(i#z0u6qhvIIj`uX(p|q(Vw~{#wS}$!MsLvJL!reZ{D3TFvDTy`f{dB zd4p?|WFNdJBs-SNnA*ecJZYEbf9Kg)k=N6wtzg=ha7pb;EWOjN#HTcpsQajqPMWp#?HH1$ybKK*d~pm#GCps zoPCsg4487-0ka%I%(rCw}&yedL~I{ZH4Pnq z(@;^LQ@>U&q`Dgr%ROyC)Z=1tyGD3BDmO0ml!G0Zf4D78d5yiGI4>Lw z36{BC57N2${zk~2aR36fmMq+)-BL>#=CcB`hVIzB25GtiRdX+J71AA+3^U?k+@i#X zh(Fgp=h~w)ubxWLo*}Md@=m02$({9_m%p`4h$d*-7Lo3CZv-I!HMB_IPTME-9Z~lJyV|bPwR_C!H3Yefq647Q8fX=?WOmTN8%EkLuyVCmOHE)by=oezb|xRu0qf z{S8aBcr3SR>F~J1N`4`I&a{UTW#jV$3#9ixF8P`pZakV>8F%JMg79CCX8dy_2hd=dwTD# zVAFSyXY4F@!ZoxWC3enzPS}|A%9vrb7ho^i3-#{0=E3D)t_P7aUJuTm$^Qs<7XRb< z5AB6g%TL+>CcQH8-I9T^9(g@VAew7bf%FrGYnj#(#w%KbB!z?8Tx!qyA-v`!kHf)q zpTUJ{`Yyofo-g_#^~Vt45;RUw>*OTuN zBCzt+m+Uj;7t99AbMkl*w+-xKeNhjIcAw=J$+y0n*7KLz3ph{;FWA{N-z&a*VZ$K( zn(>FdLVFsMtHyGnm*P~NcQ7=TzJH*B*S&}9^u;{n_kh!!azg^MUAN-dC!ZwEj%1`t zLOkbNNtfrkRT!0FzUQ$6rOT*cyb>*BiR0jD;ljXoM=Z3f;dAwXJ{H^raoZaFZoYTrF!G<9T$-qHf5hq^C5k>RB z06gg#W4ZQa8T4i!%OE=XMFD-$2Zo_p4+E0M!cnF^Fl@{zw3ttGoe(flKD6p5+`;kcgk6>C-vL zQD#~r?T5yPE1lU&g9HsrO?cKySix(5iE&VB?fb{6h zOylE(7hdp))*Txyjr3P|GU@EWLsRlqi2@V%PxVN~pm9uDQwPAAqBuTo2GZ+6q{?dm zIb$hSA9~9Lj|AJdP;E+{#H^vYCjE~NN2HO{J*vWM-Jb9E zNZzs~R4lyk$3<`Qc3d=N-T=Gmmse=jp59a9TQ1(S9`d-> zh~1!i7rKA6KJs}drdnrmg*(r)r{a^<2XP4#&lNr`TsdbeM}Tsq)Da-r2X9u>3-({p zBv%8`3vRhnrXjXj`WIs1H4kF1z2@ltCk~KaOhXs?oC@um<0BS%#@TROD&B=mz}D%5 zD)IJu4_xbk{deMu(@p15rB9q^9-g(jFI1}CNq^-EHtB_%r0#>n6N~4R%dy)-TRP~T z_r*mUrLLRnTXE5vZ#`x(+ks{D?u}X@r@b=ODoH%CP=`cNsuMw&*TH|V} zJy@wQwZ0hnM3eAr%?-nb>nSHM;uT8$i35_#3NQHQ%yWxKlu6H=@!G$2p3k!u1BP<) zK9>8q=37ytb9@}}9v_FJ>gmlKjDFVB5IZcLQ9YTXu^{tnEEhT%)X&oCb9eTd6L&0} zR&tv$kBWm-zsS^DH|J-q?0?c<5&mmB#l>3lML1I*1pjL+Fa*s5rsHqzM*+*042&qR zJ0ee2Nbg;7(c1gGZKr;@Ivb`qT&^E5tciO}HWLO#dan}PH}L}9OHRa#QRflI%#@WY z^ND0?-t`bp;n)<){>Y#(G`9&^84p> z6JNuH$~eW9eXe=MCu=P5@ww()=_Fqv3g`^L+Ng62t*!P9oLzK^x3|t|)PK@(qq){T zB6fJjZDYXjJsu0L=&xLzr@X;wBv~FalkB0Szc>B)jR#VS*$)}7f9ZVjXp>Bh#z^`m zL?rQ8ZWWSgh>DRc&#MnQ_lepu?{q8xXOw)l>pYsggagm_M&gd88>x4+MUyDlv>sf% zMUxluc!`sf7Zcm8`#CNwicPEVD1A%Vj$wFG%sq`tX7X{oq@^3-24Vg}qKSF_z98tR z4=xNE3#td<3O$X#b!=p%sL*+ddyc+^d1`z`L=y*WYJaPIksv_1aw-kq+*^40N=Bz1 znL2L!1*S6!m~!j`ll}mhV!!BDHgI6lw*y0vons-0NaJ%(F}{^P8<=$KsJRr6h*}D5 z{p=Ta*0GPMCtp)w2=n?@cX5O|(G}n7UI9$^3ShFs(w_V*f$6OyV9F;0O!gjN2>o*& zIBRHbTy<5?{+Oi_obo9!((>W2DoI_Ry=0J;=B672;dTT)^~(d8K@u zypDRZ9|2QtG`^KwLOuBs1Cwr!Z*{lgtU#l9yGC^`Oq&vyLEYI=smfQQRH~D=)34s} z1*Z3a`1WG|0v-H{eie^ME!`1;$xnfAFZP>#NcmQNRVdBm(^eLTspqGU3*O{=0QF?^ z0*3bHc0K49PB~!oI&}(+1!J4)$xci?iT zWMJ}d2gX4?=7yW))REDz@&uOpe(KhNN!JGq>#X{a-xBj>nPxqB`{ZG0C#THHx3c>I z!)Z+INpHq&T6qI173S0_P)|Poz@(=JrdUg0xkGJg0G?av$}Q*lm08PUR{oJ~?el6>jgj z;qo-kK~#rR*UUVmGpf|RQ~m>H-U&IIuAGOIZ!dVAeKbiAtT)zy$sS63vgg&8QwHW+ zQo!8e6qxK4Ww!EK+78RN@>>E%wmy`+LNCuCO*Bv1B?@0-ztWUKJ?x(Fa|Te#mjfb>@9QS#0$0NeUN&xmy!ON zgj%x?bTSjCL zX;aU|m=*8Lw;*P&2irqwmch+KlCqS&CB9CZ70C+Y@WEn1=8Qb@Mk@Ew)Rma zy`A;Qb0-))+wDm=QW3$DZHYuS&$*DZ*;?BBgdR@)DVt5cl4V_(@5PjcY}z&WR{6() zdEIcg5Pv3;R?Bl zaUZ0fm^8#GD85^9xUPo=f6x#&Z1+oi?Zg2ZE!~eQHG%3C)0us6l&KyKYJAS|R_Xpl zFL>on9V7LW4;gEK{27Yx&OMFikzz8D>XcgvnE4P^anTyFuhV|cx461Zyx>DGT-ol7 z+l}v>SRAIEj(X-VsGFvoU2Z(aDbC$mBd+_C?_!z|&#rh_%?)2keT(i!^X2UEwoXC@+5xB2{3JU)RS!%7>fQezKd8|G&jC?W3@N!a!O4;#0nXI zIKXIbn1Cj(N>ovM7)j^3S}91h2T@CDZX9n@&L);tI$zv@y{B0>wTJCf+`4h->1LP{FjDB5>(_d)l*H zN8GH=eW+ApCYn=sHB69NBiM^%E;K08-N5~(9TCR>G+1-Kq#&7eRbuC)1H3#u4~C+8 z#P!bgC2CCa3t>Rgg`=jPHXB?FWse7@m>*ymp6A?PO_NXJ{v!JfZj3q?P_26&EV^)2 z!Q8W7LN|qH9$)siIFZh8(R-^NYB4GHl6Pqjf8CNFLF2|tSLlrrA3G#v7Awm{V?n)#*E({Z=>`eEee49w}@+SmF0IS+l zo==o7);AG>V7%b^>-J>-Emv5{W}MPGyIFF{2T=2rn`lpd07UvKhaWIH9`mgbHsO?T zcFlw2^ulvFz>N0LdP`SI*on>*To-iLMQZn6#U3#CAg&NP_laqh4YJ-()_gHJ%()SB zt8*WVwPaa|KyYP%)Hi|16?rm_lC)Lp>~$|A8K!%$!Lcp zn-Q1ld2k>JR~*zD3rs?5>G-H}q1TyA)_C#adhS!W;<+NxfpZ;{CyCB@o+|U+s)*CE zr?KqltR;1{&RSgUb%(%HLp+h&mgECG5=@iDDK!>egAmNNMP3h(%z!Ccypvn4_)w+F zo_wuhUxnv#dY@xKCGC3F*YRkM=Ziao=&BsZXCL?qso#2hO7+m*2&UY)WC0@3aMnZb zGiaBcDmkal$2_pkC@(B9 zEGGid z1}K>4K1LwvJGnW}GyVw!u+FzKoVq>b$m3i2)pJnr()XZoJO{-OFz$#SAc5$>yy^DQ>(4e^?4yj*+8ubCo(U>oEMnt=q)?mhycLFiJkY0ZBuu{or-N>t^F+qTH%#DjZn)9TbE}>r z(Or@VJIQ*L+S={;j)?Y3cSPcoST{lFN|j8P&S`wO=^u7x6Hj()B!` z^1+)bHG%FQ&j?}~ABU92=Ui*`jJx+6o#D|Vspn>K!AJ~>Rg??7?Az0 zvGnR}ORPVyRfK=k~VN%k!I4X>FW6_i+m7UK`e$wBCeSs^x$A{^}-zrZk!A!Qh z5UC(uT&}gkGsZ>rL9DO#AZLnjik@c546q^bV&aZ<*4BH%>I0pT`>@@G7+KfD%UWwx zky#V>xVUP}m_tQZ7k;G|Zw!-822bDF2fn7Gy-ICk+J&(R2izC5MhGDqGj4d=AKYEE z2mRg&2loYyBTQ*5ERy~?#)Ck5Z(yb8!0SlsDJ3 zzi|zfZ%=8Jr@YHNq@$po{CI%Lj|Z6EVgk>bOu8>%dNT|d z7OpvFEd0VtN!qHXdk+tHDK8i>cJTPN;w1-fr=I-1fnj_a-y$_l`G9)Ly9G>dry~H% z<^W7Kbzm2K4IPx$7d?{3hj2FWhp23v!tg~0Ty=#jHsLtSxa2A32N(|c16QSk@>t}X z$G13~>02Vl1XFA&-^vdURgiqaiZbS%4w!V2e5?Bc^|-NWEM9ZmS?9S=d&-?qs;VhR z07C;a`{3$4%@z>)lQj}da|zq!@zJm7GPI>U>Q<9EZ%}C zUW4{z-$FeJcz%mZjP?xdRQ=-k;Ciw%At@_Pgo~;9M!9MT2ju8cA7vPtW5yJyZ!y_u z%qSV%-UZ*lrCjyuF}m4rotP8GEm8dv*Q>pNVo&w_&I$>leExC@dObKRw3a-BAiC!Q z?0P8=LM^jC4hlMZm%5e=bzo;DzEmq^+ZFNmEQa|N8Lo^l+aO_a|uFx|z0 zDGri{Gs$!}```*O_X07-;%meXPyG<>;nt@1NDe6&%<~pj*+A#HP<(gVCP>r4CF1s5 za4J7|V9LE%FQZQT0v9XUPD%sd`C_xy8r`$eH5S=T=|lP#V9G%UOmG>&S0>WfJTW7;Jvx`crM_1m1XT+!72rK_MZnsmdosLD-wMX3 zI2HmGl(&p}7kF{rH@@Zatg#^U3#L4Le9O_GdXGBe@t82JAzI9Si6zrsV5fR4uv7KR zxiEPSruS)UWB)3z0j6>^PIDgk3TZ8I!gW2xWOCt@{vMd}!_hAm;Mp(N5A}=QNBx$E zoxVl+B^Xg>6GpefA2(6$OIVn{RlZUfC;z8D6blb8)|)xScg>5@$7+0-&@^A%>J3B9 zILCsFB>bTt61}`A768ix0;Sr!pg-qxE31Zd5A}4CaK+oab9@(X+Lo(^bPA|5W5R7x+W0)7;?2hFLzvGOIHbaop=mEP!yu0ir%g=Qhs;G+*L%c)m$K zMFN)mhnC+sWkJ*jwp8(F_`T?q+M7doy&dOx6Cg(N59VeNVN9p3Uf6tBZGUVM_P zi{xjd4bKD7XYNbF9K=iTf*0Sw1TNXE!n=hlj`PXe(R_)=!XLG+#kDVksAg)*A!*A2yD(53nXtuEsn`?T3ZD@Rs z|E0YPydd9?F>^+VUsdMgY1`qlFCX>FjwHH51*)?a_h;>Mw#SSEqCMrz1cnqma9=5O zC+!lgCLTc?uJosbbBdmkd?XKnDd`G`9hU9}$y<6ZENwamx!Q}Ki5-^i5SJ5=51*Z} zZa97Mc<{ta3G)+%SmJNxF9m;*4?QsDAfaEpVrCzx=CmFc=LcF~>25Hk>K=d_qwX%1 z25s&w(kq+Ra8(n%z>I~L3)^wU`f47iA}0?+D)D*{3_s5@M0AhuL2t@qI99waFf$#Y2trGh?)NC)h3O_{Eyw{did;!)(B7$ zo^jh0ZIG)|bk7~a?aBX?O-XW*Ic9EBYR@?{>oqT(znEgC{~CQj2(u5I4An1=j9xch zrJ3I%#)|H76cp}Xcp-6o%S}>zvOr8N4M))k4gNm&Yp64_uS+kivyqXBLTDC7SBuFg^25& zG8f<4FQdeE;U3`RCB!F7w?uXe@e*DW*13RZ*`!_E^p&d#t%`Mokv4P(>oyIKG{UjFiziY+Os|kRC!R-$I<>kloy`sF}1NCpx$Q%E(6e7wR2e&%-`hY&YZT!VZDUo%lwj0Ml5A2^OAF@l3gi zIN@pQMM6@ZcVGlMO#0(WEINQcs}Gdxlb*q{_CB$`+P|ngBukMgcG3&yx$eYuc29W- zZvp!=@cNe55}{w~_F!EkS`f)JtdQ5kalH&!Z+`>Rn<{vh*e+S=8MGbSD0si<-a`nR z=7AYoXEzxFOp~}PYaSP88Q3!~Ax2Czgvnp)#_rNSLYCLMVRCRi1Fv{&Il{Z1a%PfzjqF8g&v*g#;<0$ci6+Ydb>hApb0-a8{JJ6fY_D!8~j6T+kgGX+!rYSc%SGZU&|USh2>(C0q1@o=amPu2*yqJMgy< z!rZ?cW}+bkMb$$v*7z!uu-e1>NHD#7h2IGeCjoV9n5<5Oz`9z#Oun}kE6%?9QRm{`2v5OACq2q z@<8`}LKmFLB_sL6Ga-H#b z6N+~dO(NX`;eL{{iR;zg=Sru&h0B;^se0E}xF_CV?jy`mQwD}K9J_=>BOMP?n8%0d z#C_Pe8|%L3fsJ5{8FsJOk>dCB?B*uvI6nO5BzwbQ#Y@VkW9~O@2I5y}!X^8l|MQw- z7!qxi!EEf`I?B{93A*N9MSI{eU-%gjrKvj|TUGKwsT4dGv~zQO_3E7Py7na zwP_8XBGCqlsmWt2t(x&-n?L4N@gd&-HCC*EtG z2l}ST$8pn_97a|M>2A=-cn@;Ot3BlV3)pNAL%HM}jsVL7XgqW_V)>l3#+`cU2;zEzwdFy+Jt#*55z9yk#QruhAUZ)t0cRL8Fh<5E2Z5|W$gj^;YZBh)UXa=M6}{f>uCA_gRg30D zUo#GAJ->HR_VwqS+*;W!Q!P3x{Hcu>(uo`J~ax z53uA2gJBXoZ)?AdOOHIT^w>JJczG;X-X;Qb<91%kKzP$BP8o+#2j;e|jctxMyh+?( z=Vk4$o_$$btNVbZuL~1Ga4~D)T?z~lEsXiG`xVjL7?pYRW+!DRegoGOUm!Bc6j6wGc>u=V97HAOYb}DOAfek(Rab3?}C*_fNlJe^ECcM zCU$b)z~0ck;9clSd2Q%a%%S5D!qU}c_XL(6Lbz_~#jP*-G{zec zCRl2<4z@lI-%D;ISaQHYVq1YFt^rovmIrG6K7@2}FAY9?1Djl5xZlR-d#;=C4bivY zO7>Os4JkJ37vNTjp9?IuE?8=y!BR5-mKxZA|2m;ZrpRtzTiiGn`mR~O zxs|mtV%7)XxA6^wr9J>GeSf*!5>o<8k4CV(Eqk!&tWvhO2aa*Ix48P&uHX_(A44#w zxp_&frB)qToePIC$nC3Z3!~tM(5Ao6t}E=)534#Ruoa0 z5BXkgD-q_%X|>3kTjqPX`}2GFedOiw_B~7H8`b#vTVe6!CJRvsChRy#z1iL_oCy*2*48O1k3x_VCiRh z5rWdyd2_SAypQd-^}%c775S@0WB8S*OLX)r^AFu?=dow0jydb2#cpiE`b1-9A6JXK zGF0tw?gte!XDQn@ey*!U&iWV|dzRSC#$km%la~u78kjZLj-nRl?(+(49QMMi;a9w! z@zE;ZicX`&eqt=xZsg3>C-1#fL6kTXi5H)imL}mtU^z=q!-P&{&_^Hgq`<~G@!j6@ zD<|H&steHiP=tijiy92~+%r041twu7u=Kp}y}ZXK?EL1;xvxTNL|OLHUUe77j-0+8 z75!fPNHomwaj^PTXrhr7O7X(mzvN0=e$Vq&dRB}Q{HYsT-Xyaue1oYLd;jW~`&-G_ zdqzTm;T!UYf@f(rIdl9zzm#ZQc?I+V}$XAhE@Jf+B0V;PLMFnq>!; z_%BCza-P8QP8S%8&i9fFhSBAFbOxD>&)=SYu=j}eoSbdx5y^EiNAjh>-mQhl}(alj7P zv*gN%OulDUGVYp>?e`ctnM147{#J-#f2-zf@1Rtd#6wj~g`TO(UEdr3-?i$@nyCU= zGog}kV$oUO75`@+jQhYEPZka+{@&OcDtN=2m=&?#lpsXMRL5N0b8!dvd^gLB?@i^E z`%2xSOQ{`>tmQ$8?I+VJ@|UK(d9=)my!!=~H*#sVsgVV1Y>^fb8A>aTj(J-PUX+Z6 z4utbU2f`KoZTYwoj_^*NzW5$wZ}jdnT|#TvU)R?-F)KMfH>m+{=BRBzRZEV_lK zFCuGcL(%JKv9X<1Wkn7m-SBKGNMhjkuJ7WWyEAlpEw=XDU^Hu1vmD;UyRrGUWLa$D zlUutFAhn`R63a;93?o$2YM-t+T4Pk4s zomF%t7w2kym&e{a#>;cAjZM}^1}xokd4$Jat@PdnS+9wyQ$@qCWTAB2H7^=IeytOh zw+n|B2jagg^Q6<1*2KGm7xiVCgR&Ajpx$SWD{|j8vkii0wn2aEb&32o?<|`%Z=>oK znI3-EL9%D*fU`bzcdMJR^1@@arHiZ;{^&jwM`jL%q`@C@4xSYnc3kx}bg@|pc zo;deSnMm*_?2_C|VWHd)UZgj3G#*M7SpICcjpHasTtBAFc$rnt`*FN|{VxKVk`kWd zu39`gx>50)sg({*;+(nrGI4UJ)iy_;sl(yR7*xb;4zPG%l(pgqtJ1Gd&u8AhyX(eZ(<2jKBGs7viB_8M@~bD#*aj1%j;ar ztmR(V*flGD&6&%jSe*g4iZAWjD6OpUEb{WOtNty|QTDWRNO|fwOfhH9)(8!?AKrDH zj{PBe9z9lla^$(_R_p*KSYqWTk{72+`ywkuvEzS{UL1P4$7c3CRB$A>_8w_jx_7+c zIjWSxUwAPStF*qzlutsCs~>3K9{$YZmY4|hGIl79COiU`-;8VAK<9*x6U-0pB_#xZ zm;5nwe@&tHeyC3luRC=p-(zBD-o7Ko087meSb7*xZ0irZl#ZqGYf`&B=W2L6uKqeT z#^IAP?ji@TdEnk@-CE)!yC#yMKL%Xr9*UdTQe#lbU!Kj%R%3@@Qe<-OF? zo0jJ|-1Hma;l|vTUKj_vzDubLU1@|dYvG}&kL;TCbX;?BEIGO;^{;1+5P~ZVFz2B$ zM9xjwMAm{mXKya>;(%rw*{$$1cA2u{*t(vssXor{lYIy`=FIOQ<2fS*O*x}$eV5-7 zM~fF#Ht|!7SeKuY6^lP2FZ)m;5q(@1S$s0mW20|eYSiKx3ZidZ66w+@k6`h_x3qWq zRhY2&(|$L5({6p~K!SUGbFW11d@pZqT;^|R=#m!qj4p{Z=X=d(cMf^4*_%g3vJdrO z87Cnrc-BI4d91{=o`(dEtVLLNY1dT+ZZGqTtR2PM*;`7 z@7>*RIiMfo4z|4~4)*JzdoYDmPu$0W_iwy!-;krJLO4##wlvZn(^%X zR$gh=rys==_r9ktq2so$^5*45+4bce${V-!E(hDXn1ktxzu#jAWG$&NzTa-X9vGcJ z>!b4rFR6j{TYNY3()kznxasIQ_uK6^d@zL$yEh3~&%1TwR)!vI^T!XiK2R_$HfKo& z1P78Ff`czN5B|Qly!~KXqj<1=UkFU^w#=J%Bk#AHb9XSx-s0+a+aKxX*mnRAMnTx$ zYIGOebEV~cmjn1cO&{gVgV+@phSuSqa`GV@Ytavti{meyK3eBaxe)`RW) zc?VPN`@oAzeQAs%8&9UJyi`)Sehc3tda`B;eqmeVd287i-+ga=83)@Q*av&J7R>Xp zw`<(CISKdM?Mr;HeP`-mn~QzlTi@%!7|)q^>pE`S#+?r)c_zQ*fDUYHBmGtzg3L?y zLu!SGQBcFfK*q;t9}EEmtABFlu)2rx{^rP2N)WaosWUJr;FfkT5V+CmkoD zCir7lgbq}rWFIV=u8-$-=H)_(E`Mn^JMLP5F1_#^zO<-cZIU_CO3fnlzeUB2ddy`(gv?fdv{aGpA*!I51&!4-H z{uZ}poxw71^=$RY(Ls2DV=u^x9DlYs_=mC&sb#^7K1#uXT+@zQ-{#G+d7ua5l$pI< zE$H%CGxT0c0L)!gqMh|gg;*Y;j3vB;N!)vxH$1FaLQ>9yk1T6RoY-%@oO@v^`5{MOiS#{F7V&CDw$Ci3W> z+}*o%HN3$KMKSXx7xvb-Z}oslXXtOYmg0W9wJitRSoy)WM=cm%efEp#WFL7O!Ecog zXB-6XeN&$tznOM@;St7-f7cvLwa&45@VAeBPxxT_w%vVC(c_-4dgA3nUc-o9e5Kb< zT%En2nv0YB8>4bU+xyO1_>4kB;^NOaZq3zCRTIaqAl_!)`|Z}c9c=qhf$3=%94L!f zet;AA&M?`t4~gr`|D*^{j+}XgU6#Kv^GcKWbH1m3A+*Lc$oaBKvNuy?US}Cv^W4eu zCgqLc<02x_Rn#n(Z%Ajz_w*#nJ{)D}Kx%TvNwUd4?xoj`u=g#Od5nhM=g&#k?tLp0W&5g*jjq$;$|i|OqB;C&vw>3Dnkh8`cEwQxsV zeaM7;%cw6X0}1re1&NH zJ@!YwC)X2-w(C+xh^j6(NP&$Hh*vZC)X-&!{J@?aZd2FrWQ2iy0U z4_5n)w=;5;I1i82Q6lz&s{F~VHHSQ-;5ofmZ(MCPo~7uW_dHYXud@8$UV3rxOux@K z{fu%(?$*vB>!J6YyFK@VB9{F!-}=4AnRt<6?<*ONoVk$Au9-VF_g7v)Xzf(Jy)#k* zCtpDHBYnURw(mlL0WD6cVbNK64?;tiw!UWs61)3aqwW1wH5Om4)cDEQyC_cmJ~X8G zL3l>OOQ@KkA?=zYk4TEhP?9Wnp>MleU-Fg?wmI7e+g_aq+nkewZBI5Zq4&_AxIu8w zkDhUH8TG#%M-2_nosW~A8l|&oUfcQ>&Sg`h))N9Z+uI*(3^4fEonD0dBZnH zeM@SQqg!0dhVU2s{wb5`G*rTY(O*)pL9wkJdNyou#dHp?E_rp%NGVFkz2W1nceV$z zV!!8AveE&6Q`RCaZE5J*w=eyvV~+edH|G9UtKHZsdS1kKmWG$RMT3iN#Y-Jp6E2I* zd3Di?ds)(xtE+G>{jLwD=r-rEId{gTzU+RxHKRAKI;-e@WIRt~{MkoWi&L#IBaf(N zU9;8+!GW^voF$%}Iq>@@-P;G>*Xj{qxku`vBcrhF_>7cbMOWdXh@6&s+3)f3>{>2G zed%7bJ9-HT5ZzHtb^Ms#MULL2jN9Ie#tBh%-1acN@0He&Q2b3Sy70uSpI-Wt z&M>;;$@Aq043gh+K0NjTrao%V7l|j9&s^>Lt`Yd-d-f(2nDel}&awT`@3+nEA(3Mr z(XyjIt9K5aUOIYsC)a=0cg<&a9~|E~ON~SO+wC`JA8H1cC(2wXt%)yWUUop{;M@$J z)v!I!(!7j~<$o7i%(!d7w)($mhGrLAWUC2ale_N}jv*M+U<0=iV_=j@lsyDM1GN9kvdOEU09C2~4 zo;ZF&QM%ZdSI4~j<;jR_Bz{9{>V)I7g!Z* zFIhG|3a~TKALBU9E-h4)cTaT;X~j;{PR@96lg7Cphi8y}Bsv))1CBXi`<`$X4(`2`ob^$$vy$}H%r^Gn>;2sKI*b7<9o7w_hV#>3r-upc_ zHS-ED1TV%#FD8znPhL&%(uTU@_yBm!6L)bOIU}Y}mheq>PNqqoWM&e zijCtHqVPJlbL8sz$3v%#tI&a-5;+e-xxd|aZ|qn4WPjUSSlJfkLzLFwipidH6Sm4Y zG9c^2h;qJpKiQhGx>>WLuEtJCvf{<%%KC`ftj{6M92ezWoz=WFjwfA#rOpdXQ?`uT zSO=elZ46$(!eg&sWA3HjUeVOfL1Yf?`d)N?zuh+@?tA-Y#KB~e?Hm%chbA3l_cKz z&f^}=*!2-*8P|Lo-&1k0zg1o_yh-3HyyOxtR>uU3&%XGp$Ll^fic{w~0 zj7ux{^SP|g`v$?4wg&mF{G7t#19&;VLNA)V)4a*$JWqJ`;UeX3oiea{JBy~U z?iaCbuDE#x+jhT-OZU#GuU*@ZC#ZMz+S2VA9ZL7iW$Ue8VlDAUm?ORnu+$&fh9alk zhaAYINxc}>Hub&j-()G5yr1t8T!C%=tj{J~zkq4?EcQ?YZym+aU)lf4PUS zmJeNnv*5mdbl=-|(#^rd4jl*)6sB5r_j?aw1eO?;IpRkGOK!lGfsZfE`sCAu?vYy7 zf`qabK9s_?hvdzX`b#9998$3OMg4YrXWjSS+*PR0xw%H6Kf$8hRsQ?z7rzhvamwU- z&8dWETw>3S13ENBWzD?J3-Z0>3!L{LvQgE}$kmgBnxa0|@{)NISEI!WGc6tH(9`vC znP<&%Y=V2S+3*dyJ)!&NXkN$u&?Hv3K565UM|-fyZmB6b4`o!L4XG|;iywQAkHQ?K zKattZ>G0d_XMW#{-vZI^+mAO+0m$Rr4wn2TzfCO^Sn93(w(|0(-Lq7i8$Iz&ZSIM( z=;P-yS{i2w#jcR)5_-A!iI-lOBLqz%feGX+PKE9Yt6qF14NJR9 zB-ZXW2L*NKXm1DOsxzRL*cw{+b$vqR%bR3&$Bw#ZrL$&j8*}DD3Bdu|W%;CT3bC<~ z%f>3H8zx>HG@r(}?T>D4?Zp7L{V{JG3Ac0ne)}fhIEh~wC!jL0FIrPW%}HF%_mbNP zw(q!`7pvPjXhXfz{QOVcAJ1N#9)Dij84Q}kvx)e?(hv03C!>0BaL*5}+@k8mE@NRw zmT~2-??&~}8yc79oAqe{5nOS^J@I1AdXDb;?mglCo|^RdM%=9M5)qsAEvdq-UMx}> z-CO0{8(4LDse`${S38_FQxx~kC|wHg6e0}|lhzU+l!W?O z?OA%UlCJq4<{r8d&5KNaQ*&!A`)1RvuX@h43(A`PbouVRI~+WJAyH=6r`2=zCPcNo zQwSq>OVp~kNA|%1lx8ih>t(up+T0#6I`6JoI$YK#&oc93iJ4c3VDHwUC37@)pSiVt zw$B7hPZ(=X{PIk-(gt%n>yzl4weYuRzrvvz#}|}wOoQNsUn;-FVuP1A`nV`b=}#h5 z=50>?wVdi4;UtvYIdg8{@Zx(Kv7F^~+#4eYPmkCfxtGo`cOUGHzf)>TY$lajV>j9d<0Wem zwpx1TxsLARS;qK5u3d9*%4FZWD&!h_ON2T43lpO2V@j?JxO(EOS?qZ4k?e@*wpS0E z?^#S?o67`$inKefcY&xVu9zR8N$v2m4=iGN?1eQl@5g<=!uk}u^|$5E-cyQiNhNaZ zL0RgSh?k!ZbIedPLibgIl5yN+pY3U&E&%o8EeMbU}nUcF8NhGu(u_ZXT7dv)+ zt<^FI>6mfJ6*4Y8AUIXhn-8q(L-vtdAFrA1%qyEebkE$H_2IEhp(kEm{B-WLi0f%Y zJI7tGz|!BAvYVPmurG7pt~KPI2UzrtD+S#%x)zPW0b?UDj;X?Ghu(X~d$(Aen=8a& zx$gnNr`oUV;~JqYuG+)YZ@prfajBO)Pv7QaUS@6NG%1}}jA&VW!%Cq-_g6C?zVXGr zU1-1ZMT0ABH~SEqSl+JUHaZg5O?-G>(~SP2PB?mm*MPzkwQ-0aiB%Clx)lHTRP{iN zjd-=l*~b-b@42<6ftAdl-f;>X3*(U3aa@NTw{O8Z4|_okLM^5dQt z+HuU^o~1pT=V#HxeDUam9(e^2kY!jK47FzUg={0mA(r$60Xd z*n)d)!1sRO#JT4(HNv0WqOQ4hV`>1?6Gq)c;}BP4y!S(maqgfr>G0>PG0y$PCWB|a zEP^X!8e9<|`JR}1$E8Qb)r3U$DDx_Rxq{O@H~oDI+xKyt`4MVW6)X%fyIGD?%-L1d(Kx9{o+7|dS!t8rv0Am-N;KW z%lJH`E9brmtA%%7>H4mZnH=AtG`#3_$S?bq<(4~0i-=7EmUGh~An`^~wdg)q>$`MC zf`_R%{HPksDcD#K$NfJG)>8~?rM_a1ZRAd~~6eCM~^7CECcyoZ;li!NP>y$3IEWE5{x zc%8SGi)Ty8nx$iAjw>GC^|^%E$6fS{Yo0GJP3#X{E6Nk0FLQ8l2d4^}fa@cvjh!p-TjjU@HCkd{^h4y|h z%X&s)G1;5I!QM9<;?4I`Q==1H>WTTj8uOA05#5Jrn!D<^IS;RnB?jvusaBehbk!BVds=Bdf#3;$RA%oza<080cz-4`B6z>_SpI)A$ z9IWRdc9QQ=P@lLuUbp=sUwm!&1{l>RIL%w3Xy}x<=bA6gvOJN}5FB`Su4}Gt%QT35 zlhV8MVpi!11ID5KTpyT=6j)<1S0Ko^k+X!EZ<8c(UD~H zmnVwPJkQ)2kqg-e6Kru$g2pE!T_C#rz1zHJNpwd)u-)(>Hz7LLJ!`b*AthmG_mTk8 z_Y$l!Mb<;^FE`=P_qTBlZG7{+H|x7#>SNyHolCnfJ~{GDa=UuYuJ6s>Gy@L)JnxtD(DXL< zja`=et9<6UW^^j>viv}nM|_N2DAASGa>pn06}u;UYu^u#r!>b}iCFA1CBfm(x>kfg zv!WvRMXkc0Fa0!lVW8(8ok9ILU$~No96U3^a*w#GR>otoJvX@lIS&eI&f}EAT?+*( z`%t2oeaI2cKHkJlk5W_WFLxKvJVTKec-0CE5I=F zC$7LS#lY64;|Eg2JNp2OZ2%UX5G;KguViKUGnlf5U5l!L%qv9Sae2Sd_rmYbVtVtv z-%NC_hxUcE6123%U5eGB`t{@2e17C!@+;?2yFybRm+1u?K7O$6c?OnRMzGWm(r7NRdDaXz zGU~?d+k0SgCUzfU6QkQ2mpX9#lvr5&aRrS$zj3PAW_>DyMqb{y&2bg0aDkt4^HVKm6hIfBxJ5{P>w<*H54S@u#1DxxdJi&;Iz~^MCx$zx;In zVh^u>zx(W8KKsKrKYaYnH`;c5OF#JP(|45o-+cSU58wa(%OAe@miyqluRr|%Lq7Z0 z51)Vc)1SZn>yLl_x4-|_e|+}&RcT_e-+uX>)F;k`?>~P3&G(!F|M1;6-+cSKk3W3) j`s)vW|K(3V|MK5I|M-^=gq)hh-+uVjufG4MA3ppqiRbD) literal 1403976 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�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

C6`wUHF$Ymi&HHp0zda%Gj*u_g_7Oo=`LS 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

j|t;MB*OL5xx^Xpp`m`h-K-EPK+d2~bpg4a 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≪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%0A}XOS%&v_Ofc`K7{2BC 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 z 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. -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Delay.hh" - -#include "StaConfig.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "StaState.hh" - -// Non-SSTA compilation. -#if !SSTA - -namespace sta { - -static Delay delay_init_values[MinMax::index_count]; - -void -initDelayConstants() -{ - delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); - delay_init_values[MinMax::maxIndex()] = MinMax::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); -} - -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()); -} - -bool -delayZero(const Delay &delay) -{ - return fuzzyZero(delay); -} - -bool -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, - const StaState *) -{ - return fuzzyLess(delay1, delay2); -} - -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyLess(delay1, delay2); - else - return fuzzyGreater(delay1, delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *) -{ - return fuzzyLessEqual(delay1, delay2); -} - -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyLessEqual(delay1, delay2); - else - return fuzzyGreaterEqual(delay1, delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *) -{ - return fuzzyGreater(delay1, delay2); -} - -bool -delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyGreater(delay1, delay2); - else - return fuzzyLess(delay1, delay2); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *) -{ - return fuzzyGreaterEqual(delay1, delay2); -} - -bool -delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) -{ - if (min_max == MinMax::max()) - return fuzzyGreaterEqual(delay1, delay2); - else - return fuzzyLessEqual(delay1, delay2); -} - -Delay -delayRemove(const Delay &delay1, - const Delay &delay2) -{ - return delay1 - delay2; -} - -float -delayRatio(const Delay &delay1, - const Delay &delay2) -{ - return delay1 / delay2; -} - -} // namespace - -#endif // !SSTA diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc deleted file mode 100644 index f13518e0..00000000 --- a/graph/DelayNormal1.cc +++ /dev/null @@ -1,483 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2026, 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 . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Delay.hh" - -#include // sqrt - -#include "StaConfig.hh" -#include "Error.hh" -#include "StringUtil.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "StaState.hh" -#include "Variables.hh" - -// SSTA compilation. -#if (SSTA == 1) - -namespace sta { - -inline float -square(float x) -{ - return x * x; -} - -static Delay delay_init_values[MinMax::index_count]; - -void -initDelayConstants() -{ - delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); - delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); -} - -const Delay & -delayInitValue(const MinMax *min_max) -{ - return delay_init_values[min_max->index()]; -} - -Delay::Delay() : - mean_(0.0), - sigma2_(0.0) -{ -} - -Delay::Delay(const Delay &delay) : - mean_(delay.mean_), - sigma2_(delay.sigma2_) -{ -} - -Delay::Delay(const DelayDbl &delay) : - mean_(delay.mean_), - sigma2_(delay.sigma2_) -{ -} - -Delay::Delay(float mean) : - mean_(mean), - sigma2_(0.0) -{ -} - -Delay::Delay(float mean, - float sigma2) : - mean_(mean), - sigma2_(sigma2) -{ -} - -float -Delay::sigma() const -{ - if (sigma2_ < 0.0) - // Sigma is negative for crpr to offset sigmas in the common - // clock path. - return -sqrt(-sigma2_); - else - return sqrt(sigma2_); -} - -float -Delay::sigma2() const -{ - return sigma2_; -} - -void -Delay::operator=(const Delay &delay) -{ - mean_ = delay.mean_; - sigma2_ = delay.sigma2_; -} - -void -Delay::operator=(float delay) -{ - mean_ = delay; - sigma2_ = 0.0; -} - -void -Delay::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_ += delay.sigma2_; -} - -void -Delay::operator+=(float delay) -{ - mean_ += delay; -} - -Delay -Delay::operator+(const Delay &delay) const -{ - return Delay(mean_ + delay.mean_, - sigma2_ + delay.sigma2_); -} - -Delay -Delay::operator+(float delay) const -{ - return Delay(mean_ + delay, sigma2_); -} - -Delay -Delay::operator-(const Delay &delay) const -{ - return Delay(mean_ - delay.mean_, - sigma2_ + delay.sigma2_); -} - -Delay -Delay::operator-(float delay) const -{ - return Delay(mean_ - delay, sigma2_); -} - -Delay -Delay::operator-() const -{ - return Delay(-mean_, sigma2_); -} - -void -Delay::operator-=(float delay) -{ - mean_ -= delay; -} - -void -Delay::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_ += delay.sigma2_; -} - -bool -Delay::operator==(const Delay &delay) const -{ - return delayEqual(*this, delay); -} - -//////////////////////////////////////////////////////////////// - -DelayDbl::DelayDbl() : - mean_(0.0), - sigma2_(0.0) -{ -} - -void -DelayDbl::operator=(float delay) -{ - mean_ = delay; - sigma2_ = 0.0; -} - -void -DelayDbl::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_ += delay.sigma2_; -} - -void -DelayDbl::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_ += delay.sigma2_; -} - -//////////////////////////////////////////////////////////////// - -Delay -makeDelay(float delay, - float sigma, - float) -{ - return Delay(delay, square(sigma)); -} - -Delay -makeDelay2(float delay, - float sigma2, - float ) -{ - return Delay(delay, sigma2); -} - -float -delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta) -{ - if (sta->variables()->pocvEnabled()) { - if (early_late == EarlyLate::early()) - return delay.mean() - delay.sigma() * sta->sigmaFactor(); - else if (early_late == EarlyLate::late()) - return delay.mean() + delay.sigma() * sta->sigmaFactor(); - else - sta->report()->critical(1020, "unknown early/late value."); - } - return delay.mean(); -} - -float -delaySigma2(const Delay &delay, - const EarlyLate *) -{ - return delay.sigma2(); -} - -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) -{ - const Unit *unit = sta->units()->timeUnit(); - if (sta->variables()->pocvEnabled()) { - float sigma = delay.sigma(); - return stringPrintTmp("%s[%s]", - unit->asString(delay.mean(), digits), - unit->asString(sigma, digits)); - } - else - return unit->asString(delay.mean(), digits); -} - -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits) -{ - float mean_sigma = delayAsFloat(delay, early_late, sta); - 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) -{ - 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 deleted file mode 100644 index be8936a0..00000000 --- a/graph/DelayNormal2.cc +++ /dev/null @@ -1,517 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2026, 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 . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#include "Delay.hh" - -#include // sqrt - -#include "StaConfig.hh" -#include "Error.hh" -#include "StringUtil.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "StaState.hh" - -// SSTA compilation. -#if (SSTA == 2) - -namespace sta { - -inline float -square(float x) -{ - return x * x; -} - -static Delay delay_init_values[MinMax::index_count]; - -void -initDelayConstants() -{ - delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue(); - delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); -} - -const Delay & -delayInitValue(const MinMax *min_max) -{ - return delay_init_values[min_max->index()]; -} - -Delay::Delay() : - mean_(0.0), - sigma2_{0.0, 0.0} -{ -} - -Delay::Delay(const Delay &delay) : - mean_(delay.mean_) -{ - sigma2_[EarlyLate::earlyIndex()] = delay.sigma2_[EarlyLate::earlyIndex()]; - sigma2_[EarlyLate::lateIndex()] = delay.sigma2_[EarlyLate::lateIndex()]; -} - -Delay::Delay(const DelayDbl &delay) : - mean_(delay.mean_) -{ - sigma2_[EarlyLate::earlyIndex()] = delay.sigma2_[EarlyLate::earlyIndex()]; - sigma2_[EarlyLate::lateIndex()] = delay.sigma2_[EarlyLate::lateIndex()]; -} - -Delay::Delay(float mean) : - mean_(mean), - sigma2_{0.0, 0.0} -{ -} - -Delay::Delay(float mean, - float sigma2_early, - float sigma2_late) : - mean_(mean), - sigma2_{sigma2_early, sigma2_late} -{ -} - -float -Delay::sigma(const EarlyLate *early_late) const -{ - float sigma = sigma2_[early_late->index()]; - if (sigma < 0.0) - // Sigma is negative for crpr to offset sigmas in the common - // clock path. - return -sqrt(-sigma); - else - return sqrt(sigma); -} - -float -Delay::sigma2(const EarlyLate *early_late) const -{ - return sigma2_[early_late->index()]; -} - -float -Delay::sigma2Early() const -{ - return sigma2_[early_index]; -} - -float -Delay::sigma2Late() const -{ - return sigma2_[late_index]; -} - -void -Delay::operator=(const Delay &delay) -{ - mean_ = delay.mean_; - sigma2_[early_index] = delay.sigma2_[early_index]; - sigma2_[late_index] = delay.sigma2_[late_index]; -} - -void -Delay::operator=(float delay) -{ - mean_ = delay; - sigma2_[early_index] = 0.0; - sigma2_[late_index] = 0.0; -} - -void -Delay::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -void -Delay::operator+=(float delay) -{ - mean_ += delay; -} - -Delay -Delay::operator+(const Delay &delay) const -{ - return Delay(mean_ + delay.mean_, - sigma2_[early_index] + delay.sigma2_[early_index], - sigma2_[late_index] + delay.sigma2_[late_index]); -} - -Delay -Delay::operator+(float delay) const -{ - return Delay(mean_ + delay, sigma2_[early_index], sigma2_[late_index]); -} - -Delay -Delay::operator-(const Delay &delay) const -{ - return Delay(mean_ - delay.mean_, - sigma2_[early_index] + delay.sigma2_[late_index], - sigma2_[late_index] + delay.sigma2_[early_index]); -} - -Delay -Delay::operator-(float delay) const -{ - return Delay(mean_ - delay, sigma2_[early_index], sigma2_[late_index]); -} - -Delay -Delay::operator-() const -{ - return Delay(-mean_, sigma2_[late_index], sigma2_[early_index]); -} - -void -Delay::operator-=(float delay) -{ - mean_ -= delay; -} - -void -Delay::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -bool -Delay::operator==(const Delay &delay) const -{ - return mean_ == delay.mean_ - && sigma2_[early_index] == delay.sigma2_[late_index] - && sigma2_[late_index] == delay.sigma2_[early_index]; -} - -//////////////////////////////////////////////////////////////// - -DelayDbl::DelayDbl() : - mean_(0.0), - sigma2_{0.0, 0.0} -{ -} - -void -DelayDbl::operator=(float delay) -{ - mean_ = delay; - sigma2_[early_index] = 0.0; - sigma2_[late_index] = 0.0; -} - -void -DelayDbl::operator+=(const Delay &delay) -{ - mean_ += delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -void -DelayDbl::operator-=(const Delay &delay) -{ - mean_ -= delay.mean_; - sigma2_[early_index] += delay.sigma2_[early_index]; - sigma2_[late_index] += delay.sigma2_[late_index]; -} - -//////////////////////////////////////////////////////////////// - -Delay -makeDelay(float delay, - float sigma_early, - float sigma_late) -{ - return Delay(delay, square(sigma_early), square(sigma_late)); -} - -Delay -makeDelay2(float delay, - float sigma2_early, - float sigma2_late) -{ - return Delay(delay, sigma2_early, sigma2_late); -} - -bool -delayIsInitValue(const Delay &delay, - const MinMax *min_max) -{ - return fuzzyEqual(delay.mean(), min_max->initValue()) - && fuzzyZero(delay.sigma2Early()) - && fuzzyZero(delay.sigma2Late()); -} - -bool -delayZero(const Delay &delay) -{ - return fuzzyZero(delay.mean()) - && fuzzyZero(delay.sigma2Early()) - && fuzzyZero(delay.sigma2Late()); -} - -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.sigma2Early(), delay2.sigma2Early()) - && fuzzyEqual(delay1.sigma2Late(), delay2.sigma2Late()); -} - -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 fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); -} - -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); -} - -float -delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta) -{ - if (sta->pocvEnabled()) { - if (early_late == EarlyLate::early()) - return delay.mean() - delay.sigma(early_late) * sta->sigmaFactor(); - else if (early_late == EarlyLate::late()) - return delay.mean() + delay.sigma(early_late) * sta->sigmaFactor(); - else - sta->report()->critical(1030, "unknown early/late value."); - } - return delay.mean(); -} - -float -delaySigma2(const Delay &delay, - const EarlyLate *early_late) -{ - return delay.sigma2(early_late); -} - -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) -{ - const Unit *unit = sta->units()->timeUnit(); - if (sta->pocvEnabled()) { - float sigma_early = delay.sigma(EarlyLate::early()); - float sigma_late = delay.sigma(EarlyLate::late()); - return stringPrintTmp("%s[%s:%s]", - unit->asString(delay.mean(), digits), - unit->asString(sigma_early, digits), - unit->asString(sigma_late, digits)); - } - else - return unit->asString(delay.mean(), digits); -} - -const char * -delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits) -{ - float mean_sigma = delayAsFloat(delay, early_late, sta); - 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) diff --git a/graph/Graph.cc b/graph/Graph.cc index b397b7b2..e0c2102d 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -36,6 +36,7 @@ #include "PortDirection.hh" #include "Network.hh" #include "FuncExpr.hh" +#include "Variables.hh" namespace sta { @@ -46,12 +47,10 @@ namespace sta { //////////////////////////////////////////////////////////////// Graph::Graph(StaState *sta, - int slew_rf_count, DcalcAPIndex ap_count) : StaState(sta), vertices_(nullptr), edges_(nullptr), - slew_rf_count_(slew_rf_count), ap_count_(ap_count), period_check_annotations_(nullptr), reg_clk_vertices_(makeVertexSet(this)) @@ -583,15 +582,31 @@ Graph::slew(const Vertex *vertex, const RiseFall *rf, DcalcAPIndex ap_index) { - if (slew_rf_count_) { - const Slew *slews = vertex->slews(); - size_t slew_index = (slew_rf_count_ == 1) - ? ap_index - : ap_index*slew_rf_count_+rf->index(); + size_t slew_index = ap_index * RiseFall::index_count + rf->index(); + const float *slews_flt = vertex->slewsFloat(); + if (variables_->pocvEnabled()) { + const Slew *slews = std::bit_cast(slews_flt); return slews[slew_index]; } else { - static Slew slew(0.0); + static Slew slew; + slew = slews_flt[slew_index]; + return slew; + } +} + +const Slew & +Graph::slew(const Vertex *vertex, + size_t index) +{ + const float *slews_flt = vertex->slewsFloat(); + if (variables_->pocvEnabled()) { + const Slew *slews = std::bit_cast(slews_flt); + return slews[index]; + } + else { + static Slew slew; + slew = slews_flt[index]; return slew; } } @@ -602,18 +617,15 @@ Graph::setSlew(Vertex *vertex, DcalcAPIndex ap_index, const Slew &slew) { - if (slew_rf_count_) { + size_t slew_index = ap_index * RiseFall::index_count + rf->index(); + if (variables_->pocvEnabled()) { Slew *slews = vertex->slews(); - if (slews == nullptr) { - int slew_count = slew_rf_count_ * ap_count_; - slews = new Slew[slew_count]; - vertex->setSlews(slews); - } - size_t slew_index = (slew_rf_count_ == 1) - ? ap_index - : ap_index*slew_rf_count_+rf->index(); slews[slew_index] = slew; } + else { + float *slews_flt = vertex->slewsFloat(); + slews_flt[slew_index] = slew.mean(); + } } //////////////////////////////////////////////////////////////// @@ -665,25 +677,39 @@ Graph::deleteEdge(Edge *edge) edges_->destroy(edge); } -ArcDelay +const ArcDelay & Graph::arcDelay(const Edge *edge, const TimingArc *arc, DcalcAPIndex ap_index) const { - ArcDelay *delays = edge->arcDelays(); size_t index = arc->index() * ap_count_ + ap_index; - return delays[index]; + if (variables_->pocvEnabled()) { + ArcDelay *delays = std::bit_cast(edge->arcDelays()); + return delays[index]; + } + else { + const float *delays = edge->arcDelays(); + static ArcDelay delay; + delay = delays[index]; + return delay; + } } void Graph::setArcDelay(Edge *edge, const TimingArc *arc, DcalcAPIndex ap_index, - ArcDelay delay) + const ArcDelay &delay) { - ArcDelay *arc_delays = edge->arcDelays(); size_t index = arc->index() * ap_count_ + ap_index; - arc_delays[index] = delay; + if (variables_->pocvEnabled()) { + ArcDelay *delays = std::bit_cast(edge->arcDelays()); + delays[index] = delay; + } + else { + float *delays = edge->arcDelays(); + delays[index] = delay.mean(); + } } const ArcDelay & @@ -691,9 +717,17 @@ Graph::wireArcDelay(const Edge *edge, const RiseFall *rf, DcalcAPIndex ap_index) { - ArcDelay *delays = edge->arcDelays(); size_t index = rf->index() * ap_count_ + ap_index; - return delays[index]; + if (variables_->pocvEnabled()) { + ArcDelay *delays = std::bit_cast(edge->arcDelays()); + return delays[index]; + } + else { + const float *delays = edge->arcDelays(); + static ArcDelay delay; + delay = delays[index]; + return delay; + } } void @@ -702,9 +736,15 @@ Graph::setWireArcDelay(Edge *edge, DcalcAPIndex ap_index, const ArcDelay &delay) { - ArcDelay *delays = edge->arcDelays(); size_t index = rf->index() * ap_count_ + ap_index; - delays[index] = delay; + if (variables_->pocvEnabled()) { + ArcDelay *delays = std::bit_cast(edge->arcDelays()); + delays[index] = delay; + } + else { + float *delays = edge->arcDelays(); + delays[index] = delay.mean(); + } } //////////////////////////////////////////////////////////////// @@ -788,16 +828,20 @@ void Graph::initSlews(Vertex *vertex) { size_t slew_count = slewCount(); - Slew *slews = new Slew[slew_count]; - vertex->setSlews(slews); - for (size_t i = 0; i < slew_count; i++) - slews[i] = 0.0; + if (variables_->pocvEnabled()) { + float *slews = std::bit_cast(new Slew[slew_count]{}); + vertex->setSlews(slews); + } + else { + float *slews = new float[slew_count]{}; + vertex->setSlews(slews); + } } size_t Graph::slewCount() { - return slew_rf_count_ * ap_count_; + return RiseFall::index_count * ap_count_; } void @@ -805,10 +849,14 @@ Graph::initArcDelays(Edge *edge) { size_t arc_count = edge->timingArcSet()->arcCount(); size_t delay_count = arc_count * ap_count_; - ArcDelay *arc_delays = new ArcDelay[delay_count]; - edge->setArcDelays(arc_delays); - for (size_t i = 0; i < delay_count; i++) - arc_delays[i] = 0.0; + if (variables_->pocvEnabled()) { + float *delays = std::bit_cast(new ArcDelay[delay_count]{}); + edge->setArcDelays(delays); + } + else { + float *delays = new float[delay_count]{}; + edge->setArcDelays(delays); + } } //////////////////////////////////////////////////////////////// @@ -1040,7 +1088,7 @@ Vertex::setVisited2(bool visited) } void -Vertex::setSlews(Slew *slews) +Vertex::setSlews(float *slews) { delete [] slews_; slews_ = slews; @@ -1251,10 +1299,10 @@ Edge::setTimingArcSet(TimingArcSet *set) } void -Edge::setArcDelays(ArcDelay *arc_delays) +Edge::setArcDelays(float *delays) { delete [] arc_delays_; - arc_delays_ = arc_delays; + arc_delays_ = delays; } bool diff --git a/graph/Graph.i b/graph/Graph.i index 5c16e5f8..cd8188c9 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -132,21 +132,24 @@ bool is_bidirect_driver() { return self->isBidirectDriver(); } int level() { return Sta::sta()->vertexLevel(self); } int tag_group_index() { return self->tagGroupIndex(); } -Slew +float slew(const RiseFallBoth *rf, const MinMax *min_max) { Sta *sta = Sta::sta(); - return sta->slew(self, rf, sta->scenes(), min_max); + return delayAsFloat(sta->slew(self, rf, sta->scenes(), min_max), min_max, sta); } -Slew -slew_scenes(const RiseFallBoth *rf, - const SceneSeq scenes, - const MinMax *min_max) +std::string +slew_scenes_string(const RiseFallBoth *rf, + const SceneSeq scenes, + const MinMax *min_max, + bool report_variance, + int digits) { Sta *sta = Sta::sta(); - return sta->slew(self, rf, scenes, min_max); + Slew slew = sta->slew(self, rf, scenes, min_max); + return delayAsString(slew, min_max, report_variance, digits, sta); } VertexOutEdgeIterator * @@ -223,22 +226,29 @@ arc_delays(TimingArc *arc) { Sta *sta = Sta::sta(); FloatSeq delays; - DcalcAPIndex ap_count = sta->dcalcAnalysisPtCount(); - for (DcalcAPIndex ap_index = 0; ap_index < ap_count; ap_index++) - delays.push_back(delayAsFloat(sta->arcDelay(self, arc, ap_index))); + for (Scene *scene : sta->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + delays.push_back(delayAsFloat(sta->arcDelay(self, arc, ap_index), min_max, sta)); + } + } return delays; } StringSeq arc_delay_strings(TimingArc *arc, + bool report_variance, int digits) { Sta *sta = Sta::sta(); StringSeq delays; - DcalcAPIndex ap_count = sta->dcalcAnalysisPtCount(); - for (DcalcAPIndex ap_index = 0; ap_index < ap_count; ap_index++) - delays.push_back(delayAsString(sta->arcDelay(self, arc, ap_index), - sta, digits)); + for (Scene *scene : sta->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + delays.push_back(delayAsString(sta->arcDelay(self, arc, ap_index), + min_max, report_variance, digits, sta)); + } + } return delays; } @@ -255,8 +265,9 @@ arc_delay(TimingArc *arc, const Scene *scene, const MinMax *min_max) { + Sta *sta = Sta::sta(); DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index)); + return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index), min_max, sta); } std::string diff --git a/graph/Graph.tcl b/graph/Graph.tcl index 27ea5f19..3d04bf68 100644 --- a/graph/Graph.tcl +++ b/graph/Graph.tcl @@ -26,51 +26,64 @@ namespace eval sta { -define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]} +define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]\ + [-digits digits] [-report_variance]} proc report_edges { args } { - parse_key_args "report_edges" args keys {-from -to} flags {} + global sta_report_default_digits + + parse_key_args "report_edges" args keys {-from -to -digits} flags {-report_variance} check_argc_eq0 "report_edges" $args + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + + set report_variance [info exists flags(-report_variance)] + if { [info exists keys(-from)] && [info exists keys(-to)] } { set from_pin [get_port_pin_error "from_pin" $keys(-from)] set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach from_vertex [$from_pin vertices] { foreach to_vertex [$to_pin vertices] { - report_edges_between_ $from_vertex $to_vertex + report_edges_between_ $from_vertex $to_vertex $digits $report_variance } } } elseif [info exists keys(-from)] { set from_pin [get_port_pin_error "from_pin" $keys(-from)] foreach from_vertex [$from_pin vertices] { report_edges_ $from_vertex out_edge_iterator \ - vertex_port_name vertex_path_name + vertex_port_name vertex_path_name $digits $report_variance } } elseif [info exists keys(-to)] { set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach to_vertex [$to_pin vertices] { report_edges_ $to_vertex in_edge_iterator \ - vertex_path_name vertex_port_name + vertex_path_name vertex_port_name $digits $report_variance } } } -proc report_edges_between_ { from_vertex to_vertex } { +proc report_edges_between_ { from_vertex to_vertex digits report_variance } { set iter [$from_vertex out_edge_iterator] while {[$iter has_next]} { set edge [$iter next] if { [$edge to] == $to_vertex } { if { [$edge role] == "wire" } { - report_edge_ $edge vertex_path_name vertex_path_name + report_edge_ $edge vertex_path_name vertex_path_name $digits $report_variance } else { - report_edge_ $edge vertex_port_name vertex_port_name + report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance } } } $iter finish } -proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { +proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc \ + digits report_variance } { # First report edges internal to the device. set device_header 0 set iter [$vertex $iter_proc] @@ -84,7 +97,7 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { } set device_header 1 } - report_edge_ $edge vertex_port_name vertex_port_name + report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance } } $iter finish @@ -94,15 +107,14 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { while {[$iter has_next]} { set edge [$iter next] if { [$edge role] == "wire" } { - report_edge_ $edge $wire_from_name_proc $wire_to_name_proc + report_edge_ $edge $wire_from_name_proc $wire_to_name_proc $digits $report_variance } } $iter finish } -proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { - global sta_report_default_digits - +proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc \ + digits report_variance } { set latch_enable [$edge latch_d_to_q_en] if { $latch_enable != "" } { set latch_enable " enable $latch_enable" @@ -125,7 +137,7 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { } foreach arc [$edge timing_arcs] { - set delays [$edge arc_delay_strings $arc $sta_report_default_digits] + set delays [$edge arc_delay_strings $arc $report_variance $digits] set delays_fmt [format_delays $delays] set disable_reason "" if { [timing_arc_disabled $edge $arc] } { @@ -135,18 +147,6 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { } } -# Separate list elements with colons. -proc format_times { values digits } { - set result "" - foreach value $values { - if { $result != "" } { - append result ":" - } - append result [format_time $value $digits] - } - return $result -} - # Separate delay list elements with colons. proc format_delays { values } { set result "" diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index d2b99012..45dcbae3 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -119,15 +119,15 @@ public: ArcDcalcResult(size_t load_count); void setLoadCount(size_t load_count); ArcDelay &gateDelay() { return gate_delay_; } - void setGateDelay(ArcDelay gate_delay); + void setGateDelay(const ArcDelay &gate_delay); Slew &drvrSlew() { return drvr_slew_; } - void setDrvrSlew(Slew drvr_slew); - ArcDelay wireDelay(size_t load_idx) const; + void setDrvrSlew(const Slew &drvr_slew); + const ArcDelay &wireDelay(size_t load_idx) const; void setWireDelay(size_t load_idx, - ArcDelay wire_delay); - Slew loadSlew(size_t load_idx) const; + const ArcDelay &wire_delay); + const Slew &loadSlew(size_t load_idx) const; void setLoadSlew(size_t load_idx, - Slew load_slew); + const Slew &load_slew); protected: ArcDelay gate_delay_; diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index 860b23da..24b97c60 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -24,27 +24,326 @@ #pragma once -#include "StaConfig.hh" +#include +#include -// IWYU pragma: begin_exports -#if (SSTA == 1) - // Delays are Normal PDFs. - #include "DelayNormal1.hh" -#elif (SSTA == 2) - // Delays are Normal PDFs with early/late sigma. - #include "DelayNormal2.hh" -#else - // Delays are floats. - #include "DelayFloat.hh" -#endif -// IWYU pragma: end_exports +#include "StaConfig.hh" +#include "MinMax.hh" namespace sta { +class StaState; + +class Delay +{ +public: + Delay(); + Delay(float mean); + Delay(float mean, + // std_dev^2 + float std_dev2); + Delay(float mean, + float mean_shift, + // std_dev^2 + float std_dev2, + float skewness); + void setValues(float mean, + float mean_shift, + float std_dev2, + float skewnes); + float mean() const { return values_[0]; } + void setMean(float mean); + float meanShift() const { return values_[1]; } + void setMeanShift(float mean_shift); + float stdDev() const; + // std_dev ^ 2 + float stdDev2() const { return values_[2]; } + void setStdDev(float std_dev); + float skewness() const { return values_[3]; } + void setSkewness(float skewness); + + void operator=(float delay); + +private: + std::array values_; +}; + +// Delay with doubles for accumulating Delays. +// Only a subset of operations are required for DelayDbl. +class DelayDbl +{ +public: + DelayDbl(); + DelayDbl(double value); + double mean() const { return values_[0]; } + void setMean(double mean); + double meanShift() const { return values_[1]; } + // std_dev ^ 2 + double stdDev2() const { return values_[2]; } + double stdDev() const; + double skewness() const { return values_[3]; } + void setValues(double mean, + double mean_shift, + double std_dev2, + double skewnes); + + void operator=(double delay); + +private: + std::array values_; +}; + using ArcDelay = Delay; using Slew = Delay; using Arrival = Delay; using Required = Delay; using Slack = Delay; +const Delay delay_zero(0.0); + +class DelayOps +{ +public: + virtual ~DelayOps() {} + virtual float stdDev2(const Delay &delay, + const EarlyLate *early_late) const = 0; + virtual float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const = 0; + virtual double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const = 0; + virtual bool isZero(const Delay &delay) const = 0; + virtual bool isInf(const Delay &delay) const = 0; + virtual bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const = 0; + virtual bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const = 0; + virtual Delay sum(const Delay &delay1, + const Delay &delay2) const = 0; + virtual Delay sum(const Delay &delay1, + float delay2) const = 0; + virtual Delay diff(const Delay &delay1, + const Delay &delay2) const = 0; + virtual Delay diff(const Delay &delay1, + float delay2) const = 0; + virtual Delay diff(float delay1, + const Delay &delay2) const = 0; + virtual void incr(Delay &delay1, + const Delay &delay2) const = 0; + virtual void incr(DelayDbl &delay1, + const Delay &delay2) const = 0; + virtual void decr(Delay &delay1, + const Delay &delay2) const = 0; + virtual void decr(DelayDbl &delay1, + const Delay &delay2) const = 0; + virtual Delay product(const Delay &delay1, + float delay2) const = 0; + virtual Delay div(float delay1, + const Delay &delay2) const = 0; + virtual const char *asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const = 0; + +}; + +void +initDelayConstants(); + +inline float +square(float x) +{ + return x * x; +} + +inline double +square(double x) +{ + return x * x; +} + +inline float +cube(float x) +{ + return x * x * x; +} + +inline double +cube(double x) +{ + return x * x * x; +} + +Delay +makeDelay(float mean, + float mean_shift, + float std_dev, + float skewness); +Delay +makeDelay(float mean, + float std_dev); +Delay +makeDelay2(float mean, + float std_dev); +void +delaySetMean(Delay &delay, + float mean); + +const char * +delayAsString(const Delay &delay, + const StaState *sta); +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta); +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + int digits, + const StaState *sta); +const char * +delayAsString(const Delay &delay, + const EarlyLate *early_late, + bool report_variance, + int digits, + const StaState *sta); + +float +delayAsFloat(const Delay &delay); +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta); +float +delayAsFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta); + +Delay +delayDblAsDelay(DelayDbl &delay); + +Delay +delaySum(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +Delay +delaySum(const Delay &delay1, + float delay2, + const StaState *sta); +Delay +delayDiff(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +Delay +delayDiff(const Delay &delay1, + float delay2, + const StaState *sta); +Delay +delayDiff(float delay1, + const Delay &delay2, + const StaState *sta); +void +delayIncr(Delay &delay1, + const Delay &delay2, + const StaState *sta); +void +delayIncr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta); +void +delayIncr(Delay &delay1, + float delay2, + const StaState *sta); +void +delayDecr(Delay &delay1, + const Delay &delay2, + const StaState *sta); +void +delayDecr(DelayDbl &delay1, + const Delay &delay2, + const StaState *sta); +Delay +delayProduct(const Delay &delay1, + float delay2, + const StaState *sta); +Delay +delayDiv(float delay1, + const Delay &delay2, + const StaState *sta); + +const Delay & +delayInitValue(const MinMax *min_max); +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max); +bool +delayZero(const Delay &delay, + const StaState *sta); +bool +delayInf(const Delay &delay, + const StaState *sta); +bool +delayEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const StaState *sta); +bool +delayLess(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta); +bool +delayLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); +bool +delayLessEqual(const Delay &delay1, + const Delay &delay2, + 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, + 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); + } // namespace diff --git a/include/sta/DelayFloat.hh b/include/sta/DelayFloat.hh deleted file mode 100644 index 81f047b9..00000000 --- a/include/sta/DelayFloat.hh +++ /dev/null @@ -1,152 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2026, 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 . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" - -// Delay values defined as floats. - -namespace sta { - -class StaState; - -using Delay = float; -// Delay double for accumulating Delays. -using DelayDbl = double; - -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, - float) -{ - return delay; -} - -inline Delay -makeDelay2(float delay, - float, - float) -{ - return delay; -} - -inline float -delayAsFloat(const Delay &delay) -{ - return delay; -} - -// mean late+/early- sigma -inline float -delayAsFloat(const Delay &delay, - 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 -delayZero(const Delay &delay); -bool -delayInf(const Delay &delay); -bool -delayEqual(const Delay &delay1, - const Delay &delay2); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - 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, - 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); -float -delayRatio(const Delay &delay1, - const Delay &delay2); - -} // namespace diff --git a/include/sta/DelayNormal.hh b/include/sta/DelayNormal.hh new file mode 100644 index 00000000..0d102962 --- /dev/null +++ b/include/sta/DelayNormal.hh @@ -0,0 +1,90 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include "Delay.hh" + +namespace sta { + +class DelayOpsNormal : public DelayOps +{ +public: + float stdDev2(const Delay &delay, + const EarlyLate *early_late) const override; + float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + + bool isZero(const Delay &delay) const override; + bool isInf(const Delay &delay) const override; + bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const override; + bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + Delay sum(const Delay &delay1, + const Delay &delay2) const override; + Delay sum(const Delay &delay1, + float delay2) const override; + Delay diff(const Delay &delay1, + const Delay &delay2) const override; + Delay diff(const Delay &delay1, + float delay2) const override; + Delay diff(float delay1, + const Delay &delay2) const override; + void incr(Delay &delay1, + const Delay &delay2) const override; + void incr(DelayDbl &delay1, + const Delay &delay2) const override; + void decr(Delay &delay1, + const Delay &delay2) const override; + void decr(DelayDbl &delay1, + const Delay &delay2) const override; + Delay product(const Delay &delay1, + float delay2) const override; + Delay div(float delay1, + const Delay &delay2) const override; + const char *asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const override; +}; + +} // namespace diff --git a/include/sta/DelayNormal1.hh b/include/sta/DelayNormal1.hh deleted file mode 100644 index 5787797f..00000000 --- a/include/sta/DelayNormal1.hh +++ /dev/null @@ -1,203 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2026, 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 . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" - -namespace sta { - -class Delay; -class DelayDbl; -class StaState; - -// Normal distribution with std deviation. -class Delay -{ -public: - Delay(); - Delay(const Delay &delay); - Delay(const DelayDbl &delay); - Delay(float mean); - Delay(float mean, - float sigma2); - float mean() const { return mean_; } - float sigma() const; - // sigma^2 - float sigma2() const; - void operator=(const Delay &delay); - void operator=(float delay); - void operator+=(const Delay &delay); - void operator+=(float delay); - Delay operator+(const Delay &delay) const; - Delay operator+(float delay) const; - Delay operator-(const Delay &delay) const; - Delay operator-(float delay) const; - Delay operator-() const; - void operator-=(float delay); - void operator-=(const Delay &delay); - bool operator==(const Delay &delay) const; - -private: - float mean_; - // Sigma^2 - float sigma2_; - - friend class DelayDbl; -}; - -// Dwlay with doubles for accumulating delays. -class DelayDbl -{ -public: - DelayDbl(); - float mean() const { return mean_; } - float sigma() const; - // sigma^2 - float sigma2() const; - void operator=(float delay); - void operator+=(const Delay &delay); - void operator-=(const Delay &delay); - -private: - double mean_; - // Sigma^2 - double sigma2_; - - friend class Delay; -}; - -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, - 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 -delayIsInitValue(const Delay &delay, - const MinMax *min_max); -bool -delayZero(const Delay &delay); -bool -delayInf(const Delay &delay); -bool -delayEqual(const Delay &delay1, - const Delay &delay2); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - 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, - 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); -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 deleted file mode 100644 index 7355fd77..00000000 --- a/include/sta/DelayNormal2.hh +++ /dev/null @@ -1,214 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2026, 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 . -// -// The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. -// -// Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// This notice may not be removed or altered from any source distribution. - -#pragma once - -#include "MinMax.hh" - -namespace sta { - -class Delay; -class DelayDbl; -class StaState; - -// Normal distribution with early(left)/late(right) std deviations. -class Delay -{ -public: - Delay(); - Delay(const Delay &delay); - Delay(const DelayDbl &delay); - Delay(float mean); - Delay(float mean, - float sigma2_early, - float sigma2_late); - float mean() const { return mean_; } - float sigma(const EarlyLate *early_late) const; - // sigma^2 - float sigma2(const EarlyLate *early_late) const; - float sigma2Early() const; - float sigma2Late() const; - void operator=(const Delay &delay); - void operator=(float delay); - void operator+=(const Delay &delay); - void operator+=(float delay); - Delay operator+(const Delay &delay) const; - Delay operator+(float delay) const; - Delay operator-(const Delay &delay) const; - Delay operator-(float delay) const; - Delay operator-() const; - void operator-=(float delay); - void operator-=(const Delay &delay); - bool operator==(const Delay &delay) const; - -protected: - static const int early_index = 0; - static const int late_index = 1; - -private: - float mean_; - // Sigma^2 - float sigma2_[EarlyLate::index_count]; - - friend class DelayDbl; -}; - -// Dwlay with doubles for accumulating delays. -class DelayDbl -{ -public: - DelayDbl(); - float mean() const { return mean_; } - float sigma() const; - // sigma^2 - float sigma2() const; - void operator=(float delay); - void operator+=(const Delay &delay); - void operator-=(const Delay &delay); - -protected: - static const int early_index = 0; - static const int late_index = 1; - -private: - double mean_; - // Sigma^2 - double sigma2_[EarlyLate::index_count]; - - friend class Delay; -}; - -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, - 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 -delayIsInitValue(const Delay &delay, - const MinMax *min_max); -bool -delayZero(const Delay &delay); -bool -delayInf(const Delay &delay); -bool -delayEqual(const Delay &delay1, - const Delay &delay2); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); -bool -delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); -bool -delayLessEqual(const Delay &delay1, - const Delay &delay2, - 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, - 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); -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/DelayScalar.hh b/include/sta/DelayScalar.hh new file mode 100644 index 00000000..c7bd07a9 --- /dev/null +++ b/include/sta/DelayScalar.hh @@ -0,0 +1,90 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include "Delay.hh" + +namespace sta { + +class DelayOpsScalar : public DelayOps +{ +public: + float stdDev2(const Delay &delay, + const EarlyLate *early_late) const override; + float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + + bool isZero(const Delay &delay) const override; + bool isInf(const Delay &delay) const override; + bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const override; + bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + Delay sum(const Delay &delay1, + const Delay &delay2) const override; + Delay sum(const Delay &delay1, + float delay2) const override; + Delay diff(const Delay &delay1, + const Delay &delay2) const override; + Delay diff(const Delay &delay1, + float delay2) const override; + Delay diff(float delay1, + const Delay &delay2) const override; + void incr(Delay &delay1, + const Delay &delay2) const override; + void incr(DelayDbl &delay1, + const Delay &delay2) const override; + void decr(Delay &delay1, + const Delay &delay2) const override; + void decr(DelayDbl &delay1, + const Delay &delay2) const override; + Delay product(const Delay &delay1, + float delay2) const override; + Delay div(float delay1, + const Delay &delay2) const override; + const char *asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const override; +}; + +} // namespace diff --git a/include/sta/DelaySkewNormal.hh b/include/sta/DelaySkewNormal.hh new file mode 100644 index 00000000..090f5701 --- /dev/null +++ b/include/sta/DelaySkewNormal.hh @@ -0,0 +1,98 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include "Delay.hh" + +namespace sta { + +class DelayOpsSkewNormal : public DelayOps +{ +public: + float stdDev2(const Delay &delay, + const EarlyLate *early_late) const override; + float asFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + double asFloat(const DelayDbl &delay, + const EarlyLate *early_late, + const StaState *sta) const override; + + bool isZero(const Delay &delay) const override; + bool isInf(const Delay &delay) const override; + bool equal(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const Delay &delay1, + const Delay &delay2, + const StaState *sta) const override; + bool less(const DelayDbl &delay1, + const DelayDbl &delay2, + const StaState *sta) const override; + bool lessEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const override; + bool greater(const Delay &delay1, + const Delay &delay2, + const StaState *) const override; + bool greaterEqual(const Delay &delay1, + const Delay &delay2, + const StaState *) const override; + Delay sum(const Delay &delay1, + const Delay &delay2) const override; + Delay sum(const Delay &delay1, + float delay2) const override; + Delay diff(const Delay &delay1, + const Delay &delay2) const override; + Delay diff(const Delay &delay1, + float delay2) const override; + Delay diff(float delay1, + const Delay &delay2) const override; + void incr(Delay &delay1, + const Delay &delay2) const override; + void incr(DelayDbl &delay1, + const Delay &delay2) const override; + void decr(Delay &delay1, + const Delay &delay2) const override; + void decr(DelayDbl &delay1, + const Delay &delay2) const override; + Delay product(const Delay &delay1, + float delay2) const override; + Delay div(float delay1, + const Delay &delay2) const override; + const char *asStringVariance(const Delay &delay, + int digits, + const StaState *sta) const override; + +private: + float skewnessSum(const Delay &delay1, + const Delay &delay2) const; + double skewnessSum(double std_dev1, + double skewness1, + double std_dev2, + double skewness2) const; +}; + +} // namespace diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index e96a2c64..18a50015 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -58,13 +58,7 @@ static constexpr ObjectIdx vertex_idx_null = object_idx_null; class Graph : public StaState { public: - // slew_rf_count is - // 0 no slews - // 1 one slew for rise/fall - // 2 rise/fall slews - // ap_count is the dcalc analysis point count. Graph(StaState *sta, - int slew_rf_count, DcalcAPIndex ap_count); void makeGraph(); ~Graph(); @@ -100,6 +94,8 @@ public: const Slew &slew(const Vertex *vertex, const RiseFall *rf, DcalcAPIndex ap_index); + const Slew &slew(const Vertex *vertex, + size_t index); void setSlew(Vertex *vertex, const RiseFall *rf, DcalcAPIndex ap_index, @@ -128,13 +124,13 @@ public: Edge *&edge, const TimingArc *&arc) const; - ArcDelay arcDelay(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const; + const ArcDelay &arcDelay(const Edge *edge, + const TimingArc *arc, + DcalcAPIndex ap_index) const; void setArcDelay(Edge *edge, const TimingArc *arc, DcalcAPIndex ap_index, - ArcDelay delay); + const ArcDelay &delay); // Alias for arcDelays using library wire arcs. const ArcDelay &wireArcDelay(const Edge *edge, const RiseFall *rf, @@ -222,7 +218,6 @@ protected: // driver/source (top level input, instance pin output) vertex // in pin_bidirect_drvr_vertex_map PinVertexMap pin_bidirect_drvr_vertex_map_; - int slew_rf_count_; DcalcAPIndex ap_count_; // Sdf period check annotations. PeriodCheckAnnotations *period_check_annotations_; @@ -258,8 +253,6 @@ public: [[nodiscard]] bool isRoot() const{ return level_ == 0; } [[nodiscard]] bool hasFanin() const; [[nodiscard]] bool hasFanout() const; - Slew *slews() { return slews_; } - const Slew *slews() const { return slews_; } Path *paths() const { return paths_; } Path *makePaths(uint32_t count); void setPaths(Path *paths); @@ -298,14 +291,18 @@ protected: bool is_bidirect_drvr, bool is_reg_clk); void clear(); - void setSlews(Slew *slews); + Slew *slews() { return std::bit_cast(slews_); } + const Slew *slews() const { return std::bit_cast(slews_); } + float *slewsFloat() { return slews_; } + const float *slewsFloat() const { return slews_; } + void setSlews(float *slews); Pin *pin_; EdgeId in_edges_; // Edges to this vertex. EdgeId out_edges_; // Edges from this vertex. // Delay calc - Slew *slews_; + float *slews_; // Search Path *paths_; @@ -356,8 +353,9 @@ public: TimingSense sense() const; TimingArcSet *timingArcSet() const { return arc_set_; } void setTimingArcSet(TimingArcSet *set); - ArcDelay *arcDelays() const { return arc_delays_; } - void setArcDelays(ArcDelay *arc_delays); + float *arcDelays() { return arc_delays_; } + const float *arcDelays() const { return arc_delays_; } + void setArcDelays(float *delays); bool delay_Annotation_Is_Incremental() const {return delay_annotation_is_incremental_;}; void setDelayAnnotationIsIncremental(bool is_incr); // Edge is disabled to break combinational loops. @@ -398,7 +396,7 @@ protected: EdgeId vertex_in_link_; // Vertex in edges list. EdgeId vertex_out_next_; // Vertex out edges doubly linked list. EdgeId vertex_out_prev_; - ArcDelay *arc_delays_; + float *arc_delays_; union { uintptr_t bits_; std::vector *seq_; diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index a1f5373a..60c12d43 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -245,8 +245,8 @@ protected: bool annotateDelaySlew(Edge *edge, const TimingArc *arc, - ArcDelay &gate_delay, - Slew &gate_slew, + const ArcDelay &gate_delay, + const Slew &gate_slew, const Scene *scene, const MinMax *min_max); bool annotateLoadDelays(Vertex *drvr_vertex, diff --git a/include/sta/InternalPower.hh b/include/sta/InternalPower.hh index e37003ac..30d20ebb 100644 --- a/include/sta/InternalPower.hh +++ b/include/sta/InternalPower.hh @@ -33,44 +33,11 @@ namespace sta { -class InternalPowerModel; - -using InternalPowerModels = - std::array, RiseFall::index_count>; - -class InternalPower -{ -public: - InternalPower(LibertyPort *port, - LibertyPort *related_port, - LibertyPort *related_pg_pin, - const std::shared_ptr &when, - InternalPowerModels &models); - //InternalPower(InternalPower &&other) noexcept; - LibertyCell *libertyCell() const; - LibertyPort *port() const { return port_; } - LibertyPort *relatedPort() const { return related_port_; } - FuncExpr *when() const { return when_.get(); } - LibertyPort *relatedPgPin() const { return related_pg_pin_; } - float power(const RiseFall *rf, - const Pvt *pvt, - float in_slew, - float load_cap) const; - const InternalPowerModel *model(const RiseFall *rf) const; - -protected: - LibertyPort *port_; - LibertyPort *related_port_; - LibertyPort *related_pg_pin_; - std::shared_ptr when_; - InternalPowerModels models_; -}; - class InternalPowerModel { public: - InternalPowerModel(TableModel *model); - ~InternalPowerModel(); + InternalPowerModel(); + InternalPowerModel(std::shared_ptr model); float power(const LibertyCell *cell, const Pvt *pvt, float in_slew, @@ -80,7 +47,7 @@ public: float in_slew, float load_cap, int digits) const; - const TableModel *model() const { return model_; } + const TableModel *model() const { return model_.get(); } protected: void findAxisValues(float in_slew, @@ -95,7 +62,36 @@ protected: bool checkAxes(const TableModel *model); bool checkAxis(const TableAxis *axis); - TableModel *model_; + std::shared_ptr model_; +}; + +using InternalPowerModels = std::array; + +class InternalPower +{ +public: + InternalPower(LibertyPort *port, + LibertyPort *related_port, + LibertyPort *related_pg_pin, + const std::shared_ptr &when, + const InternalPowerModels &models); + LibertyCell *libertyCell() const; + LibertyPort *port() const { return port_; } + LibertyPort *relatedPort() const { return related_port_; } + FuncExpr *when() const { return when_.get(); } + LibertyPort *relatedPgPin() const { return related_pg_pin_; } + float power(const RiseFall *rf, + const Pvt *pvt, + float in_slew, + float load_cap) const; + const InternalPowerModel &model(const RiseFall *rf) const; + +protected: + LibertyPort *port_; + LibertyPort *related_port_; + LibertyPort *related_pg_pin_; + std::shared_ptr when_; + InternalPowerModels models_; }; } // namespace diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 76bc763e..450f1dff 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -589,7 +589,7 @@ public: LibertyPort *related_port, LibertyPort *related_pg_pin, const std::shared_ptr &when, - InternalPowerModels &models); + const InternalPowerModels &models); void makeLeakagePower(LibertyPort *related_pg_port, FuncExpr *when, float power); diff --git a/include/sta/LinearModel.hh b/include/sta/LinearModel.hh index aa7ba59e..07251739 100644 --- a/include/sta/LinearModel.hh +++ b/include/sta/LinearModel.hh @@ -37,14 +37,22 @@ public: void gateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) const override; + float &gate_delay, + float &drvr_slew) const override; + void gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const override; std::string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; float driveResistance(const Pvt *pvt) const override; @@ -64,13 +72,15 @@ public: float from_slew, float to_slew, float related_out_cap, - bool pocv_enabled) const override; + const MinMax *min_max, + PocvMode pocv_mode) const override; std::string reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, float related_out_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; protected: diff --git a/include/sta/Path.hh b/include/sta/Path.hh index 6ca1d8cc..c12c080b 100644 --- a/include/sta/Path.hh +++ b/include/sta/Path.hh @@ -45,14 +45,14 @@ public: const StaState *sta); Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, const StaState *sta); Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -62,11 +62,11 @@ public: bool isNull() const; // prev_path null void init(Vertex *vertex, - Arrival arrival, + const Arrival &arrival, const StaState *sta); void init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -76,7 +76,7 @@ public: const StaState *sta); void init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, const StaState *sta); Vertex *vertex(const StaState *sta) const; @@ -98,14 +98,12 @@ public: const MinMax *minMax(const StaState *sta) const; PathAPIndex pathAnalysisPtIndex(const StaState *sta) const; DcalcAPIndex dcalcAnalysisPtIndex(const StaState *sta) const; - Arrival &arrival() { return arrival_; } const Arrival &arrival() const { return arrival_; } void setArrival(Arrival arrival); - Required &required() { return required_; } const Required &required() const {return required_; } void setRequired(const Required &required); Slack slack(const StaState *sta) const; - Slew slew(const StaState *sta) const; + const Slew &slew(const StaState *sta) const; // This takes the same time as prevPath and prevArc combined. Path *prevPath() const; void setPrevPath(Path *prev_path); diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index 899c96ac..8fc1a1dc 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -98,7 +98,7 @@ public: virtual const char *typeName() const = 0; virtual int exceptPathCmp(const PathEnd *path_end, const StaState *sta) const; - virtual Arrival dataArrivalTime(const StaState *sta) const; + virtual const Arrival &dataArrivalTime(const StaState *sta) const; // Arrival time with source clock offset. Arrival dataArrivalTimeOffset(const StaState *sta) const; virtual Required requiredTime(const StaState *sta) const = 0; diff --git a/graph/Delay.cc b/include/sta/PocvMode.hh similarity index 81% rename from graph/Delay.cc rename to include/sta/PocvMode.hh index 2f538a30..29d19a63 100644 --- a/graph/Delay.cc +++ b/include/sta/PocvMode.hh @@ -22,19 +22,15 @@ // // This notice may not be removed or altered from any source distribution. -#include "Machine.hh" -#include "StringUtil.hh" -#include "Units.hh" -#include "StaState.hh" -#include "Delay.hh" +#pragma once namespace sta { +enum class PocvMode { scalar, normal, skew_normal }; + const char * -delayAsString(const Delay &delay, - const StaState *sta) -{ - return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); -} +pocvModeName(PocvMode mode); +PocvMode +findPocvMode(const char *mode_name); } // namespace diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 7b39c47e..1464aa06 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -982,11 +982,11 @@ public: bool report_cap, bool report_slew, bool report_fanout, + bool report_variation, bool report_src_attr); ReportField *findReportPathField(const char *name); void setReportPathDigits(int digits); void setReportPathNoSplit(bool no_split); - void setReportPathSigmas(bool report_sigmas); void reportPathEnd(PathEnd *end); void reportPathEnds(PathEndSeq *ends); ReportPath *reportPath() { return report_path_; } @@ -998,7 +998,7 @@ public: const SetupHold *setup_hold, bool include_internal_latency, int digits); - float findWorstClkSkew(const SetupHold *setup_hold, + Delay findWorstClkSkew(const SetupHold *setup_hold, bool include_internal_latency); void reportClkLatency(ConstClockSeq &clks, @@ -1126,12 +1126,15 @@ public: void reportArrivalWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits); void reportRequiredWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits); void reportSlackWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits); Slew slew(Vertex *vertex, @@ -1139,9 +1142,9 @@ public: const SceneSeq &scenes, const MinMax *min_max); - ArcDelay arcDelay(Edge *edge, - TimingArc *arc, - DcalcAPIndex ap_index); + const ArcDelay &arcDelay(Edge *edge, + TimingArc *arc, + DcalcAPIndex ap_index); // True if the timing arc has been back-annotated. bool arcDelayAnnotated(Edge *edge, TimingArc *arc, @@ -1403,12 +1406,13 @@ public: // TCL variable sta_crpr_mode. CrprMode crprMode() const; void setCrprMode(CrprMode mode); - // TCL variable sta_pocv_enabled. + // TCL variable sta_pocv_mode. // Parametric on chip variation (statisical sta). - bool pocvEnabled() const; - void setPocvEnabled(bool enabled); + PocvMode pocvMode() const; + void setPocvMode(PocvMode mode); // Number of std deviations from mean to use for normal distributions. - void setSigmaFactor(float factor); + float pocvQuantile(); + void setPocvQuantile(float quantile); // TCL variable sta_propagate_gated_clock_enable. // Propagate gated clock enable arrivals. bool propagateGatedClockEnable() const; @@ -1505,17 +1509,20 @@ protected: void reportDelaysWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits, bool find_required, PathDelayFunc get_path_delay); void reportDelaysWrtClks(Vertex *vertex, const Scene *scene, + bool report_variance, int digits, bool find_required, PathDelayFunc get_path_delay); void reportDelaysWrtClks(Vertex *vertex, const ClockEdge *clk_edge, const Scene *scene, + bool report_variance, int digits, PathDelayFunc get_path_delay); RiseFallMinMaxDelay findDelaysWrtClks(Vertex *vertex, @@ -1525,6 +1532,7 @@ protected: std::string formatDelay(const RiseFall *rf, const MinMax *min_max, const RiseFallMinMaxDelay &delays, + bool report_variance, int digits); void connectDrvrPinAfter(Vertex *vertex); diff --git a/include/sta/StaState.hh b/include/sta/StaState.hh index 2cfbede4..8f96e137 100644 --- a/include/sta/StaState.hh +++ b/include/sta/StaState.hh @@ -47,6 +47,7 @@ class GraphDelayCalc; class Latches; class DispatchQueue; class Variables; +class DelayOps; using ModeSeq = std::vector; using ModeSet = std::set; @@ -96,10 +97,10 @@ public: GraphDelayCalc *graphDelayCalc() const { return graph_delay_calc_; } Search *search() { return search_; } Search *search() const { return search_; } + const DelayOps *delayOps() const { return delay_ops_; } Latches *latches() { return latches_; } Latches *latches() const { return latches_; } unsigned threadCount() const { return thread_count_; } - float sigmaFactor() const { return sigma_factor_; } bool crprActive(const Mode *mode) const; Variables *variables() { return variables_; } const Variables *variables() const { return variables_; } @@ -133,11 +134,11 @@ protected: ArcDelayCalc *arc_delay_calc_; GraphDelayCalc *graph_delay_calc_; Search *search_; + DelayOps *delay_ops_; Latches *latches_; Variables *variables_; int thread_count_; DispatchQueue *dispatch_queue_; - float sigma_factor_; }; } // namespace diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 59e26ba0..95b51132 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -33,12 +33,14 @@ #include "Transition.hh" #include "LibertyClass.hh" #include "TimingModel.hh" +#include "Variables.hh" namespace sta { class Unit; class Units; class Report; +class TableModels; class Table; class TableModel; class TableAxis; @@ -63,43 +65,41 @@ class GateTableModel : public GateTimingModel { public: GateTableModel(LibertyCell *cell, - TableModel *delay_model, - TableModelsEarlyLate delay_sigma_models, - TableModel *slew_model, - TableModelsEarlyLate slew_sigma_models, + TableModels *delay_models, + TableModels *slew_models, ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms); GateTableModel(LibertyCell *cell, - TableModel *delay_model, - TableModel *slew_model); + TableModels *delay_models, + TableModels *slew_models); ~GateTableModel() override; void gateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) const override; - // deprecated 2024-01-07 - // related_out_cap arg removed. - void gateDelay(const Pvt *pvt, - float in_slew, - float load_cap, - float related_out_cap, - bool pocv_enabled, - ArcDelay &gate_delay, - Slew &drvr_slew) const __attribute__ ((deprecated)); + float &gate_delay, + float &drvr_slew) const override; + // Fill in pocv parameters in gate_delay, drvr_slew. + void gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const override; std::string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; float driveResistance(const Pvt *pvt) const override; - const TableModel *delayModel() const { return delay_model_.get(); } - const TableModel *slewModel() const { return slew_model_.get(); } - const TableModel *delaySigmaModel(const EarlyLate *el) const; - const TableModel *slewSigmaModel(const EarlyLate *el) const; + const TableModels *delayModels() const { return delay_models_.get(); } + const TableModel *delayModel() const; + const TableModels *slewModels() const { return slew_models_.get(); } + const TableModel *slewModel() const; const ReceiverModel *receiverModel() const { return receiver_model_.get(); } OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); } // Check the axes before making the model. @@ -138,10 +138,8 @@ protected: float &axis_value3) const; static bool checkAxis(const TableAxis *axis); - std::unique_ptr delay_model_; - TableModelsEarlyLate delay_sigma_models_; - std::unique_ptr slew_model_; - TableModelsEarlyLate slew_sigma_models_; + std::unique_ptr delay_models_; + std::unique_ptr slew_models_; ReceiverModelPtr receiver_model_; std::unique_ptr output_waveforms_; }; @@ -150,25 +148,24 @@ class CheckTableModel : public CheckTimingModel { public: CheckTableModel(LibertyCell *cell, - TableModel *model, - TableModelsEarlyLate sigma_models); - CheckTableModel(LibertyCell *cell, - TableModel *model); + TableModels *check_models); ~CheckTableModel() override; ArcDelay checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, - bool pocv_enabled) const override; + const MinMax *min_max, + PocvMode pocv_mode) const override; std::string reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, float related_out_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const override; - const TableModel *model() const { return model_.get(); } - const TableModel *sigmaModel(const EarlyLate *el) const; + const TableModels *checkModels() const { return check_models_.get(); } + const TableModel *checkModel() const; // Check the axes before making the model. // Return true if the model axes are supported. @@ -202,8 +199,7 @@ protected: int digits) const; static bool checkAxis(const TableAxis *axis); - std::unique_ptr model_; - TableModelsEarlyLate sigma_models_; + std::unique_ptr check_models_; }; class TableAxis @@ -311,6 +307,8 @@ public: private: void clear(); + float findValueOrder2(float axis_value1, float axis_value2) const; + float findValueOrder3(float axis_value1, float axis_value2, float axis_value3) const; std::string reportValueOrder0(const char *result_name, const char *comment1, const Unit *table_unit, @@ -408,6 +406,34 @@ protected: bool is_scaled_:1; }; +// cell/transition/check nldm/ocv/lvf models for one rise/fall edge. +class TableModels +{ +public: + TableModels(); + TableModels(TableModel *model); + ~TableModels(); + TableModel *model() const { return model_.get(); } + void setModel(TableModel *model); + TableModel *sigma(const EarlyLate *early_late) const; + void setSigma(TableModel *table, + const EarlyLate *early_late); + TableModel *meanShift() const { return mean_shift_.get(); } + void setMeanShift(TableModel *table); + TableModel *skewness() const { return skewness_.get(); } + void setSkewness(TableModel *table); + TableModel *stdDev() const { return std_dev_.get(); } + void setStdDev(TableModel *table); + +protected: + std::unique_ptr model_; + // Note early/late can point to the same model. + std::array sigma_; + std::unique_ptr std_dev_; + std::unique_ptr mean_shift_; + std::unique_ptr skewness_; +}; + //////////////////////////////////////////////////////////////// class ReceiverModel diff --git a/include/sta/TimingModel.hh b/include/sta/TimingModel.hh index db9caf08..0ff157f2 100644 --- a/include/sta/TimingModel.hh +++ b/include/sta/TimingModel.hh @@ -28,6 +28,7 @@ #include "Delay.hh" #include "LibertyClass.hh" +#include "Variables.hh" namespace sta { @@ -52,14 +53,23 @@ public: virtual void gateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) const = 0; + float &gate_delay, + float &drvr_slew) const = 0; + // Fill in pocv parameters in gate_delay, drvr_slew. + virtual void gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const = 0; virtual std::string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const = 0; virtual float driveResistance(const Pvt *pvt) const = 0; }; @@ -74,13 +84,15 @@ public: float from_slew, float to_slew, float related_out_cap, - bool pocv_enabled) const = 0; + const MinMax *min_max, + PocvMode pocv_mode) const = 0; virtual std::string reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, float to_slew, float related_out_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const = 0; }; diff --git a/include/sta/Variables.hh b/include/sta/Variables.hh index 9e553193..7e8e7a69 100644 --- a/include/sta/Variables.hh +++ b/include/sta/Variables.hh @@ -24,6 +24,8 @@ #pragma once +#include "PocvMode.hh" + namespace sta { enum class CrprMode { same_pin, same_transition }; @@ -72,8 +74,11 @@ public: // TCL variable sta_input_port_default_clock. bool useDefaultArrivalClock() { return use_default_arrival_clock_; } void setUseDefaultArrivalClock(bool enable); - bool pocvEnabled() const { return pocv_enabled_; } - void setPocvEnabled(bool enabled); + bool pocvEnabled() const; + PocvMode pocvMode() const { return pocv_mode_; } + void setPocvMode(PocvMode mode); + float pocvQuantile() const { return pocv_quantile_; } + void setPocvQuantile(float quartile); private: bool crpr_enabled_; @@ -88,7 +93,8 @@ private: bool dynamic_loop_breaking_; bool propagate_all_clks_; bool use_default_arrival_clock_; - bool pocv_enabled_; + PocvMode pocv_mode_; + float pocv_quantile_; }; } // namespace diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index 5380a359..d9ecd617 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -37,7 +37,7 @@ InternalPower::InternalPower(LibertyPort *port, LibertyPort *related_port, LibertyPort *related_pg_pin, const std::shared_ptr &when, - InternalPowerModels &models) : + const InternalPowerModels &models) : port_(port), related_port_(related_port), related_pg_pin_(related_pg_pin), @@ -52,36 +52,32 @@ InternalPower::libertyCell() const return port_->libertyCell(); } +const InternalPowerModel & +InternalPower::model(const RiseFall *rf) const +{ + return models_[rf->index()]; +} + float InternalPower::power(const RiseFall *rf, const Pvt *pvt, float in_slew, float load_cap) const { - const std::shared_ptr &model = models_[rf->index()]; - if (model) - return model->power(libertyCell(), pvt, in_slew, load_cap); - else - return 0.0; -} - -const InternalPowerModel * -InternalPower::model(const RiseFall *rf) const -{ - const std::shared_ptr &m = models_[rf->index()]; - return m.get(); + const InternalPowerModel &model = models_[rf->index()]; + return model.power(libertyCell(), pvt, in_slew, load_cap); } //////////////////////////////////////////////////////////////// -InternalPowerModel::InternalPowerModel(TableModel *model) : - model_(model) +InternalPowerModel::InternalPowerModel() : + model_(nullptr) { } -InternalPowerModel::~InternalPowerModel() +InternalPowerModel::InternalPowerModel(std::shared_ptr model) : + model_(model) { - delete model_; } float diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index de019009..1c659ad8 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1262,7 +1262,7 @@ LibertyCell::makeInternalPower(LibertyPort *port, LibertyPort *related_port, LibertyPort *related_pg_pin, const std::shared_ptr &when, - InternalPowerModels &models) + const InternalPowerModels &models) { internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models); port_internal_powers_[port].push_back(internal_powers_.size() - 1); diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 32e551b4..e95ed876 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2122,80 +2122,76 @@ LibertyReader::makeTableModels(LibertyCell *cell, { bool found_model = false; for (const RiseFall *rf : RiseFall::range()) { - std::string delay_attr_name = "cell_" + rf->to_string(); - TableModel *delay = readGateTableModel(timing_group, delay_attr_name.c_str(), rf, - TableTemplateType::delay, time_scale_, - ScaleFactorType::cell); - std::string transition_attr_name = rf->to_string() + "_transition"; - TableModel *transition = readGateTableModel(timing_group, - transition_attr_name.c_str(), - rf, TableTemplateType::delay, - time_scale_, - ScaleFactorType::transition); - if (delay || transition) { - std::string delay_sigma_attr_name = "ocv_sigma_cell_" + rf->to_string(); - TableModelsEarlyLate delay_sigmas = - readEarlyLateTableModels(timing_group, - delay_sigma_attr_name.c_str(), - rf, TableTemplateType::delay, - time_scale_, - ScaleFactorType::unknown); + TableModel *delay_model = readTableModel(timing_group, + "cell_" + rf->to_string(), + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::cell, + GateTableModel::checkAxes); + TableModel *slew_model = readTableModel(timing_group, + rf->to_string() + "_transition", + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::transition, + GateTableModel::checkAxes); + if (delay_model || slew_model) { + TableModels *delay_models = new TableModels(delay_model); + readLvfModels(timing_group, + "ocv_sigma_cell_" + rf->to_string(), + "ocv_std_dev_cell_" + rf->to_string(), + "ocv_mean_shift_cell_" + rf->to_string(), + "ocv_skewness_cell_" + rf->to_string(), + rf, delay_models, GateTableModel::checkAxes); - std::string slew_sigma_attr_name = "ocv_sigma_" + rf->to_string() - + "_transition"; - TableModelsEarlyLate slew_sigmas = - readEarlyLateTableModels(timing_group, - slew_sigma_attr_name.c_str(), - rf, TableTemplateType::delay, - time_scale_, - ScaleFactorType::unknown); + TableModels *slew_models = new TableModels(slew_model); + readLvfModels(timing_group, + "ocv_sigma_" + rf->to_string() + "_transition", + "ocv_std_dev_" + rf->to_string() + "_transition", + "ocv_mean_shift_" + rf->to_string() + "_transition", + "ocv_skewness_" + rf->to_string() + "_transition", + rf, slew_models, GateTableModel::checkAxes); ReceiverModelPtr receiver_model = readReceiverCapacitance(timing_group, rf); OutputWaveforms *output_waveforms = readOutputWaveforms(timing_group, rf); - timing_attrs->setModel(rf, new GateTableModel(cell, delay, - std::move(delay_sigmas), - transition, - std::move(slew_sigmas), + timing_attrs->setModel(rf, new GateTableModel(cell, delay_models, + slew_models, receiver_model, output_waveforms)); TimingType timing_type = timing_attrs->timingType(); if (isGateTimingType(timing_type)) { - if (transition == nullptr) + if (slew_model == nullptr) libWarn(1210, timing_group, "missing %s_transition.", rf->name()); - if (delay == nullptr) + if (delay_model == nullptr) libWarn(1211, timing_group, "missing cell_%s.", rf->name()); } found_model = true; } - else { - std::string constraint_attr_name = rf->to_string() + "_constraint"; - ScaleFactorType scale_factor_type = - timingTypeScaleFactorType(timing_attrs->timingType()); - TableModel *constraint = readCheckTableModel(timing_group, - constraint_attr_name.c_str(), - rf, TableTemplateType::delay, - time_scale_, scale_factor_type); - if (constraint) { - std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string() - + "_constraint"; - TableModelsEarlyLate constraint_sigmas = - readEarlyLateTableModels(timing_group, - constraint_sigma_attr_name.c_str(), - rf, TableTemplateType::delay, - time_scale_, - ScaleFactorType::unknown); - timing_attrs->setModel(rf, new CheckTableModel(cell, constraint, - std::move(constraint_sigmas))); - found_model = true; - } + + std::string constraint_attr_name = rf->to_string() + "_constraint"; + ScaleFactorType scale_factor_type = + timingTypeScaleFactorType(timing_attrs->timingType()); + TableModel *check_model = readTableModel(timing_group, + constraint_attr_name.c_str(), + rf, TableTemplateType::delay, + time_scale_, scale_factor_type, + CheckTableModel::checkAxes); + if (check_model) { + TableModels *check_models = new TableModels(check_model); + readLvfModels(timing_group, + "ocv_sigma_" + rf->to_string() + "_constraint", + "ocv_std_dev_" + rf->to_string() + "_constraint", + "ocv_mean_shift_" + rf->to_string() + "_constraint", + "ocv_skewness_" + rf->to_string() + "_constraint", + rf, check_models, CheckTableModel::checkAxes); + timing_attrs->setModel(rf, new CheckTableModel(cell, check_models)); + found_model = true; } } if (!found_model) libWarn(1311, timing_group, "no table models found in timing group."); } - bool LibertyReader::isGateTimingType(TimingType timing_type) { @@ -2215,41 +2211,66 @@ LibertyReader::isGateTimingType(TimingType timing_type) } TableModel * -LibertyReader::readGateTableModel(const LibertyGroup *timing_group, - const char *table_group_name, - const RiseFall *rf, - TableTemplateType template_type, - float scale, - ScaleFactorType scale_factor_type) +LibertyReader::readTableModel(const LibertyGroup *timing_group, + const std::string &table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function check_axes) { const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name); - if (table_group) { - TableModel *model = readTableModel(table_group, rf, template_type, scale, - scale_factor_type); - if (model && !GateTableModel::checkAxes(model)) - libWarn(1251, table_group, "unsupported model axis."); - return model; - } + if (table_group) + return readTableModel(table_group, rf, template_type, scale, + scale_factor_type, check_axes); return nullptr; } -TableModel * -LibertyReader::readCheckTableModel(const LibertyGroup *timing_group, - const char *table_group_name, - const RiseFall *rf, - TableTemplateType template_type, - float scale, - ScaleFactorType scale_factor_type) +void +LibertyReader::readLvfModels(const LibertyGroup *timing_group, + const std::string &sigma_group_name, + const std::string &std_dev_group_name, + const std::string &mean_shift_group_name, + const std::string &skewness_group_name, + const RiseFall *rf, + TableModels *table_models, + const std::function check_axes) { - const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name); - if (table_group) { - TableModel *model = readTableModel(table_group, rf, template_type, scale, - scale_factor_type); - if (model && !CheckTableModel::checkAxes(model)) - libWarn(1252, table_group, "unsupported model axis."); - return model; + TableModelsEarlyLate sigmas = + readEarlyLateTableModels(timing_group, + sigma_group_name.c_str(), + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::unknown, + check_axes); + for (const EarlyLate *early_late : EarlyLate::range()) + table_models->setSigma(sigmas[early_late->index()], early_late); + + const LibertyGroup *std_dev_group = timing_group->findSubgroup(std_dev_group_name); + if (std_dev_group) { + TableModel *std_dev = readTableModel(std_dev_group, rf, TableTemplateType::delay, + time_scale_, ScaleFactorType::unknown, + check_axes); + table_models->setStdDev(std_dev); + } + + const LibertyGroup *mean_shift_group=timing_group->findSubgroup(mean_shift_group_name); + if (mean_shift_group) { + TableModel *mean_shift = readTableModel(mean_shift_group, rf, + TableTemplateType::delay, + time_scale_, ScaleFactorType::unknown, + check_axes); + table_models->setMeanShift(mean_shift); + } + + const LibertyGroup *skewness_group = timing_group->findSubgroup(skewness_group_name); + if (skewness_group) { + TableModel *skewness = readTableModel(skewness_group, rf, + TableTemplateType::delay, + 1.0, ScaleFactorType::unknown, + check_axes); + table_models->setSkewness(skewness); } - return nullptr; } TableModelsEarlyLate @@ -2258,12 +2279,13 @@ LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, const RiseFall *rf, TableTemplateType template_type, float scale, - ScaleFactorType scale_factor_type) + ScaleFactorType scale_factor_type, + const std::function check_axes) { TableModelsEarlyLate models{}; for (const LibertyGroup *table_group : timing_group->findSubgroups(table_group_name)) { TableModel *model = readTableModel(table_group, rf, template_type, scale, - scale_factor_type); + scale_factor_type, check_axes); const std::string *early_late = table_group->findAttrString("sigma_type"); if (early_late == nullptr || *early_late == "early_and_late") { @@ -2274,9 +2296,6 @@ LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, models[EarlyLate::early()->index()] = model; else if (*early_late == "late") models[EarlyLate::late()->index()] = model; - - //if (model && !GateTableModel::checkAxes(model)) - // libWarn(1182, table_group, "unsupported model axis."); } return models; } @@ -2445,7 +2464,8 @@ LibertyReader::readTableModel(const LibertyGroup *table_group, const RiseFall *rf, TableTemplateType template_type, float scale, - ScaleFactorType scale_factor_type) + ScaleFactorType scale_factor_type, + const std::function &check_axes) { const char *template_name = table_group->firstName(); if (library_ && template_name) { @@ -2456,6 +2476,9 @@ LibertyReader::readTableModel(const LibertyGroup *table_group, if (table) { TableModel *table_model = new TableModel(table, tbl_template, scale_factor_type, rf); + if (!check_axes(table_model)) { + libWarn(1251, table_group, "unsupported model axis."); + } return table_model; } } @@ -2682,7 +2705,7 @@ LibertyReader::readInternalPowerGroups(LibertyCell *cell, FuncExpr *when1 = readFuncExpr(cell, ipwr_group, "when"); if (when1) when = std::shared_ptr(when1); - InternalPowerModels models; + InternalPowerModels models{}; // rise/fall_power group for (const RiseFall *rf : RiseFall::range()) { std::string pwr_attr_name = rf->to_string() + "_power"; @@ -2691,7 +2714,9 @@ LibertyReader::readInternalPowerGroups(LibertyCell *cell, TableModel *model = readTableModel(pwr_group, rf, TableTemplateType::power, energyScale(), ScaleFactorType::internal_power); - models[rf->index()] = std::make_shared(model); + std::shared_ptr table_model(model); + InternalPowerModel pwr_model(table_model); + models[rf->index()] = pwr_model; } } // power group (rise/fall power are the same) @@ -2701,9 +2726,11 @@ LibertyReader::readInternalPowerGroups(LibertyCell *cell, TableTemplateType::power, energyScale(), ScaleFactorType::internal_power); - auto pwr_model = std::make_shared(model); - for (const RiseFall *rf : RiseFall::range()) + std::shared_ptr table_model(model); + for (const RiseFall *rf : RiseFall::range()) { + InternalPowerModel pwr_model(table_model); models[rf->index()] = pwr_model; + } } if (related_ports.empty()) cell->makeInternalPower(port, nullptr, related_pg_port, when, models); @@ -2792,9 +2819,9 @@ LibertyReader::makeScalarCheckModel(LibertyCell *cell, library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *table_model = new TableModel(table, tbl_template, scale_factor_type, rf); - TableModelsEarlyLate sigmas{}; - CheckTableModel *check_model = new CheckTableModel(cell, table_model, - std::move(sigmas)); + + TableModels *check_models = new TableModels(table_model); + TimingModel *check_model = new CheckTableModel(cell, check_models); return check_model; } @@ -3713,7 +3740,7 @@ PortNameBitIterator::init(const char *port_name) range_bit_ = from; findRangeBusNameNext(); } - size_ = abs(from - to) + 1; + size_ = std::abs(from - to) + 1; } else visitor_->libWarn(1294, line_, "port %s not found.", port_name); diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 474cc799..070b28e7 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -232,25 +232,21 @@ protected: const LibertyPortSeq &ports, const LibertyGroup *port_group); bool isGateTimingType(TimingType timing_type); - TableModel *readGateTableModel(const LibertyGroup *timing_group, - const char *table_group_name, - const RiseFall *rf, - TableTemplateType template_type, - float scale, - ScaleFactorType scale_factor_type); + TableModel *readTableModel(const LibertyGroup *timing_group, + const std::string &table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type, + const std::function check_axes); TableModelsEarlyLate readEarlyLateTableModels(const LibertyGroup *timing_group, const char *table_group_name, const RiseFall *rf, TableTemplateType template_type, float scale, - ScaleFactorType scale_factor_type); - TableModel *readCheckTableModel(const LibertyGroup *timing_group, - const char *table_group_name, - const RiseFall *rf, - TableTemplateType template_type, - float scale, - ScaleFactorType scale_factor_type); + ScaleFactorType scale_factor_type, + const std::function check_axes); ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group, const RiseFall *rf); void readReceiverCapacitance(const LibertyGroup *timing_group, @@ -268,7 +264,9 @@ protected: const RiseFall *rf, TableTemplateType template_type, float scale, - ScaleFactorType scale_factor_type); + ScaleFactorType scale_factor_type, + const std::function &check_axes = + [](TableModel *) { return true; }); TablePtr readTableModel(const LibertyGroup *table_group, const TableTemplate *tbl_template, float scale); @@ -281,6 +279,14 @@ protected: void makeTableModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs); + void readLvfModels(const LibertyGroup *timing_group, + const std::string &sigma_group_name, + const std::string &std_dev_group_name, + const std::string &mean_shift_group_name, + const std::string &skewness_group_name, + const RiseFall *rf, + TableModels *table_models, + const std::function check_axes); TableAxisPtr makeTableAxis(const LibertyGroup *table_group, const char *index_attr_name, diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 19e08729..ada26b09 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -466,13 +466,14 @@ LibertyWriter::writeTimingModels(const TimingArc *arc, const TableModel *slew_model = gate_model->slewModel(); if (slew_model) { const std::string &slew_template_name = slew_model->tblTemplate()->name(); - fprintf(stream_, " %s_transition(%s) {\n", rf->name(), slew_template_name.c_str()); + fprintf(stream_, " %s_transition(%s) {\n", rf->name(), + slew_template_name.c_str()); writeTableModel(slew_model); fprintf(stream_, " }\n"); } } else if (check_model) { - const TableModel *model = check_model->model(); + const TableModel *model = check_model->checkModel(); const std::string &template_name = model->tblTemplate()->name(); fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name.c_str()); writeTableModel(model); diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index 85d158e7..b8e6a78c 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -42,20 +42,32 @@ void GateLinearModel::gateDelay(const Pvt *, float, float load_cap, - bool, // return values - ArcDelay &gate_delay, - Slew &drvr_slew) const + float &gate_delay, + float &drvr_slew) const { gate_delay = intrinsic_ + resistance_ * load_cap; drvr_slew = 0.0; } +void +GateLinearModel::gateDelayPocv(const Pvt *, + float, + float, + const MinMax *, + PocvMode, + // return values + ArcDelay &, + Slew &) const +{ +} + std::string GateLinearModel::reportGateDelay(const Pvt *, float, float load_cap, - bool, + const MinMax *, + PocvMode, int digits) const { const LibertyLibrary *library = cell_->libertyLibrary(); @@ -98,7 +110,8 @@ CheckLinearModel::checkDelay(const Pvt *, float, float, float, - bool) const + const MinMax *, + PocvMode) const { return intrinsic_; } @@ -109,7 +122,8 @@ CheckLinearModel::reportCheckDelay(const Pvt *, const char *, float, float, - bool, + const MinMax *, + PocvMode, int digits) const { const LibertyLibrary *library = cell_->libertyLibrary(); diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 0d5257a8..a235dcb4 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -38,8 +38,6 @@ namespace sta { size_t findValueIndex(float value, const FloatSeq *values); -static void -sigmaModelsDelete(TableModelsEarlyLate &models); static std::string reportPvt(const LibertyCell *cell, const Pvt *pvt, @@ -53,139 +51,189 @@ TimingModel::TimingModel(LibertyCell *cell) : { } +//////////////////////////////////////////////////////////////// + GateTableModel::GateTableModel(LibertyCell *cell, - TableModel *delay_model, - TableModelsEarlyLate delay_sigma_models, - TableModel *slew_model, - TableModelsEarlyLate slew_sigma_models, + TableModels *delay_models, + TableModels *slew_models, ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms) : GateTimingModel(cell), - delay_model_(delay_model), - delay_sigma_models_(std::move(delay_sigma_models)), - slew_model_(slew_model), - slew_sigma_models_(std::move(slew_sigma_models)), + delay_models_(delay_models), + slew_models_(slew_models), receiver_model_(receiver_model), output_waveforms_(output_waveforms) { } GateTableModel::GateTableModel(LibertyCell *cell, - TableModel *delay_model, - TableModel *slew_model) : + TableModels *delay_models, + TableModels *slew_models) : GateTimingModel(cell), - delay_model_(delay_model), - delay_sigma_models_{}, - slew_model_(slew_model), - slew_sigma_models_{}, + delay_models_(delay_models), + slew_models_(slew_models), receiver_model_(nullptr), output_waveforms_(nullptr) { } -GateTableModel::~GateTableModel() +GateTableModel::~GateTableModel() = default; + +const TableModel * +GateTableModel::delayModel() const { - sigmaModelsDelete(slew_sigma_models_); - sigmaModelsDelete(delay_sigma_models_); + return delay_models_ ? delay_models_->model() : nullptr; } -static void -sigmaModelsDelete(TableModelsEarlyLate &models) +const TableModel * +GateTableModel::slewModel() const { - TableModel *early_model = models[EarlyLate::earlyIndex()]; - TableModel *late_model = models[EarlyLate::lateIndex()]; - if (early_model == late_model) - delete early_model; - else { - delete early_model; - delete late_model; - } + return slew_models_ ? slew_models_->model() : nullptr;; } void GateTableModel::setIsScaled(bool is_scaled) { - if (delay_model_) - delay_model_->setIsScaled(is_scaled); - if (slew_model_) - slew_model_->setIsScaled(is_scaled); + if (delay_models_) + delay_models_->model()->setIsScaled(is_scaled); + if (slew_models_) + slew_models_->model()->setIsScaled(is_scaled); } void GateTableModel::gateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, // return values - ArcDelay &gate_delay, - Slew &drvr_slew) const + float &gate_delay, + float &drvr_slew) const { - float delay = findValue(pvt, delay_model_.get(), in_slew, load_cap, 0.0); - float sigma_early = 0.0; - float sigma_late = 0.0; - if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(pvt, delay_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0); - if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, delay_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0); - gate_delay = makeDelay(delay, sigma_early, sigma_late); - - float slew = findValue(pvt, slew_model_.get(), in_slew, load_cap, 0.0); - if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(pvt, slew_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0); - if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, slew_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0); + if (delay_models_) + gate_delay = findValue(pvt, delay_models_->model(), in_slew, load_cap, 0.0); + else + gate_delay = 0.0; + if (slew_models_) + drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); + else + drvr_slew = 0.0; // Clip negative slews to zero. - if (slew < 0.0) - slew = 0.0; - drvr_slew = makeDelay(slew, sigma_early, sigma_late); + if (drvr_slew < 0.0) + drvr_slew = 0.0; } void -GateTableModel::gateDelay(const Pvt *pvt, - float in_slew, - float load_cap, - float, - bool pocv_enabled, - ArcDelay &gate_delay, - Slew &drvr_slew) const +GateTableModel::gateDelayPocv(const Pvt *pvt, + float in_slew, + float load_cap, + const MinMax *min_max, + PocvMode pocv_mode, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew) const { - gateDelay(pvt, in_slew, load_cap, pocv_enabled, gate_delay, drvr_slew); + switch (pocv_mode) { + case PocvMode::normal: { + // Delay + TableModel *std_dev_model = delay_models_->stdDev(); + if (std_dev_model == nullptr) + std_dev_model = delay_models_->sigma(min_max); + if (std_dev_model) { + float std_dev = findValue(pvt, std_dev_model, in_slew, load_cap, 0.0); + gate_delay.setStdDev(std_dev); + } + + // Slew + std_dev_model = slew_models_->stdDev(); + if (std_dev_model == nullptr) + std_dev_model = slew_models_->sigma(min_max); + if (std_dev_model) { + float std_dev = findValue(pvt, std_dev_model, in_slew, load_cap, 0.0); + drvr_slew.setStdDev(std_dev); + } + break; + } + case PocvMode::skew_normal: { + // Delay + if (delay_models_->meanShift()) { + float mean_shift = findValue(pvt, delay_models_->meanShift(), + in_slew, load_cap, 0.0); + gate_delay.setMeanShift(mean_shift); + } + + if (delay_models_->stdDev()) { + float std_dev = findValue(pvt, delay_models_->stdDev(), in_slew, load_cap, 0.0); + gate_delay.setStdDev(std_dev); + } + + if (delay_models_->skewness()) { + float skewness = findValue(pvt, delay_models_->skewness(), in_slew, load_cap, 0.0); + gate_delay.setSkewness(skewness); + } + + // Slew + if (slew_models_->meanShift()) { + float mean_shift = findValue(pvt, slew_models_->meanShift(), + in_slew, load_cap, 0.0); + drvr_slew.setMeanShift(mean_shift); + } + + if (slew_models_->stdDev()) { + float std_dev = findValue(pvt, slew_models_->stdDev(), in_slew, load_cap, 0.0); + drvr_slew.setStdDev(std_dev); + } + + if (slew_models_->skewness()) { + float skewness = findValue(pvt, slew_models_->skewness(), in_slew, load_cap, 0.0); + drvr_slew.setSkewness(skewness); + } + break; + } + default: + break; + } } std::string GateTableModel::reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const { std::string result = reportPvt(cell_, pvt, digits); - result += reportTableLookup("Delay", pvt, delay_model_.get(), in_slew, + result += reportTableLookup("Delay", pvt, delay_models_->model(), in_slew, load_cap, 0.0, digits); - if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableLookup("Delay sigma(early)", pvt, - delay_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0, digits); - if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) - result += reportTableLookup("Delay sigma(late)", pvt, - delay_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0, digits); - result += '\n'; - result += reportTableLookup("Slew", pvt, slew_model_.get(), in_slew, - load_cap, 9.0, digits); - if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableLookup("Slew sigma(early)", pvt, - slew_sigma_models_[EarlyLate::earlyIndex()], - in_slew, load_cap, 0.0, digits); - if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) - result += reportTableLookup("Slew sigma(late)", pvt, - slew_sigma_models_[EarlyLate::lateIndex()], - in_slew, load_cap, 0.0, digits); - float drvr_slew = findValue(pvt, slew_model_.get(), in_slew, load_cap, 0.0); + if (pocv_mode != PocvMode::scalar) { + if (delay_models_->sigma(min_max)) + result += reportTableLookup("Delay sigma(early)", pvt, + delay_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + if (delay_models_->sigma(EarlyLate::late())) + result += reportTableLookup("Delay sigma(late)", pvt, + delay_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + result += '\n'; + + result += reportTableLookup("Slew", pvt, slew_models_->model(), in_slew, + load_cap, 9.0, digits); + + if (slew_models_->sigma(EarlyLate::early())) + result += reportTableLookup("Slew sigma(early)", pvt, + slew_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + if (slew_models_->sigma(EarlyLate::late())) + result += reportTableLookup("Slew sigma(late)", pvt, + slew_models_->sigma(min_max), + in_slew, load_cap, 0.0, digits); + } + else { + result += '\n'; + result += reportTableLookup("Slew", pvt, slew_models_->model(), in_slew, + load_cap, 9.0, digits); + } + + float drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); if (drvr_slew < 0.0) result += "Negative slew clipped to 0.0\n"; return result; @@ -284,46 +332,30 @@ GateTableModel::driveResistance(const Pvt *pvt) const return slew / cap; } -const TableModel * -GateTableModel::delaySigmaModel(const EarlyLate *el) const -{ - return delay_sigma_models_[el->index()]; -} - -const TableModel * -GateTableModel::slewSigmaModel(const EarlyLate *el) const -{ - return slew_sigma_models_[el->index()]; -} - void GateTableModel::maxCapSlew(float in_slew, const Pvt *pvt, float &slew, float &cap) const { - if (!slew_model_) { - cap = 1.0; - slew = 0.0; - return; - } - const TableAxis *axis1 = slew_model_->axis1(); - const TableAxis *axis2 = slew_model_->axis2(); - const TableAxis *axis3 = slew_model_->axis3(); + TableModel *model = slew_models_->model(); + const TableAxis *axis1 = model->axis1(); + const TableAxis *axis2 = model->axis2(); + const TableAxis *axis3 = model->axis3(); if (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis1->axisValue(axis1->size() - 1); - slew = findValue(pvt, slew_model_.get(), in_slew, cap, 0.0); + slew = findValue(pvt, model, in_slew, cap, 0.0); } else if (axis2 && axis2->variable()==TableAxisVariable::total_output_net_capacitance) { cap = axis2->axisValue(axis2->size() - 1); - slew = findValue(pvt, slew_model_.get(), in_slew, cap, 0.0); + slew = findValue(pvt, model, in_slew, cap, 0.0); } else if (axis3 && axis3->variable()==TableAxisVariable::total_output_net_capacitance) { cap = axis3->axisValue(axis3->size() - 1); - slew = findValue(pvt, slew_model_.get(), in_slew, cap, 0.0); + slew = findValue(pvt, model, in_slew, cap, 0.0); } else { // Table not dependent on capacitance. @@ -416,38 +448,24 @@ ReceiverModel::checkAxes(const TableModel *table) //////////////////////////////////////////////////////////////// CheckTableModel::CheckTableModel(LibertyCell *cell, - TableModel *model, - TableModelsEarlyLate sigma_models) : + TableModels *check_models) : CheckTimingModel(cell), - model_(model), - sigma_models_(std::move(sigma_models)) + check_models_(check_models) { } -CheckTableModel::CheckTableModel(LibertyCell *cell, - TableModel *model) : - CheckTimingModel(cell), - model_(model), - sigma_models_{} -{ -} +CheckTableModel::~CheckTableModel() = default; -CheckTableModel::~CheckTableModel() +const TableModel * +CheckTableModel::checkModel() const { - sigmaModelsDelete(sigma_models_); + return check_models_ ? check_models_->model() : nullptr; } void CheckTableModel::setIsScaled(bool is_scaled) { - if (model_) - model_->setIsScaled(is_scaled); -} - -const TableModel * -CheckTableModel::sigmaModel(const EarlyLate *el) const -{ - return sigma_models_[el->index()]; + check_models_->model()->setIsScaled(is_scaled); } ArcDelay @@ -455,22 +473,51 @@ CheckTableModel::checkDelay(const Pvt *pvt, float from_slew, float to_slew, float related_out_cap, - bool pocv_enabled) const + const MinMax *min_max, + PocvMode pocv_mode) const { - if (model_) { - float mean = findValue(pvt, model_.get(), from_slew, to_slew, related_out_cap); - float sigma_early = 0.0; - float sigma_late = 0.0; - if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) - sigma_early = findValue(pvt, sigma_models_[EarlyLate::earlyIndex()], - from_slew, to_slew, related_out_cap); - if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, sigma_models_[EarlyLate::lateIndex()], - from_slew, to_slew, related_out_cap); - return makeDelay(mean, sigma_early, sigma_late); + ArcDelay check_delay; + if (check_models_) { + float margin = findValue(pvt, check_models_->model(), from_slew, + to_slew, related_out_cap); + check_delay.setMean(margin); + + switch (pocv_mode) { + case PocvMode::normal: { + TableModel *std_dev_model = check_models_->stdDev(); + if (std_dev_model == nullptr) + std_dev_model = check_models_->sigma(min_max); + if (std_dev_model) { + float std_dev = findValue(pvt, std_dev_model, from_slew, to_slew, related_out_cap); + check_delay.setStdDev(std_dev); + } + break; + } + case PocvMode::skew_normal: { + if (check_models_->meanShift()) { + float mean_shift = findValue(pvt, check_models_->meanShift(), + from_slew, to_slew, related_out_cap); + check_delay.setMeanShift(mean_shift); + } + + if (check_models_->stdDev()) { + float std_dev = findValue(pvt, check_models_->stdDev(), + from_slew, to_slew, related_out_cap); + check_delay.setStdDev(std_dev); + } + + if (check_models_->skewness()) { + float skewness = findValue(pvt, check_models_->skewness(), + from_slew, to_slew, related_out_cap); + check_delay.setSkewness(skewness); + } + break; + } + default: + break; + } } - else - return 0.0; + return check_delay; } float @@ -496,22 +543,28 @@ CheckTableModel::reportCheckDelay(const Pvt *pvt, const char *from_slew_annotation, float to_slew, float related_out_cap, - bool pocv_enabled, + const MinMax *min_max, + PocvMode pocv_mode, int digits) const { - std::string result = reportTableDelay("Check", pvt, model_.get(), - from_slew, from_slew_annotation, to_slew, - related_out_cap, digits); - if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) - result += reportTableDelay("Check sigma early", pvt, - sigma_models_[EarlyLate::earlyIndex()], - from_slew, from_slew_annotation, to_slew, - related_out_cap, digits); - if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) - result += reportTableDelay("Check sigma late", pvt, - sigma_models_[EarlyLate::lateIndex()], - from_slew, from_slew_annotation, to_slew, - related_out_cap, digits); + std::string result = reportTableDelay("Check", pvt, check_models_->model(), + from_slew, from_slew_annotation, to_slew, + related_out_cap, digits); + switch (pocv_mode) { + case PocvMode::normal: + case PocvMode::skew_normal: { + TableModel *check_table = check_models_->stdDev(); + if (check_table == nullptr) + check_table = check_models_->sigma(min_max); + if (check_table) + result += reportTableDelay("Check sigma", pvt, check_table, + from_slew, from_slew_annotation, to_slew, + related_out_cap, digits); + break; + } + default: + break; + } return result; } @@ -530,10 +583,11 @@ CheckTableModel::reportTableDelay(const char *result_name, findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, axis_value3); std::string result = reportPvt(cell_, pvt, digits); - result += model_->reportValue(result_name, cell_, pvt, - axis_value1, from_slew_annotation, axis_value2, - axis_value3, - cell_->libertyLibrary()->units()->timeUnit(), digits); + const Unit *time_unit = cell_->libertyLibrary()->units()->timeUnit(); + result += check_models_->model()->reportValue(result_name, cell_, pvt, + axis_value1, from_slew_annotation, + axis_value2, axis_value3, + time_unit, digits); return result; } return ""; @@ -548,31 +602,32 @@ CheckTableModel::findAxisValues(float from_slew, float &axis_value2, float &axis_value3) const { - switch (model_->order()) { + TableModel *model = check_models_->model(); + switch (model->order()) { case 0: axis_value1 = 0.0; axis_value2 = 0.0; axis_value3 = 0.0; break; case 1: - axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, + axis_value1 = axisValue(model->axis1(), from_slew, to_slew, related_out_cap); axis_value2 = 0.0; axis_value3 = 0.0; break; case 2: - axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, + axis_value1 = axisValue(model->axis1(), from_slew, to_slew, related_out_cap); - axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, + axis_value2 = axisValue(model->axis2(), from_slew, to_slew, related_out_cap); axis_value3 = 0.0; break; case 3: - axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, + axis_value1 = axisValue(model->axis1(), from_slew, to_slew, related_out_cap); - axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, + axis_value2 = axisValue(model->axis2(), from_slew, to_slew, related_out_cap); - axis_value3 = axisValue(model_->axis3(), from_slew, to_slew, + axis_value3 = axisValue(model->axis3(), from_slew, to_slew, related_out_cap); break; default: @@ -626,6 +681,75 @@ CheckTableModel::checkAxis(const TableAxis *axis) //////////////////////////////////////////////////////////////// +TableModels::TableModels() : + model_(nullptr), + sigma_{}, + std_dev_(nullptr), + mean_shift_(nullptr), + skewness_(nullptr) +{ +} + +TableModels::TableModels(TableModel *model) : + model_(model), + sigma_{}, + std_dev_(nullptr), + mean_shift_(nullptr), + skewness_(nullptr) +{ +} + +TableModels::~TableModels() +{ + TableModel *sigma_early = sigma_[EarlyLate::earlyIndex()]; + TableModel *sigma_late = sigma_[EarlyLate::lateIndex()]; + if (sigma_early == sigma_late) + delete sigma_early; + else { + delete sigma_early; + delete sigma_late; + } +} + +void +TableModels::setModel(TableModel *model) +{ + model_.reset(model); +} + +TableModel * +TableModels::sigma(const EarlyLate *early_late) const +{ + return sigma_[early_late->index()]; +} + +void +TableModels::setSigma(TableModel *table, + const EarlyLate *early_late) +{ + sigma_[early_late->index()] = table; +} + +void +TableModels::setMeanShift(TableModel *table) +{ + mean_shift_.reset(table); +} + +void +TableModels::setSkewness(TableModel *table) +{ + skewness_.reset(table); +} + +void +TableModels::setStdDev(TableModel *table) +{ + std_dev_.reset(table); +} + +//////////////////////////////////////////////////////////////// + TableModel::TableModel() : table_(nullptr), tbl_template_(nullptr), @@ -959,51 +1083,65 @@ Table::findValue(float axis_value1, return value_; if (order_ == 1) return findValue(axis_value1); - if (order_ == 2) { - size_t size1 = axis1_->size(); - size_t size2 = axis2_->size(); - if (size1 == 1) { - if (size2 == 1) - return value(0, 0); - size_t axis_index2 = axis2_->findAxisIndex(axis_value2); - double x2 = axis_value2; - double y00 = value(0, axis_index2); - double x2l = axis2_->axisValue(axis_index2); - double x2u = axis2_->axisValue(axis_index2 + 1); - double dx2 = (x2 - x2l) / (x2u - x2l); - double y01 = value(0, axis_index2 + 1); - return (1 - dx2) * y00 + dx2 * y01; - } - if (size2 == 1) { - size_t axis_index1 = axis1_->findAxisIndex(axis_value1); - double x1 = axis_value1; - double y00 = value(axis_index1, 0); - double x1l = axis1_->axisValue(axis_index1); - double x1u = axis1_->axisValue(axis_index1 + 1); - double dx1 = (x1 - x1l) / (x1u - x1l); - double y10 = value(axis_index1 + 1, 0); - return (1 - dx1) * y00 + dx1 * y10; - } - size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + if (order_ == 2) + return findValueOrder2(axis_value1, axis_value2); + else + return findValueOrder3(axis_value1, axis_value2, axis_value3); +} + +float +Table::findValueOrder2(float axis_value1, + float axis_value2) const +{ + size_t size1 = axis1_->size(); + size_t size2 = axis2_->size(); + if (size1 == 1) { + if (size2 == 1) + return value(0, 0); size_t axis_index2 = axis2_->findAxisIndex(axis_value2); - double x1 = axis_value1; double x2 = axis_value2; - double y00 = value(axis_index1, axis_index2); - double x1l = axis1_->axisValue(axis_index1); - double x1u = axis1_->axisValue(axis_index1 + 1); - double dx1 = (x1 - x1l) / (x1u - x1l); - double y10 = value(axis_index1 + 1, axis_index2); - double y11 = value(axis_index1 + 1, axis_index2 + 1); + double y00 = value(0, axis_index2); double x2l = axis2_->axisValue(axis_index2); double x2u = axis2_->axisValue(axis_index2 + 1); double dx2 = (x2 - x2l) / (x2u - x2l); - double y01 = value(axis_index1, axis_index2 + 1); - return (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + double y01 = value(0, axis_index2 + 1); + return (1 - dx2) * y00 + dx2 * y01; } - // order_ == 3 - trilinear interpolation + if (size2 == 1) { + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + double x1 = axis_value1; + double y00 = value(axis_index1, 0); + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + double y10 = value(axis_index1 + 1, 0); + return (1 - dx1) * y00 + dx1 * y10; + } + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + double x1 = axis_value1; + double x2 = axis_value2; + double y00 = value(axis_index1, axis_index2); + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + double y10 = value(axis_index1 + 1, axis_index2); + double y11 = value(axis_index1 + 1, axis_index2 + 1); + double x2l = axis2_->axisValue(axis_index2); + double x2u = axis2_->axisValue(axis_index2 + 1); + double dx2 = (x2 - x2l) / (x2u - x2l); + double y01 = value(axis_index1, axis_index2 + 1); + return (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; +} + +float +Table::findValueOrder3(float axis_value1, + float axis_value2, + float axis_value3) const +{ size_t axis_index1 = axis1_->findAxisIndex(axis_value1); size_t axis_index2 = axis2_->findAxisIndex(axis_value2); size_t axis_index3 = axis3_->findAxisIndex(axis_value3); diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 5d77e931..375ad68e 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -152,9 +152,8 @@ TimingArc::intrinsicDelay() const { GateTimingModel *model = dynamic_cast(model_); if (model) { - ArcDelay arc_delay; - Slew slew; - model->gateDelay(nullptr, 0.0, 0.0, false, arc_delay, slew); + float arc_delay, slew; + model->gateDelay(nullptr, 0.0, 0.0, arc_delay, slew); return arc_delay; } else diff --git a/power/Power.cc b/power/Power.cc index eb8d40cc..7a3de2af 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1173,7 +1173,7 @@ Power::findInputInternalPower(const Pin *pin, int rf_count = 0; for (const RiseFall *rf : RiseFall::range()) { float slew = getSlew(vertex, rf, scene); - if (!delayInf(slew)) { + if (!delayInf(slew, this)) { float table_energy = pwr->power(rf, pvt, slew, load_cap); energy += table_energy; rf_count++; @@ -1238,17 +1238,18 @@ Power::getMinRfSlew(const Pin *pin) graph_->pinVertices(pin, vertex, bidir_vertex); if (vertex) { const MinMax *min_max = MinMax::min(); - Slew mm_slew = min_max->initValue(); + float mm_slew = min_max->initValue(); for (DcalcAPIndex ap_index = 0; ap_index < dcalcAnalysisPtCount(); ap_index++) { const Slew &slew1 = graph_->slew(vertex, RiseFall::rise(), ap_index); const Slew &slew2 = graph_->slew(vertex, RiseFall::fall(), ap_index); - Slew slew = delayAsFloat(slew1 + slew2) / 2.0; - if (delayGreater(slew, mm_slew, min_max, this)) - mm_slew = slew; + float slew_avg = (delayAsFloat(slew1, min_max, this) + + delayAsFloat(slew2, min_max, this)) / 2.0; + if (delayGreater(slew_avg, mm_slew, min_max, this)) + mm_slew = slew_avg; } - return delayAsFloat(mm_slew); + return mm_slew; } return 0.0; } @@ -1343,7 +1344,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, float slew = from_vertex ? getSlew(from_vertex, from_rf, scene) : 0.0; - if (!delayInf(slew)) { + if (!delayInf(slew, this)) { float table_energy = pwr->power(to_rf, pvt, slew, load_cap); energy += table_energy; rf_count++; diff --git a/sdc/Variables.cc b/sdc/Variables.cc index 6aae9894..f877fc45 100644 --- a/sdc/Variables.cc +++ b/sdc/Variables.cc @@ -39,7 +39,8 @@ Variables::Variables() : dynamic_loop_breaking_(false), propagate_all_clks_(false), use_default_arrival_clock_(false), - pocv_enabled_(false) + pocv_mode_(PocvMode::scalar), + pocv_quantile_(3.0) { } @@ -115,10 +116,24 @@ Variables::setUseDefaultArrivalClock(bool enable) use_default_arrival_clock_ = enable; } -void -Variables::setPocvEnabled(bool enabled) +//////////////////////////////////////////////////////////////// + +bool +Variables::pocvEnabled() const { - pocv_enabled_ = enabled; + return pocv_mode_ != PocvMode::scalar; } - + +void +Variables::setPocvMode(PocvMode mode) +{ + pocv_mode_ = mode; +} + +void +Variables::setPocvQuantile(float quantile) +{ + pocv_quantile_ = quantile; +} + } // namespace diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 93804d87..81818127 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -735,7 +735,7 @@ SdfReader::setEdgeArcDelays(Edge *edge, if (value_ptr) { ArcDelay delay; if (in_incremental_) - delay = *value_ptr + graph_->arcDelay(edge, arc, arc_delay_index); + delay = delaySum(graph_->arcDelay(edge, arc, arc_delay_index), *value_ptr, this); else delay = *value_ptr; graph_->setArcDelay(edge, arc, arc_delay_index, delay); @@ -784,7 +784,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, && triple_index != null_index_) { ArcDelay delay(*value); if (!is_incremental_only_ && in_incremental_) - delay = graph_->arcDelay(edge, arc, arc_delay_index) + *value; + delay = delaySum(graph_->arcDelay(edge, arc, arc_delay_index), *value, this); 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, this)) diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 6cc3cd1d..db7af6b0 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -430,11 +430,11 @@ SdfWriter::writeArcDelays(Edge *edge) TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { const RiseFall *rf = arc->toEdge()->asRiseFall(); - ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); - delays.setValue(rf, MinMax::min(), delayAsFloat(min_delay)); + const ArcDelay &min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); + delays.setValue(rf, MinMax::min(), delayAsFloat(min_delay, MinMax::min(), this)); - ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); - delays.setValue(rf, MinMax::max(), delayAsFloat(max_delay)); + const ArcDelay &max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); + delays.setValue(rf, MinMax::max(), delayAsFloat(max_delay, MinMax::max(), this)); } if (delays.hasValue(RiseFall::rise(), MinMax::min()) @@ -527,8 +527,10 @@ SdfWriter::writeTimingChecks(const Instance *inst, TimingArc *arc; graph_->minPulseWidthArc(vertex, hi_low, edge, arc); if (edge) { - min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_)); - max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_)); + min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_), + MinMax::min(), this); + max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_), + MinMax::max(), this); ensureTimingCheckheaders(check_header, inst, inst_header); writeWidthCheck(pin, hi_low, min_width, max_width); } @@ -615,30 +617,37 @@ SdfWriter::writeEdgeCheck(Edge *edge, if (arcs[clk_rf_index][RiseFall::riseIndex()] && arcs[clk_rf_index][RiseFall::fallIndex()] && arcs[clk_rf_index][RiseFall::riseIndex()] - && arcs[clk_rf_index][RiseFall::fallIndex()] - && 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); - else { - if (arcs[clk_rf_index][RiseFall::riseIndex()]) - writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], - sdf_check, true, true); - if (arcs[clk_rf_index][RiseFall::fallIndex()]) - writeCheck(edge, arcs[clk_rf_index][RiseFall::fallIndex()], - sdf_check, true, true); + && arcs[clk_rf_index][RiseFall::fallIndex()]) { + float rise_min=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::riseIndex()], + arc_delay_min_index_), + MinMax::min(), this); + float fall_min=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::fallIndex()], + arc_delay_min_index_), + MinMax::min(), this); + float rise_max=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::riseIndex()], + arc_delay_max_index_), + MinMax::max(), this); + float fall_max=delayAsFloat(graph_->arcDelay(edge, + arcs[clk_rf_index][RiseFall::fallIndex()], + arc_delay_max_index_), + MinMax::max(), this); + if (fuzzyEqual(rise_min, fall_min) + && fuzzyEqual(rise_max, fall_max)) { + // 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); + return; + } } + if (arcs[clk_rf_index][RiseFall::riseIndex()]) + writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], + sdf_check, true, true); + if (arcs[clk_rf_index][RiseFall::fallIndex()]) + writeCheck(edge, arcs[clk_rf_index][RiseFall::fallIndex()], + sdf_check, true, true); } void @@ -689,9 +698,11 @@ SdfWriter::writeCheck(Edge *edge, gzprintf(stream_, " "); - ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); - ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); - writeSdfTriple(delayAsFloat(min_delay), delayAsFloat(max_delay)); + float min_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_), + MinMax::min(), this); + float max_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_), + MinMax::max(), this); + writeSdfTriple(min_delay, max_delay); gzprintf(stream_, ")\n"); } diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index 8682f6f1..20cb5224 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -178,15 +178,15 @@ MaxSkewCheck::maxSkew(const StaState *sta) const } Delay -MaxSkewCheck::skew() const +MaxSkewCheck::skew(const StaState *sta) const { - return Delay(clk_path_->arrival() - ref_path_->arrival()); + return delayDiff(clk_path_->arrival(), ref_path_->arrival(), sta); } Slack MaxSkewCheck::slack(const StaState *sta) const { - return maxSkew(sta) - skew(); + return delayDiff(maxSkew(sta), skew(sta), sta); } //////////////////////////////////////////////////////////////// @@ -203,7 +203,7 @@ MaxSkewSlackLess::operator()(const MaxSkewCheck &check1, Slack slack1 = check1.slack(sta_); Slack slack2 = check2.slack(sta_); return delayLess(slack1, slack2, sta_) - || (delayEqual(slack1, slack2) + || (delayEqual(slack1, slack2, sta_) // Break ties based on constrained pin names. && sta_->network()->pinLess(check1.clkPin(sta_), check2.clkPin(sta_))); } diff --git a/search/CheckMaxSkews.hh b/search/CheckMaxSkews.hh index ac1660cc..dc64f0c0 100644 --- a/search/CheckMaxSkews.hh +++ b/search/CheckMaxSkews.hh @@ -48,7 +48,7 @@ public: Pin *clkPin(const StaState *sta) const; const Path *refPath() const { return ref_path_; } Pin *refPin(const StaState *sta) const; - Delay skew() const; + Delay skew(const StaState *sta) const; ArcDelay maxSkew(const StaState *sta) const; Slack slack(const StaState *sta) const; TimingArc *checkArc() const { return check_arc_; } diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index bb9b9282..195187c5 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -198,7 +198,7 @@ MinPeriodSlackLess::operator()(const MinPeriodCheck &check1, const Pin *pin2 = check2.pin(); return delayLess(slack1, slack2, sta_) // Break ties based on pin and clock names. - || (delayEqual(slack1, slack2) + || (delayEqual(slack1, slack2, sta_) && (sta_->network()->pinLess(pin1, pin2) || (pin1 == pin2 && ClockNameLess()(check1.clk(), diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index 0f7ff320..12c795ea 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -252,13 +252,13 @@ MinPulseWidthCheck::closeArrival(const StaState *sta) const Arrival MinPulseWidthCheck::openDelay(const StaState *sta) const { - return openArrival(sta) - openClkEdge(sta)->time(); + return delayDiff(openArrival(sta), openClkEdge(sta)->time(), sta); } Arrival MinPulseWidthCheck::closeDelay(const StaState *sta) const { - return closeArrival(sta) - closeClkEdge(sta)->time(); + return delayDiff(closeArrival(sta), closeClkEdge(sta)->time(), sta); } const ClockEdge * @@ -289,9 +289,11 @@ MinPulseWidthCheck::closeOffset(const StaState *sta) const Arrival MinPulseWidthCheck::width(const StaState *sta) const { - return closeArrival(sta) + closeOffset(sta) - - open_path_->arrival() - + checkCrpr(sta); + Arrival close_with_offset = delaySum(closeArrival(sta), + closeOffset(sta), + sta); + Arrival minus_open = delayDiff(close_with_offset, open_path_->arrival(), sta); + return delaySum(minus_open, checkCrpr(sta), sta); } float @@ -323,6 +325,7 @@ minPulseWidth(const Path *path, // set_min_pulse_width command. sdc->minPulseWidth(pin, clk, rf, min_width, exists); if (!exists) { + const MinMax *min_max = path->minMax(sta); DcalcAPIndex dcalc_ap = path->dcalcAnalysisPtIndex(sta); Vertex *vertex = path->vertex(sta); Graph *graph = sta->graph(); @@ -330,7 +333,7 @@ minPulseWidth(const Path *path, TimingArc *arc; graph->minPulseWidthArc(vertex, rf, edge, arc); if (edge) { - min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap)); + min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap), min_max, sta); exists = true; } } @@ -350,7 +353,7 @@ MinPulseWidthCheck::checkCrpr(const StaState *sta) const Slack MinPulseWidthCheck::slack(const StaState *sta) const { - return width(sta) - minWidth(sta); + return delayDiff(width(sta), minWidth(sta), sta); } Scene * @@ -375,7 +378,7 @@ MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck &check1, const Pin *pin1 = check1.pin(sta_); const Pin *pin2 = check2.pin(sta_); return delayLess(slack1, slack2, sta_) - || (delayEqual(slack1, slack2) + || (delayEqual(slack1, slack2, sta_) // Break ties for the sake of regression stability. && (sta_->network()->pinLess(pin1, pin2) || (pin1 == pin2 diff --git a/search/ClkDelays.hh b/search/ClkDelays.hh index 2b9d6a36..e8a15749 100644 --- a/search/ClkDelays.hh +++ b/search/ClkDelays.hh @@ -61,9 +61,9 @@ public: StaState *sta); private: - static float insertionDelay(Path *clk_path, + static Delay insertionDelay(Path *clk_path, StaState *sta); - static float delay(Path *clk_path, + static Delay delay(Path *clk_path, StaState *sta); static float clkTreeDelay(Path *clk_path, StaState *sta); diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc index 767a44a1..e3e3a05a 100644 --- a/search/ClkInfo.cc +++ b/search/ClkInfo.cc @@ -102,7 +102,7 @@ ClkInfo::findHash(const StaState *sta) hashIncr(hash_, hash_float(uncertainty)); } hashIncr(hash_, hash_float(latency_)); - hashIncr(hash_, hash_float(delayAsFloat(insertion_))); + hashIncr(hash_, hash_float(insertion_.mean())); hashIncr(hash_, is_propagated_); hashIncr(hash_, is_gen_clk_src_path_); hashIncr(hash_, is_pulse_clk_); @@ -152,9 +152,10 @@ ClkInfo::to_string(const StaState *sta) const Network *network = sta->network(); std::string result; + const MinMax *min_max = minMax(); result += scene_->name(); result += "/"; - result += minMax()->to_string(); + result += min_max->to_string(); result += " "; if (clk_edge_) @@ -186,7 +187,7 @@ ClkInfo::to_string(const StaState *sta) const if (delayGreater(insertion_, 0.0, sta)) { result += " insert"; - result += delayAsString(insertion_, sta); + result += delayAsString(insertion_, min_max, sta); } if (uncertainties_) { diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index 0c186704..b7b4bac5 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -116,15 +116,15 @@ ClkLatency::reportClkLatency(const Clock *clk, report_->reportLine(" min max"); report_->reportLine("%7s %7s source latency", - delayAsString(insertion_min, this, digits), - delayAsString(insertion_max, this, digits)); + delayAsString(insertion_min, MinMax::min(), digits, this), + delayAsString(insertion_max, MinMax::max(), digits, this)); report_->reportLine("%7s %7s network latency %s", - delayAsString(delay_min, this, digits), + delayAsString(delay_min, MinMax::min(), digits, this), "", sdc_network_->pathName(path_min.pin(this))); report_->reportLine("%7s %7s network latency %s", "", - delayAsString(delay_max, this, digits), + delayAsString(delay_max, MinMax::max(), digits, this), sdc_network_->pathName(path_max.pin(this))); if (internal_latency_min != 0.0 || internal_latency_max != 0.0) @@ -133,11 +133,11 @@ ClkLatency::reportClkLatency(const Clock *clk, time_unit->asString(internal_latency_max, digits)); report_->reportLine("---------------"); report_->reportLine("%7s %7s latency", - delayAsString(latency_min, this, digits), - delayAsString(latency_max, this, digits)); - Delay skew = latency_max - latency_min; + delayAsString(latency_min, MinMax::min(), digits, this), + delayAsString(latency_max, MinMax::max(), digits, this)); + Delay skew = delayDiff(latency_max, latency_min, this); report_->reportLine(" %7s skew", - delayAsString(skew, this, digits)); + delayAsString(skew, MinMax::max(), digits, this)); report_->reportBlankLine(); } } @@ -255,10 +255,10 @@ ClkDelays::setLatency(const RiseFall *src_rf, int end_rf_index = end_rf->index(); int mm_index = min_max->index(); - float insertion = insertionDelay(path, sta); + Delay insertion = insertionDelay(path, sta); insertion_[src_rf_index][end_rf_index][mm_index] = insertion; - float delay1 = delay(path, sta); + Delay delay1 = delay(path, sta); delay_[src_rf_index][end_rf_index][mm_index] = delay1; float internal_latency = 0.0; @@ -267,7 +267,7 @@ ClkDelays::setLatency(const RiseFall *src_rf, internal_latency_[src_rf_index][end_rf_index][mm_index] = internal_latency; } - float latency = insertion + delay1 + internal_latency; + Delay latency = delaySum(delay1, delaySum(insertion, internal_latency, sta), sta); latency_[src_rf_index][end_rf_index][mm_index] = latency; path_[src_rf_index][end_rf_index][mm_index] = *path; @@ -279,22 +279,22 @@ ClkDelays::latency(Path *clk_path, StaState *sta) { - float insertion = insertionDelay(clk_path, sta); - float delay1 = delay(clk_path, sta); + Delay insertion = insertionDelay(clk_path, sta); + Delay delay1 = delay(clk_path, sta); float lib_clk_delay = clkTreeDelay(clk_path, sta); - return insertion + delay1 + lib_clk_delay; + return delaySum(delay1, delaySum(insertion, lib_clk_delay, sta), sta); } -float +Delay ClkDelays::delay(Path *clk_path, StaState *sta) { Arrival arrival = clk_path->arrival(); const ClockEdge *path_clk_edge = clk_path->clkEdge(sta); - return delayAsFloat(arrival) - path_clk_edge->time(); + return delayDiff(arrival, path_clk_edge->time(), sta); } -float +Delay ClkDelays::insertionDelay(Path *clk_path, StaState *sta) { @@ -305,8 +305,7 @@ ClkDelays::insertionDelay(Path *clk_path, const Pin *src_pin = clk_info->clkSrc(); const MinMax *min_max = clk_path->minMax(sta); const Mode *mode = clk_path->mode(sta); - return delayAsFloat(sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max, - min_max, mode)); + return sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max, min_max, mode); } float @@ -318,7 +317,7 @@ ClkDelays::clkTreeDelay(Path *clk_path, const LibertyPort *port = sta->network()->libertyPort(pin); const MinMax *min_max = clk_path->minMax(sta); const RiseFall *rf = clk_path->transition(sta); - float slew = delayAsFloat(clk_path->slew(sta)); + float slew = delayAsFloat(clk_path->slew(sta), min_max, sta); return port->clkTreeDelay(slew, rf, min_max); } diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index ddbeb397..56590fc4 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -95,16 +95,17 @@ ClkSkews::reportClkSkew(ClkSkew &clk_skew, Unit *time_unit = units_->timeUnit(); Path *src_path = clk_skew.srcPath(); Path *tgt_path = clk_skew.tgtPath(); - float src_latency = clk_skew.srcLatency(this); + const MinMax *src_min_max = src_path->minMax(this); + Arrival src_latency = clk_skew.srcLatency(this); float tgt_latency = clk_skew.tgtLatency(this); float src_internal_clk_latency = clk_skew.srcInternalClkLatency(this); float tgt_internal_clk_latency = clk_skew.tgtInternalClkLatency(this); float uncertainty = clk_skew.uncertainty(this); if (src_internal_clk_latency != 0.0) - src_latency -= src_internal_clk_latency; + delayDecr(src_latency, src_internal_clk_latency, this); report_->reportLine("%7s source latency %s %s", - time_unit->asString(src_latency, digits), + delayAsString(src_latency, src_min_max, digits, this), sdc_network_->pathName(src_path->pin(this)), src_path->transition(this)->shortName()); if (src_internal_clk_latency != 0.0) @@ -124,15 +125,32 @@ ClkSkews::reportClkSkew(ClkSkew &clk_skew, report_->reportLine("%7s clock uncertainty", time_unit->asString(uncertainty, digits)); report_->reportLine("%7s CRPR", - time_unit->asString(delayAsFloat(-clk_skew.crpr(this)), - digits)); + delayAsString(delayDiff(0.0, clk_skew.crpr(this), this), + MinMax::max(), digits, this)); report_->reportLine("--------------"); report_->reportLine("%7s %s skew", - time_unit->asString(clk_skew.skew(), digits), + delayAsString(clk_skew.skew(), MinMax::max(), digits, this), src_path->minMax(this) == MinMax::max() ? "setup" : "hold"); } -float +static float +delayAbsMax(const Delay &delay, + const StaState *sta) +{ + float min = delayAsFloat(delay, MinMax::min(), sta); + float max = delayAsFloat(delay, MinMax::max(), sta); + return std::max(std::abs(min), std::abs(max)); +} + +static bool +delayAbsGreater(const Delay &delay1, + const Delay &delay2, + const StaState *sta) +{ + return delayAbsMax(delay1, sta) > delayAbsMax(delay2, sta); +} + +Delay ClkSkews::findWorstClkSkew(const SceneSeq &scenes, const SetupHold *setup_hold, bool include_internal_latency) @@ -143,10 +161,10 @@ ClkSkews::findWorstClkSkew(const SceneSeq &scenes, clks.push_back(clk); } findClkSkew(clks, scenes, include_internal_latency); - float worst_skew = 0.0; + Delay worst_skew = 0.0; for (const auto& [clk, clk_skews] : skews_) { - float skew = clk_skews[setup_hold->index()].skew(); - if (std::abs(skew) > std::abs(worst_skew)) + Delay skew = clk_skews[setup_hold->index()].skew(); + if (delayAbsGreater(skew, worst_skew, this)) worst_skew = skew; } return worst_skew; @@ -206,10 +224,12 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, for (int setup_hold_idx : SetupHold::rangeIndex()) { ClkSkew &final_skew = itr->second[setup_hold_idx]; ClkSkew &partial_skew_val = partial_skew[setup_hold_idx]; - float partial_skew1 = partial_skew_val.skew(); - float final_skew1 = final_skew.skew(); - if (std::abs(partial_skew1) > std::abs(final_skew1) - || (fuzzyEqual(std::abs(partial_skew1), std::abs(final_skew1)) + Delay partial_skew1 = partial_skew_val.skew(); + Delay final_skew1 = final_skew.skew(); + float partial_skew_max = delayAbsMax(partial_skew1, this); + float final_skew_max = delayAbsMax(final_skew1, this); + if (partial_skew_max > final_skew_max + || (fuzzyEqual(partial_skew_max, final_skew_max) // Break ties based on source/target path names. && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this))) final_skew = partial_skew_val; @@ -299,7 +319,8 @@ ClkSkews::findClkSkew(Vertex *src_vertex, && src_rf->matches(src_path->transition(this)) && clk_set_.contains(src_clk) && scenes_set_.contains(src_scene)) { - const MinMax *tgt_min_max = src_path->minMax(this)->opposite(); + const MinMax *src_min_max = src_path->minMax(this); + const MinMax *tgt_min_max = src_min_max->opposite(); VertexPathIterator tgt_iter(tgt_vertex, this); while (tgt_iter.hasNext()) { Path *tgt_path = tgt_iter.next(); @@ -316,14 +337,14 @@ ClkSkews::findClkSkew(Vertex *src_vertex, "%s %s %s -> %s %s %s crpr = %s skew = %s", network_->pathName(src_path->pin(this)), src_path->transition(this)->shortName(), - time_unit->asString(probe.srcLatency(this)), + delayAsString(probe.srcLatency(this), src_min_max, this), network_->pathName(tgt_path->pin(this)), tgt_path->transition(this)->shortName(), time_unit->asString(probe.tgtLatency(this)), delayAsString(probe.crpr(this), this), - time_unit->asString(probe.skew())); + delayAsString(probe.skew(), MinMax::max(), this)); if (clk_skew.srcPath() == nullptr - || std::abs(probe.skew()) > std::abs(clk_skew.skew())) + || delayAbsGreater(probe.skew(), clk_skew.skew(), this)) clk_skew = probe; } } @@ -380,10 +401,8 @@ ClkSkew::ClkSkew(Path *src_path, tgt_path_(tgt_path), include_internal_latency_(include_internal_latency) { - skew_ = srcLatency(sta) - - tgtLatency(sta) - - delayAsFloat(crpr(sta)) - + uncertainty(sta); + skew_ = delayDiff(delaySum(srcLatency(sta), uncertainty(sta), sta), + delaySum(tgtLatency(sta), crpr(sta), sta), sta); } ClkSkew::ClkSkew(const ClkSkew &clk_skew) @@ -403,12 +422,11 @@ ClkSkew::operator=(const ClkSkew &clk_skew) skew_ = clk_skew.skew_; } -float +Arrival ClkSkew::srcLatency(const StaState *sta) { - Arrival src_arrival = src_path_->arrival(); - return delayAsFloat(src_arrival) - src_path_->clkEdge(sta)->time() - + clkTreeDelay(src_path_, sta); + return delayDiff(delaySum(src_path_->arrival(), clkTreeDelay(src_path_, sta), sta), + src_path_->clkEdge(sta)->time(), sta); } float @@ -421,8 +439,8 @@ float ClkSkew::tgtLatency(const StaState *sta) { Arrival tgt_arrival = tgt_path_->arrival(); - return delayAsFloat(tgt_arrival) - tgt_path_->clkEdge(sta)->time() - + clkTreeDelay(tgt_path_, sta); + return delayAsFloat(delaySum(delayDiff(tgt_arrival, tgt_path_->clkEdge(sta)->time(),sta), + clkTreeDelay(tgt_path_, sta), sta)); } float @@ -441,7 +459,7 @@ ClkSkew::clkTreeDelay(Path *clk_path, const LibertyPort *port = sta->network()->libertyPort(pin); const MinMax *min_max = clk_path->minMax(sta); const RiseFall *rf = clk_path->transition(sta); - float slew = delayAsFloat(clk_path->slew(sta)); + float slew = delayAsFloat(clk_path->slew(sta), min_max, sta); return port->clkTreeDelay(slew, rf, min_max); } else diff --git a/search/ClkSkew.hh b/search/ClkSkew.hh index 349589e3..1ce1cf43 100644 --- a/search/ClkSkew.hh +++ b/search/ClkSkew.hh @@ -52,13 +52,13 @@ public: void operator=(const ClkSkew &clk_skew); Path *srcPath() { return src_path_; } Path *tgtPath() { return tgt_path_; } - float srcLatency(const StaState *sta); + Arrival srcLatency(const StaState *sta); float tgtLatency(const StaState *sta); float srcInternalClkLatency(const StaState *sta); float tgtInternalClkLatency(const StaState *sta); Crpr crpr(const StaState *sta); float uncertainty(const StaState *sta); - float skew() const { return skew_; } + Delay skew() const { return skew_; } static bool srcTgtPathNameLess(ClkSkew &clk_skew1, ClkSkew &clk_skew2, const StaState *sta); @@ -70,7 +70,7 @@ private: Path *src_path_; Path *tgt_path_; bool include_internal_latency_; - float skew_; + Delay skew_; }; using ClkSkewMap = std::map; @@ -97,7 +97,7 @@ public: bool include_internal_latency, int digits); // Find worst clock skew between src/target registers. - float findWorstClkSkew(const SceneSeq &scenes, + Delay findWorstClkSkew(const SceneSeq &scenes, const SetupHold *setup_hold, bool include_internal_latency); diff --git a/search/Crpr.cc b/search/Crpr.cc index 4d1bc3a0..21361ed2 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -273,19 +273,16 @@ CheckCrpr::findCrpr1(const Path *src_clk_path, if (variables_->pocvEnabled()) { // Remove variation on the common path. // Note that the crpr sigma is negative to offset the - // sigma of the common clock path. - const EarlyLate *src_el = src_clk_path->minMax(this); - const EarlyLate *tgt_el = tgt_clk_path->minMax(this); - Arrival src_arrival = src_clk_path->arrival(); - Arrival tgt_arrival = tgt_clk_path->arrival(); + // std_dev of the common clock path. + const Arrival &src_arrival = src_clk_path->arrival(); + const Arrival &tgt_arrival = tgt_clk_path->arrival(); float src_clk_time = src_clk_path->clkEdge(this)->time(); float tgt_clk_time = tgt_clk_path->clkEdge(this)->time(); - float crpr_mean = std::abs(delayAsFloat(src_arrival) - src_clk_time - - (delayAsFloat(tgt_arrival) - tgt_clk_time)); + float crpr_mean = std::abs(src_arrival.mean() - src_clk_time + - (tgt_arrival.mean() - 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); + float crpr_sigma2 = src_arrival.stdDev2() + tgt_arrival.stdDev2(); + return makeDelay2(crpr_mean, -crpr_sigma2); } else { // The source and target edges are different so the crpr @@ -308,8 +305,9 @@ float CheckCrpr::crprArrivalDiff(const Path *path) { Arrival other_arrival = otherMinMaxArrival(path); - float crpr_diff = std::abs(delayAsFloat(path->arrival()) - - delayAsFloat(other_arrival)); + const MinMax *min_max = path->minMax(this); + float crpr_diff = std::abs(delayAsFloat(path->arrival(), min_max, this) + - delayAsFloat(other_arrival, min_max->opposite(), this)); return crpr_diff; } diff --git a/search/Latches.cc b/search/Latches.cc index 8389c9c1..16bde088 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -70,7 +70,7 @@ Latches::latchRequired(const Path *data_path, ignore_clk_latency = path_delay->ignoreClkLatency(); } if (ignore_clk_latency) { - required = max_delay + src_clk_latency; + required = delaySum(src_clk_latency, max_delay, this); borrow = 0.0; adjusted_data_arrival = data_arrival; time_given_to_startpoint = 0.0; @@ -98,13 +98,15 @@ Latches::latchRequired(const Path *data_path, // checkTgtClkTime float tgt_clk_time = path_delay ? 0.0 : acct->requiredTime(check_role); // checkTgtClkArrival broken down into components. - Arrival enable_arrival = max_delay - + tgt_clk_time - + open_latency - + open_uncertainty - + PathEnd::checkSetupMcpAdjustment(data_clk_edge, enable_clk_edge, mcp, - 1, sdc) - + open_crpr; + Arrival enable_arrival = delaySum(max_delay + + tgt_clk_time + + open_uncertainty + + PathEnd::checkSetupMcpAdjustment(data_clk_edge, + enable_clk_edge, + mcp, 1, sdc), + open_latency, + this); + enable_arrival = delaySum(enable_arrival, open_crpr, this); debugPrint(debug_, "latch", 1, "data %s enable %s", delayAsString(data_arrival, this), delayAsString(enable_arrival, this)); @@ -117,25 +119,31 @@ Latches::latchRequired(const Path *data_path, } else { // Data arrives while latch is transparent. - borrow = data_arrival - enable_arrival; + borrow = delayDiff(data_arrival, enable_arrival, this); if (delayLessEqual(borrow, max_borrow, this)) required = data_arrival; else { borrow = max_borrow; - required = enable_arrival + max_borrow; + required = delaySum(enable_arrival, max_borrow, this); } - time_given_to_startpoint = borrow + open_uncertainty + open_crpr; + time_given_to_startpoint = delaySum(delaySum(borrow, + open_uncertainty, + this), + open_crpr, this); // Cycle accounting for required time is with respect to the // data clock zeroth cycle. The data departs the latch // with respect to the enable clock zeroth cycle. float data_shift_to_enable_clk = acct->sourceTimeOffset(check_role) - acct->targetTimeOffset(check_role); - adjusted_data_arrival = required + data_shift_to_enable_clk; + adjusted_data_arrival = delaySum(required, data_shift_to_enable_clk, this); } } else if (disable_path) { - required = max_delay + search_->clkPathArrival(disable_path) - margin; + required = delayDiff(delaySum(max_delay, + search_->clkPathArrival(disable_path), + this), + margin, this); // Borrow cannot be determined without enable path. borrow = 0.0; adjusted_data_arrival = data_arrival; @@ -176,7 +184,7 @@ Latches::latchBorrowInfo(const Path *data_path, const ClockEdge *enable_clk_edge = enable_path->clkEdge(this); const ClockEdge *disable_clk_edge = disable_path->clkEdge(this); bool is_pulse_clk = enable_path->clkInfo(this)->isPulseClk(); - nom_pulse_width = is_pulse_clk ? 0.0F : enable_clk_edge->pulseWidth(); + nom_pulse_width = is_pulse_clk ? 0.0 : enable_clk_edge->pulseWidth(); open_uncertainty = PathEnd::checkClkUncertainty(data_clk_edge, enable_clk_edge, enable_path, TimingRole::latchSetup(), sdc); @@ -190,14 +198,14 @@ Latches::latchBorrowInfo(const Path *data_path, CheckCrpr *check_crpr = search_->checkCrpr(); open_crpr = check_crpr->checkCrpr(data_path, enable_path); Crpr close_crpr = check_crpr->checkCrpr(data_path, disable_path); - crpr_diff = open_crpr - close_crpr; + crpr_diff = delayDiff(open_crpr, close_crpr, this); open_latency = PathEnd::checkTgtClkDelay(enable_path, enable_clk_edge, TimingRole::setup(), this); Arrival close_latency = PathEnd::checkTgtClkDelay(disable_path, disable_clk_edge, TimingRole::latchSetup(), this); - latency_diff = open_latency - close_latency; + latency_diff = delayDiff(open_latency, close_latency, this); } float borrow_limit; sdc->latchBorrowLimit(data_path->pin(this), disable_path->pin(this), @@ -206,8 +214,9 @@ Latches::latchBorrowInfo(const Path *data_path, if (borrow_limit_exists) max_borrow = borrow_limit; else - max_borrow = nom_pulse_width - delayAsFloat(latency_diff) - - delayAsFloat(crpr_diff) - delayAsFloat(margin); + max_borrow = delayDiff(nom_pulse_width, + delaySum(delaySum(latency_diff, crpr_diff, this), + margin, this), this); } else { nom_pulse_width = 0.0; @@ -349,7 +358,7 @@ Latches::latchOutArrival(const Path *data_path, if (!(excpt && excpt->isFalse())) { arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, false, min_max, dcalc_ap, sdc); - q_arrival = data_path->arrival() + arc_delay; + q_arrival = delaySum(data_path->arrival(), arc_delay, this); // Copy the data tag but remove the drprClkPath. // Levelization does not traverse latch D->Q edges, so in some cases // level(Q) < level(D) @@ -405,7 +414,7 @@ Latches::latchOutArrival(const Path *data_path, // Latch is transparent when data arrives. arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, false, min_max, dcalc_ap, sdc); - q_arrival = adjusted_data_arrival + arc_delay; + q_arrival = delaySum(adjusted_data_arrival, arc_delay, this); // Tag switcheroo - data passing thru gets latch enable tag. // States and path ap come from Q, everything else from enable. Path *crpr_clk_path = crprActive(mode) ? enable_path : nullptr; diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index e1f9cba7..39f712fe 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -281,9 +281,9 @@ MakeEndTimingArcs::visit(PathEnd *path_end) Arrival data_delay = src_path->arrival(); Delay clk_latency = path_end->targetClkDelay(sta_); ArcDelay check_margin = path_end->margin(sta_); - Delay margin = min_max == MinMax::max() - ? data_delay - clk_latency + check_margin - : clk_latency - data_delay + check_margin; + Delay margin = (min_max == MinMax::max()) + ? delaySum(delayDiff(data_delay, clk_latency, sta_), check_margin, sta_) + : delaySum(delayDiff(clk_latency, data_delay, sta_), check_margin, sta_); float delay1 = delayAsFloat(margin, MinMax::max(), sta_); debugPrint(debug, "make_timing_model", 2, "%s -> %s clock %s %s %s %s", input_rf_->shortName(), @@ -605,10 +605,10 @@ MakeTimingModel::makeScalarCheckModel(float value, TablePtr table = std::make_shared(value); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); - TableModel *table_model = new TableModel(table, tbl_template, - scale_factor_type, rf); - CheckTableModel *check_model = new CheckTableModel(cell_, table_model); - return check_model; + TableModel *check_table = new TableModel(table, tbl_template, scale_factor_type, rf); + TableModels *check_tables = new TableModels(check_table); + CheckTableModel *check = new CheckTableModel(cell_, check_tables); + return check; } TimingModel * @@ -622,9 +622,12 @@ MakeTimingModel::makeGateModelScalar(Delay delay, library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, ScaleFactorType::cell, rf); + TableModels *delay_models = new TableModels(delay_model); TableModel *slew_model = new TableModel(slew_table, tbl_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(cell_, delay_model, slew_model); + TableModels *slew_models = new TableModels(slew_model); + GateTableModel *gate_model = new GateTableModel(cell_, delay_models, slew_models, + nullptr, nullptr); return gate_model; } @@ -637,7 +640,9 @@ MakeTimingModel::makeGateModelScalar(Delay delay, library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(cell_, delay_model, nullptr); + TableModels *models = new TableModels(delay_model); + GateTableModel *gate_model = new GateTableModel(cell_, models, nullptr, + nullptr, nullptr); return gate_model; } @@ -674,12 +679,11 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, float output_load_cap = graph_delay_calc_->loadCap(output_pin, scene_, min_max_); - ArcDelay drvr_self_delay; - Slew drvr_self_slew; - drvr_gate_model->gateDelay(pvt, in_slew1, output_load_cap, false, + float drvr_self_delay, drvr_self_slew; + drvr_gate_model->gateDelay(pvt, in_slew1, output_load_cap, drvr_self_delay, drvr_self_slew); - const TableModel *drvr_table = drvr_gate_model->delayModel(); + const TableModel *drvr_table = drvr_gate_model->delayModels()->model(); const TableTemplate *drvr_template = drvr_table->tblTemplate(); const TableAxis *drvr_load_axis = loadCapacitanceAxis(drvr_table); if (drvr_load_axis) { @@ -689,13 +693,13 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, for (size_t i = 0; i < drvr_axis_values.size(); i++) { float load_cap = drvr_axis_values[i]; // get slew from driver input pin - ArcDelay gate_delay; - Slew gate_slew; - drvr_gate_model->gateDelay(pvt, in_slew1, load_cap, false, + float gate_delay, gate_slew; + drvr_gate_model->gateDelay(pvt, in_slew1, load_cap, gate_delay, gate_slew); // Remove the self delay driving the output pin net load cap. - load_values->push_back(delayAsFloat(delay + gate_delay - - drvr_self_delay)); + load_values->push_back(delayAsFloat(delay) + + gate_delay + - drvr_self_delay); slew_values->push_back(delayAsFloat(gate_slew)); } @@ -711,10 +715,14 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, load_axis); TableModel *delay_model = new TableModel(delay_table, model_template, ScaleFactorType::cell, rf); + TableModels *delay_models = new TableModels(delay_model); TableModel *slew_model = new TableModel(slew_table, model_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(cell_, delay_model, - slew_model); + TableModels *slew_models = new TableModels(slew_model); + GateTableModel *gate_model = new GateTableModel(cell_, + delay_models, + slew_models, + nullptr, nullptr); return gate_model; } } diff --git a/search/Path.cc b/search/Path.cc index 8ec27d20..84b4764b 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -75,7 +75,7 @@ Path::Path(Vertex *vertex, Path::Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -99,7 +99,7 @@ Path::Path(Vertex *vertex, Path::Path(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -124,7 +124,7 @@ Path::Path(Vertex *vertex, void Path::init(Vertex *vertex, - Arrival arrival, + const Arrival &arrival, const StaState *sta) { const Graph *graph = sta->graph(); @@ -155,7 +155,7 @@ Path::init(Vertex *vertex, void Path::init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, const StaState *sta) { const Graph *graph = sta->graph(); @@ -171,7 +171,7 @@ Path::init(Vertex *vertex, void Path::init(Vertex *vertex, Tag *tag, - Arrival arrival, + const Arrival &arrival, Path *prev_path, Edge *prev_edge, TimingArc *prev_arc, @@ -330,7 +330,7 @@ Path::pathAnalysisPtIndex(const StaState *sta) const return scene(sta)->pathIndex(minMax(sta)); } -Slew +const Slew & Path::slew(const StaState *sta) const { DcalcAPIndex slew_index = scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); @@ -365,9 +365,9 @@ Slack Path::slack(const StaState *sta) const { if (minMax(sta) == MinMax::max()) - return required_ - arrival_; + return delayDiff(required_, arrival_, sta); else - return arrival_ - required_; + return delayDiff(arrival_, required_, sta); } Path * diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 6c644592..13e23fea 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -102,7 +102,7 @@ PathEnd::sourceClkEdge(const StaState *sta) const return path_->clkEdge(sta); } -Arrival +const Arrival & PathEnd::dataArrivalTime(const StaState *) const { return path_->arrival(); @@ -111,13 +111,17 @@ PathEnd::dataArrivalTime(const StaState *) const Arrival PathEnd::dataArrivalTimeOffset(const StaState *sta) const { - return dataArrivalTime(sta) + sourceClkOffset(sta); + return delaySum(dataArrivalTime(sta), + sourceClkOffset(sta), + sta); } Required PathEnd::requiredTimeOffset(const StaState *sta) const { - return requiredTime(sta) + sourceClkOffset(sta); + return delaySum(requiredTime(sta), + sourceClkOffset(sta), + sta); } const RiseFall * @@ -259,9 +263,9 @@ Crpr PathEnd::checkCrpr(const StaState *sta) const { if (checkRole(sta)->genericRole() == TimingRole::hold()) - return -crpr(sta); + return delayDiff(delay_zero, crpr(sta), sta); else - return crpr(sta);; + return crpr(sta); } Crpr @@ -301,7 +305,7 @@ PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, Delay insertion, latency; checkTgtClkDelay(tgt_clk_path, tgt_clk_edge, check_role, sta, insertion, latency); - return Delay(insertion + latency); + return delaySum(insertion, latency, sta); } void @@ -334,7 +338,8 @@ PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, Delay path_insertion = search->clockInsertion(tgt_clk, tgt_src_pin, tgt_clk_rf, min_max, min_max, mode); - latency = delayRemove(clk_arrival - tgt_clk_edge->time(), path_insertion); + latency = delayRemove(delayDiff(clk_arrival, tgt_clk_edge->time(), sta), + path_insertion); } else // Ideal clock. @@ -615,21 +620,25 @@ PathEndClkConstrained::targetClkTime(const StaState *sta) const Arrival PathEndClkConstrained::targetClkArrival(const StaState *sta) const { - return targetClkArrivalNoCrpr(sta) - + checkCrpr(sta); + return delaySum(targetClkArrivalNoCrpr(sta), + checkCrpr(sta), + sta); } Arrival PathEndClkConstrained::targetClkArrivalNoCrpr(const StaState *sta) const { Sdc *sdc = path_->sdc(sta); - return targetClkTime(sta) - + targetClkDelay(sta) - + checkClkUncertainty(sourceClkEdge(sta), - targetClkEdge(sta), - targetClkPath(), - checkRole(sta), sdc) - + targetClkMcpAdjustment(sta); + Arrival clk_arrival = delaySum(targetClkDelay(sta), + targetClkTime(sta), + sta); + float uncertainty = checkClkUncertainty(sourceClkEdge(sta), + targetClkEdge(sta), + targetClkPath(), + checkRole(sta), + sdc); + return delaySum(delaySum(clk_arrival, uncertainty, sta), + targetClkMcpAdjustment(sta), sta); } Delay @@ -704,8 +713,9 @@ PathEndClkConstrained::crpr(const StaState *sta) const Required PathEndClkConstrained::requiredTime(const StaState *sta) const { - return requiredTimeNoCrpr(sta) - + checkCrpr(sta); + return delaySum(requiredTimeNoCrpr(sta), + checkCrpr(sta), + sta); } Required @@ -714,9 +724,9 @@ PathEndClkConstrained::requiredTimeNoCrpr(const StaState *sta) const Arrival tgt_clk_arrival = targetClkArrivalNoCrpr(sta); ArcDelay check_margin = margin(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return tgt_clk_arrival - check_margin; + return delayDiff(tgt_clk_arrival, check_margin, sta); else - return tgt_clk_arrival + check_margin; + return delaySum(tgt_clk_arrival, check_margin, sta); } Slack @@ -725,9 +735,9 @@ PathEndClkConstrained::slack(const StaState *sta) const Arrival arrival = dataArrivalTime(sta); Required required = requiredTime(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return required - arrival; + return delayDiff(required, arrival, sta); else - return arrival - required; + return delayDiff(arrival, required, sta); } int @@ -862,9 +872,9 @@ PathEndClkConstrained::slackNoCrpr(const StaState *sta) const Arrival arrival = dataArrivalTime(sta); Required required = requiredTimeNoCrpr(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return required - arrival; + return delayDiff(required, arrival, sta); else - return arrival - required; + return delayDiff(arrival, required, sta); } void @@ -1017,10 +1027,18 @@ PathEndCheck::exceptPathCmp(const PathEnd *path_end, Delay PathEndCheck::clkSkew(const StaState *sta) { - return sourceClkDelay(sta) - targetClkDelay(sta) - crpr(sta) - // Uncertainty decreases slack, but increases skew. - - checkTgtClkUncertainty(clk_path_, clk_path_->clkEdge(sta), - checkRole(sta), sta); + Delay skew = delayDiff(sourceClkDelay(sta), + targetClkDelay(sta), + sta); + skew = delayDiff(skew, crpr(sta), sta); + // Uncertainty decreases slack, but increases skew. + skew = delayDiff(skew, + checkTgtClkUncertainty(clk_path_, + clk_path_->clkEdge(sta), + checkRole(sta), + sta), + sta); + return skew; } Delay @@ -1035,7 +1053,8 @@ PathEndCheck::sourceClkDelay(const StaState *sta) const Arrival clk_arrival = src_clk_path->arrival(); const ClockEdge *src_clk_edge = src_clk_info->clkEdge(); Delay insertion = sourceClkInsertionDelay(sta); - return delayRemove(clk_arrival - src_clk_edge->time(), insertion); + return delayRemove(delayDiff(clk_arrival, src_clk_edge->time(), sta), + insertion); } else // Ideal clock. @@ -1052,9 +1071,17 @@ PathEndCheck::requiredTimeNoCrpr(const StaState *sta) const ArcDelay check_margin = margin(sta); float macro_clk_tree_delay = macroClkTreeDelay(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return tgt_clk_arrival - (check_margin + macro_clk_tree_delay); + return delayDiff(tgt_clk_arrival, + delaySum(check_margin, + macro_clk_tree_delay, + sta), + sta); else - return tgt_clk_arrival + (check_margin - macro_clk_tree_delay); + return delaySum(tgt_clk_arrival, + delayDiff(check_margin, + macro_clk_tree_delay, + sta), + sta); } float @@ -1072,7 +1099,7 @@ PathEndCheck::macroClkTreeDelay(const StaState *sta) const if (clk_port) { const MinMax *min_max = clk_path_->minMax(sta); const RiseFall *rf = clk_path_->transition(sta); - float slew = delayAsFloat(clk_path_->slew(sta)); + float slew = delayAsFloat(clk_path_->slew(sta), min_max, sta); return clk_port->clkTreeDelay(slew, rf, min_max); } } @@ -1276,14 +1303,16 @@ PathEndLatchCheck::targetClkWidth(const StaState *sta) const Arrival enable_arrival = search->clkPathArrival(clk_path_); const ClkInfo *enable_clk_info = clk_path_->clkInfo(sta); if (enable_clk_info->isPulseClk()) - return disable_arrival - enable_arrival; + return delayDiff(disable_arrival, enable_arrival, sta); else { if (delayGreater(enable_arrival, disable_arrival, sta)) { const Clock *disable_clk = enable_clk_info->clock(); if (disable_clk) - disable_arrival += disable_clk->period(); + disable_arrival = delaySum(disable_arrival, + disable_clk->period(), + sta); } - return disable_arrival - enable_arrival; + return delayDiff(disable_arrival, enable_arrival, sta); } } @@ -1416,10 +1445,14 @@ PathEndOutputDelay::targetClkArrivalNoCrpr(const StaState *sta) const else { const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); - return targetClkTime(sta) - + tgtClkDelay(tgt_clk_edge, check_role, sta) - + targetClkUncertainty(sta) - + checkMcpAdjustment(path_, tgt_clk_edge, sta); + Arrival base = delaySum(targetClkTime(sta), + tgtClkDelay(tgt_clk_edge, check_role, sta), + sta); + return delaySum(delaySum(base, + targetClkUncertainty(sta), + sta), + checkMcpAdjustment(path_, tgt_clk_edge, sta), + sta); } } @@ -1451,7 +1484,7 @@ PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge, Arrival insertion, latency; tgtClkDelay(tgt_clk_edge, check_role, sta, insertion, latency); - return insertion + latency; + return delaySum(insertion, latency, sta); } void @@ -1691,17 +1724,23 @@ PathEndDataCheck::requiredTimeNoCrpr(const StaState *sta) const { Arrival data_clk_arrival = data_clk_path_->arrival(); float data_clk_time = data_clk_path_->clkEdge(sta)->time(); - Arrival data_clk_delay = data_clk_arrival - data_clk_time; - Arrival tgt_clk_arrival = targetClkTime(sta) - + data_clk_delay - + targetClkUncertainty(sta) - + targetClkMcpAdjustment(sta); + Arrival data_clk_delay = delayDiff(data_clk_arrival, + data_clk_time, + sta); + Arrival tgt_clk_arrival = + delaySum(delaySum(targetClkTime(sta), + data_clk_delay, + sta), + delaySum(targetClkUncertainty(sta), + targetClkMcpAdjustment(sta), + sta), + sta); ArcDelay check_margin = margin(sta); if (checkGenericRole(sta) == TimingRole::setup()) - return tgt_clk_arrival - check_margin; + return delayDiff(tgt_clk_arrival, check_margin, sta); else - return tgt_clk_arrival + check_margin; + return delaySum(tgt_clk_arrival, check_margin, sta); } ArcDelay @@ -1917,7 +1956,7 @@ PathEnd::pathDelaySrcClkOffset(const Path *path, const ClockEdge *clk_edge = path->clkEdge(sta); if (clk_edge) { if (ignoreClkLatency(path, path_delay, sta)) - offset = -delayAsFloat(src_clk_arrival); + offset = -delayAsFloat(src_clk_arrival, path->minMax(sta), sta); else // Arrival includes src clock edge time that is not counted in the // path delay. @@ -1960,8 +1999,9 @@ PathEndPathDelay::targetClkArrivalNoCrpr(const StaState *sta) const { const ClockEdge *tgt_clk_edge = targetClkEdge(sta); if (tgt_clk_edge) - return targetClkDelay(sta) - + targetClkUncertainty(sta); + return delaySum(targetClkDelay(sta), + targetClkUncertainty(sta), + sta); else if (clk_path_) return clk_path_->arrival(); else @@ -1979,19 +2019,29 @@ PathEndPathDelay::requiredTime(const StaState *sta) const { float delay = path_delay_->delay(); if (path_delay_->ignoreClkLatency()) { - Required src_offset = path_->isClock(sta) - ? path_->clkEdge(sta)->time() - : src_clk_arrival_; - return src_offset + delay - + ((minMax(sta) == MinMax::max()) ? -margin(sta) : margin(sta)); + Delay with_delay = path_->isClock(sta) + ? Delay(path_->clkEdge(sta)->time() + delay) + : delaySum(src_clk_arrival_, delay, sta); + ArcDelay m = margin(sta); + return (minMax(sta) == MinMax::max()) + ? delayDiff(with_delay, m, sta) + : delaySum(with_delay, m, sta); } else { 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. - return delay - src_clk_offset + tgt_clk_arrival - + ((minMax(sta) == MinMax::max()) ? -margin(sta) : margin(sta)); + Delay base = delaySum(tgt_clk_arrival, + delay, + sta); + Delay with_src = delayDiff(base, + src_clk_offset, + sta); + ArcDelay m = margin(sta); + return (minMax(sta) == MinMax::max()) + ? delayDiff(with_src, m, sta) + : delaySum(with_src, m, sta); } } @@ -2073,22 +2123,22 @@ PathEnd::cmpSlack(const PathEnd *path_end1, { Slack slack1 = path_end1->slack(sta); Slack slack2 = path_end2->slack(sta); - if (delayZero(slack1) - && delayZero(slack2) + if (delayZero(slack1, sta) + && delayZero(slack2, sta) && 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 (delayEqual(borrow1, borrow2)) + if (delayEqual(borrow1, borrow2, sta)) return 0; else if (delayGreater(borrow1, borrow2, sta)) return -1; else return 1; } - else if (delayEqual(slack1, slack2)) + else if (delayEqual(slack1, slack2, sta)) return 0; else if (delayLess(slack1, slack2, sta)) return -1; @@ -2104,7 +2154,7 @@ 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 (delayEqual(arrival1, arrival2)) + if (delayEqual(arrival1, arrival2, sta)) return 0; else if (delayLess(arrival1, arrival2, min_max, sta)) return -1; diff --git a/search/PathEnum.cc b/search/PathEnum.cc index ed57ed28..c5e11778 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -511,9 +511,11 @@ PathEnumFaninVisitor::reportDiversion(const Edge *div_edge, Arrival path_delay = path_enum_->cmp_slack_ ? path_end_->slack(this) : path_end_->dataArrivalTime(this); - Arrival div_delay = path_delay - path_enum_->divSlack(before_div_, - after_div, div_edge, - div_arc); + Arrival div_delay = delayDiff(path_delay, + path_enum_->divSlack(before_div_, + after_div, div_edge, + div_arc), + this); Path *div_prev = before_div_->prevPath(); report_->reportLine("path_enum: diversion %s %s %s -> %s", path->to_string(this).c_str(), @@ -580,7 +582,7 @@ PathEnum::divSlack(Path *before_div, Tag *q_tag; latches_->latchOutArrival(after_div, div_arc, div_edge, q_tag, div_delay, div_arrival); - return div_arrival - before_div_arrival; + return delayDiff(div_arrival, before_div_arrival, this); } else { DcalcAPIndex dcalc_ap = before_div->dcalcAnalysisPtIndex(this); @@ -589,8 +591,8 @@ PathEnum::divSlack(Path *before_div, false, before_div->minMax(this), dcalc_ap, before_div->sdc(this)); - Arrival div_arrival = search_->clkPathArrival(after_div) + div_delay; - return div_arrival - before_div_arrival; + Arrival div_arrival = delaySum(search_->clkPathArrival(after_div), div_delay, this); + return delayDiff(div_arrival, before_div_arrival, this); } } else { @@ -719,7 +721,7 @@ PathEnum::updatePathHeadDelays(PathSeq &paths, path->minMax(this), path->dcalcAnalysisPtIndex(this), path->sdc(this)); - arrival = prev_arrival + arc_delay; + arrival = delaySum(prev_arrival, arc_delay, this); path->setArrival(arrival); const Tag *tag = path->tag(this); const ClkInfo *clk_info = tag->clkInfo(); diff --git a/search/PocvMode.cc b/search/PocvMode.cc new file mode 100644 index 00000000..57186138 --- /dev/null +++ b/search/PocvMode.cc @@ -0,0 +1,48 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2025, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#include "PocvMode.hh" + +#include "EnumNameMap.hh" + +namespace sta { + +static EnumNameMap pocv_mode_map = + {{PocvMode::scalar, "scalar"}, + {PocvMode::normal, "normal"}, + {PocvMode::skew_normal, "skew_normal"}}; + +const char * +pocvModeName(PocvMode mode) +{ + return pocv_mode_map.find(mode); +} + +PocvMode +findPocvMode(const char *mode_name) +{ + return pocv_mode_map.find(mode_name, PocvMode::scalar); +} + +} // namespace diff --git a/search/Property.cc b/search/Property.cc index 3a2a973b..dc37d4b3 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -1133,7 +1133,7 @@ Properties::edgeDelay(Edge *edge, if (to_rf == rf) { for (const Scene *scene : sta_->scenes()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - ArcDelay arc_delay = sta_->arcDelay(edge, arc, ap_index); + const ArcDelay &arc_delay = sta_->arcDelay(edge, arc, ap_index); if (!delay_exists || delayGreater(arc_delay, delay, min_max, sta_)) { delay = arc_delay; diff --git a/search/ReportPath.cc b/search/ReportPath.cc index b7d3c9d2..664af765 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -134,20 +134,18 @@ ReportField::setEnabled(bool enabled) //////////////////////////////////////////////////////////////// -const float ReportPath::field_blank_ = -1.0; - ReportPath::ReportPath(StaState *sta) : StaState(sta), format_(ReportPathFormat::full), no_split_(false), - report_sigmas_(false), start_end_pt_width_(80), + field_width_extra_(5), plus_zero_(nullptr), minus_zero_(nullptr) { - setDigits(2); makeFields(); - setReportFields(false, false, false, false, false, false, false); + setDigits(2); + setReportFields(false, false, false, false, false, false, false, false); } ReportPath::~ReportPath() @@ -158,6 +156,7 @@ ReportPath::~ReportPath() delete field_capacitance_; delete field_slew_; delete field_fanout_; + delete field_variation_; delete field_src_attr_; delete field_edge_; delete field_case_; @@ -169,6 +168,7 @@ ReportPath::~ReportPath() void ReportPath::makeFields() { + // The order corresponds to the default field order. field_fanout_ = makeField("fanout", "Fanout", 6, false, nullptr, true); field_capacitance_ = makeField("capacitance", "Cap", 6, false, units_->capacitanceUnit(), true); @@ -176,6 +176,8 @@ ReportPath::makeFields() true); field_incr_ = makeField("incr", "Delay", 6, false, units_->timeUnit(), true); + field_variation_ = makeField("variation", "Variation", 6, false, + units_->timeUnit(), false); field_total_ = makeField("total", "Time", 6, false, units_->timeUnit(), true); field_edge_ = makeField("edge", "", 1, false, nullptr, true); @@ -243,6 +245,7 @@ ReportPath::setReportFields(bool report_input_pin, bool report_cap, bool report_slew, bool report_fanout, + bool report_variation, bool report_src_attr) { report_input_pin_ = report_input_pin; @@ -252,6 +255,7 @@ ReportPath::setReportFields(bool report_input_pin, field_capacitance_->setEnabled(report_cap); field_slew_->setEnabled(report_slew); field_fanout_->setEnabled(report_fanout); + field_variation_->setEnabled(report_variation); field_src_attr_->setEnabled(report_src_attr); // for debug field_case_->setEnabled(false); @@ -278,12 +282,14 @@ ReportPath::setDigits(int digits) stringDelete(minus_zero_); minus_zero_ = stringPrint("-%.*f", digits_, 0.0); plus_zero_ = stringPrint("%.*f", digits_, 0.0); -} -void -ReportPath::setReportSigmas(bool report) -{ - report_sigmas_ = report; + // Numeric field width expands with digits. + int field_width = digits + field_width_extra_; + field_capacitance_->setWidth(field_width); + field_slew_->setWidth(field_width); + field_variation_->setWidth(field_width); + field_incr_->setWidth(field_width); + field_total_->setWidth(field_width); } //////////////////////////////////////////////////////////////// @@ -556,7 +562,7 @@ ReportPath::reportFull(const PathEndLatchCheck *end) const end->latchRequired(this, req_time, borrow, adjusted_data_arrival, time_given_to_startpoint); // Adjust required to requiredTimeOffset. - req_time += end->sourceClkOffset(this); + req_time = delaySum(req_time, end->sourceClkOffset(this), this); if (path_delay) { float delay = path_delay->delay(); reportLine("max_delay", delay, delay, early_late); @@ -630,7 +636,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); - if (!delayZero(latency_diff)) + if (!delayZero(latency_diff, this)) reportLineTotalMinus("clock latency difference", latency_diff, early_late); } else { @@ -640,18 +646,18 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, ArcDelay margin = end->margin(this); reportLineTotalMinus("library setup time", margin, early_late); reportDashLineTotal(); - if (!delayZero(crpr_diff)) + if (!delayZero(crpr_diff, this)) reportLineTotalMinus("CRPR difference", crpr_diff, early_late); reportLineTotal("max time borrow", max_borrow, early_late); } if (delayGreater(borrow, delay_zero, this) && (!fuzzyZero(open_uncertainty) - || !delayZero(open_crpr))) { + || !delayZero(open_crpr, this))) { reportDashLineTotal(); reportLineTotal("actual time borrow", borrow, early_late); if (!fuzzyZero(open_uncertainty)) reportLineTotal("open edge uncertainty", open_uncertainty, early_late); - if (!delayZero(open_crpr)) + if (!delayZero(open_crpr, this)) reportLineTotal("open edge CRPR", open_crpr, early_late); reportDashLineTotal(); reportLineTotal("time given to startpoint", time_given_to_startpoint, early_late); @@ -720,7 +726,7 @@ ReportPath::reportFull(const PathEndPathDelay *end) const ArcDelay margin = end->margin(this); const MinMax *min_max = path_delay->minMax()->asMinMax(); if (min_max == MinMax::max()) - margin = -margin; + margin = delayDiff(delay_zero, margin, this); std::string delay_msg = min_max->to_string() + "_delay"; float delay = path_delay->delay(); @@ -734,8 +740,8 @@ ReportPath::reportFull(const PathEndPathDelay *end) const reportTgtClk(end, delay, 0.0, true); else { Arrival tgt_clk_delay = end->targetClkDelay(this); - Arrival tgt_clk_arrival = delay + tgt_clk_delay; - if (!delayZero(tgt_clk_delay)) + Arrival tgt_clk_arrival = delaySum(tgt_clk_delay, delay, this); + if (!delayZero(tgt_clk_delay, this)) reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)), tgt_clk_delay, tgt_clk_arrival, early_late); reportClkUncertainty(end, tgt_clk_arrival); @@ -924,10 +930,11 @@ ReportPath::reportFull(const PathEndDataCheck *end) const PathExpanded clk_expanded(data_clk_path, this); float src_offset = end->sourceClkOffset(this); Delay clk_delay = end->targetClkDelay(this); + const MinMax *min_max = data_clk_path->minMax(this); Arrival clk_arrival = end->targetClkArrival(this); const ClockEdge *tgt_clk_edge = end->targetClkEdge(this); - float prev = delayAsFloat(clk_arrival) + src_offset; - float offset = prev - delayAsFloat(clk_delay) - tgt_clk_edge->time(); + float prev = delayAsFloat(clk_arrival, min_max, this) + src_offset; + float offset = prev - delayAsFloat(clk_delay, min_max, this) - tgt_clk_edge->time(); // Delay to startpoint is already included. reportPath6(data_clk_path, clk_expanded, clk_expanded.startIndex(), true, false, prev, offset); @@ -1397,13 +1404,15 @@ ReportPath::reportVerbose(const MinPulseWidthCheck &check) const float close_clk_time = close_clk_edge->time() + close_offset; auto close_clk_msg = stdstrPrint("clock %s (%s edge)", close_clk_name, close_rise_fall); reportLine(close_clk_msg.c_str(), close_clk_time, close_clk_time, close_el); - Arrival close_arrival = check.closeArrival(this) + close_offset; + + Arrival close_arrival = delaySum(check.closeArrival(this), close_offset, this); reportLine(clk_ideal_prop, check.closeDelay(this), close_arrival, close_el); + reportLine(pin_name, delay_zero, close_arrival, close_el); if (variables_->crprEnabled()) { Crpr pessimism = check.checkCrpr(this); - close_arrival += pessimism; + close_arrival = delaySum(close_arrival, pessimism, this); reportLine("clock reconvergence pessimism", pessimism, close_arrival, close_el); } reportLine("close edge arrival time", close_arrival, close_el); @@ -1578,7 +1587,7 @@ ReportPath::reportShort(const MaxSkewCheck &check) const reportDescription(what.c_str(), line); const EarlyLate *early_late = EarlyLate::early(); reportSpaceFieldDelay(check.maxSkew(this), early_late, line); - reportSpaceFieldDelay(check.skew(), early_late, line); + reportSpaceFieldDelay(check.skew(this), early_late, line); reportSpaceSlack(check.slack(this), line); report_->reportLineString(line); } @@ -1607,7 +1616,7 @@ ReportPath::reportVerbose(const MaxSkewCheck &check) const reportDashLine(); reportLine("allowable skew", check.maxSkew(this), EarlyLate::early()); - reportLine("actual skew", check.skew(), EarlyLate::late()); + reportLine("actual skew", check.skew(this), EarlyLate::late()); reportDashLine(); reportSlack(check.slack(this)); } @@ -1625,7 +1634,9 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, std::string clk_name = clkName(clk, clk_end_rf != clk_rf); float clk_time = clk_edge->time(); const Arrival &clk_arrival = search_->clkPathArrival(clk_path); - Arrival clk_delay = clk_arrival - clk_time; + Arrival clk_delay = delayDiff(clk_arrival, + clk_time, + this); const MinMax *min_max = clk_path->minMax(this); Vertex *clk_vertex = clk_path->vertex(this); reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); @@ -2066,15 +2077,19 @@ ReportPath::reportSrcClkAndPath(const Path *path, const Path *clk_path = expanded.clkPath(); const RiseFall *clk_end_rf; if (clk_path) { - clk_end_time = search_->clkPathArrival(clk_path) + time_offset; - clk_delay = clk_end_time - clk_time; + clk_end_time = delaySum(search_->clkPathArrival(clk_path), + time_offset, + this); + clk_delay = delayDiff(clk_end_time, + clk_time, + this); clk_end_rf = clk_path->transition(this); } else { // Path from input port or clk used as data. clk_end_rf = clk_rf; - clk_delay = clk_insertion + clk_latency; - clk_end_time = clk_time + clk_delay; + clk_delay = delaySum(clk_insertion, clk_latency, this); + clk_end_time = delaySum(clk_time, clk_delay, this); const Path *first_path = expanded.startPath(); const InputDelay *input_delay = pathInputDelay(first_path); @@ -2086,8 +2101,12 @@ ReportPath::reportSrcClkAndPath(const Path *path, pathInputDelayRefPath(first_path, input_delay, ref_path); if (!ref_path.isNull()) { const Arrival &ref_end_time = ref_path.arrival(); - clk_delay = ref_end_time - clk_time; - clk_end_time = ref_end_time + time_offset; + clk_delay = delayDiff(ref_end_time, + clk_time, + this); + clk_end_time = delaySum(ref_end_time, + time_offset, + this); input_has_ref_path = true; } } @@ -2127,8 +2146,10 @@ ReportPath::reportSrcClkAndPath(const Path *path, reportPath1(path, expanded, true, time_offset); else { Arrival clk_arrival = clk_end_time; - Arrival end_arrival = path->arrival() + time_offset; - Delay clk_delay = end_arrival - clk_arrival; + Arrival end_arrival = delaySum(path->arrival(), + time_offset, + this); + Delay clk_delay = delayDiff(end_arrival, clk_arrival, this); reportLine("clock network delay", clk_delay, end_arrival, early_late); Vertex *end_vertex = path->vertex(this); @@ -2187,7 +2208,7 @@ ReportPath::reportTgtClk(const PathEnd *end, bool is_prop) const { const ClockEdge *clk_edge = end->targetClkEdge(this); - Clock *clk = clk_edge->clock(); + const Clock *clk = clk_edge->clock(); const RiseFall *clk_rf = clk_edge->transition(); const RiseFall *clk_end_rf = end->targetClkEndTrans(this); std::string clk_name = clkName(clk, clk_end_rf != clk_rf); @@ -2196,7 +2217,7 @@ ReportPath::reportTgtClk(const PathEnd *end, + end->targetClkMcpAdjustment(this) + src_offset; Arrival clk_delay = end->targetClkDelay(this); - Arrival clk_arrival = clk_time + clk_delay; + Arrival clk_arrival = delaySum(clk_delay, clk_time, this); const MinMax *min_max = end->path()->tgtClkMinMax(this); const Path *clk_path = end->targetClkPath(); reportClkLine(clk, clk_name.c_str(), clk_end_rf, prev_time, clk_time, min_max); @@ -2225,7 +2246,7 @@ ReportPath::reportTgtClk(const PathEnd *end, } else { // Output departure. - Arrival clk_arrival = clk_time + clk_delay; + Arrival clk_arrival = delaySum(clk_time, clk_delay, this); reportLine(clkNetworkDelayIdealProp(clk->isPropagated()), clk_delay, clk_arrival, min_max); } @@ -2241,9 +2262,11 @@ ReportPath::reportTgtClk(const PathEnd *end, if (clk_path) { Vertex *clk_vertex = clk_path->vertex(this); reportLine(descriptionField(clk_vertex).c_str(), - prev_time - + end->targetClkArrival(this) - + end->sourceClkOffset(this), + delaySum(delaySum(prev_time, + end->targetClkArrival(this), + this), + end->sourceClkOffset(this), + this), min_max, clk_end_rf); } } @@ -2264,7 +2287,7 @@ ReportPath::tgtClkInsertionOffet(const Path *clk_path, min_max, min_max, mode); Arrival tgt_insertion = search_->clockInsertion(clk, src_pin, clk_rf, min_max, early_late, mode); - return delayAsFloat(tgt_insertion - path_insertion); + return delayAsFloat(delayDiff(tgt_insertion, path_insertion, this)); } bool @@ -2327,11 +2350,18 @@ ReportPath::reportClkLine(const Clock *clk, const char *rise_fall = asRiseFall(clk_rf); auto clk_msg = stdstrPrint("clock %s (%s edge)", clk_name, rise_fall); if (clk->isPropagated()) - reportLine(clk_msg.c_str(), clk_time - prev_time, clk_time, min_max); + reportLine(clk_msg.c_str(), + delayDiff(clk_time, prev_time, this), + clk_time, + min_max); else { // Report ideal clock slew. float clk_slew = clk->slew(clk_rf, min_max); - reportLine(clk_msg.c_str(), clk_slew, clk_time - prev_time, clk_time, min_max); + reportLine(clk_msg.c_str(), + clk_slew, + delayDiff(clk_time, prev_time, this), + clk_time, + min_max); } } @@ -2433,13 +2463,16 @@ ReportPath::reportClkSrcLatency(Arrival insertion, float clk_time, const EarlyLate *early_late) const { - reportLine("clock source latency", insertion, clk_time + insertion, early_late); + Arrival clk_arrival = delaySum(clk_time, + insertion, + this); + reportLine("clock source latency", insertion, clk_arrival, early_late); } void ReportPath::reportPathLine(const Path *path, - Arrival incr, - Arrival time, + const Delay &incr, + const Arrival &time, const char *line_case) const { Vertex *vertex = path->vertex(this); @@ -2461,7 +2494,7 @@ ReportPath::reportPathLine(const Path *path, if (is_driver && field_capacitance_->enabled()) cap = graph_delay_calc_->loadCap(pin, rf, scene, min_max); reportLine(what.c_str(), cap, slew, field_blank_, - incr, time, false, early_late, rf, src_attr, + incr, field_blank_, time, false, early_late, rf, src_attr, line_case); } @@ -2474,13 +2507,16 @@ ReportPath::reportRequired(const PathEnd *end, float macro_clk_tree_delay = end->macroClkTreeDelay(this); ArcDelay margin = end->margin(this); if (end->minMax(this) == MinMax::min()) { - margin = -margin; + margin = delayDiff(delay_zero, margin, this); macro_clk_tree_delay = -macro_clk_tree_delay; } if (macro_clk_tree_delay != 0.0) reportLine("macro clock tree delay", -macro_clk_tree_delay, - req_time + margin, early_late); - reportLine(margin_msg.c_str(), -margin, req_time, early_late); + delaySum(req_time, margin, this), early_late); + reportLine(margin_msg.c_str(), + delayDiff(delay_zero, margin, this), + req_time, + early_late); reportLine("data required time", req_time, early_late); reportDashLine(); } @@ -2531,7 +2567,7 @@ ReportPath::reportCommonClkPessimism(const PathEnd *end, { if (variables_->crprEnabled()) { Crpr pessimism = end->checkCrpr(this); - clk_arrival += pessimism; + clk_arrival = delaySum(clk_arrival, pessimism, this); reportLine("clock reconvergence pessimism", pessimism, clk_arrival, end->clkEarlyLate(this)); } @@ -2543,11 +2579,15 @@ ReportPath::reportClkUncertainty(const PathEnd *end, { const EarlyLate *early_late = end->clkEarlyLate(this); float uncertainty = end->targetNonInterClkUncertainty(this); - clk_arrival += uncertainty; + clk_arrival = delaySum(clk_arrival, + uncertainty, + this); if (uncertainty != 0.0) reportLine("clock uncertainty", uncertainty, clk_arrival, early_late); float inter_uncertainty = end->interClkUncertainty(this); - clk_arrival += inter_uncertainty; + clk_arrival = delaySum(clk_arrival, + inter_uncertainty, + this); if (inter_uncertainty != 0.0) reportLine("inter-clock uncertainty", inter_uncertainty, clk_arrival, early_late); @@ -2659,7 +2699,9 @@ ReportPath::reportPath4(const Path *path, reportPath5(latch_enable_path, enable_expanded, skip_first_path, propagated_clk, report_clk_path, time_offset); } - Arrival time = latch_enable_time + latch_time_given; + Arrival time = delaySum(latch_enable_time, + latch_time_given, + this); Arrival incr = latch_time_given; if (delayGreaterEqual(incr, 0.0, this)) reportLine("time given to startpoint", incr, time, early_late); @@ -2668,7 +2710,10 @@ ReportPath::reportPath4(const Path *path, // Override latch D arrival with enable + given. reportPathLine(expanded.path(0), delay_zero, time, "latch_D"); reportPath6(path, expanded, 1, propagated_clk, report_clk_path, - latch_enable_time + latch_time_given, time_offset); + delaySum(latch_enable_time, + latch_time_given, + this), + time_offset); } } else @@ -2689,7 +2734,9 @@ ReportPath::reportPath5(const Path *path, if (skip_first_path) { path_first_index = 1; const Path *start = expanded.path(0); - prev_time = start->arrival() + time_offset; + prev_time = delaySum(start->arrival(), + time_offset, + this); } reportPath6(path, expanded, path_first_index, propagated_clk, report_clk_path, prev_time, time_offset); @@ -2717,7 +2764,7 @@ ReportPath::reportPath6(const Path *path, const TimingArc *prev_arc = path1->prevArc(this); Vertex *vertex = path1->vertex(this); Pin *pin = vertex->pin(); - Arrival time = path1->arrival() + time_offset; + Arrival time = delaySum(path1->arrival(), time_offset, this); Delay incr = 0.0; const char *line_case = nullptr; bool is_clk_start = path1->vertex(this) == clk_start; @@ -2746,7 +2793,9 @@ ReportPath::reportPath6(const Path *path, // The delay calculator annotates wire delays on the edges // from the input to the loads. Report the wire delay on the // input pin instead. - Arrival next_time = next_path->arrival() + time_offset; + Arrival next_time = delaySum(next_path->arrival(), + time_offset, + this); incr = delayIncr(next_time, time, min_max); time = next_time; line_case = "input_drive"; @@ -2755,7 +2804,7 @@ ReportPath::reportPath6(const Path *path, if (!propagated_clk) // Clock latency at path endpoint in case latency was set // on a clock pin other than the clock source. - time = search_->clkPathArrival(path1) + time_offset; + time = delaySum(search_->clkPathArrival(path1), time_offset, this); incr = 0.0; line_case = "clk_first"; } @@ -2772,7 +2821,9 @@ ReportPath::reportPath6(const Path *path, if (!propagated_clk) { // Ideal clock. const ClockEdge *src_clk_edge = path->clkEdge(this); - time = search_->clkPathArrival(path1) + time_offset; + time = delaySum(search_->clkPathArrival(path1), + time_offset, + this); if (src_clk_edge) { Clock *src_clk = src_clk_edge->clock(); const RiseFall *src_clk_rf = src_clk_edge->transition(); @@ -2803,6 +2854,8 @@ ReportPath::reportPath6(const Path *path, } if (vertex->isDriver(network_)) { + // Report delay arc pocv variation between input and driver. + reportVariation(path1); float cap = field_blank_; float fanout = field_blank_; if (field_capacitance_->enabled()) @@ -2811,13 +2864,13 @@ ReportPath::reportPath6(const Path *path, fanout = drvrFanout(vertex, scene, min_max); const std::string what = descriptionField(vertex); reportLine(what.c_str(), cap, slew, fanout, - incr, time, false, min_max, rf, src_attr, + incr, field_blank_, time, false, min_max, rf, src_attr, line_case); if (report_net_) { const std::string what2 = descriptionNet(pin); reportLine(what2.c_str(), field_blank_, field_blank_, field_blank_, - field_blank_, field_blank_, false, min_max, + field_blank_, field_blank_, field_blank_, false, min_max, nullptr, src_attr, ""); } prev_time = time; @@ -2830,7 +2883,7 @@ ReportPath::reportPath6(const Path *path, || is_clk_start) { const std::string what = descriptionField(vertex); reportLine(what.c_str(), field_blank_, slew, field_blank_, - incr, time, false, min_max, rf, src_attr, + incr, field_blank_, time, false, min_max, rf, src_attr, line_case); prev_time = time; } @@ -2841,6 +2894,59 @@ ReportPath::reportPath6(const Path *path, } } +void +ReportPath::reportVariation(const Path *path) const +{ + if (field_variation_->enabled()) { + const Edge *prev_edge = path->prevEdge(this); + if (prev_edge) { + const TimingArc *prev_arc = path->prevArc(this); + const MinMax *min_max = path->minMax(this); + DcalcAPIndex slew_index = path->dcalcAnalysisPtIndex(this); + const ArcDelay &arc_delay=graph_->arcDelay(prev_edge, prev_arc,slew_index); + switch (variables_->pocvMode()) { + case PocvMode::normal: { + float std_dev = arc_delay.stdDev(); + reportLine("sigma", field_blank_, field_blank_, field_blank_, + field_blank_, std_dev, field_blank_, true, min_max, nullptr, + "", nullptr); + break; + } + case PocvMode::skew_normal: { + float mean = arc_delay.mean(); + reportLine("mean", field_blank_, field_blank_, field_blank_, + field_blank_, mean, field_blank_, true, min_max, nullptr, + "", nullptr); + float mean_shift = arc_delay.meanShift(); + reportLine("mean_shift", field_blank_, field_blank_, field_blank_, + field_blank_, mean_shift, field_blank_, true, min_max, nullptr, + "", nullptr); + float std_dev = arc_delay.stdDev(); + reportLine("std_dev", field_blank_, field_blank_, field_blank_, + field_blank_, std_dev, field_blank_, true, min_max, nullptr, + "", nullptr); + // skewness is dimensionless, so scale it to the field's time units. + float skewness = arc_delay.skewness() * units_->timeUnit()->scale(); + reportLine("skewness", field_blank_, field_blank_, field_blank_, + field_blank_, skewness, field_blank_, true, min_max, nullptr, + "", nullptr); + break; + } + default: + break; + } + } + } +} + +Delay +ReportPath::delayIncr(const Delay &time, + const Delay &prev, + const MinMax *min_max) const +{ + return delayAsFloat(time, min_max, this) - delayAsFloat(prev, min_max, this); +} + void ReportPath::reportHierPinsThru(const Path *path) const { @@ -2850,24 +2956,13 @@ ReportPath::reportHierPinsThru(const Path *path) const for (const Pin *hpin : hierPinsThruEdge(prev_edge, network_, graph_)) { const std::string what = descriptionField(hpin); reportLine(what.c_str(), field_blank_, field_blank_, field_blank_, - field_blank_, field_blank_, false, path->minMax(this), + field_blank_, field_blank_, field_blank_, false, path->minMax(this), nullptr, "", ""); } } } } -Delay -ReportPath::delayIncr(Delay time, - Delay prev, - const MinMax *min_max) const -{ - if (report_sigmas_) - return delayRemove(time, prev); - else - return delayAsFloat(time, min_max, this) - delayAsFloat(prev, min_max, this); -} - bool ReportPath::nextArcAnnotated(const Path *next_path, size_t next_index, @@ -2974,7 +3069,9 @@ ReportPath::reportInputExternalDelay(const Path *first_path, const Pin *first_pin = first_path->pin(graph_); if (!pathFromClkPin(first_path, first_pin)) { const RiseFall *rf = first_path->transition(this); - Arrival time = first_path->arrival() + time_offset; + Arrival time = delaySum(first_path->arrival(), + time_offset, + this); const EarlyLate *early_late = first_path->minMax(this); InputDelay *input_delay = pathInputDelay(first_path); if (input_delay) { @@ -3054,7 +3151,7 @@ ReportPath::reportLine(const char *what, Delay total, const EarlyLate *early_late) const { - reportLine(what, field_blank_, field_blank_, field_blank_, + reportLine(what, field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, total, false, early_late, nullptr, "", nullptr); } @@ -3066,8 +3163,8 @@ ReportPath::reportLineNegative(const char *what, const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, true, early_late, nullptr, - "", nullptr); + field_blank_, field_blank_, total, true /* tota_with_minus */, + early_late, nullptr, "", nullptr); } // Report total, and transition suffix. @@ -3078,55 +3175,56 @@ ReportPath::reportLine(const char *what, const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, false, early_late, rf, "", + field_blank_, field_blank_, total, false, early_late, rf, "", nullptr); } // Report increment, and total. void ReportPath::reportLine(const char *what, - Delay incr, - Delay total, + const Delay &incr, + const Delay &total, const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, total, false, early_late, nullptr, "", + incr, field_blank_, total, false, early_late, nullptr, "", nullptr); } // Report increment, total, and transition suffix. void ReportPath::reportLine(const char *what, - Delay incr, - Delay total, + const Delay &incr, + const Delay &total, const EarlyLate *early_late, const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, total, false, early_late, rf, "", + incr, field_blank_, total, false, early_late, rf, "", nullptr); } // Report slew, increment, and total. void ReportPath::reportLine(const char *what, - Slew slew, - Delay incr, - Delay total, + const Slew &slew, + const Delay &incr, + const Delay &total, const EarlyLate *early_late) const { reportLine(what, field_blank_, slew, field_blank_, - incr, total, false, early_late, nullptr, + incr, field_blank_, total, false, early_late, nullptr, "", nullptr); } void ReportPath::reportLine(const char *what, float cap, - Slew slew, + const Slew &slew, float fanout, - Delay incr, - Delay total, + const Delay &incr, + float variation, + const Delay &total, bool total_with_minus, const EarlyLate *early_late, const RiseFall *rf, @@ -3159,6 +3257,8 @@ ReportPath::reportLine(const char *what, reportFieldDelay(slew, early_late, field, line); else if (field == field_incr_) reportFieldDelay(incr, early_late, field, line); + else if (field == field_variation_) + reportFieldDelay(variation, early_late, field, line); else if (field == field_total_) { if (total_with_minus) reportFieldDelayMinus(total, early_late, field, line); @@ -3171,6 +3271,8 @@ ReportPath::reportLine(const char *what, else reportFieldBlank(field, line); } + else if (field == field_variation_) + reportFieldBlank(field, line); else if (field == field_src_attr_) { if (src_attr != "") reportField(src_attr.c_str(), field, line); @@ -3195,7 +3297,7 @@ ReportPath::reportLine(const char *what, // Only the total field. void ReportPath::reportLineTotal(const char *what, - Delay incr, + const Delay &incr, const EarlyLate *early_late) const { reportLineTotal1(what, incr, false, early_late); @@ -3204,7 +3306,7 @@ ReportPath::reportLineTotal(const char *what, // Only the total field and always with leading minus sign. void ReportPath::reportLineTotalMinus(const char *what, - Delay decr, + const Delay &decr, const EarlyLate *early_late) const { reportLineTotal1(what, decr, true, early_late); @@ -3212,7 +3314,7 @@ ReportPath::reportLineTotalMinus(const char *what, void ReportPath::reportLineTotal1(const char *what, - Delay incr, + const Delay &incr, bool incr_with_minus, const EarlyLate *early_late) const { @@ -3287,7 +3389,7 @@ ReportPath::reportSpaceFieldTime(float value, } void -ReportPath::reportSpaceFieldDelay(Delay value, +ReportPath::reportSpaceFieldDelay(const Delay &value, const EarlyLate *early_late, std::string &line) const { @@ -3296,11 +3398,11 @@ ReportPath::reportSpaceFieldDelay(Delay value, } void -ReportPath::reportTotalDelay(Delay value, +ReportPath::reportTotalDelay(const Delay &value, const EarlyLate *early_late, std::string &line) const { - const char *str = delayAsString(value, early_late, this, digits_); + const char *str = delayAsString(value, early_late, digits_, this); if (stringEq(str, minus_zero_)) // Filter "-0.00" fields. str = plus_zero_; @@ -3309,7 +3411,7 @@ ReportPath::reportTotalDelay(Delay value, // Total time always with leading minus sign. void -ReportPath::reportFieldDelayMinus(Delay value, +ReportPath::reportFieldDelayMinus(const Delay &value, const EarlyLate *early_late, const ReportField *field, std::string &line) const @@ -3317,10 +3419,9 @@ ReportPath::reportFieldDelayMinus(Delay value, if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); else { - const char *str = report_sigmas_ - ? delayAsString(-value, this, digits_) - // Opposite min/max for negative value. - : delayAsString(-value, early_late->opposite(), this, digits_); + // Opposite min/max for negative value. + const char *str = delayAsString(delayDiff(delay_zero, value, this), + early_late->opposite(), digits_, this); if (stringEq(str, plus_zero_)) // Force leading minus sign. str = minus_zero_; @@ -3329,17 +3430,15 @@ ReportPath::reportFieldDelayMinus(Delay value, } void -ReportPath::reportFieldDelay(Delay value, +ReportPath::reportFieldDelay(const Delay &value, const EarlyLate *early_late, const ReportField *field, std::string &line) const { - if (delayAsFloat(value) == field_blank_) + if (value.mean() == field_blank_) reportFieldBlank(field, line); else { - const char *str = report_sigmas_ - ? delayAsString(value, this, digits_) - : delayAsString(value, early_late, this, digits_); + const char *str = delayAsString(value, early_late, digits_, this); if (stringEq(str, minus_zero_)) // Filter "-0.00" fields. str = plus_zero_; diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 642d4314..cad81c74 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -56,12 +56,11 @@ public: bool report_cap, bool report_slew, bool report_fanout, + bool report_variation, bool report_src_attr); int digits() const { return digits_; } void setDigits(int digits); void setNoSplit(bool no_split); - bool reportSigmas() const { return report_sigmas_; } - void setReportSigmas(bool report); ReportField *findField(const char *name) const; // Header above reportPathEnd results. @@ -267,8 +266,8 @@ protected: float clk_time, const EarlyLate *early_late) const; void reportPathLine(const Path *path, - Delay incr, - Arrival time, + const Delay &incr, + const Arrival &time, const char *line_case) const; void reportCommonClkPessimism(const PathEnd *end, Arrival &clk_arrival) const ; @@ -331,6 +330,7 @@ protected: bool report_clk_path, Arrival prev_time, float time_offset) const; + void reportVariation(const Path *path) const; void reportHierPinsThru(const Path *path) const; void reportInputExternalDelay(const Path *path, float time_offset) const; @@ -345,38 +345,39 @@ protected: const EarlyLate *early_late, const RiseFall *rf) const; void reportLine(const char *what, - Delay incr, - Delay total, + const Delay &incr, + const Delay &total, const EarlyLate *early_late) const; void reportLine(const char *what, - Delay incr, - Delay total, + const Delay &incr, + const Delay &total, const EarlyLate *early_late, const RiseFall *rf) const; void reportLine(const char *what, - Slew slew, - Delay incr, - Delay total, + const Slew &slew, + const Delay &incr, + const Delay &total, const EarlyLate *early_late) const; void reportLine(const char *what, float cap, - Slew slew, + const Slew &slew, float fanout, - Delay incr, - Delay total, + const Delay &incr, + float variation, + const Delay &total, bool total_with_minus, const EarlyLate *early_late, const RiseFall *rf, std::string src_attr, const char *line_case) const; void reportLineTotal(const char *what, - Delay incr, + const Delay &incr, const EarlyLate *early_late) const; void reportLineTotalMinus(const char *what, - Delay decr, + const Delay &decr, const EarlyLate *early_late) const; void reportLineTotal1(const char *what, - Delay incr, + const Delay &incr, bool incr_with_minus, const EarlyLate *early_late) const; void reportDashLineTotal() const; @@ -391,17 +392,17 @@ protected: std::string &result) const; void reportSpaceFieldTime(float value, std::string &result) const; - void reportSpaceFieldDelay(Delay value, + void reportSpaceFieldDelay(const Delay &value, const EarlyLate *early_late, std::string &result) const; - void reportFieldDelayMinus(Delay value, + void reportFieldDelayMinus(const Delay &value, const EarlyLate *early_late, const ReportField *field, std::string &result) const; - void reportTotalDelay(Delay value, + void reportTotalDelay(const Delay &value, const EarlyLate *early_late, std::string &result) const; - void reportFieldDelay(Delay value, + void reportFieldDelay(const Delay &value, const EarlyLate *early_late, const ReportField *field, std::string &result) const; @@ -465,8 +466,8 @@ protected: Path &ref_path) const; const char *asRisingFalling(const RiseFall *rf) const; const char *asRiseFall(const RiseFall *rf) const; - Delay delayIncr(Delay time, - Delay prev, + Delay delayIncr(const Delay &time, + const Delay &prev, const MinMax *min_max) const; // Path options. @@ -477,7 +478,6 @@ protected: bool report_net_; bool no_split_; int digits_; - bool report_sigmas_; int start_end_pt_width_; @@ -487,15 +487,15 @@ protected: ReportField *field_capacitance_; ReportField *field_slew_; ReportField *field_fanout_; + ReportField *field_variation_; ReportField *field_src_attr_; ReportField *field_edge_; ReportField *field_case_; + static constexpr float field_blank_ = -1; + int field_width_extra_; const char *plus_zero_; const char *minus_zero_; - - static const float field_blank_; - static const float field_skip_; }; class ReportField diff --git a/search/Search.cc b/search/Search.cc index 92fe7f7d..eeafaace 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1334,7 +1334,7 @@ Search::arrivalsChanged(Vertex *vertex, Path *path2 = tag_bldr->tagMatchPath(tag1); if (path2 == nullptr || path1->tag(this) != path2->tag(this) - || !delayEqual(path1->arrival(), path2->arrival()) + || !delayEqual(path1->arrival(), path2->arrival(), this) || path1->prevEdge(this) != path2->prevEdge(this) || path1->prevArc(this) != path2->prevArc(this) || path1->prevPath() != path2->prevPath()) @@ -1420,8 +1420,8 @@ ArrivalVisitor::pruneCrprArrivals() const ClkInfo *clk_info_no_crpr = path_no_crpr->clkInfo(this); Arrival max_crpr = crpr->maxCrpr(clk_info_no_crpr); Arrival max_arrival_max_crpr = (min_max == MinMax::max()) - ? max_arrival - max_crpr - : max_arrival + max_crpr; + ? delayDiff(max_arrival, max_crpr, this) + : delaySum(max_arrival, max_crpr, this); debugPrint(debug_, "search", 4, " cmp %s %s - %s = %s", tag->to_string(this).c_str(), delayAsString(max_arrival, this), @@ -1622,7 +1622,7 @@ Search::seedClkArrival(const Pin *pin, sdc->exceptionFromClkStates(pin,rf,clk,rf,min_max,states); Tag *tag = findTag(scene, rf, min_max, clk_info, true, nullptr, false, states, true, nullptr); - Arrival arrival(clk_edge->time() + insertion); + Arrival arrival = delaySum(insertion, clk_edge->time(), this); tag_bldr->setArrival(tag, arrival); } @@ -1639,7 +1639,7 @@ Search::seedClkDataArrival(const Pin *pin, Tag *tag = clkDataTag(pin, clk, rf, clk_edge, insertion, min_max, scene); if (tag) { // Data arrivals include insertion delay. - Arrival arrival(clk_edge->time() + insertion); + Arrival arrival = delaySum(insertion, clk_edge->time(), this); tag_bldr->setArrival(tag, arrival); } } @@ -1878,8 +1878,8 @@ Search::inputDelayRefPinArrival(Path *ref_path, Clock *clk = clk_edge->clock(); if (clk->isPropagated()) { const ClkInfo *clk_info = ref_path->clkInfo(this); - ref_arrival = delayAsFloat(ref_path->arrival()); - ref_insertion = delayAsFloat(clk_info->insertion()); + ref_arrival = delayAsFloat(ref_path->arrival(), min_max, this); + ref_insertion = delayAsFloat(clk_info->insertion(), min_max, this); ref_latency = clk_info->latency(); } else { @@ -2186,13 +2186,12 @@ PathVisitor::visitFromPath(const Pin *from_pin, true, min_max, dcalc_ap, sdc); - bool arc_delay_min_max_eq = - fuzzyEqual(delayAsFloat(arc_delay), delayAsFloat(arc_delay_opp)); + bool arc_delay_min_max_eq = delayEqual(arc_delay, arc_delay_opp, this); to_tag = search_->thruClkTag(from_path, from_vertex, from_tag, true, edge, to_rf, arc_delay_min_max_eq, min_max, scene); - if (to_tag) - to_arrival = from_arrival + arc_delay; + if (to_tag) + to_arrival = delaySum(from_arrival, arc_delay, this); } } } @@ -2210,7 +2209,9 @@ PathVisitor::visitFromPath(const Pin *from_pin, const LibertyCell *inst_cell = clk_port->libertyCell(); if (inst_cell->isMacro()) { float slew = delayAsFloat(from_path->slew(this)); - arc_delay -= clk_port->clkTreeDelay(slew, from_rf, min_max); + arc_delay = delayDiff(arc_delay, + clk_port->clkTreeDelay(slew, from_rf, min_max), + this); } } @@ -2237,7 +2238,7 @@ PathVisitor::visitFromPath(const Pin *from_pin, to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_); from_arrival = search_->clkPathArrival(from_path, from_clk_info, clk_edge, min_max); - to_arrival = from_arrival + arc_delay; + to_arrival = delaySum(from_arrival, arc_delay, this); } else to_tag = nullptr; @@ -2309,7 +2310,7 @@ PathVisitor::visitFromPath(const Pin *from_pin, to_propagates_clk, edge, to_rf, arc_delay_min_max_eq, min_max, scene); - to_arrival = from_arrival + arc_delay; + to_arrival = delaySum(from_arrival, arc_delay, this); } } else { @@ -2317,8 +2318,8 @@ PathVisitor::visitFromPath(const Pin *from_pin, || sdc->isPathDelayInternalToBreak(from_pin))) { arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max, dcalc_ap, sdc); - if (!delayInf(arc_delay)) { - to_arrival = from_arrival + arc_delay; + if (!delayInf(arc_delay, this)) { + to_arrival = delaySum(from_arrival, arc_delay, this); to_tag = search_->thruTag(from_tag, edge, to_rf, tag_cache_); } } @@ -2355,11 +2356,11 @@ Search::clkPathArrival(const Path *clk_path, && !clk_info->isPropagated()) { // Ideal clock, apply ideal insertion delay and latency. const EarlyLate *early_late = min_max; - return clk_edge->time() - + clockInsertion(clk_edge->clock(), clk_info->clkSrc(), - clk_edge->transition(), min_max, - early_late, scene->mode()) - + clk_info->latency(); + Arrival insertion = clockInsertion(clk_edge->clock(), clk_info->clkSrc(), + clk_edge->transition(), min_max, + early_late, scene->mode()); + return delaySum(delaySum(insertion, clk_edge->time(), this), + clk_info->latency(), this); } else return clk_path->arrival(); @@ -3166,8 +3167,8 @@ Search::deratedDelay(const Vertex *from_vertex, const Sdc *sdc) { float derate = timingDerate(from_vertex, arc, edge, is_clk, sdc, min_max); - ArcDelay delay = graph_->arcDelay(edge, arc, dcalc_ap); - return delay * derate; + const ArcDelay &delay = graph_->arcDelay(edge, arc, dcalc_ap); + return delayProduct(delay, derate, this);; } float @@ -3568,9 +3569,9 @@ RequiredCmp::requiredsSave(Vertex *vertex, while (path_iter.hasNext()) { Path *path = path_iter.next(); size_t path_index = path->pathIndex(sta); - Required req = requireds_[path_index]; - Required &prev_req = path->required(); - bool changed = !delayEqual(prev_req, req); + const Required req = requireds_[path_index]; + const Required &prev_req = path->required(); + bool changed = !delayEqual(prev_req, req, sta); debugPrint(debug, "search", 3, "required %s save %s -> %s%s", path->to_string(sta).c_str(), delayAsString(prev_req, sta), @@ -3669,8 +3670,8 @@ RequiredVisitor::visitFromToPath(const Pin *, if (to_tag_group && to_tag_group->hasTag(to_tag)) { size_t to_path_index = to_tag_group->pathIndex(to_tag); Path &to_path = to_vertex->paths()[to_path_index]; - Required &to_required = to_path.required(); - Required from_required = to_required - arc_delay; + const Required &to_required = to_path.required(); + Required from_required = delayDiff(to_required, arc_delay, this); debugPrint(debug_, "search", 3, " to tag %2u: %s", to_tag->index(), to_tag->to_string(this).c_str()); @@ -3694,7 +3695,7 @@ RequiredVisitor::visitFromToPath(const Pin *, Tag *to_path_tag = to_path->tag(this); if (Tag::matchNoCrpr(to_path_tag, to_tag)) { Required to_required = to_path->required(); - Required from_required = to_required - arc_delay; + Required from_required = delayDiff(to_required, arc_delay, this); debugPrint(debug_, "search", 3, " to tag %2u: %s", to_path_tag->index(), to_path_tag->to_string(this).c_str()); @@ -3873,14 +3874,14 @@ Slack Search::totalNegativeSlack(const MinMax *min_max) { tnsPreamble(); - Slack tns = 0.0; + DelayDbl tns = 0.0; for (Scene *scene : scenes_) { size_t path_index = scene->pathIndex(min_max); - Slack tns1 = tns_[path_index]; + DelayDbl tns1 = tns_[path_index]; if (delayLess(tns1, tns, this)) tns = tns1; } - return tns; + return delayDblAsDelay(tns); } Slack @@ -3889,7 +3890,7 @@ Search::totalNegativeSlack(const Scene *scene, { tnsPreamble(); PathAPIndex path_ap_index = scene->pathIndex(min_max); - return tns_[path_ap_index]; + return delayDblAsDelay(tns_[path_ap_index]); } void @@ -3976,7 +3977,7 @@ Search::tnsIncr(Vertex *vertex, debugPrint(debug_, "tns", 3, "tns+ %s %s", delayAsString(slack, this), vertex->to_string(this).c_str()); - tns_[path_ap_index] += slack; + delayIncr(tns_[path_ap_index], slack, this); if (tns_slacks_[path_ap_index].contains(vertex)) report_->critical(1513, "tns incr existing vertex"); tns_slacks_[path_ap_index][vertex] = slack; @@ -3995,7 +3996,7 @@ Search::tnsDecr(Vertex *vertex, debugPrint(debug_, "tns", 3, "tns- %s %s", delayAsString(slack, this), vertex->to_string(this).c_str()); - tns_[path_ap_index] -= slack; + delayDecr(tns_[path_ap_index], slack, this); tns_slacks_[path_ap_index].erase(vertex); } } diff --git a/search/Search.i b/search/Search.i index 90ac5e47..f83399b5 100644 --- a/search/Search.i +++ b/search/Search.i @@ -39,6 +39,7 @@ #include "Bfs.hh" #include "Scene.hh" #include "Sta.hh" +#include "StaConfig.hh" using namespace sta; @@ -155,26 +156,29 @@ find_requireds() Sta::sta()->findRequireds(); } -Slack +float total_negative_slack_cmd(const MinMax *min_max) { - return Sta::sta()->totalNegativeSlack(min_max); + Sta *sta = Sta::sta(); + return delayAsFloat(sta->totalNegativeSlack(min_max), min_max, sta); } -Slack +float total_negative_slack_scene_cmd(const Scene *scene, const MinMax *min_max) { - return Sta::sta()->totalNegativeSlack(scene, min_max); + Sta *sta = Sta::sta(); + return delayAsFloat(sta->totalNegativeSlack(scene, min_max), min_max, sta); } -Slack +float worst_slack_cmd(const MinMax *min_max) { + Sta *sta = Sta::sta(); Slack worst_slack; Vertex *worst_vertex; - Sta::sta()->worstSlack(min_max, worst_slack, worst_vertex); - return worst_slack; + sta->worstSlack(min_max, worst_slack, worst_vertex); + return delayAsFloat(worst_slack, min_max, sta); } Vertex * @@ -186,14 +190,15 @@ worst_slack_vertex(const MinMax *min_max) return worst_vertex;; } -Slack +float worst_slack_scene(const Scene *scene, const MinMax *min_max) { + Sta *sta = Sta::sta(); Slack worst_slack; Vertex *worst_vertex; - Sta::sta()->worstSlack(scene, min_max, worst_slack, worst_vertex); - return worst_slack; + sta->worstSlack(scene, min_max, worst_slack, worst_vertex); + return delayAsFloat(worst_slack, min_max, sta); } Path * @@ -224,7 +229,7 @@ vertex_worst_slack_path(Vertex *vertex, return sta->vertexWorstSlackPath(vertex, min_max); } -Slack +float endpoint_slack(const Pin *pin, const char *path_group_name, const MinMax *min_max) @@ -233,7 +238,7 @@ endpoint_slack(const Pin *pin, sta->ensureLibLinked(); if (sta->isGroupPathName(path_group_name, sta->cmdSdc())) { Slack slack = sta->endpointSlack(pin, std::string(path_group_name), min_max); - return sta->units()->timeUnit()->staToUser(delayAsFloat(slack)); + return sta->units()->timeUnit()->staToUser(delayAsFloat(slack, min_max, sta)); } else { sta->report()->error(1577, "%s is not a known path group name.", @@ -407,6 +412,7 @@ set_report_path_fields(bool report_input_pin, bool report_cap, bool report_slew, bool report_fanout, + bool report_variation, bool report_src_attr) { Sta::sta()->setReportPathFields(report_input_pin, @@ -415,6 +421,7 @@ set_report_path_fields(bool report_input_pin, report_cap, report_slew, report_fanout, + report_variation, report_src_attr); } @@ -432,18 +439,6 @@ set_report_path_field_properties(const char *field_name, sta->report()->warn(1575, "unknown report path field %s", field_name); } -void -set_report_path_field_width(const char *field_name, - int width) -{ - Sta *sta = Sta::sta(); - ReportField *field = sta->findReportPathField(field_name); - if (field) - field->setWidth(width); - else - sta->report()->warn(1576, "unknown report path field %s", field_name); -} - void set_report_path_digits(int digits) { @@ -456,12 +451,6 @@ set_report_path_no_split(bool no_split) Sta::sta()->setReportPathNoSplit(no_split); } -void -set_report_path_sigmas(bool report_sigmas) -{ - Sta::sta()->setReportPathSigmas(report_sigmas); -} - void report_path_cmd(Path *path) { @@ -480,25 +469,28 @@ report_path_ends(PathEndSeq *ends) void report_arrival_wrt_clks(const Pin *pin, const Scene *scene, + bool report_variance, int digits) { - Sta::sta()->reportArrivalWrtClks(pin, scene, digits); + Sta::sta()->reportArrivalWrtClks(pin, scene, report_variance, digits); } void report_required_wrt_clks(const Pin *pin, const Scene *scene, + bool report_variance, int digits) { - Sta::sta()->reportRequiredWrtClks(pin, scene, digits); + Sta::sta()->reportRequiredWrtClks(pin, scene, report_variance, digits); } void report_slack_wrt_clks(const Pin *pin, const Scene *scene, + bool report_variance, int digits) { - Sta::sta()->reportSlackWrtClks(pin, scene, digits); + Sta::sta()->reportSlackWrtClks(pin, scene, report_variance, digits); } //////////////////////////////////////////////////////////////// @@ -518,7 +510,9 @@ float worst_clk_skew_cmd(const SetupHold *setup_hold, bool include_internal_latency) { - return Sta::sta()->findWorstClkSkew(setup_hold, include_internal_latency); + Sta *sta = Sta::sta(); + Delay skew = sta->findWorstClkSkew(setup_hold, include_internal_latency); + return delayAsFloat(skew, MinMax::max(), sta); } //////////////////////////////////////////////////////////////// @@ -571,7 +565,7 @@ report_max_skew_checks(const Net *net, //////////////////////////////////////////////////////////////// -Slack +float find_clk_min_period(const Clock *clk, bool ignore_port_paths) { @@ -963,39 +957,37 @@ set_crpr_mode(const char *mode) { Sta *sta = Sta::sta(); if (stringEq(mode, "same_pin")) - Sta::sta()->setCrprMode(CrprMode::same_pin); + sta->setCrprMode(CrprMode::same_pin); else if (stringEq(mode, "same_transition")) - Sta::sta()->setCrprMode(CrprMode::same_transition); + sta->setCrprMode(CrprMode::same_transition); else - sta->report()->critical(1573, "unknown common clk pessimism mode."); + sta->report()->error(1573, "unknown common clk pessimism mode."); } -bool -pocv_enabled() +const char * +pocv_mode() { - return Sta::sta()->pocvEnabled(); + return pocvModeName(Sta::sta()->pocvMode()); } void -set_pocv_enabled(bool enabled) +set_pocv_mode(const char *mode_name) { -#if !SSTA - if (enabled) - Sta::sta()->report()->error(1574, "POCV support requires compilation with SSTA=1."); -#endif - return Sta::sta()->setPocvEnabled(enabled); + Sta *sta = Sta::sta(); + PocvMode mode = findPocvMode(mode_name); + sta->setPocvMode(mode); } float -pocv_sigma_factor() +pocv_quantile() { - return Sta::sta()->sigmaFactor(); + return Sta::sta()->pocvQuantile(); } void -set_pocv_sigma_factor(float factor) +set_pocv_quantile(float quantile) { - Sta::sta()->setSigmaFactor(factor); + Sta::sta()->setPocvQuantile(quantile); } bool @@ -1141,58 +1133,83 @@ Vertex *vertex() { return self->vertex(Sta::sta()); } Path *path() { return self->path(); } RiseFall *end_transition() { return const_cast(self->path()->transition(Sta::sta())); } -Slack slack() { return self->slack(Sta::sta()); } -ArcDelay margin() { return self->margin(Sta::sta()); } -Required data_required_time() { return self->requiredTimeOffset(Sta::sta()); } -Arrival data_arrival_time() { return self->dataArrivalTimeOffset(Sta::sta()); } + +float +slack() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->slack(sta), self->minMax(sta), sta); +} + +float +margin() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->margin(sta), self->minMax(sta), sta); +} + +float +data_required_time() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->requiredTimeOffset(sta), self->minMax(sta), sta); +} + +float +data_arrival_time() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->dataArrivalTimeOffset(sta), self->minMax(sta), sta); +} + +float +target_clk_delay() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->targetClkDelay(sta), self->minMax(sta), sta); +} + +float +source_clk_latency() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->sourceClkLatency(sta), self->minMax(sta), sta); +} + +float +clk_skew() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->clkSkew(sta), self->minMax(sta), sta); +} + const TimingRole *check_role() { return self->checkRole(Sta::sta()); } MinMax *min_max() { return const_cast(self->minMax(Sta::sta())); } -float source_clk_offset() { return self->sourceClkOffset(Sta::sta()); } -Arrival source_clk_latency() { return self->sourceClkLatency(Sta::sta()); } -Arrival source_clk_insertion_delay() -{ return self->sourceClkInsertionDelay(Sta::sta()); } const Clock *target_clk() { return self->targetClk(Sta::sta()); } const ClockEdge *target_clk_edge() { return self->targetClkEdge(Sta::sta()); } Path *target_clk_path() { return self->targetClkPath(); } -float target_clk_time() { return self->targetClkTime(Sta::sta()); } -float target_clk_offset() { return self->targetClkOffset(Sta::sta()); } -float target_clk_mcp_adjustment() -{ return self->targetClkMcpAdjustment(Sta::sta()); } -Arrival target_clk_delay() { return self->targetClkDelay(Sta::sta()); } -Arrival target_clk_insertion_delay() -{ return self->targetClkInsertionDelay(Sta::sta()); } -float target_clk_uncertainty() -{ return self->targetNonInterClkUncertainty(Sta::sta()); } -float inter_clk_uncertainty() -{ return self->interClkUncertainty(Sta::sta()); } -Arrival target_clk_arrival() { return self->targetClkArrival(Sta::sta()); } -bool path_delay_margin_is_external() -{ return self->pathDelayMarginIsExternal();} -Crpr check_crpr() { return self->checkCrpr(Sta::sta()); } -RiseFall *target_clk_end_trans() -{ return const_cast(self->targetClkEndTrans(Sta::sta())); } -Delay clk_skew() { return self->clkSkew(Sta::sta()); } - } %extend Path { float arrival() { - return delayAsFloat(self->arrival()); + Sta *sta = Sta::sta(); + return delayAsFloat(self->arrival(), self->minMax(sta), sta); } float required() { - return delayAsFloat(self->required()); + Sta *sta = Sta::sta(); + return delayAsFloat(self->required(), self->minMax(sta), sta); } float slack() { Sta *sta = Sta::sta(); - return delayAsFloat(self->slack(sta)); + return delayAsFloat(self->slack(sta), self->minMax(sta), sta); } const Pin * diff --git a/search/Search.tcl b/search/Search.tcl index f7a548c5..73ef64b3 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -683,7 +683,6 @@ proc_redirect report_path { proc parse_report_path_options { cmd args_var default_format unknown_key_is_error } { variable path_options - variable report_path_field_width_extra global sta_report_default_digits upvar 1 $args_var args @@ -691,7 +690,7 @@ proc parse_report_path_options { cmd args_var default_format unset path_options } parse_key_args $cmd args path_options {-format -digits -fields} \ - path_options {-no_line_splits -report_sigmas} $unknown_key_is_error + path_options {-no_line_splits} $unknown_key_is_error set format $default_format if [info exists path_options(-format)] { @@ -712,24 +711,8 @@ proc parse_report_path_options { cmd args_var default_format check_positive_integer "-digits" $digits } - set report_sigmas [info exists path_options(-report_sigmas)] - set_report_path_sigmas $report_sigmas - set path_options(num_fmt) "%.${digits}f" set_report_path_digits $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] - } else { - set delay_field_width $field_width - } - foreach field {total incr} { - set_report_path_field_width $field $delay_field_width - } - foreach field {capacitance slew} { - set_report_path_field_width $field $field_width - } set report_input_pin 0 set report_hier_pins 0 @@ -737,6 +720,7 @@ proc parse_report_path_options { cmd args_var default_format set report_net 0 set report_slew 0 set report_fanout 0 + set report_variation 0 set report_src_attr 0 if { [info exists path_options(-fields)] } { foreach field $path_options(-fields) { @@ -752,6 +736,8 @@ proc parse_report_path_options { cmd args_var default_format set report_slew 1 } elseif { [string match "fanout" $field] } { set report_fanout 1 + } elseif { [string match "variation" $field] } { + set report_variation 1 } elseif { [string match "src*" $field] } { set report_src_attr 1 } else { @@ -759,20 +745,21 @@ proc parse_report_path_options { cmd args_var default_format } } } + set_report_path_fields $report_input_pin $report_hier_pins $report_net \ - $report_cap $report_slew $report_fanout $report_src_attr + $report_cap $report_slew $report_fanout $report_variation $report_src_attr set_report_path_no_split [info exists path_options(-no_line_splits)] } ################################################################ -define_cmd_args "report_arrival" {[-scene scene] [-digits digits] pin} +define_cmd_args "report_arrival" {[-scene scene] [-report_variance] [-digits digits] pin} proc report_arrival { args } { global sta_report_default_digits - parse_key_args "report_arrival" args keys {-scene -digits} flags {} + parse_key_args "report_arrival" args keys {-scene -digits} flags {-report_variance} check_argc_eq1 "report_arrival" $args set pin [get_port_pin_error "pin" [lindex $args 0]] @@ -783,17 +770,18 @@ proc report_arrival { args } { } else { set digits $sta_report_default_digits } - report_arrival_wrt_clks $pin $scene $digits + set report_variance [info exists flags(-report_variance)] + report_arrival_wrt_clks $pin $scene $report_variance $digits } ################################################################ -define_cmd_args "report_required" {[-scene scene] [-digits digits] pin} +define_cmd_args "report_required" {[-scene scene] [-report_variance] [-digits digits] pin} proc report_required { args } { global sta_report_default_digits - parse_key_args "report_required" args keys {-scene -digits} flags {} + parse_key_args "report_required" args keys {-scene -digits} flags {-report_variance} check_argc_eq1 "report_required" $args set pin [get_port_pin_error "pin" [lindex $args 0]] @@ -804,17 +792,18 @@ proc report_required { args } { } else { set digits $sta_report_default_digits } - report_required_wrt_clks $pin $scene $digits + set report_variance [info exists flags(-report_variance)] + report_required_wrt_clks $pin $scene $report_variance $digits } ################################################################ -define_cmd_args "report_slack" {[-scene scene] [-digits digits] pin} +define_cmd_args "report_slack" {[-scene scene] [-report_variance] [-digits digits] pin} proc report_slack { args } { global sta_report_default_digits - parse_key_args "report_slack" args keys {-scene -digits} flags {} + parse_key_args "report_slack" args keys {-scene -digits} flags {-report_variance} check_argc_eq1 "report_slack" $args set pin [get_port_pin_error "pin" [lindex $args 0]] @@ -825,7 +814,8 @@ proc report_slack { args } { } else { set digits $sta_report_default_digits } - report_slack_wrt_clks $pin $scene $digits + set report_variance [info exists flags(-report_variance)] + report_slack_wrt_clks $pin $scene $report_variance $digits } ################################################################ diff --git a/search/Sta.cc b/search/Sta.cc index d0d643ee..e8fe636c 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -87,6 +87,9 @@ #include "MakeTimingModel.hh" #include "parasitics/ConcreteParasitics.hh" #include "spice/WritePathSpice.hh" +#include "DelayScalar.hh" +#include "DelayNormal.hh" +#include "DelaySkewNormal.hh" namespace sta { @@ -300,6 +303,7 @@ Sta::makeComponents() makePower(); makeClkSkews(); makeCheckTiming(); + delay_ops_ = new DelayOpsScalar(); setCmdNamespace1(CmdNamespace::sdc); setThreadCount1(defaultThreadCount()); @@ -526,6 +530,7 @@ Sta::~Sta() delete equiv_cells_; delete dispatch_queue_; delete variables_; + delete delay_ops_; deleteContents(parasitics_name_map_); deleteContents(modes_); deleteContents(scenes_); @@ -2262,25 +2267,47 @@ Sta::setCrprMode(CrprMode mode) variables_->setCrprMode(mode); } -bool -Sta::pocvEnabled() const +PocvMode +Sta::pocvMode() const { - return variables_->pocvEnabled(); + return variables_->pocvMode(); } void -Sta::setPocvEnabled(bool enabled) +Sta::setPocvMode(PocvMode mode) { - if (enabled != variables_->pocvEnabled()) + if (mode != variables_->pocvMode()) { + variables_->setPocvMode(mode); + + delete delay_ops_; + switch (mode) { + case PocvMode::scalar: + default: + delay_ops_ = new DelayOpsScalar(); + break; + case PocvMode::normal: + delay_ops_ = new DelayOpsNormal(); + break; + case PocvMode::skew_normal: + delay_ops_ = new DelayOpsSkewNormal(); + break; + } + updateComponentsState(); delaysInvalid(); - variables_->setPocvEnabled(enabled); + } +} + +float +Sta::pocvQuantile() +{ + return variables_->pocvQuantile(); } void -Sta::setSigmaFactor(float factor) +Sta::setPocvQuantile(float quantile) { - if (!fuzzyEqual(factor, sigma_factor_)) { - sigma_factor_ = factor; + if (!fuzzyEqual(quantile, variables_->pocvQuantile())) { + variables_->setPocvQuantile(quantile); search_->arrivalsInvalid(); updateComponentsState(); } @@ -2739,11 +2766,12 @@ Sta::setReportPathFields(bool report_input_pin, bool report_cap, bool report_slew, bool report_fanout, + bool report_variation, bool report_src_attr) { report_path_->setReportFields(report_input_pin, report_hier_pins, report_net, report_cap, report_slew, report_fanout, - report_src_attr); + report_variation, report_src_attr); } ReportField * @@ -2764,12 +2792,6 @@ Sta::setReportPathNoSplit(bool no_split) report_path_->setNoSplit(no_split); } -void -Sta::setReportPathSigmas(bool report_sigmas) -{ - report_path_->setReportSigmas(report_sigmas); -} - void Sta::reportPathEnd(PathEnd *end) { @@ -2811,7 +2833,7 @@ Sta::reportClkSkew(ConstClockSeq &clks, include_internal_latency, digits); } -float +Delay Sta::findWorstClkSkew(const SetupHold *setup_hold, bool include_internal_latency) { @@ -3246,10 +3268,11 @@ Sta::endpointSlack(const Pin *pin, void Sta::reportArrivalWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits) { searchPreamble(); - reportDelaysWrtClks(pin, scene, digits, false, + reportDelaysWrtClks(pin, scene, report_variance, digits, false, [] (const Path *path) { return path->arrival(); }); @@ -3258,9 +3281,10 @@ Sta::reportArrivalWrtClks(const Pin *pin, void Sta::reportRequiredWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits) { - reportDelaysWrtClks(pin, scene, digits, true, + reportDelaysWrtClks(pin, scene, report_variance, digits, true, [] (const Path *path) { return path->required(); }); @@ -3269,9 +3293,10 @@ Sta::reportRequiredWrtClks(const Pin *pin, void Sta::reportSlackWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits) { - reportDelaysWrtClks(pin, scene, digits, true, + reportDelaysWrtClks(pin, scene, report_variance, digits, true, [this] (const Path *path) { return path->slack(this); }); @@ -3280,6 +3305,7 @@ Sta::reportSlackWrtClks(const Pin *pin, void Sta::reportDelaysWrtClks(const Pin *pin, const Scene *scene, + bool report_variance, int digits, bool find_required, PathDelayFunc get_path_delay) @@ -3288,14 +3314,17 @@ Sta::reportDelaysWrtClks(const Pin *pin, Vertex *vertex, *bidir_vertex; graph_->pinVertices(pin, vertex, bidir_vertex); if (vertex) - reportDelaysWrtClks(vertex, scene, digits, find_required, get_path_delay); + reportDelaysWrtClks(vertex, scene, report_variance, digits, + find_required, get_path_delay); if (bidir_vertex) - reportDelaysWrtClks(vertex, scene, digits, find_required, get_path_delay); + reportDelaysWrtClks(vertex, scene, report_variance, digits, + find_required, get_path_delay); } void Sta::reportDelaysWrtClks(Vertex *vertex, const Scene *scene, + bool report_variance, int digits, bool find_required, PathDelayFunc get_path_delay) @@ -3305,13 +3334,15 @@ Sta::reportDelaysWrtClks(Vertex *vertex, else search_->findArrivals(vertex->level()); const Sdc *sdc = scene->sdc(); - reportDelaysWrtClks(vertex, nullptr, scene, digits, get_path_delay); + reportDelaysWrtClks(vertex, nullptr, scene, report_variance, digits, get_path_delay); const ClockEdge *default_clk_edge = sdc->defaultArrivalClock()->edge(RiseFall::rise()); - reportDelaysWrtClks(vertex, default_clk_edge, scene, digits, get_path_delay); + reportDelaysWrtClks(vertex, default_clk_edge, scene, report_variance, + digits, get_path_delay); for (const Clock *clk : sdc->sortedClocks()) { for (const RiseFall *rf : RiseFall::range()) { const ClockEdge *clk_edge = clk->edge(rf); - reportDelaysWrtClks(vertex, clk_edge, scene, digits, get_path_delay); + reportDelaysWrtClks(vertex, clk_edge, scene, report_variance, digits, + get_path_delay); } } } @@ -3320,6 +3351,7 @@ void Sta::reportDelaysWrtClks(Vertex *vertex, const ClockEdge *clk_edge, const Scene *scene, + bool report_variance, int digits, PathDelayFunc get_path_delay) { @@ -3335,13 +3367,13 @@ Sta::reportDelaysWrtClks(Vertex *vertex, report_->reportLine("%s r %s:%s f %s:%s", clk_name.c_str(), formatDelay(RiseFall::rise(), MinMax::min(), - delays, digits).c_str(), + delays, report_variance, digits).c_str(), formatDelay(RiseFall::rise(), MinMax::max(), - delays, digits).c_str(), + delays, report_variance, digits).c_str(), formatDelay(RiseFall::fall(), MinMax::min(), - delays, digits).c_str(), + delays, report_variance, digits).c_str(), formatDelay(RiseFall::fall(), MinMax::max(), - delays, digits).c_str()); + delays, report_variance, digits).c_str()); } } @@ -3360,7 +3392,7 @@ Sta::findDelaysWrtClks(Vertex *vertex, const MinMax *min_max = path->minMax(this); const ClockEdge *path_clk_edge = path->clkEdge(this); if (path_clk_edge == clk_edge - && !delayInf(delay)) + && !delayInf(delay, this)) delays.mergeValue(rf, min_max, delay, this); } return delays; @@ -3370,13 +3402,14 @@ std::string Sta::formatDelay(const RiseFall *rf, const MinMax *min_max, const RiseFallMinMaxDelay &delays, + bool report_variance, int digits) { Delay delay; bool exists; delays.value(rf, min_max, delay, exists); if (exists) - return delayAsString(delay, this, digits); + return delayAsString(delay, min_max, report_variance, digits, this); else return "---"; } @@ -3439,7 +3472,7 @@ MinPeriodEndVisitor::visit(PathEnd *path_end) || !(network->isTopLevelPort(path->pin(sta_)) || pathIsFromInputPort(path_end)))) { Slack slack = path_end->slack(sta_); - float period = clk_->period() - delayAsFloat(slack); + float period = clk_->period() - delayAsFloat(slack, MinMax::min(), sta_); min_period_ = std::max(min_period_, period); } } @@ -3589,7 +3622,7 @@ Sta::setIncrementalDelayTolerance(float tol) graph_delay_calc_->setIncrementalDelayTolerance(tol); } -ArcDelay +const ArcDelay& Sta::arcDelay(Edge *edge, TimingArc *arc, DcalcAPIndex ap_index) @@ -3684,7 +3717,7 @@ Sta::ensureGraph() void Sta::makeGraph() { - graph_ = new Graph(this, 2, dcalcAnalysisPtCount()); + graph_ = new Graph(this, dcalcAnalysisPtCount()); graph_->makeGraph(); } @@ -5535,12 +5568,12 @@ Sta::reportSlewChecks(const Net *net, if (verbose) report_path_->reportLimitVerbose(report_path_->fieldSlew(), check.pin(), check.edge(), - delayAsFloat(check.slew()), + delayAsFloat(check.slew(), min_max, this), check.limit(), check.slack(), check.scene(), min_max); else report_path_->reportLimitShort(report_path_->fieldSlew(), check.pin(), - delayAsFloat(check.slew()), + delayAsFloat(check.slew(), min_max, this), check.limit(), check.slack()); } report_->reportLine(""); diff --git a/search/StaState.cc b/search/StaState.cc index 6ba49123..661c02c9 100644 --- a/search/StaState.cc +++ b/search/StaState.cc @@ -49,11 +49,11 @@ StaState::StaState() : arc_delay_calc_(nullptr), graph_delay_calc_(nullptr), search_(nullptr), + delay_ops_(nullptr), latches_(nullptr), variables_(nullptr), thread_count_(1), - dispatch_queue_(nullptr), - sigma_factor_(1.0) + dispatch_queue_(nullptr) { } diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index d2e6d5ce..8a19ca57 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -166,7 +166,7 @@ WorstSlack::initQueue(PathAPIndex path_ap_index) slack_threshold_ = slack_init_; for(Vertex *vertex : search_->endpoints()) { Slack slack = search_->wnsSlack(vertex, path_ap_index); - if (!delayEqual(slack, slack_init_)) { + if (!delayEqual(slack, slack_init_, this)) { if (delayLess(slack, worst_slack_, this)) setWorstSlack(vertex, slack); if (delayLessEqual(slack, slack_threshold_, this)) @@ -177,8 +177,8 @@ WorstSlack::initQueue(PathAPIndex path_ap_index) } } debugPrint(debug_, "wns", 3, "threshold %s", - delayAsString(slack_threshold_, this)); -// checkQueue(); + delayAsString(slack_threshold_, MinMax::max(), this)); + //checkQueue(); } void @@ -199,7 +199,7 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index) Vertex *threshold_vertex = vertices[threshold_index]; slack_threshold_ = search_->wnsSlack(threshold_vertex, path_ap_index); debugPrint(debug_, "wns", 3, "threshold %s", - delayAsString(slack_threshold_, this)); + delayAsString(slack_threshold_, MinMax::max(), this)); // Reinsert vertices with slack < threshold. queue_->clear(); @@ -281,7 +281,7 @@ WorstSlack::updateWorstSlack(Vertex *vertex, // Mark worst slack as unknown (updated by findWorstSlack(). worst_vertex_ = nullptr; - if (!delayEqual(slack, slack_init_) + if (!delayEqual(slack, slack_init_, this) && delayLessEqual(slack, slack_threshold_, this)) { debugPrint(debug_, "wns", 3, "insert %s %s", vertex->to_string(this).c_str(), diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 3e646bea..1e844ed5 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -484,7 +484,7 @@ WriteSpice::slewAxisMinValue(const TimingArc *arc) { GateTableModel *gate_model = arc->gateTableModel(scene_, min_max_); if (gate_model) { - const TableModel *model = gate_model->delayModel(); + const TableModel *model = gate_model->delayModels()->model(); const TableAxis *axis1 = model->axis1(); TableAxisVariable var1 = axis1->variable(); if (var1 == TableAxisVariable::input_transition_time diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index e29d5a6b..732a651a 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -1198,32 +1198,8 @@ using namespace sta; Tcl_SetObjResult(interp, obj); } -%typemap(out) Delay { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) Arrival { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) Required { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) Slack { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) ArcDelay { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) Slew { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); -} - -%typemap(out) Crpr { - Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +%typemap(in) StringSet* { + $1 = tclListSetConstChar($input, interp); } %typemap(out) Mode* { diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 8667048b..0de899c5 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -170,7 +170,7 @@ tclArcDcalcArg(ArcDcalcArg &gate, obj = Tcl_NewStringObj(to_edge, strlen(to_edge)); Tcl_ListObjAppendElement(interp, list, obj); - const char *input_delay = delayAsString(gate.inputDelay(), sta, 3); + const char *input_delay = delayAsString(gate.inputDelay(), sta); obj = Tcl_NewStringObj(input_delay, strlen(input_delay)); Tcl_ListObjAppendElement(interp, list, obj); diff --git a/tcl/Variables.tcl b/tcl/Variables.tcl index e37f7396..ba8f37b3 100644 --- a/tcl/Variables.tcl +++ b/tcl/Variables.tcl @@ -152,16 +152,42 @@ proc trace_propagate_gated_clock_enable { name1 name2 op } { propagate_gated_clock_enable set_propagate_gated_clock_enable } -trace variable ::sta_pocv_enabled "rw" \ - sta::trace_pocv_enabled +trace variable ::sta_pocv_mode "rw" \ + sta::trace_pocv_mode -proc trace_pocv_enabled { name1 name2 op } { - trace_boolean_var $op ::sta_pocv_enabled \ - pocv_enabled set_pocv_enabled +proc trace_pocv_mode { name1 name2 op } { + global sta_pocv_mode + + if { $op == "r" } { + set sta_pocv_mode [pocv_mode] + } elseif { $op == "w" } { + if { $sta_pocv_mode == "scalar" \ + || $sta_pocv_mode == "normal" \ + || $sta_pocv_mode == "skew_normal" } { + set_pocv_mode $sta_pocv_mode + } else { + sta_error 593 "sta_pocv_mode must be scalar, normal, or skew_normal." + } + } } -# Report path numeric field width is digits + extra. -set report_path_field_width_extra 5 +trace variable ::sta_pocv_quantile "rw" \ + sta::trace_pocv_quantile + +proc trace_pocv_quantile { name1 name2 op } { + global sta_pocv_quantile + + if { $op == "r" } { + set sta_pocv_quantile [pocv_quantile] + } elseif { $op == "w" } { + if { [string is double $sta_pocv_quantile] \ + && $sta_pocv_quantile >= 0.0 } { + set_pocv_quantile $sta_pocv_quantile + } else { + sta_error 594 "sta_pocv_quantile must be a positive floating point number." + } + } +} ################################################################ diff --git a/util/StaConfig.hh.cmake b/util/StaConfig.hh.cmake index 78d2e304..2efd9720 100644 --- a/util/StaConfig.hh.cmake +++ b/util/StaConfig.hh.cmake @@ -6,6 +6,4 @@ #cmakedefine ZLIB_FOUND -#define SSTA ${SSTA} - #define TCL_READLINE ${TCL_READLINE} diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 3d402e79..e018fae9 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1032,7 +1032,7 @@ VerilogDclBus::VerilogDclBus(PortDirection *dir, int VerilogDclBus::size() const { - return abs(to_index_ - from_index_) + 1; + return std::abs(to_index_ - from_index_) + 1; } VerilogDclArg::VerilogDclArg(const std::string &net_name) : From 464d4047ad34df15b74beb18fb6a00213ffbf9a7 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 15 Mar 2026 08:33:49 -0700 Subject: [PATCH 02/14] PathGroup revert f1b33edd9 Signed-off-by: James Cherry --- include/sta/PathGroup.hh | 10 ++-- search/PathGroup.cc | 107 +++++++++++++++++++++++++++------------ 2 files changed, 82 insertions(+), 35 deletions(-) diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index 418238bf..954bc2cc 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -29,7 +29,6 @@ #include #include -#include "BoundedHeap.hh" #include "SdcClass.hh" #include "StaState.hh" #include "SearchClass.hh" @@ -70,7 +69,7 @@ public: ~PathGroup(); const std::string &name() const { return name_; } const MinMax *minMax() const { return min_max_;} - PathEndSeq pathEnds() const; + PathEndSeq pathEnds() const { return path_ends_; } void insert(PathEnd *path_end); // Push group_path_count into path_ends. void pushEnds(PathEndSeq &path_ends); @@ -93,6 +92,9 @@ protected: bool cmp_slack, const MinMax *min_max, const StaState *sta); + void ensureSortedMaxPaths(); + void prune(); + void sort(); std::string name_; int group_path_count_; @@ -101,9 +103,11 @@ protected: bool unique_edges_; float slack_min_; float slack_max_; + PathEndSeq path_ends_; const MinMax *min_max_; bool cmp_slack_; - BoundedHeap heap_; + float threshold_; + std::mutex lock_; const StaState *sta_; }; diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 47086f3b..a60b6c42 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -100,41 +100,42 @@ PathGroup::PathGroup(const char *name, slack_max_(slack_max), min_max_(min_max), cmp_slack_(cmp_slack), - heap_(group_path_count, PathEndLess(cmp_slack, sta)), + threshold_(min_max->initValue()), sta_(sta) { } PathGroup::~PathGroup() { - PathEndSeq path_ends = heap_.extract(); - deleteContents(path_ends); -} - -PathEndSeq -PathGroup::pathEnds() const -{ - return heap_.contents(); + deleteContents(path_ends_); } bool PathGroup::saveable(PathEnd *path_end) { + float threshold; + { + LockGuard lock(lock_); + threshold = threshold_; + } if (cmp_slack_) { // Crpr increases the slack, so check the slack // without crpr first because it is expensive to find. - Slack slack = path_end->slackNoCrpr(sta_); - if (!delayIsInitValue(slack, min_max_) - && delayLessEqual(slack, slack_max_, sta_)) { + Slack slack_no_crpr = path_end->slackNoCrpr(sta_); + if (!delayIsInitValue(slack_no_crpr, min_max_) + && delayLessEqual(slack_no_crpr, threshold, sta_) + && delayLessEqual(slack_no_crpr, slack_max_, sta_)) { // Now check with crpr. - slack = path_end->slack(sta_); - return delayLessEqual(slack, slack_max_, sta_) + Slack slack = path_end->slack(sta_); + return delayLessEqual(slack, threshold, sta_) + && delayLessEqual(slack, slack_max_, sta_) && delayGreaterEqual(slack, slack_min_, sta_); } } else { const Arrival &arrival = path_end->dataArrivalTime(sta_); - return !delayIsInitValue(arrival, min_max_); + return !delayIsInitValue(arrival, min_max_) + && delayGreaterEqual(arrival, threshold, min_max_, sta_); } return false; } @@ -176,33 +177,72 @@ void PathGroup::insert(PathEnd *path_end) { LockGuard lock(lock_); - auto [inserted, displaced] = heap_.insert(path_end); - if (inserted) - path_end->setPathGroup(this); + path_ends_.push_back(path_end); + path_end->setPathGroup(this); + if (group_path_count_ != group_path_count_max + && path_ends_.size() > static_cast(group_path_count_) * 2) + prune(); +} + +void +PathGroup::prune() +{ + sort(); + VertexPathCountMap path_counts; + size_t end_count = 0; + for (unsigned i = 0; i < path_ends_.size(); i++) { + PathEnd *path_end = path_ends_[i]; + Vertex *vertex = path_end->vertex(sta_); + // Squish up to endpoint_path_count path ends per vertex + // up to the front of path_ends_. + if (end_count < static_cast(group_path_count_) + && path_counts[vertex] < static_cast(endpoint_path_count_)) { + path_ends_[end_count++] = path_end; + path_counts[vertex]++; + } + else + delete path_end; + } + path_ends_.resize(end_count); + + // Set a threshold to the bottom of the sorted list that future + // inserts need to beat. + PathEnd *last_end = path_ends_[end_count - 1]; + if (cmp_slack_) + threshold_ = delayAsFloat(last_end->slack(sta_)); else - delete path_end; - if (displaced) - delete *displaced; + threshold_ = delayAsFloat(last_end->dataArrivalTime(sta_)); } void PathGroup::pushEnds(PathEndSeq &path_ends) { - if (!heap_.empty()) { - PathEndSeq ends = heap_.contents(); - path_ends.reserve(path_ends.size() + ends.size()); - // Append heap path ends to path_ends. - path_ends.insert(path_ends.end(), - std::make_move_iterator(ends.begin()), - std::make_move_iterator(ends.end())); - } + ensureSortedMaxPaths(); + for (PathEnd *path_end : path_ends_) + path_ends.push_back(path_end); +} + +void +PathGroup::ensureSortedMaxPaths() +{ + if (path_ends_.size() > static_cast(group_path_count_)) + prune(); + else + sort(); +} + +void +PathGroup::sort() +{ + sta::sort(path_ends_, PathEndLess(cmp_slack_, sta_)); } void PathGroup::clear() { + threshold_ = min_max_->initValue(); LockGuard lock(lock_); - heap_.clear(); + path_ends_.clear(); } //////////////////////////////////////////////////////////////// @@ -857,8 +897,11 @@ PathGroups::enumPathEnds(PathGroup *group, // enumerator. PathEnum path_enum(group_path_count, endpoint_path_count, unique_pins, unique_edges, cmp_slack, this); - for (PathEnd *end : group->pathEnds()) - path_enum.insert(end); + for (PathEnd *end : group->pathEnds()) { + if (group->saveable(end) + || group->enumMinSlackUnderMin(end)) + path_enum.insert(end); + } group->clear(); // Parallel path enumeratation to find the endpoint_path_count/max path ends. From fbd171019ad180564bafd07ce5a5059bc44fde47 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 15 Mar 2026 14:29:58 -0700 Subject: [PATCH 03/14] PathEnd::copy use copy constructors Signed-off-by: James Cherry --- include/sta/PathEnd.hh | 60 ------------------- search/PathEnd.cc | 128 +++-------------------------------------- 2 files changed, 7 insertions(+), 181 deletions(-) diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index 8fc1a1dc..1fca8d5a 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -270,11 +270,6 @@ public: protected: PathEndClkConstrained(Path *path, Path *clk_path); - PathEndClkConstrained(Path *path, - Path *clk_path, - Crpr crpr, - bool crpr_valid); - float sourceClkOffset(const ClockEdge *src_clk_edge, const ClockEdge *tgt_clk_edge, const TimingRole *check_role, @@ -300,11 +295,6 @@ protected: PathEndClkConstrainedMcp(Path *path, Path *clk_path, MultiCyclePath *mcp); - PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); float checkMcpAdjustment(const Path *path, const ClockEdge *tgt_clk_edge, const StaState *sta) const; @@ -341,13 +331,6 @@ public: virtual Delay clkSkew(const StaState *sta); protected: - PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); Delay sourceClkDelay(const StaState *sta) const; virtual Required requiredTimeNoCrpr(const StaState *sta) const; @@ -404,18 +387,6 @@ public: virtual bool ignoreClkLatency(const StaState *sta) const; protected: - PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - Path *disable, - MultiCyclePath *mcp, - PathDelay *path_delay, - Delay src_clk_arrival, - Crpr crpr, - bool crpr_valid); - -private: Path *disable_path_; PathDelay *path_delay_; // Source clk arrival for set_max_delay -ignore_clk_latency. @@ -450,12 +421,6 @@ public: const StaState *sta) const; protected: - PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); Arrival tgtClkDelay(const ClockEdge *tgt_clk_edge, const TimingRole *check_role, const StaState *sta) const; @@ -491,14 +456,6 @@ public: const StaState *sta) const; protected: - PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - Crpr crpr, - bool crpr_valid); - const TimingRole *check_role_; ArcDelay margin_; }; @@ -525,20 +482,12 @@ public: virtual const Path *dataClkPath() const { return data_clk_path_; } protected: - PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); Path *clkPath(Path *path, const StaState *sta); Arrival requiredTimeNoCrpr(const StaState *sta) const; // setup uses zero cycle default virtual int setupDefaultCycles() const { return 0; } -private: Path *data_clk_path_; DataCheck *check_; }; @@ -588,15 +537,6 @@ public: virtual bool ignoreClkLatency(const StaState *sta) const; protected: - PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - OutputDelay *output_delay, - Arrival src_clk_arrival, - Crpr crpr, - bool crpr_valid); void findSrcClkArrival(const StaState *sta); PathDelay *path_delay_; diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 13e23fea..da7d0d77 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -449,7 +449,7 @@ PathEndUnconstrained::PathEndUnconstrained(Path *path) : PathEnd * PathEndUnconstrained::copy() const { - return new PathEndUnconstrained(path_); + return new PathEndUnconstrained(*this); } bool @@ -512,17 +512,6 @@ PathEndClkConstrained::PathEndClkConstrained(Path *path, { } -PathEndClkConstrained::PathEndClkConstrained(Path *path, - Path *clk_path, - Crpr crpr, - bool crpr_valid) : - PathEnd(path), - clk_path_(clk_path), - crpr_(crpr), - crpr_valid_(crpr_valid) -{ -} - void PathEndClkConstrained::setPath(Path *path) { @@ -765,16 +754,6 @@ PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, { } -PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrained(path, clk_path, crpr, crpr_valid), - mcp_(mcp) -{ -} - float PathEndClkConstrainedMcp::targetClkMcpAdjustment(const StaState *sta) const { @@ -945,24 +924,10 @@ PathEndCheck::PathEndCheck(Path *path, { } -PathEndCheck::PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid), - check_arc_(check_arc), - check_edge_(check_edge) -{ -} - PathEnd * PathEndCheck::copy() const { - return new PathEndCheck(path_, check_arc_, check_edge_, - clk_path_, mcp_, crpr_, crpr_valid_); + return new PathEndCheck(*this); } PathEnd::Type @@ -1129,29 +1094,10 @@ PathEndLatchCheck::PathEndLatchCheck(Path *path, src_clk_arrival_ = search->pathClkPathArrival(path_); } -PathEndLatchCheck::PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - Path *disable_path, - MultiCyclePath *mcp, - PathDelay *path_delay, - Delay src_clk_arrival, - Crpr crpr, - bool crpr_valid) : - PathEndCheck(path, check_arc, check_edge, clk_path, mcp, crpr, crpr_valid), - disable_path_(disable_path), - path_delay_(path_delay), - src_clk_arrival_(src_clk_arrival) -{ -} - PathEnd * PathEndLatchCheck::copy() const { - return new PathEndLatchCheck(path_, check_arc_, check_edge_, - clk_path_, disable_path_, mcp_, path_delay_, - src_clk_arrival_, crpr_, crpr_valid_); + return new PathEndLatchCheck(*this); } PathEnd::Type @@ -1357,22 +1303,10 @@ PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, { } -PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid), - output_delay_(output_delay) -{ -} - PathEnd * PathEndOutputDelay::copy() const { - return new PathEndOutputDelay(output_delay_, path_, clk_path_, - mcp_, crpr_, crpr_valid_); + return new PathEndOutputDelay(*this); } PathEnd::Type @@ -1564,24 +1498,10 @@ PathEndGatedClock::PathEndGatedClock(Path *gating_ref, { } -PathEndGatedClock::PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(gating_ref, clk_path, mcp, crpr, crpr_valid), - check_role_(check_role), - margin_(margin) -{ -} - PathEnd * PathEndGatedClock::copy() const { - return new PathEndGatedClock(path_, clk_path_, check_role_, - mcp_, margin_, crpr_, crpr_valid_); + return new PathEndGatedClock(*this); } PathEnd::Type @@ -1680,24 +1600,10 @@ PathEndDataCheck::clkPath(Path *path, return nullptr; } -PathEndDataCheck::PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrainedMcp(data_path, clk_path, mcp, crpr, crpr_valid), - data_clk_path_(data_clk_path), - check_(check) -{ -} - PathEnd * PathEndDataCheck::copy() const { - return new PathEndDataCheck(check_, path_, data_clk_path_, - clk_path_, mcp_, crpr_, crpr_valid_); + return new PathEndDataCheck(*this); } PathEnd::Type @@ -1838,30 +1744,10 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, findSrcClkArrival(sta); } -PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - OutputDelay *output_delay, - Arrival src_clk_arrival, - Crpr crpr, - bool crpr_valid) : - PathEndClkConstrained(path, clk_path, crpr, crpr_valid), - path_delay_(path_delay), - check_arc_(check_arc), - check_edge_(check_edge), - output_delay_(output_delay), - src_clk_arrival_(src_clk_arrival) -{ -} - PathEnd * PathEndPathDelay::copy() const { - return new PathEndPathDelay(path_delay_, path_, clk_path_, - check_arc_, check_edge_, output_delay_, - src_clk_arrival_, crpr_, crpr_valid_); + return new PathEndPathDelay(*this); } PathEnd::Type From 5fbd2a18b57e13fa76c204b10e5df12e13f895af Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 16 Mar 2026 14:53:02 -0700 Subject: [PATCH 04/14] Graph::slew/arcDelay rm static Signed-off-by: James Cherry --- dcalc/GraphDelayCalc.cc | 16 ++++++++-------- graph/Graph.cc | 30 ++++++++++-------------------- include/sta/Graph.hh | 22 +++++++++++----------- include/sta/Path.hh | 2 +- include/sta/Sta.hh | 6 +++--- search/Path.cc | 2 +- search/Sta.cc | 2 +- tcl/Util.tcl | 1 + 8 files changed, 36 insertions(+), 45 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 10c64e93..9d4cd733 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -504,7 +504,7 @@ GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - Slew slew(default_slew); + Slew slew = default_slew; // Top level bidirect driver uses load slew unless // bidirect instance paths are disabled. if (bidirectDrvrSlewFromLoad(drvr_pin)) { @@ -744,7 +744,7 @@ GraphDelayCalc::loadSlewsChanged(DrvrLoadSlews &load_slews_prev, Vertex *load_vertex = graph_->pinLoadVertex(pin); SlewSeq &slews_prev = load_slews_prev[index];; for (size_t i = 0; i < slew_count; i++) { - const Slew &slew = graph_->slew(load_vertex, i); + const Slew slew = graph_->slew(load_vertex, i); if (!delayEqual(slew, slews_prev[i], this)) return true; } @@ -1216,13 +1216,13 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge, Vertex *drvr_vertex = edge->to(graph_); const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); // Merge slews. - const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); + const Slew drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); if (delayGreater(gate_slew, drvr_slew, min_max, this) && !drvr_vertex->slewAnnotated(drvr_rf, min_max) && !edge->role()->isLatchDtoQ()) graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { - const ArcDelay &prev_gate_delay = graph_->arcDelay(edge,arc,ap_index); + const ArcDelay prev_gate_delay = graph_->arcDelay(edge,arc,ap_index); float gate_delay1 = delayAsFloat(gate_delay); float prev_gate_delay1 = delayAsFloat(prev_gate_delay); if (prev_gate_delay1 == 0.0 @@ -1267,12 +1267,12 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, if (!load_vertex->slewAnnotated(drvr_rf, min_max)) { if (drvr_vertex->slewAnnotated(drvr_rf, min_max)) { // Copy the driver slew to the load if it is annotated. - const Slew &drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index); + const Slew drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index); graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); load_changed = true; } else { - const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index); + const Slew slew = graph_->slew(load_vertex, drvr_rf, ap_index); if (!merge || delayGreater(load_slew, slew, min_max, this)) { graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); @@ -1600,7 +1600,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, scene, min_max); - const Slew &to_slew = graph_->slew(to_vertex, to_rf, ap_index); + const Slew to_slew = graph_->slew(to_vertex, to_rf, ap_index); debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s) scene:%s/%s", arc_set->from()->name(), @@ -1682,7 +1682,7 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, if (role->isTimingCheck()) { const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, scene, min_max); DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max); - const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); + const Slew to_slew = graph_->slew(to_vertex, to_rf, slew_index); const ClkNetwork *clk_network = scene->mode()->clkNetwork(); bool from_ideal_clk = clk_network->isIdealClock(from_vertex); const char *from_slew_annotation = from_ideal_clk ? " (ideal clock)" : nullptr; diff --git a/graph/Graph.cc b/graph/Graph.cc index e0c2102d..9878c6ac 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -577,7 +577,7 @@ Graph::gateEdgeArc(const Pin *in_pin, //////////////////////////////////////////////////////////////// -const Slew & +Slew Graph::slew(const Vertex *vertex, const RiseFall *rf, DcalcAPIndex ap_index) @@ -588,14 +588,11 @@ Graph::slew(const Vertex *vertex, const Slew *slews = std::bit_cast(slews_flt); return slews[slew_index]; } - else { - static Slew slew; - slew = slews_flt[slew_index]; - return slew; - } + else + return slews_flt[slew_index]; } -const Slew & +Slew Graph::slew(const Vertex *vertex, size_t index) { @@ -604,11 +601,8 @@ Graph::slew(const Vertex *vertex, const Slew *slews = std::bit_cast(slews_flt); return slews[index]; } - else { - static Slew slew; - slew = slews_flt[index]; - return slew; - } + else + return slews_flt[index]; } void @@ -677,7 +671,7 @@ Graph::deleteEdge(Edge *edge) edges_->destroy(edge); } -const ArcDelay & +ArcDelay Graph::arcDelay(const Edge *edge, const TimingArc *arc, DcalcAPIndex ap_index) const @@ -689,9 +683,7 @@ Graph::arcDelay(const Edge *edge, } else { const float *delays = edge->arcDelays(); - static ArcDelay delay; - delay = delays[index]; - return delay; + return delays[index]; } } @@ -712,7 +704,7 @@ Graph::setArcDelay(Edge *edge, } } -const ArcDelay & +ArcDelay Graph::wireArcDelay(const Edge *edge, const RiseFall *rf, DcalcAPIndex ap_index) @@ -724,9 +716,7 @@ Graph::wireArcDelay(const Edge *edge, } else { const float *delays = edge->arcDelays(); - static ArcDelay delay; - delay = delays[index]; - return delay; + return delays[index]; } } diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 18a50015..6846ae89 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -91,11 +91,11 @@ public: // Reported slew are the same as those in the liberty tables. // reported_slews = measured_slews / slew_derate_from_library // Measured slews are between slew_lower_threshold and slew_upper_threshold. - const Slew &slew(const Vertex *vertex, - const RiseFall *rf, - DcalcAPIndex ap_index); - const Slew &slew(const Vertex *vertex, - size_t index); + Slew slew(const Vertex *vertex, + const RiseFall *rf, + DcalcAPIndex ap_index); + Slew slew(const Vertex *vertex, + size_t index); void setSlew(Vertex *vertex, const RiseFall *rf, DcalcAPIndex ap_index, @@ -124,17 +124,17 @@ public: Edge *&edge, const TimingArc *&arc) const; - const ArcDelay &arcDelay(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const; + ArcDelay arcDelay(const Edge *edge, + const TimingArc *arc, + DcalcAPIndex ap_index) const; void setArcDelay(Edge *edge, const TimingArc *arc, DcalcAPIndex ap_index, const ArcDelay &delay); // Alias for arcDelays using library wire arcs. - const ArcDelay &wireArcDelay(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index); + ArcDelay wireArcDelay(const Edge *edge, + const RiseFall *rf, + DcalcAPIndex ap_index); void setWireArcDelay(Edge *edge, const RiseFall *rf, DcalcAPIndex ap_index, diff --git a/include/sta/Path.hh b/include/sta/Path.hh index c12c080b..7095a269 100644 --- a/include/sta/Path.hh +++ b/include/sta/Path.hh @@ -103,7 +103,7 @@ public: const Required &required() const {return required_; } void setRequired(const Required &required); Slack slack(const StaState *sta) const; - const Slew &slew(const StaState *sta) const; + const Slew slew(const StaState *sta) const; // This takes the same time as prevPath and prevArc combined. Path *prevPath() const; void setPrevPath(Path *prev_path); diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 1464aa06..4c49b7f3 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1142,9 +1142,9 @@ public: const SceneSeq &scenes, const MinMax *min_max); - const ArcDelay &arcDelay(Edge *edge, - TimingArc *arc, - DcalcAPIndex ap_index); + const ArcDelay arcDelay(Edge *edge, + TimingArc *arc, + DcalcAPIndex ap_index); // True if the timing arc has been back-annotated. bool arcDelayAnnotated(Edge *edge, TimingArc *arc, diff --git a/search/Path.cc b/search/Path.cc index 84b4764b..38470f8e 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -330,7 +330,7 @@ Path::pathAnalysisPtIndex(const StaState *sta) const return scene(sta)->pathIndex(minMax(sta)); } -const Slew & +const Slew Path::slew(const StaState *sta) const { DcalcAPIndex slew_index = scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); diff --git a/search/Sta.cc b/search/Sta.cc index e8fe636c..d5d58bbb 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3622,7 +3622,7 @@ Sta::setIncrementalDelayTolerance(float tol) graph_delay_calc_->setIncrementalDelayTolerance(tol); } -const ArcDelay& +const ArcDelay Sta::arcDelay(Edge *edge, TimingArc *arc, DcalcAPIndex ap_index) diff --git a/tcl/Util.tcl b/tcl/Util.tcl index 15443529..3236cb93 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -267,6 +267,7 @@ define_cmd_args "user_run_time" {} # Write run time statistics to filename. proc write_stats { filename } { +puts "stats $filename" if { ![catch {open $filename w} stream] } { puts $stream "[elapsed_run_time] [user_run_time] [memory_usage]" close $stream From 134b547501820da4d14e99a5d42f2e6f378d907e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 15 Mar 2026 14:35:24 -0700 Subject: [PATCH 05/14] use std::format squash --- CMakeLists.txt | 12 + app/StaMain.cc | 9 +- dcalc/ArcDcalcWaveforms.cc | 2 +- dcalc/ArcDelayCalc.cc | 12 +- dcalc/ArnoldiDelayCalc.cc | 16 +- dcalc/ArnoldiReduce.cc | 376 +++--- dcalc/CcsCeffDelayCalc.cc | 244 ++-- dcalc/Delay.cc | 8 +- dcalc/DelayCalc.tcl | 32 +- dcalc/DelayCalcBase.cc | 1 + dcalc/DelayNormal.cc | 9 +- dcalc/DelayScalar.cc | 2 +- dcalc/DelaySkewNormal.cc | 13 +- dcalc/DmpCeff.cc | 300 ++--- dcalc/GraphDelayCalc.cc | 72 +- dcalc/LumpedCapDelayCalc.cc | 2 +- dcalc/PrimaDelayCalc.cc | 201 ++- doc/ApiChanges.txt | 13 + doc/ChangeLog.txt | 1 + etc/FindMessages.tcl | 2 +- graph/Graph.cc | 2 +- graph/Graph.i | 6 +- include/sta/Clock.hh | 5 +- include/sta/ClockGroups.hh | 8 +- include/sta/Debug.hh | 22 +- include/sta/Delay.hh | 10 +- include/sta/DelayNormal.hh | 2 +- include/sta/DelayScalar.hh | 2 +- include/sta/DelaySkewNormal.hh | 2 +- include/sta/Error.hh | 21 +- include/sta/ExceptionPath.hh | 29 +- include/sta/Format.hh | 153 +++ include/sta/ParseBus.hh | 21 +- include/sta/PathGroup.hh | 6 +- include/sta/Report.hh | 154 ++- include/sta/ReportTcl.hh | 20 +- include/sta/Sdc.hh | 22 +- include/sta/SdcNetwork.hh | 2 +- include/sta/Sta.hh | 14 +- include/sta/StringUtil.hh | 34 - include/sta/Units.hh | 5 +- include/sta/VerilogNamespace.hh | 16 +- include/sta/VerilogReader.hh | 69 +- liberty/LibExprReader.cc | 19 +- liberty/Liberty.cc | 31 +- liberty/Liberty.i | 7 +- liberty/LibertyBuilder.cc | 8 +- liberty/LibertyParse.yy | 12 +- liberty/LibertyParser.cc | 69 +- liberty/LibertyReader.cc | 442 +++---- liberty/LibertyReaderPvt.hh | 79 +- liberty/LibertyWriter.cc | 356 +++-- liberty/TableModel.cc | 397 +++--- liberty/Units.cc | 15 +- network/ConcreteLibrary.cc | 23 +- network/ConcreteNetwork.cc | 2 +- network/HpinDrvrLoad.cc | 15 +- network/Network.cc | 51 +- network/Network.i | 6 +- network/ParseBus.cc | 103 +- network/SdcNetwork.cc | 92 +- network/VerilogNamespace.cc | 65 +- parasitics/ConcreteParasitics.cc | 6 +- parasitics/Parasitics.cc | 128 +- parasitics/ReduceParasitics.cc | 22 +- parasitics/ReportParasiticAnnotation.cc | 42 +- parasitics/SpefParse.yy | 9 +- parasitics/SpefReader.cc | 111 +- parasitics/SpefReaderPvt.hh | 14 +- power/Power.cc | 615 ++++----- power/ReportPower.cc | 124 +- power/SaifParse.yy | 2 +- power/SaifReader.cc | 42 +- power/VcdParse.cc | 84 +- power/VcdReader.cc | 97 +- sdc/Clock.cc | 4 +- sdc/ClockGroups.cc | 5 +- sdc/CycleAccting.cc | 24 +- sdc/ExceptionPath.cc | 105 +- sdc/Sdc.cc | 156 +-- sdc/Sdc.i | 26 +- sdc/Sdc.tcl | 6 +- sdc/WriteSdc.cc | 596 ++++----- sdf/ReportAnnotation.cc | 134 +- sdf/SdfParse.yy | 3 +- sdf/SdfReader.cc | 203 ++- sdf/SdfReaderPvt.hh | 24 +- sdf/SdfWriter.cc | 178 ++- search/Bfs.cc | 147 +-- search/CheckMinPulseWidths.cc | 16 +- search/CheckTiming.cc | 99 +- search/CheckTiming.hh | 5 +- search/ClkLatency.cc | 84 +- search/ClkSkew.cc | 123 +- search/Crpr.cc | 8 +- search/Genclks.cc | 201 ++- search/Latches.cc | 10 +- search/Levelize.cc | 147 +-- search/MakeTimingModel.cc | 184 ++- search/Path.cc | 95 +- search/PathEnum.cc | 216 ++- search/PathGroup.cc | 18 +- search/Property.cc | 49 +- search/ReportPath.cc | 365 +++--- search/ReportPath.hh | 10 +- search/Search.cc | 853 +++++------- search/Search.i | 6 +- search/Sim.cc | 169 +-- search/Sta.cc | 1592 +++++++++++------------ search/Tag.cc | 4 +- search/TagGroup.cc | 60 +- search/VisitPathEnds.cc | 4 +- search/WorstSlack.cc | 76 +- spice/WritePathSpice.cc | 113 +- spice/WriteSpice.cc | 528 ++++---- spice/WriteSpice.hh | 8 +- tcl/StaTclTypes.i | 67 +- tcl/TclTypeHelpers.cc | 4 +- util/Debug.cc | 15 - util/Error.cc | 19 +- util/MachineLinux.cc | 4 +- util/Report.cc | 226 +--- util/ReportTcl.cc | 6 +- util/Stats.cc | 23 +- util/StringUtil.cc | 140 +- util/Util.i | 28 +- verilog/VerilogParse.yy | 3 +- verilog/VerilogReader.cc | 571 ++++---- verilog/VerilogWriter.cc | 88 +- 129 files changed, 5898 insertions(+), 6990 deletions(-) create mode 100644 include/sta/Format.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 055927fd..3c6ab462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,6 +403,17 @@ find_package(Threads) find_package(Eigen3 REQUIRED) +# fmt library: fallback when std::format is not available (e.g. GCC 11 on Ubuntu 22.04) +find_package(fmt QUIET) +if(NOT fmt_FOUND) + include(FetchContent) + FetchContent_Declare(fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 10.2.1 + ) + FetchContent_MakeAvailable(fmt) +endif() + include(cmake/FindCUDD.cmake) # configure a header file to pass some of the CMake settings @@ -518,6 +529,7 @@ target_sources(OpenSTA target_link_libraries(OpenSTA Eigen3::Eigen + fmt::fmt ${TCL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${CUDD_LIB} diff --git a/app/StaMain.cc b/app/StaMain.cc index a7147ab0..079112e1 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -96,11 +96,10 @@ sourceTclFile(const char *filename, bool verbose, Tcl_Interp *interp) { - std::string cmd; - stringPrint(cmd, "sta::include_file %s %s %s", - filename, - echo ? "1" : "0", - verbose ? "1" : "0"); + std::string cmd = sta::format("sta::include_file {} {} {}", + filename, + echo ? "1" : "0", + verbose ? "1" : "0"); int code = Tcl_Eval(interp, cmd.c_str()); const char *result = Tcl_GetStringResult(interp); if (result[0] != '\0') diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 6c1e7560..00f1e3bb 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -60,7 +60,7 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, bool vdd_exists; library->supplyVoltage("VDD", vdd, vdd_exists); if (!vdd_exists) - report->error(1751, "VDD not defined in library %s", library->name()); + report->error(1751, "VDD not defined in library {}", library->name()); float slew1 = delayAsFloat(in_slew, min_max, sta); Waveform in_waveform = driver_waveform->waveform(slew1); // Delay time axis. diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index 54143b28..86759411 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -94,24 +94,24 @@ makeArcDcalcArg(const char *inst_name, else { const Network *network = sta->network(); const Instance *inst = network->instance(in_pin); - report->warn(2100, "no timing arc for %s input/driver pins.", + report->warn(2100, "no timing arc for {} input/driver pins.", network->pathName(inst)); } } else - report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name); + report->warn(2101, "{} not a valid rise/fall.", drvr_rf_name); } else - report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name); + report->warn(2102, "Pin {}/{} not found.", inst_name, drvr_port_name); } else - report->warn(2103, "%s not a valid rise/fall.", in_rf_name); + report->warn(2103, "{} not a valid rise/fall.", in_rf_name); } else - report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name); + report->warn(2104, "Pin {}/{} not found.", inst_name, in_port_name); } else - report->warn(2105, "Instance %s not found.", inst_name); + report->warn(2105, "Instance {} not found.", inst_name); return ArcDcalcArg(); } diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 2b44188e..c85d1998 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -1156,7 +1156,7 @@ ra_hinv(double y, ex = exp(-x); f = x+ex-1.0-y; if (f<-1e-8 || f>1e-8) - debugPrint(debug, "arnoldi", 1, "y f %g %g", y, f); + debugPrint(debug, "arnoldi", 1, "y f {:g} {:g}", y, f); return x; } @@ -1290,7 +1290,7 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, s = s - f/df; if (std::abs(f)>.5e-12) // .5ps - debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p %g tlohi %s err %s", + debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p {:g} tlohi {} err {}", p, units_->timeUnit()->asString(tlohi), units_->timeUnit()->asString(f)); @@ -1399,7 +1399,7 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, double ceff,tlohi,t50_sy,r,s,t50_sr,rdelay; float df, sf; - debugPrint(debug_, "arnoldi", 1, "ctot=%s", + debugPrint(debug_, "arnoldi", 1, "ctot={}", units_->capacitanceUnit()->asString(ctot)); rdelay = ra_rdelay_1(tab,ctot); @@ -1421,18 +1421,18 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, if (debug_->check("arnoldi", 1)) { double p = 1.0/(r*ctot); double thix,tlox; - debugPrint(debug_, "arnoldi", 1, "at r=%s s=%s", + debugPrint(debug_, "arnoldi", 1, "at r={} s={}", units_->resistanceUnit()->asString(r), units_->timeUnit()->asString(s)); thix = ra_solve_for_t(p,s,vhi); tlox = ra_solve_for_t(p,s,vlo); tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, df, sf); - debugPrint(debug_, "arnoldi", 1, "table slew (in_slew %s ctot %s) = %s", + debugPrint(debug_, "arnoldi", 1, "table slew (in_slew {} ctot {}) = {}", units_->timeUnit()->asString(tab->in_slew), units_->capacitanceUnit()->asString(ctot), delayAsString(sf, this)); tlohi = slew_derate*delayAsFloat(sf); - debugPrint(debug_, "arnoldi", 1, "tlohi %s %s", + debugPrint(debug_, "arnoldi", 1, "tlohi {} {}", units_->timeUnit()->asString(tlohi), units_->timeUnit()->asString(tlox-thix)); } @@ -1468,11 +1468,11 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, // new mvs at ceff s = ra_get_s(D,tab,r,ceff); - debugPrint(debug_, "arnoldi", 1, "new mvs s = %s", + debugPrint(debug_, "arnoldi", 1, "new mvs s = {}", units_->timeUnit()->asString(s)); } } - debugPrint(debug_, "arnoldi", 1, "r %s s %s ceff_time %s ceff %s", + debugPrint(debug_, "arnoldi", 1, "r {} s {} ceff_time {} ceff {}", units_->resistanceUnit()->asString(r), units_->timeUnit()->asString(s), units_->timeUnit()->asString(ceff_time), diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index 5fee6823..046e8e8d 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. // (c) 2018 Nefelus, Inc. @@ -34,6 +34,7 @@ #include "Network.hh" #include "Units.hh" #include "Arnoldi.hh" +#include "Format.hh" #include "parasitics/ConcreteParasiticsPvt.hh" namespace sta { @@ -43,10 +44,7 @@ rcmodel::rcmodel() : { } -rcmodel::~rcmodel() -{ - free(pinV); -} +rcmodel::~rcmodel() { free(pinV); } float rcmodel::capacitance() const @@ -67,7 +65,7 @@ struct ts_point ParasiticNode *node_; int eN; bool is_term; - int tindex; // index into termV of corresponding term + int tindex; // index into termV of corresponding term ts_edge **eV; bool visited; ts_edge *in_edge; @@ -85,7 +83,6 @@ struct ts_edge //////////////////////////////////////////////////////////////// - const int ArnoldiReduce::ts_point_count_incr_ = 1024; const int ArnoldiReduce::ts_edge_count_incr_ = 1024; @@ -96,31 +93,32 @@ ArnoldiReduce::ArnoldiReduce(StaState *sta) : termNmax(256), dNmax(8) { - ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point)); - ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int)); - ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*)); - _u0 = (double*)malloc(ts_pointNmax*sizeof(double)); - _u1 = (double*)malloc(ts_pointNmax*sizeof(double)); - y = (double*)malloc(ts_pointNmax*sizeof(double)); - iv = (double*)malloc(ts_pointNmax*sizeof(double)); - r = (double*)malloc(ts_pointNmax*sizeof(double)); - c = (double*)malloc(ts_pointNmax*sizeof(double)); - par = (int*)malloc(ts_pointNmax*sizeof(int)); + ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point)); + ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int)); + ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *)); + _u0 = (double *)malloc(ts_pointNmax * sizeof(double)); + _u1 = (double *)malloc(ts_pointNmax * sizeof(double)); + y = (double *)malloc(ts_pointNmax * sizeof(double)); + iv = (double *)malloc(ts_pointNmax * sizeof(double)); + r = (double *)malloc(ts_pointNmax * sizeof(double)); + c = (double *)malloc(ts_pointNmax * sizeof(double)); + par = (int *)malloc(ts_pointNmax * sizeof(int)); - ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge)); - ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*)); - ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*)); + ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge)); + ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *)); + ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *)); - pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*)); - termV = (int*)malloc(termNmax*sizeof(int)); - outV = (int*)malloc(termNmax*sizeof(int)); + pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *)); + termV = (int *)malloc(termNmax * sizeof(int)); + outV = (int *)malloc(termNmax * sizeof(int)); - d = (double*)malloc(dNmax*sizeof(double)); - e = (double*)malloc(dNmax*sizeof(double)); - U = (double**)malloc(dNmax*sizeof(double*)); - U0 = (double*)malloc(dNmax*termNmax*sizeof(double)); + d = (double *)malloc(dNmax * sizeof(double)); + e = (double *)malloc(dNmax * sizeof(double)); + U = (double **)malloc(dNmax * sizeof(double *)); + U0 = (double *)malloc(dNmax * termNmax * sizeof(double)); int h; - for (h=0;hparasitics(min_max); - parasitic_network_ = reinterpret_cast(parasitic); + parasitic_network_ = reinterpret_cast(parasitic); loadWork(); return makeRcmodelDrv(); @@ -200,7 +198,7 @@ ArnoldiReduce::loadWork() ts_edge *e; int tindex; - for (p = p0; p!=pend; p++) { + for (p = p0; p != pend; p++) { p->node_ = nullptr; p->eN = 0; p->is_term = false; @@ -246,14 +244,14 @@ ArnoldiReduce::loadWork() e++; } - for (p=p0;p!=pend;p++) { + for (p = p0; p != pend; p++) { if (p->node_) { p->eV = eV; eV += p->eN; p->eN = 0; } } - for (e=e0;e!=eend;e++) { + for (e = e0; e != eend; e++) { e->from->eV[e->from->eN++] = e; if (e->to != e->from) e->to->eV[e->to->eN++] = e; @@ -267,30 +265,33 @@ ArnoldiReduce::allocPoints() free(par); free(c); free(r); - free(iv); free(y); free(_u1); free(_u0); + free(iv); + free(y); + free(_u1); + free(_u0); free(ts_pordV); free(ts_ordV); free(ts_pointV); ts_pointNmax = ts_pointN + ts_point_count_incr_; - ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point)); - ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int)); - ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*)); - _u0 = (double*)malloc(ts_pointNmax*sizeof(double)); - _u1 = (double*)malloc(ts_pointNmax*sizeof(double)); - y = (double*)malloc(ts_pointNmax*sizeof(double)); - iv = (double*)malloc(ts_pointNmax*sizeof(double)); - r = (double*)malloc(ts_pointNmax*sizeof(double)); - c = (double*)malloc(ts_pointNmax*sizeof(double)); - par = (int*)malloc(ts_pointNmax*sizeof(int)); + ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point)); + ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int)); + ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *)); + _u0 = (double *)malloc(ts_pointNmax * sizeof(double)); + _u1 = (double *)malloc(ts_pointNmax * sizeof(double)); + y = (double *)malloc(ts_pointNmax * sizeof(double)); + iv = (double *)malloc(ts_pointNmax * sizeof(double)); + r = (double *)malloc(ts_pointNmax * sizeof(double)); + c = (double *)malloc(ts_pointNmax * sizeof(double)); + par = (int *)malloc(ts_pointNmax * sizeof(int)); } if (ts_edgeN > ts_edgeNmax) { free(ts_edgeV); free(ts_eV); free(ts_stackV); ts_edgeNmax = ts_edgeN + ts_edge_count_incr_; - ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge)); - ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*)); - ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*)); + ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge)); + ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *)); + ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *)); } } @@ -302,65 +303,69 @@ ArnoldiReduce::allocTerms(int nterms) free(outV); free(termV); free(pinV); - termNmax = nterms+256; - pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*)); - termV = (int*)malloc(termNmax*sizeof(int)); - outV = (int*)malloc(termNmax*sizeof(int)); + termNmax = nterms + 256; + pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *)); + termV = (int *)malloc(termNmax * sizeof(int)); + outV = (int *)malloc(termNmax * sizeof(int)); - U0 = (double*)malloc(dNmax*termNmax*sizeof(double)); + U0 = (double *)malloc(dNmax * termNmax * sizeof(double)); int h; - for (h=0;h(node)]]; + return &ts_pointV[pt_map_[reinterpret_cast(node)]]; } rcmodel * ArnoldiReduce::makeRcmodelDrv() { ParasiticNode *drv_node = - parasitics_->findParasiticNode(parasitic_network_, drvr_pin_); + parasitics_->findParasiticNode(parasitic_network_, drvr_pin_); ts_point *pdrv = findPt(drv_node); makeRcmodelDfs(pdrv); getRC(); - if (ctot_ < 1e-22) // 1e-10ps + if (ctot_ < 1e-22) // 1e-10ps return nullptr; setTerms(pdrv); makeRcmodelFromTs(); return makeRcmodelFromW(); } -#define ts_orient( pp, ee) \ - if (ee->from!=pp) { ee->to = ee->from; ee->from = pp; } +#define ts_orient(pp, ee) \ + if (ee->from != pp) { \ + ee->to = ee->from; \ + ee->from = pp; \ + } void ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv) { bool loop = false; int k; - ts_point *p,*q; + ts_point *p, *q; ts_point *p0 = ts_pointV; ts_point *pend = p0 + ts_pointN; - for (p=p0;p!=pend;p++) + for (p = p0; p != pend; p++) p->visited = 0; ts_edge *e; ts_edge **stackV = ts_stackV; int stackN = 1; stackV[0] = e = pdrv->eV[0]; - ts_orient(pdrv,e); + ts_orient(pdrv, e); pdrv->visited = 1; pdrv->in_edge = nullptr; pdrv->ts = 0; - ts_ordV[0] = pdrv-p0; + ts_ordV[0] = pdrv - p0; ts_pordV[0] = pdrv; ts_ordN = 1; - while (stackN>0) { - e = stackV[stackN-1]; + while (stackN > 0) { + e = stackV[stackN - 1]; q = e->to; if (q->visited) { @@ -368,47 +373,53 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv) // ignore, and do not even set *loop if (e->to != e->from) loop = true; - } else { + } + else { // try to descend q->visited = 1; q->ts = ts_ordN++; ts_pordV[q->ts] = q; - ts_ordV[q->ts] = q-p0; + ts_ordV[q->ts] = q - p0; q->in_edge = e; - if (q->eN>1) { - for (k=0;keN;k++) if (q->eV[k] != e) break; + if (q->eN > 1) { + for (k = 0; k < q->eN; k++) + if (q->eV[k] != e) + break; e = q->eV[k]; - ts_orient(q,e); + ts_orient(q, e); stackV[stackN++] = e; - continue; // descent + continue; // descent } } // try to ascend - while (--stackN>=0) { + while (--stackN >= 0) { e = stackV[stackN]; p = e->from; // find e in p->eV - for (k=0;keN;k++) if (p->eV[k]==e) break; + for (k = 0; k < p->eN; k++) + if (p->eV[k] == e) + break; // if (k==p->eN) notice(0,"ERROR, e not found!\n"); ++k; - if (k>=p->eN) continue; + if (k >= p->eN) + continue; e = p->eV[k]; // check that next sibling is not the incoming edge - if (stackN>0 && e==stackV[stackN-1]) { - ++k; - if (k>=p->eN) continue; - e = p->eV[k]; + if (stackN > 0 && e == stackV[stackN - 1]) { + ++k; + if (k >= p->eN) + continue; + e = p->eV[k]; } - ts_orient(p,e); + ts_orient(p, e); stackV[stackN++] = e; break; } - } // while (stackN) + } // while (stackN) if (loop) - debugPrint(debug_, "arnoldi", 1, "net %s loop", - network_->pathName(drvr_pin_)); + debugPrint(debug_, "arnoldi", 1, "net {} loop", network_->pathName(drvr_pin_)); } // makeRcmodelGetRC @@ -418,13 +429,12 @@ ArnoldiReduce::getRC() ts_point *p, *p0 = ts_pointV; ts_point *pend = p0 + ts_pointN; ctot_ = 0.0; - for (p=p0;p!=pend;p++) { + for (p = p0; p != pend; p++) { p->c = 0.0; p->r = 0.0; if (p->node_) { ParasiticNode *node = p->node_; - double cap = parasitics_->nodeGndCap(node) - + pinCapacitance(node); + double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node); if (cap > 0.0) { p->c = cap; ctot_ += cap; @@ -433,11 +443,9 @@ ArnoldiReduce::getRC() p->c = 0.0; if (p->in_edge && p->in_edge->resistor_) p->r = parasitics_->value(p->in_edge->resistor_); - if (!(p->r>=0.0 && p->r<100e+3)) { // 0 < r < 100kohm - debugPrint(debug_, "arnoldi", 1, - "R value %g out of range, drvr pin %s", - p->r, - network_->pathName(drvr_pin_)); + if (!(p->r >= 0.0 && p->r < 100e+3)) { // 0 < r < 100kohm + debugPrint(debug_, "arnoldi", 1, "R value {:g} out of range, drvr pin {}", + p->r, network_->pathName(drvr_pin_)); } } } @@ -466,7 +474,7 @@ ArnoldiReduce::pinCapacitance(ParasiticNode *node) LibertyPort *lib_port = network_->libertyPort(port); const Sdc *sdc = scene_->sdc(); if (lib_port) - pin_cap = sdc->pinCapacitance(pin,rf_, scene_, min_max_); + pin_cap = sdc->pinCapacitance(pin, rf_, scene_, min_max_); else if (network_->isTopLevelPort(pin)) pin_cap = sdc->portExtCap(port, rf_, min_max_); } @@ -479,13 +487,15 @@ ArnoldiReduce::setTerms(ts_point *pdrv) // termV: from drv-ordered to fixed order // outV: from drv-ordered to ts_pordV ts_point *p; - int k,k0; + int k, k0; termV[0] = k0 = pdrv->tindex; - for (k=1;kts; } @@ -498,38 +508,37 @@ ArnoldiReduce::makeRcmodelFromTs() ts_point *p, *p0 = ts_pointV; int n = ts_ordN; int nterms = termN; - int i,j,k,h; + int i, j, k, h; if (debug_->check("arnoldi", 1)) { - for (k=0;kts, - p-p0, + debugPrint(debug_, "arnoldi", 1, "T{} P{} c={}", p->ts, p - p0, units_->capacitanceUnit()->asString(p->c)); if (p->is_term) - debugPrint(debug_, "arnoldi", 1, " term %d", p->tindex); + debugPrint(debug_, "arnoldi", 1, " term {}", p->tindex); if (p->in_edge) - debugPrint(debug_, "arnoldi", 1, " from T%d,P%ld r=%s", - p->in_edge->from->ts, - p->in_edge->from-p0, + debugPrint(debug_, "arnoldi", 1, " from T{} P{} r={}", + p->in_edge->from->ts, p->in_edge->from - p0, units_->resistanceUnit()->asString(p->r)); } - for (i=0;ic; - for (j=1;jc; r[j] = p->r; @@ -537,92 +546,99 @@ ArnoldiReduce::makeRcmodelFromTs() } sum = 0.0; - for (j=0;jcapacitanceUnit()->asString(sum)); ctot_ = sum; sqc_ = sqrt(sum); - double sqrt_ctot_inv = 1.0/sqc_; - for (j=0;j0;j--) { - iv[j] += c[j]*u0[j]; + for (j = n - 1; j > 0; j--) { + iv[j] += c[j] * u0[j]; iv[par[j]] += iv[j]; } - iv[0] += c[0]*u0[0]; + iv[0] += c[0] * u0[0]; y[0] = 0.0; - for (j=1;jcheck("arnoldi", 1)) { - report_->reportLine("tridiagonal reduced matrix, drvr pin %s", - network_->pathName(drvr_pin_)); - report_->reportLine("order %d n %d",order,n); - for (h=0;hreportLine(" d[%d] %s e[%d] %s", - h, - units_->timeUnit()->asString(d[h]), - h, - units_->timeUnit()->asString(e[h])); + report_->report("tridiagonal reduced matrix, drvr pin {}", + network_->pathName(drvr_pin_)); + report_->report("order {} n {}", order, n); + for (h = 0; h < order; h++) { + if (h < order - 1) + report_->report(" d[{}] {} e[{}] {}", h, + units_->timeUnit()->asString(d[h]), h, + units_->timeUnit()->asString(e[h])); else - report_->reportLine(" d[%d] %s", - h, - units_->timeUnit()->asString(d[h])); - std::string line = stdstrPrint("U[%d]",h); - for (i=0;ireportLineString(line); + report_->report(" d[{}] {}", h, units_->timeUnit()->asString(d[h])); + std::string line = sta::format("U[{}]", h); + for (i = 0; i < nterms; i++) + line += sta::format(" {:6.2e}", U[h][i]); + report_->reportLine(line); } } } @@ -630,29 +646,33 @@ ArnoldiReduce::makeRcmodelFromTs() rcmodel * ArnoldiReduce::makeRcmodelFromW() { - int j,h; + int j, h; int n = termN; rcmodel *mod = new rcmodel(); mod->order = order; mod->n = n; - if (order>0) { - int totd = order + order - 1 + order*n; - mod->d = (double *)malloc(totd*sizeof(double)); - if (order>1) mod->e = mod->d + order; - else mod->e = nullptr; - mod->U = (double **)malloc(order*sizeof(double*)); + if (order > 0) { + int totd = order + order - 1 + order * n; + mod->d = (double *)malloc(totd * sizeof(double)); + if (order > 1) + mod->e = mod->d + order; + else + mod->e = nullptr; + mod->U = (double **)malloc(order * sizeof(double *)); mod->U[0] = mod->d + order + order - 1; - for (h=1;hU[h]=mod->U[0] + h*n; - for (h=0;hU[h] = mod->U[0] + h * n; + for (h = 0; h < order; h++) { mod->d[h] = d[h]; - if (he[h] = e[h]; - for (j=0;je[h] = e[h]; + for (j = 0; j < n; j++) mod->U[h][j] = U[h][j]; } } - mod->pinV = (const Pin **)malloc(n*sizeof(const Pin*)); - for (j=0;jpinV = (const Pin **)malloc(n * sizeof(const Pin *)); + for (j = 0; j < n; j++) { int k = termV[j]; mod->pinV[j] = pinV[k]; } @@ -662,4 +682,4 @@ ArnoldiReduce::makeRcmodelFromW() return mod; } -} // namespace +} // namespace sta diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 31762fd0..0228df30 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "CcsCeffDelayCalc.hh" @@ -63,10 +63,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) : { } -CcsCeffDelayCalc::~CcsCeffDelayCalc() -{ - delete table_dcalc_; -} +CcsCeffDelayCalc::~CcsCeffDelayCalc() { delete table_dcalc_; } ArcDelayCalc * CcsCeffDelayCalc::copy() @@ -95,8 +92,8 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, OutputWaveforms *output_waveforms = table_model->outputWaveforms(); Parasitics *parasitics = scene->parasitics(min_max); parasitics->piModel(parasitic, c2_, rpi_, c1_); - if (output_waveforms - && rpi_ > 0.0 && c1_ > 0.0 + if (output_waveforms && rpi_ > 0.0 + && c1_ > 0.0 // Bounds check because extrapolating waveforms does not work for shit. && output_waveforms->slewAxis()->inBounds(in_slew_) && output_waveforms->capAxis()->inBounds(c2_) @@ -107,7 +104,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, drvr_rf_ = arc->toEdge()->asRiseFall(); drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); if (!vdd_exists) - report_->error(1700, "VDD not defined in library %s", drvr_library->name()); + report_->error(1700, "VDD not defined in library {}", drvr_library->name()); vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; @@ -115,12 +112,12 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, drvr_cell->ensureVoltageWaveforms(scenes_); output_waveforms_ = output_waveforms; ref_time_ = output_waveforms_->referenceTime(in_slew_); - debugPrint(debug_, "ccs_dcalc", 1, "%s %s", - drvr_cell->name(), + debugPrint(debug_, "ccs_dcalc", 1, "{} {}", drvr_cell->name(), drvr_rf_->shortName()); double gate_delay, drvr_slew; gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew); - return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map); + return makeResult(drvr_library, drvr_rf_, gate_delay, drvr_slew, + load_pin_index_map); } } return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, @@ -140,19 +137,19 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, gate_delay = region_times_[region_vth_idx_] - ref_time_; drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); debugPrint(debug_, "ccs_dcalc", 2, - "gate_delay %s drvr_slew %s (initial)", + "gate_delay {} drvr_slew {} (initial)", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); float prev_drvr_slew = drvr_slew; constexpr int max_iterations = 5; for (int iter = 0; iter < max_iterations; iter++) { - debugPrint(debug_, "ccs_dcalc", 2, "iteration %d", iter); + debugPrint(debug_, "ccs_dcalc", 2, "iteration {}", iter); // Init drvr ramp model for vl. for (size_t i = 0; i <= region_count_; i++) { region_ramp_times_[i] = region_times_[i]; if (i < region_count_) region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i]) - / (region_times_[i + 1] - region_times_[i]); + / (region_times_[i + 1] - region_times_[i]); } for (size_t i = 0; i < region_count_; i++) { @@ -172,15 +169,14 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, double q2 = v2 * c2_ + c1_v2 * c1_; double ceff = (q2 - q1) / (v2 - v1); - debugPrint(debug_, "ccs_dcalc", 2, "ceff %s", + debugPrint(debug_, "ccs_dcalc", 2, "ceff {}", capacitance_unit_->asString(ceff)); region_ceff_[i] = ceff; } findCsmWaveform(); gate_delay = region_times_[region_vth_idx_] - ref_time_; drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); - debugPrint(debug_, "ccs_dcalc", 2, - "gate_delay %s drvr_slew %s", + debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); if (std::abs(drvr_slew - prev_drvr_slew) < .01 * prev_drvr_slew) @@ -215,68 +211,68 @@ CcsCeffDelayCalc::initRegions(const LibertyLibrary *drvr_library, double vth_vh = (vh_ - vth_); switch (region_count_) { - case 4: - region_vth_idx_ = 2; - region_volts_ = {0.0, vl_, vth_, vh_, vdd_}; - break; - case 5: { - region_vth_idx_ = 2; - double v1 = vth_ + .7 * vth_vh; - region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_}; - break; - } - case 6: { - region_vth_idx_ = 2; - double v1 = vth_ + .3 * vth_vh; - double v2 = vth_ + .6 * vth_vh; - region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_}; - break; - } - case 7: { - region_vth_idx_ = 2; - region_vh_idx_ = 5; - double v1 = vth_ + .3 * vth_vh; - double v2 = vth_ + .6 * vth_vh; - double v3 = vh_ + .5 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_}; - break; - } - case 8: { - region_vth_idx_ = 2; - region_vh_idx_ = 6; - double v1 = vth_ + .25 * vth_vh; - double v2 = vth_ + .50 * vth_vh; - double v3 = vth_ + .75 * vth_vh; - double v4 = vh_ + .5 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_}; - break; - } - case 9: { - region_vth_idx_ = 2; - region_vh_idx_ = 7; - double v1 = vth_ + .2 * vth_vh; - double v2 = vth_ + .4 * vth_vh; - double v3 = vth_ + .6 * vth_vh; - double v4 = vth_ + .8 * vth_vh; - double v5 = vh_ + .5 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_}; - break; - } - case 10: { - region_vth_idx_ = 2; - region_vh_idx_ = 7; - double v1 = vth_ + .2 * vth_vh; - double v2 = vth_ + .4 * vth_vh; - double v3 = vth_ + .6 * vth_vh; - double v4 = vth_ + .8 * vth_vh; - double v5 = vh_ + .3 * (vdd_ - vh_); - double v6 = vh_ + .6 * (vdd_ - vh_); - region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_}; - break; - } - default: - report_->error(1701, "unsupported ccs region count."); - break; + case 4: + region_vth_idx_ = 2; + region_volts_ = {0.0, vl_, vth_, vh_, vdd_}; + break; + case 5: { + region_vth_idx_ = 2; + double v1 = vth_ + .7 * vth_vh; + region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_}; + break; + } + case 6: { + region_vth_idx_ = 2; + double v1 = vth_ + .3 * vth_vh; + double v2 = vth_ + .6 * vth_vh; + region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_}; + break; + } + case 7: { + region_vth_idx_ = 2; + region_vh_idx_ = 5; + double v1 = vth_ + .3 * vth_vh; + double v2 = vth_ + .6 * vth_vh; + double v3 = vh_ + .5 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_}; + break; + } + case 8: { + region_vth_idx_ = 2; + region_vh_idx_ = 6; + double v1 = vth_ + .25 * vth_vh; + double v2 = vth_ + .50 * vth_vh; + double v3 = vth_ + .75 * vth_vh; + double v4 = vh_ + .5 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_}; + break; + } + case 9: { + region_vth_idx_ = 2; + region_vh_idx_ = 7; + double v1 = vth_ + .2 * vth_vh; + double v2 = vth_ + .4 * vth_vh; + double v3 = vth_ + .6 * vth_vh; + double v4 = vth_ + .8 * vth_vh; + double v5 = vh_ + .5 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_}; + break; + } + case 10: { + region_vth_idx_ = 2; + region_vh_idx_ = 7; + double v1 = vth_ + .2 * vth_vh; + double v2 = vth_ + .4 * vth_vh; + double v3 = vth_ + .6 * vth_vh; + double v4 = vth_ + .8 * vth_vh; + double v5 = vh_ + .3 * (vdd_ - vh_); + double v6 = vh_ + .6 * (vdd_ - vh_); + region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_}; + break; + } + default: + report_->error(1701, "unsupported ccs region count."); + break; } fill(region_ceff_.begin(), region_ceff_.end(), c2_ + c1_); } @@ -285,15 +281,15 @@ void CcsCeffDelayCalc::findCsmWaveform() { for (size_t i = 0; i < region_count_; i++) { - double t1 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i], - region_volts_[i]); + double t1 = + output_waveforms_->voltageTime(in_slew_, region_ceff_[i], region_volts_[i]); double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i], region_volts_[i + 1]); region_begin_times_[i] = t1; region_end_times_[i] = t2; double time_offset = (i == 0) - ? 0.0 - : t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]); + ? 0.0 + : t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]); region_time_offsets_[i] = time_offset; if (i == 0) @@ -312,10 +308,8 @@ CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library, const LoadPinIndexMap &load_pin_index_map) { ArcDcalcResult dcalc_result(load_pin_index_map.size()); - debugPrint(debug_, "ccs_dcalc", 2, - "gate_delay %s drvr_slew %s", - delayAsString(gate_delay, this), - delayAsString(drvr_slew, this)); + debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}", + delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); @@ -342,8 +336,7 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, bool elmore_exists = false; float elmore = 0.0; - if (parasitic_ - && parasitics_->isPiElmore(parasitic_)) + if (parasitic_ && parasitics_->isPiElmore(parasitic_)) parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); if (elmore_exists && @@ -371,7 +364,7 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, region_ramp_times_[i] = region_times_[i]; if (i < region_count_) region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i]) - / (region_times_[i + 1] - region_times_[i]); + / (region_times_[i + 1] - region_times_[i]); } vl_fail_ = false; @@ -387,10 +380,8 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, slew = drvr_slew; fail("load delay threshold crossing"); } - debugPrint(debug_, "ccs_dcalc", 2, - "load %s delay %s slew %s", - network_->pathName(load_pin), - delayAsString(delay, this), + debugPrint(debug_, "ccs_dcalc", 2, "load {} delay {} slew {}", + network_->pathName(load_pin), delayAsString(delay, this), delayAsString(slew, this)); } @@ -455,12 +446,12 @@ CcsCeffDelayCalc::findVlTime(double v, double t_init = region_ramp_times_[0]; double t_final = region_ramp_times_[region_count_]; bool root_fail = false; - double time = findRoot([&] (double t, - double &y, - double &dy) { - vl(t, elmore, y, dy); - y -= v; - }, t_init, t_final + elmore * 3.0, .001, 20, root_fail); + double time = findRoot( + [&](double t, double &y, double &dy) { + vl(t, elmore, y, dy); + y -= v; + }, + t_init, t_final + elmore * 3.0, .001, 20, root_fail); vl_fail_ |= root_fail; return time; } @@ -485,7 +476,7 @@ PinSeq CcsCeffDelayCalc::watchPins() const { PinSeq pins; - for (const auto& [pin, values] : watch_pin_values_) + for (const auto &[pin, values] : watch_pin_values_) pins.push_back(pin); return pins; } @@ -521,8 +512,8 @@ CcsCeffDelayCalc::drvrWaveform() drvr_volts->push_back(v); } } - TableAxisPtr drvr_time_axis = std::make_shared(TableAxisVariable::time, - std::move(*drvr_times)); + TableAxisPtr drvr_time_axis = + std::make_shared(TableAxisVariable::time, std::move(*drvr_times)); delete drvr_times; Table drvr_table(drvr_volts, drvr_time_axis); return drvr_table; @@ -553,8 +544,8 @@ CcsCeffDelayCalc::loadWaveform(const Pin *load_pin) double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v; load_volts->push_back(v1); } - TableAxisPtr load_time_axis = std::make_shared(TableAxisVariable::time, - std::move(*load_times)); + TableAxisPtr load_time_axis = std::make_shared( + TableAxisVariable::time, std::move(*load_times)); delete load_times; Table load_table(load_volts, load_time_axis); return load_table; @@ -576,10 +567,9 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, float elmore = 0.0; if (parasitic_) { parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); - bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, - drvr_rf, scene, min_max); - if (dcalc_success - && elmore_exists) { + bool dcalc_success = + makeWaveformPreamble(in_pin, in_rf, drvr_pin, drvr_rf, scene, min_max); + if (dcalc_success && elmore_exists) { FloatSeq *load_times = new FloatSeq; FloatSeq *load_volts = new FloatSeq; for (size_t j = 0; j <= region_count_; j++) { @@ -598,8 +588,8 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v; load_volts->push_back(v1); } - TableAxisPtr load_time_axis = std::make_shared(TableAxisVariable::time, - std::move(*load_times)); + TableAxisPtr load_time_axis = std::make_shared( + TableAxisVariable::time, std::move(*load_times)); delete load_times; Table load_table(load_volts, load_time_axis); return load_table; @@ -628,7 +618,7 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, break; } if (edge) { - TimingArc *arc = nullptr; + TimingArc *arc = nullptr; for (TimingArc *arc1 : edge->timingArcSet()->arcs()) { if (arc1->fromEdge()->asRiseFall() == in_rf && arc1->toEdge()->asRiseFall() == drvr_rf) { @@ -643,9 +633,9 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, if (parasitic_) { parasitics_->piModel(parasitic_, c2_, rpi_, c1_); LoadPinIndexMap load_pin_index_map = - graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex); - gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, - load_pin_index_map, scene, min_max); + graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex); + gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, load_pin_index_map, + scene, min_max); return true; } } @@ -669,12 +659,12 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, Parasitic *pi_elmore = nullptr; const RiseFall *rf = arc->toEdge()->asRiseFall(); if (parasitic && !parasitics_->isPiElmore(parasitic)) { - pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, - scene, min_max); + pi_elmore = + parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, scene, min_max); } - std::string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, - pi_elmore, load_pin_index_map, - scene, min_max, digits); + std::string report = + table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, pi_elmore, + load_pin_index_map, scene, min_max, digits); parasitics_->deleteDrvrReducedParasitics(drvr_pin); return report; } @@ -684,7 +674,7 @@ CcsCeffDelayCalc::fail(const char *reason) { // Report failures with a unique debug flag. if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1)) - report_->reportLine("delay_calc: CCS failed - %s", reason); + report_->report("delay_calc: CCS failed - {}", reason); } -} // namespace +} // namespace sta diff --git a/dcalc/Delay.cc b/dcalc/Delay.cc index 7154e3d8..c40bd429 100644 --- a/dcalc/Delay.cc +++ b/dcalc/Delay.cc @@ -215,7 +215,7 @@ delayDblAsDelay(DelayDbl &delay) return Delay(delay.mean(), delay.meanShift(), delay.stdDev2(), delay.skewness()); } -const char * +std::string delayAsString(const Delay &delay, const StaState *sta) { @@ -223,7 +223,7 @@ delayAsString(const Delay &delay, sta->units()->timeUnit()->digits(), sta); } -const char * +std::string delayAsString(const Delay &delay, const EarlyLate *early_late, const StaState *sta) @@ -231,7 +231,7 @@ delayAsString(const Delay &delay, return delayAsString(delay, early_late, sta->units()->timeUnit()->digits(), sta); } -const char * +std::string delayAsString(const Delay &delay, const EarlyLate *early_late, int digits, @@ -242,7 +242,7 @@ delayAsString(const Delay &delay, return unit->asString(mean_std_dev, digits); } -const char * +std::string delayAsString(const Delay &delay, const EarlyLate *early_late, bool report_variance, diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl index 7d15b5a9..3658c219 100644 --- a/dcalc/DelayCalc.tcl +++ b/dcalc/DelayCalc.tcl @@ -131,7 +131,7 @@ proc set_delay_calculator { alg } { if { [is_delay_calc_name $alg] } { set_delay_calculator_cmd $alg } else { - sta_error 195 "delay calculator $alg not found." + sta_error 2500 "delay calculator $alg not found." } } @@ -154,38 +154,38 @@ proc set_assigned_delay { args } { if [info exists keys(-from)] { set from_pins [get_port_pins_error "from_pins" $keys(-from)] } else { - sta_error 196 "set_assigned_delay missing -from argument." + sta_error 2501 "set_assigned_delay missing -from argument." } if [info exists keys(-to)] { set to_pins [get_port_pins_error "to_pins" $keys(-to)] } else { - sta_error 182 "set_assigned_delay missing -to argument." + sta_error 2502 "set_assigned_delay missing -to argument." } set delay [lindex $args 0] if {![string is double $delay]} { - sta_error 183 "set_assigned_delay delay is not a float." + sta_error 2503 "set_assigned_delay delay is not a float." } set delay [time_ui_sta $delay] if {[info exists flags(-cell)] && [info exists flags(-net)]} { - sta_error 184 "set_annotated_delay -cell and -net options are mutually excluive." + sta_error 2504 "set_annotated_delay -cell and -net options are mutually excluive." } elseif {[info exists flags(-cell)]} { if { $from_pins != {} } { set inst [[lindex $from_pins 0] instance] foreach pin $from_pins { if {[$pin instance] != $inst} { - sta_error 185 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]." + sta_error 2505 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]." } } foreach pin $to_pins { if {[$pin instance] != $inst} { - sta_error 186 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" + sta_error 2506 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" } } } } elseif {![info exists flags(-net)]} { - sta_error 187 "set_assigned_delay -cell or -net required." + sta_error 2508 "set_assigned_delay -cell or -net required." } foreach from_pin $from_pins { set from_vertices [$from_pin vertices] @@ -229,7 +229,7 @@ proc set_assigned_delay2 {from_vertex to_vertex to_rf scene min_max delay} { } $edge_iter finish if { !$matched } { - sta_error 193 "set_assigned_delay no timing arcs found between from/to pins." + sta_error 2509 "set_assigned_delay no timing arcs found between from/to pins." } } @@ -250,7 +250,7 @@ proc set_assigned_check { args } { if { [info exists keys(-from)] } { set from_pins [get_port_pins_error "from_pins" $keys(-from)] } else { - sta_error 188 "set_assigned_check missing -from argument." + sta_error 2510 "set_assigned_check missing -from argument." } set from_rf "rise_fall" if { [info exists keys(-clock)] } { @@ -259,14 +259,14 @@ proc set_assigned_check { args } { || $clk_arg eq "fall" } { set from_rf $clk_arg } else { - sta_error 189 "set_assigned_check -clock must be rise or fall." + sta_error 2511 "set_assigned_check -clock must be rise or fall." } } if { [info exists keys(-to)] } { set to_pins [get_port_pins_error "to_pins" $keys(-to)] } else { - sta_error 190 "set_assigned_check missing -to argument." + sta_error 2512 "set_assigned_check missing -to argument." } set to_rf [parse_rise_fall_flags flags] set scene [parse_scene keys] @@ -281,7 +281,7 @@ proc set_assigned_check { args } { } elseif { [info exists flags(-removal)] } { set role "removal" } else { - sta_error 191 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.." + sta_error 2513 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.." } set cond "" if { [info exists key(-cond)] } { @@ -289,7 +289,7 @@ proc set_assigned_check { args } { } set check_value [lindex $args 0] if { ![string is double $check_value] } { - sta_error 192 "set_assigned_check check_value is not a float." + sta_error 2514 "set_assigned_check check_value is not a float." } set check_value [time_ui_sta $check_value] @@ -341,7 +341,7 @@ proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \ } $edge_iter finish if { !$matched } { - sta_error 194 "set_assigned_check no check arcs found between from/to pins." + sta_error 2516 "set_assigned_check no check arcs found between from/to pins." } } @@ -362,7 +362,7 @@ proc set_assigned_transition { args } { set slew [lindex $args 0] if {![string is double $slew]} { - sta_error 210 "set_assigned_transition transition is not a float." + sta_error 2518 "set_assigned_transition transition is not a float." } set slew [time_ui_sta $slew] set pins [get_port_pins_error "pins" [lindex $args 1]] diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index 25429d68..f153e44c 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -123,6 +123,7 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin, wire_delay += (rf == RiseFall::rise()) ? wire_delay_delta : -wire_delay_delta; + float load_slew_delta = load_library->slewUpperThreshold(rf) - load_library->slewLowerThreshold(rf); float drvr_slew_derate = drvr_library->slewDerateFromLibrary(); diff --git a/dcalc/DelayNormal.cc b/dcalc/DelayNormal.cc index 31f03a52..2c3cb059 100644 --- a/dcalc/DelayNormal.cc +++ b/dcalc/DelayNormal.cc @@ -29,6 +29,7 @@ #include "Error.hh" #include "Fuzzy.hh" #include "Units.hh" +#include "Format.hh" #include "StaState.hh" #include "Variables.hh" @@ -217,15 +218,15 @@ DelayOpsNormal::div(float delay1, return Delay(delay1 / delay2.mean()); } -const char * +std::string DelayOpsNormal::asStringVariance(const Delay &delay, int digits, const StaState *sta) const { const Unit *unit = sta->units()->timeUnit(); - return stringPrintTmp("%s[%s]", - unit->asString(delay.mean(), digits), - unit->asString(delay.stdDev(), digits)); + return sta::format("{}[{}]", + unit->asString(delay.mean(), digits), + unit->asString(delay.stdDev(), digits)); } } // namespace diff --git a/dcalc/DelayScalar.cc b/dcalc/DelayScalar.cc index 69346355..6ad49178 100644 --- a/dcalc/DelayScalar.cc +++ b/dcalc/DelayScalar.cc @@ -193,7 +193,7 @@ DelayOpsScalar::div(float delay1, return Delay(delay1 / delay2.mean()); } -const char * +std::string DelayOpsScalar::asStringVariance(const Delay &delay, int digits, const StaState *sta) const diff --git a/dcalc/DelaySkewNormal.cc b/dcalc/DelaySkewNormal.cc index 82bdfbe4..306634ba 100644 --- a/dcalc/DelaySkewNormal.cc +++ b/dcalc/DelaySkewNormal.cc @@ -29,6 +29,7 @@ #include "Error.hh" #include "Fuzzy.hh" #include "Units.hh" +#include "Format.hh" #include "StaState.hh" #include "Variables.hh" @@ -276,17 +277,17 @@ DelayOpsSkewNormal::div(float delay1, return Delay(delay1 / delay2.mean()); } -const char * +std::string DelayOpsSkewNormal::asStringVariance(const Delay &delay, int digits, const StaState *sta) const { const Unit *unit = sta->units()->timeUnit(); - return stringPrintTmp("%s[%s,%s,%s]", - unit->asString(delay.mean(), digits), - unit->asString(delay.meanShift(), digits), - unit->asString(delay.stdDev(), digits), - sta->units()->scalarUnit()->asString(delay.skewness(), digits)); + return sta::format("{}[{},{},{}]", + unit->asString(delay.mean(), digits), + unit->asString(delay.meanShift(), digits), + unit->asString(delay.stdDev(), digits), + sta->units()->scalarUnit()->asString(delay.skewness(), digits)); } } // namespace diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 48114895..38c04648 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. // "Performance Computation for Precharacterized CMOS Gates with RC Loads", @@ -36,6 +36,7 @@ #include #include +#include "Format.hh" #include "Report.hh" #include "Debug.hh" #include "Units.hh" @@ -97,7 +98,7 @@ newtonRaphson(const int max_iter, const int n, const double x_tol, // eval(state) is called to fill fvec and fjac. - std::function eval, + std::function eval, // Temporaries supplied by caller. double *fvec, double **fjac, @@ -122,7 +123,8 @@ luDecomp(double **a, class DmpAlg : public StaState { public: - DmpAlg(int nr_order, StaState *sta); + DmpAlg(int nr_order, + StaState *sta); ~DmpAlg() override = default; virtual const char *name() = 0; // Set driver model and pi model parameters for delay calculation. @@ -136,9 +138,9 @@ public: double c2, double rpi, double c1); - virtual void gateDelaySlew(// Return values. - double &delay, - double &slew) = 0; + virtual void gateDelaySlew( // Return values. + double &delay, + double &slew) = 0; virtual void loadDelaySlew(const Pin *load_pin, double elmore, // Return values. @@ -188,9 +190,9 @@ protected: void showX(); void showFvec(); void showJacobian(); - void findDriverDelaySlew(// Return values. - double &delay, - double &slew); + void findDriverDelaySlew( // Return values. + double &delay, + double &slew); double findVoCrossing(double vth, double lower_bound, double upper_bound); @@ -260,7 +262,7 @@ protected: double fjac_storage_[max_nr_order_ * max_nr_order_]; double *fjac_[max_nr_order_]; double scale_[max_nr_order_]; - double p_[max_nr_order_ ]; + double p_[max_nr_order_]; int index_[max_nr_order_]; // Driver slew used to check load delay. @@ -274,7 +276,7 @@ protected: }; DmpAlg::DmpAlg(int nr_order, - StaState *sta): + StaState *sta) : StaState(sta), c2_(0.0), rpi_(0.0), @@ -328,14 +330,13 @@ DmpAlg::findDriverParams(double ceff) double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt; x_[DmpParam::dt] = dt; x_[DmpParam::t0] = t0; - newtonRaphson(100, x_, nr_order_, driver_param_tol, - [this] () { evalDmpEqns(); }, - fvec_, fjac_, index_, p_, scale_); + newtonRaphson( + 100, x_, nr_order_, driver_param_tol, [this]() { evalDmpEqns(); }, fvec_, + fjac_, index_, p_, scale_); t0_ = x_[DmpParam::t0]; dt_ = x_[DmpParam::dt]; - debugPrint(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s", - units_->timeUnit()->asString(t0_), - units_->timeUnit()->asString(dt_), + debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}", + units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_), units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); if (debug_->check("dmp_ceff", 4)) showVo(); @@ -409,8 +410,7 @@ DmpAlg::dy(double t, } else { dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt; - dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) - + y0dt(t1 - dt, cl) / dt; + dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) + y0dt(t1 - dt, cl) / dt; dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt; } } @@ -433,14 +433,14 @@ void DmpAlg::showX() { for (int i = 0; i < nr_order_; i++) - report_->reportLine("%4s %12.3e", dmp_param_index_strings[i], x_[i]); + report_->report("{:4} {:12.3e}", dmp_param_index_strings[i], x_[i]); } void DmpAlg::showFvec() { for (int i = 0; i < nr_order_; i++) - report_->reportLine("%4s %12.3e", dmp_func_index_strings[i], fvec_[i]); + report_->report("{:4} {:12.3e}", dmp_func_index_strings[i], fvec_[i]); } void @@ -448,21 +448,21 @@ DmpAlg::showJacobian() { std::string line = " "; for (int j = 0; j < nr_order_; j++) - line += stdstrPrint("%12s", dmp_param_index_strings[j]); - report_->reportLineString(line); + line += sta::format("{:12}", dmp_param_index_strings[j]); + report_->reportLine(line); line.clear(); for (int i = 0; i < nr_order_; i++) { - line += stdstrPrint("%4s ", dmp_func_index_strings[i]); + line += sta::format("{:4} ", dmp_func_index_strings[i]); for (int j = 0; j < nr_order_; j++) - line += stdstrPrint("%12.3e ", fjac_[i][j]); - report_->reportLineString(line); + line += sta::format("{:12.3e} ", fjac_[i][j]); + report_->reportLine(line); } } void -DmpAlg::findDriverDelaySlew(// Return values. - double &delay, - double &slew) +DmpAlg::findDriverDelaySlew( // Return values. + double &delay, + double &slew) { double t_upper = voCrossingUpperBound(); delay = findVoCrossing(vth_, t0_, t_upper); @@ -478,17 +478,15 @@ DmpAlg::findVoCrossing(double vth, double t_lower, double t_upper) { - FindRootFunc vo_func = [&] (double t, - double &y, - double &dy) { + FindRootFunc vo_func = [&](double t, double &y, double &dy) { double vo, vo_dt; Vo(t, vo, vo_dt); y = vo - vth; dy = vo_dt; }; bool fail; - double t_vth = findRoot(vo_func, t_lower, t_upper, vth_time_tol, - find_root_max_iter, fail); + double t_vth = + findRoot(vo_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail); if (fail) throw DmpError("find Vo crossing failed"); return t_vth; @@ -510,7 +508,7 @@ DmpAlg::Vo(double t, V0(t1, v0, dv0_dt); vo = v0 / dt_; - dvo_dt = dv0_dt / dt_; + dvo_dt = dv0_dt / dt_; } else { double v0, dv0_dt; @@ -527,12 +525,12 @@ DmpAlg::Vo(double t, void DmpAlg::showVo() { - report_->reportLine(" t vo(t)"); + report_->report(" t vo(t)"); double ub = voCrossingUpperBound(); for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) { double vo, dvo_dt; Vo(t, vo, dvo_dt); - report_->reportLine(" %g %g", t, vo); + report_->report(" {:g} {:g}", t, vo); } } @@ -581,8 +579,7 @@ DmpAlg::loadDelaySlew(const Pin *, } delay = delay1; slew = slew1; - } - catch (DmpError &error) { + } catch (DmpError &error) { fail(error.what()); delay = elmore_; slew = drvr_slew_; @@ -596,17 +593,15 @@ DmpAlg::findVlCrossing(double vth, double t_lower, double t_upper) { - FindRootFunc vl_func = [&] (double t, - double &y, - double &dy) { + FindRootFunc vl_func = [&](double t, double &y, double &dy) { double vl, vl_dt; Vl(t, vl, vl_dt); y = vl - vth; dy = vl_dt; }; bool fail; - double t_vth = findRoot(vl_func, t_lower, t_upper, vth_time_tol, - find_root_max_iter, fail); + double t_vth = + findRoot(vl_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail); if (fail) throw DmpError("find Vl crossing failed"); return t_vth; @@ -650,12 +645,12 @@ DmpAlg::Vl(double t, void DmpAlg::showVl() { - report_->reportLine(" t vl(t)"); + report_->report(" t vl(t)"); double ub = vlCrossingUpperBound(); for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) { double vl, dvl_dt; Vl(t, vl, dvl_dt); - report_->reportLine(" %g %g", t, vl); + report_->report(" {:g} {:g}", t, vl); } } @@ -664,12 +659,11 @@ DmpAlg::fail(const char *reason) { // Report failures with a unique debug flag. if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1)) - report_->reportLine("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s", - reason, - units_->capacitanceUnit()->asString(c2_), - units_->resistanceUnit()->asString(rpi_), - units_->capacitanceUnit()->asString(c1_), - units_->resistanceUnit()->asString(rd_)); + report_->report("delay_calc: DMP failed - {} c2={} rpi={} c1={} rd={}", reason, + units_->capacitanceUnit()->asString(c2_), + units_->resistanceUnit()->asString(rpi_), + units_->capacitanceUnit()->asString(c1_), + units_->resistanceUnit()->asString(rd_)); } //////////////////////////////////////////////////////////////// @@ -690,9 +684,9 @@ public: double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; + void gateDelaySlew( // Return values. + double &delay, + double &slew) override; void loadDelaySlew(const Pin *, double elmore, // Return values. @@ -712,8 +706,9 @@ private: double &dvl_dt) override; }; -DmpCap::DmpCap(StaState *sta): - DmpAlg(1, sta) +DmpCap::DmpCap(StaState *sta) : + DmpAlg(1, + sta) { } @@ -730,17 +725,17 @@ DmpCap::init(const LibertyLibrary *drvr_library, double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP cap"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, - rd, in_slew, c2, rpi, c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, + c1); ceff_ = c1 + c2; } void -DmpCap::gateDelaySlew(// Return values. - double &delay, - double &slew) +DmpCap::gateDelaySlew( // Return values. + double &delay, + double &slew) { - debugPrint(debug_, "dmp_ceff", 3, " ceff = %s", + debugPrint(debug_, "dmp_ceff", 3, " ceff = {}", units_->capacitanceUnit()->asString(ceff_)); gateCapDelaySlew(ceff_, delay, slew); drvr_slew_ = slew; @@ -778,7 +773,7 @@ DmpCap::voCrossingUpperBound() } void -DmpCap::Vl0(double , +DmpCap::Vl0(double, // Return values. double &vl, double &dvl_dt) @@ -805,9 +800,9 @@ public: double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; + void gateDelaySlew( // Return values. + double &delay, + double &slew) override; void evalDmpEqns() override; double voCrossingUpperBound() override; @@ -843,7 +838,8 @@ private: }; DmpPi::DmpPi(StaState *sta) : - DmpAlg(3, sta), + DmpAlg(3, + sta), p1_(0.0), p2_(0.0), z1_(0.0), @@ -871,8 +867,8 @@ DmpPi::init(const LibertyLibrary *drvr_library, double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP Pi"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, - in_slew, c2, rpi, c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, + c1); // Find poles/zeros. z1_ = 1.0 / (rpi_ * c1_); @@ -896,9 +892,9 @@ DmpPi::init(const LibertyLibrary *drvr_library, } void -DmpPi::gateDelaySlew(// Return values. - double &delay, - double &slew) +DmpPi::gateDelaySlew( // Return values. + double &delay, + double &slew) { driver_valid_ = false; try { @@ -907,23 +903,21 @@ DmpPi::gateDelaySlew(// Return values. double table_delay, table_slew; gateCapDelaySlew(ceff_, table_delay, table_slew); delay = table_delay; - //slew = table_slew; + // slew = table_slew; try { double vo_delay, vo_slew; findDriverDelaySlew(vo_delay, vo_slew); driver_valid_ = true; // Save Vo delay to measure load wire delay waveform. vo_delay_ = vo_delay; - //delay = vo_delay; + // delay = vo_delay; slew = vo_slew; - } - catch (DmpError &error) { + } catch (DmpError &error) { fail(error.what()); // Fall back to table slew. slew = table_slew; } - } - catch (DmpError &error) { + } catch (DmpError &error) { fail(error.what()); // Driver calculation failed - use Ceff=c1+c2. ceff_ = c1_ + c2_; @@ -937,8 +931,7 @@ DmpPi::findDriverParamsPi() { try { findDriverParams(c2_ + c1_); - } - catch (DmpError &) { + } catch (DmpError &) { findDriverParams(c2_); } } @@ -981,36 +974,33 @@ DmpPi::evalDmpEqns() fvec_[DmpFunc::y20] = y20 - vl_; fjac_[DmpFunc::ipi][DmpParam::t0] = 0.0; fjac_[DmpFunc::ipi][DmpParam::dt] = - (-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_dt) - + D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt) - + rd_ * ceff * (dt + dt * exp_dt_rd_ceff - - 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff))) - / (rd_ * dt * dt * dt); + (-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_dt) + + D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt) + + rd_ * ceff + * (dt + dt * exp_dt_rd_ceff - 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff))) + / (rd_ * dt * dt * dt); fjac_[DmpFunc::ipi][DmpParam::ceff] = - (2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff))) - / (dt * dt); + (2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff))) + / (dt * dt); - dy(t_vl, t0, dt, ceff, - fjac_[DmpFunc::y20][DmpParam::t0], - fjac_[DmpFunc::y20][DmpParam::dt], - fjac_[DmpFunc::y20][DmpParam::ceff]); + dy(t_vl, t0, dt, ceff, fjac_[DmpFunc::y20][DmpParam::t0], + fjac_[DmpFunc::y20][DmpParam::dt], fjac_[DmpFunc::y20][DmpParam::ceff]); - dy(t_vth, t0, dt, ceff, - fjac_[DmpFunc::y50][DmpParam::t0], - fjac_[DmpFunc::y50][DmpParam::dt], - fjac_[DmpFunc::y50][DmpParam::ceff]); + dy(t_vth, t0, dt, ceff, fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], fjac_[DmpFunc::y50][DmpParam::ceff]); if (debug_->check("dmp_ceff", 4)) { showX(); showFvec(); showJacobian(); - report_->reportLine("................."); + report_->report("................."); } } // Eqn 13, Eqn 14. double -DmpPi::ipiIceff(double, double dt, +DmpPi::ipiIceff(double, + double dt, double ceff_time, double ceff) { @@ -1018,11 +1008,11 @@ DmpPi::ipiIceff(double, double dt, double exp_p2_dt = exp2(-p2_ * ceff_time); double exp_dt_rd_ceff = exp2(-ceff_time / (rd_ * ceff)); double ipi = (A_ * ceff_time + (B_ / p1_) * (1.0 - exp_p1_dt) - + (D_ / p2_) * (1.0 - exp_p2_dt)) - / (rd_ * ceff_time * dt); - double iceff = (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) - * (1.0 - exp_dt_rd_ceff)) - / (rd_ * ceff_time * dt); + + (D_ / p2_) * (1.0 - exp_p2_dt)) + / (rd_ * ceff_time * dt); + double iceff = + (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) * (1.0 - exp_dt_rd_ceff)) + / (rd_ * ceff_time * dt); return ipi - iceff; } @@ -1047,14 +1037,13 @@ DmpPi::Vl0(double t, double D1 = k0_ * (k1_ - k2_ / p3_); double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_); - double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) - + p3_ * k4_ / (p2_ - p3_)); + double D5 = + k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) + p3_ * k4_ / (p2_ - p3_)); double exp_p1 = exp2(-p1_ * t); double exp_p2 = exp2(-p2_ * t); double exp_p3 = exp2(-p3_ * t); vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3; - dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - - D5 * p3_ * exp_p3; + dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - D5 * p3_ * exp_p3; } double @@ -1076,7 +1065,8 @@ public: }; DmpOnePole::DmpOnePole(StaState *sta) : - DmpAlg(2, sta) + DmpAlg(2, + sta) { } @@ -1100,19 +1090,15 @@ DmpOnePole::evalDmpEqns() showFvec(); } - dy(t_vl, t0, dt, ceff_, - fjac_[DmpFunc::y20][DmpParam::t0], - fjac_[DmpFunc::y20][DmpParam::dt], - ignore2); + dy(t_vl, t0, dt, ceff_, fjac_[DmpFunc::y20][DmpParam::t0], + fjac_[DmpFunc::y20][DmpParam::dt], ignore2); - dy(t_vth, t0, dt, ceff_, - fjac_[DmpFunc::y50][DmpParam::t0], - fjac_[DmpFunc::y50][DmpParam::dt], - ignore2); + dy(t_vth, t0, dt, ceff_, fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], ignore2); if (debug_->check("dmp_ceff", 4)) { showJacobian(); - report_->reportLine("................."); + report_->report("................."); } } @@ -1140,19 +1126,19 @@ public: double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; + void gateDelaySlew( // Return values. + double &delay, + double &slew) override; private: void V0(double t, // Return values. double &vo, double &dvo_dt) override; - void Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) override; + void Vl0(double t, + // Return values. + double &vl, + double &dvl_dt) override; double voCrossingUpperBound() override; // Pole/zero. @@ -1189,8 +1175,8 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library, double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP C2=0"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, - in_slew, c2, rpi, c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, + c1); ceff_ = c1; z1_ = 1.0 / (rpi_ * c1_); @@ -1203,9 +1189,9 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library, } void -DmpZeroC2::gateDelaySlew(// Return values. - double &delay, - double &slew) +DmpZeroC2::gateDelaySlew( // Return values. + double &delay, + double &slew) { try { findDriverParams(c1_); @@ -1213,8 +1199,7 @@ DmpZeroC2::gateDelaySlew(// Return values. findDriverDelaySlew(delay, slew); driver_valid_ = true; vo_delay_ = delay; - } - catch (DmpError &error) { + } catch (DmpError &error) { fail(error.what()); // Fall back to table slew. driver_valid_ = false; @@ -1237,9 +1222,9 @@ DmpZeroC2::V0(double t, void DmpZeroC2::Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) + // Return values. + double &vl, + double &dvl_dt) { double D1 = k0_ * (k1_ - k2_ / p3_); double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); @@ -1267,7 +1252,7 @@ newtonRaphson(const int max_iter, double x[], const int size, const double x_tol, - std::function eval, + std::function eval, // Temporaries supplied by caller. double *fvec, double **fjac, @@ -1529,12 +1514,13 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, } else { ArcDcalcResult dcalc_result = - LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, - load_pin_index_map, scene, min_max); - if (parasitic - && !unsuppored_model_warned_) { + LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, + load_pin_index_map, scene, min_max); + if (parasitic && !unsuppored_model_warned_) { unsuppored_model_warned_ = true; - report_->warn(1041, "cell %s delay model not supported on SPF parasitics by DMP delay calculator", + report_->warn(1041, + "cell {} delay model not supported on SPF parasitics by DMP " + "delay calculator", drvr_cell->name()); } return dcalc_result; @@ -1570,16 +1556,15 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, } else dmp_alg_ = dmp_cap_; - dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, - rf, rd, in_slew, c2, rpi, c1); + dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, + c1); debugPrint(debug_, "dmp_ceff", 3, - " DMP in_slew = %s c2 = %s rpi = %s c1 = %s Rd = %s (%s alg)", + " DMP in_slew = {} c2 = {} rpi = {} c1 = {} Rd = {} ({} alg)", units_->timeUnit()->asString(in_slew), units_->capacitanceUnit()->asString(c2), units_->resistanceUnit()->asString(rpi), units_->capacitanceUnit()->asString(c1), - units_->resistanceUnit()->asString(rd), - dmp_alg_->name()); + units_->resistanceUnit()->asString(rd), dmp_alg_->name()); } std::string @@ -1593,8 +1578,9 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, const MinMax *min_max, int digits) { - ArcDcalcResult dcalc_result = gateDelay(drvr_pin, arc, in_slew, load_cap, - parasitic, load_pin_index_map, scene, min_max); + ArcDcalcResult dcalc_result = + gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, + scene, min_max); GateTableModel *model = arc->gateTableModel(scene, min_max); float c_eff = 0.0; std::string result; @@ -1653,9 +1639,9 @@ gateModelRd(const LibertyCell *cell, } void -DmpCeffDelayCalc::gateDelaySlew(// Return values. - double &delay, - double &slew) +DmpCeffDelayCalc::gateDelaySlew( // Return values. + double &delay, + double &slew) { dmp_alg_->gateDelaySlew(delay, slew); } @@ -1683,7 +1669,7 @@ DmpCeffDelayCalc::copyState(const StaState *sta) DmpError::DmpError(const char *what) : what_(what) { - //printf("DmpError %s\n", what); + // printf("DmpError %s\n", what); } // This saves about 2.5% in overall run time on designs with SPEF. @@ -1712,4 +1698,4 @@ exp2(double x) } } -} // namespace +} // namespace sta diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 9d4cd733..6d859755 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -251,8 +251,8 @@ GraphDelayCalc::delayInvalid(const Pin *pin) void GraphDelayCalc::delayInvalid(Vertex *vertex) { - debugPrint(debug_, "delay_calc", 2, "delay invalid %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "delay_calc", 2, "delay invalid {}", + vertex->to_string(this)); if (graph_ && incremental_) { invalid_delays_.insert(vertex); // Invalidate driver that triggers dcalc for multi-driver nets. @@ -340,7 +340,7 @@ GraphDelayCalc::findDelays(Level level) if (arc_delay_calc_) { Stats stats(debug_, report_); int dcalc_count = 0; - debugPrint(debug_, "delay_calc", 1, "find delays to level %d", level); + debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level); if (!delays_seeded_) { iter_->clear(); seedRootSlews(); @@ -368,7 +368,7 @@ GraphDelayCalc::findDelays(Level level) delays_exist_ = true; incremental_ = true; - debugPrint(debug_, "delay_calc", 1, "found %d delays", dcalc_count); + debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count); stats.report("Delay calc"); } } @@ -404,8 +404,8 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc) { const Pin *drvr_pin = drvr_vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "seed driver slew %s", - drvr_vertex->to_string(this).c_str()); + debugPrint(debug_, "delay_calc", 2, "seed driver slew {}", + drvr_vertex->to_string(this)); for (const Scene *scene : scenes_) { const Sdc *sdc = scene->sdc(); for (const MinMax *min_max : MinMax::range()) { @@ -527,8 +527,8 @@ void GraphDelayCalc::seedLoadSlew(Vertex *vertex) { const Pin *pin = vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "seed load slew %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "delay_calc", 2, "seed load slew {}", + vertex->to_string(this)); initSlew(vertex); for (const Scene *scene : scenes_) { const Sdc *sdc = scene->sdc(); @@ -602,7 +602,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, const Scene *scene, const MinMax *min_max) { - debugPrint(debug_, "delay_calc", 2, " driver cell %s %s", + debugPrint(debug_, "delay_calc", 2, " driver cell {} {}", drvr_cell->name(), rf->shortName()); for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) { @@ -627,12 +627,12 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, const Scene *scene, const MinMax *min_max) { - debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)", + debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({})", arc->from()->name(), - arc->fromEdge()->to_string().c_str(), + arc->fromEdge()->to_string(), arc->to()->name(), - arc->toEdge()->to_string().c_str(), - arc->role()->to_string().c_str()); + arc->toEdge()->to_string(), + arc->role()->to_string()); const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); if (drvr_rf) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); @@ -658,7 +658,7 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, const ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this); debugPrint(debug_, "delay_calc", 3, - " gate delay = %s intrinsic = %s slew = %s", + " gate delay = {} intrinsic = {} slew = {}", delayAsString(gate_delay, this), delayAsString(intrinsic_delay, this), delayAsString(gate_slew, this)); @@ -681,8 +681,8 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex, bool propagate) { const Pin *pin = vertex->pin(); - debugPrint(debug_, "delay_calc", 2, "find delays %s (%s)", - vertex->to_string(this).c_str(), + debugPrint(debug_, "delay_calc", 2, "find delays {} ({})", + vertex->to_string(this), network_->cellName(network_->instance(pin))); if (vertex->isRoot()) seedRootSlew(vertex, arc_delay_calc); @@ -885,7 +885,7 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) Vertex *drvr = edge->from(graph_); const Pin *drvr_pin = drvr->pin(); if (isLeafDriver(drvr_pin, network_)) { - debugPrint(debug_, "delay_calc", 3, " %s", + debugPrint(debug_, "delay_calc", 3, " {}", network_->pathName(drvr_pin)); multi_drvr_net_map_[drvr] = multi_drvr; drvr_vertices.push_back(drvr); @@ -977,7 +977,7 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge) Vertex *drvr_vertex = edge->to(graph_); const Pin *drvr_pin = drvr_vertex->pin(); Instance *drvr_inst = network_->instance(drvr_pin); - debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s", + debugPrint(debug_, "delay_calc", 2, "find latch D->Q {}", sdc_network_->pathName(drvr_inst)); std::array delay_exists = {false, false}; LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); @@ -1200,16 +1200,16 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge, { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); debugPrint(debug_, "delay_calc", 3, - " %s %s -> %s %s (%s) scene:%s/%s", + " {} {} -> {} {} ({}) scene:{}/{}", arc->from()->name(), - arc->fromEdge()->to_string().c_str(), + arc->fromEdge()->to_string(), arc->to()->name(), - arc->toEdge()->to_string().c_str(), - arc->role()->to_string().c_str(), - scene->name().c_str(), - min_max->to_string().c_str()); + arc->toEdge()->to_string(), + arc->role()->to_string(), + scene->name(), + min_max->to_string()); debugPrint(debug_, "delay_calc", 3, - " gate delay = %s slew = %s", + " gate delay = {} slew = {}", delayAsString(gate_delay, this), delayAsString(gate_slew, this)); bool delay_changed = false; @@ -1259,8 +1259,8 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, const ArcDelay &wire_delay = dcalc_result.wireDelay(load_idx); const Slew &load_slew = dcalc_result.loadSlew(load_idx); debugPrint(debug_, "delay_calc", 3, - " %s load delay = %s slew = %s", - load_vertex->to_string(this).c_str(), + " {} load delay = {} slew = {}", + load_vertex->to_string(this), delayAsString(wire_delay, this), delayAsString(load_slew, this)); bool load_changed = false; @@ -1580,7 +1580,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, TimingArcSet *arc_set = edge->timingArcSet(); const Pin *to_pin = to_vertex->pin(); Instance *inst = network_->instance(to_pin); - debugPrint(debug_, "delay_calc", 2, "find check %s %s -> %s", + debugPrint(debug_, "delay_calc", 2, "find check {} {} -> {}", sdc_network_->pathName(inst), network_->portName(from_vertex->pin()), network_->portName(to_pin)); @@ -1602,16 +1602,16 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, scene, min_max); const Slew to_slew = graph_->slew(to_vertex, to_rf, ap_index); debugPrint(debug_, "delay_calc", 3, - " %s %s -> %s %s (%s) scene:%s/%s", + " {} {} -> {} {} ({}) scene:{}/{}", arc_set->from()->name(), - arc->fromEdge()->to_string().c_str(), + arc->fromEdge()->to_string(), arc_set->to()->name(), - arc->toEdge()->to_string().c_str(), - arc_set->role()->to_string().c_str(), - scene->name().c_str(), - min_max->to_string().c_str()); + arc->toEdge()->to_string(), + arc_set->role()->to_string(), + scene->name(), + min_max->to_string()); debugPrint(debug_, "delay_calc", 3, - " from_slew = %s to_slew = %s", + " from_slew = {} to_slew = {}", delayAsString(from_slew, this), delayAsString(to_slew, this)); float related_out_cap = 0.0; @@ -1622,7 +1622,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, to_slew, related_out_cap, scene, min_max); debugPrint(debug_, "delay_calc", 3, - " check_delay = %s", + " check_delay = {}", delayAsString(check_delay, this)); graph_->setArcDelay(edge, arc, ap_index, check_delay); delay_changed = true; diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 07a68e5b..692c01f0 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -133,7 +133,7 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, { GateTimingModel *model = arc->gateModel(scene, min_max); debugPrint(debug_, "delay_calc", 3, - " in_slew = %s load_cap = %s lumped", + " in_slew = {} load_cap = {} lumped", delayAsString(in_slew, this), units()->capacitanceUnit()->asString(load_cap)); const RiseFall *rf = arc->toEdge()->asRiseFall(); diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 90e31ed3..fc1b0208 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -1,30 +1,30 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "PrimaDelayCalc.hh" -#include // abs +#include // abs #include "Debug.hh" #include "Units.hh" @@ -38,15 +38,16 @@ #include "Parasitics.hh" #include "GraphDelayCalc.hh" #include "DmpDelayCalc.hh" +#include "Format.hh" #include #include namespace sta { -using Eigen::SparseLU; -using Eigen::HouseholderQR; using Eigen::ColPivHouseholderQR; +using Eigen::HouseholderQR; +using Eigen::SparseLU; // Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998 // McGraw-Hill, Inc. New York, NY. @@ -90,10 +91,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) : { } -PrimaDelayCalc::~PrimaDelayCalc() -{ - delete table_dcalc_; -} +PrimaDelayCalc::~PrimaDelayCalc() { delete table_dcalc_; } ArcDelayCalc * PrimaDelayCalc::copy() @@ -130,8 +128,8 @@ PrimaDelayCalc::findParasitic(const Pin *drvr_pin, bool has_wire_cap; graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_wire_cap); - parasitic = parasitics->makeWireloadNetwork(drvr_pin, wireload, - fanout, scene, min_max); + parasitic = + parasitics->makeWireloadNetwork(drvr_pin, wireload, fanout, scene, min_max); } return parasitic; } @@ -160,8 +158,8 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin, LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); const Parasitic *pi_elmore = nullptr; if (parasitic && parasitics->isParasiticNetwork(parasitic)) - pi_elmore = parasitics->reduceToPiElmore(parasitic, drvr_pin, rf, - scene, min_max); + pi_elmore = + parasitics->reduceToPiElmore(parasitic, drvr_pin, rf, scene, min_max); for (auto load_pin_index : load_pin_index_map) { const Pin *load_pin = load_pin_index.first; @@ -190,12 +188,13 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, const Scene *scene, - const MinMax *min_max) + const MinMax *min_max) { ArcDcalcArgSeq dcalc_args; - dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic); - ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, - scene, min_max); + dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, + parasitic); + ArcDcalcResultSeq dcalc_results = + gateDelays(dcalc_args, load_pin_index_map, scene, min_max); return dcalc_results[0]; } @@ -229,15 +228,15 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, && output_waveforms->slewAxis()->inBounds(in_slew) && output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { output_waveforms_[drvr_idx] = output_waveforms; - debugPrint(debug_, "ccs_dcalc", 1, "%s %s", - dcalc_arg.drvrCell()->name(), + debugPrint(debug_, "ccs_dcalc", 1, "{} {}", dcalc_arg.drvrCell()->name(), drvr_rf_->shortName()); LibertyCell *drvr_cell = dcalc_arg.drvrCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); bool vdd_exists; drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); if (!vdd_exists) - report_->error(1720, "VDD not defined in library %s", drvr_library->name()); + report_->error(1720, "VDD not defined in library {}", + drvr_library->name()); drvr_cell->ensureVoltageWaveforms(scenes_); if (drvr_idx == 0) { vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; @@ -268,13 +267,13 @@ PrimaDelayCalc::tableDcalcResults() const Pin *drvr_pin = dcalc_arg.drvrPin(); if (drvr_pin) { const RiseFall *rf = dcalc_arg.drvrEdge(); - const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, - scene_, min_max_); + const Parasitic *parasitic = + table_dcalc_->findParasitic(drvr_pin, rf, scene_, min_max_); dcalc_arg.setParasitic(parasitic); } } - return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, - scene_, min_max_); + return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, scene_, + min_max_); } void @@ -284,8 +283,7 @@ PrimaDelayCalc::simulate() stampEqns(); setXinit(); - if (prima_order_ > 0 - && node_count_ > prima_order_) { + if (prima_order_ > 0 && node_count_ > prima_order_) { primaReduce(); simulate1(Gq_, Cq_, Bq_, xq_init_, Vq_, prima_order_); } @@ -297,11 +295,11 @@ PrimaDelayCalc::simulate() void PrimaDelayCalc::simulate1(const MatrixSd &G, - const MatrixSd &C, - const Eigen::MatrixXd &B, - const Eigen::VectorXd &x_init, - const Eigen::MatrixXd &x_to_v, - const size_t order) + const MatrixSd &C, + const Eigen::MatrixXd &B, + const Eigen::VectorXd &x_init, + const Eigen::MatrixXd &x_to_v, + const size_t order) { Eigen::VectorXd x(order); Eigen::VectorXd x_prev(order); @@ -315,7 +313,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, v_ = v_prev_ = x_to_v * x_init; time_step_ = time_step_prev_ = timeStep(); - debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this)); + debugPrint(debug_, "ccs_dcalc", 1, "time step {}", + delayAsString(time_step_, this)); MatrixSd A(order, order); A = G + (2.0 / time_step_) * C; @@ -336,8 +335,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, v_ = v_prev_ = x_to_v * x_init; // voltageTime is always for a rising waveform so 0.0v is initial voltage. - double time_begin = output_waveforms_[0]->voltageTime((*dcalc_args_)[0].inSlewFlt(), - ceff_[0], 0.0); + double time_begin = output_waveforms_[0]->voltageTime( + (*dcalc_args_)[0].inSlewFlt(), ceff_[0], 0.0); // Limit in case load voltage waveforms don't get to final value. double time_end = time_begin + maxTime(); @@ -349,9 +348,9 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, rhs = B * u_ + (1.0 / time_step_) * C * (3.0 * x_prev - x_prev2); x = A_solver.solve(rhs); v_ = x_to_v * x; - + const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[0]; - debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s", + debugPrint(debug_, "ccs_dcalc", 3, "{} ceff {} VDrvr {:.4f} Idrvr {}", delayAsString(time, this), units_->capacitanceUnit()->asString(ceff_[0]), voltage(dcalc_arg.drvrPin()), @@ -384,7 +383,7 @@ double PrimaDelayCalc::maxTime() { return (*dcalc_args_)[0].inSlewFlt() - + (driverResistance() + resistance_sum_) * load_cap_ * 4; + + (driverResistance() + resistance_sum_) * load_cap_ * 4; } float @@ -429,9 +428,8 @@ PrimaDelayCalc::findNodeCount() const Pin *pin = parasitics_->pin(node); if (pin) { pin_node_map_[pin] = node_idx; - debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu", - network_->pathName(pin), - node_idx); + debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}", + network_->pathName(pin), node_idx); } double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node); node_capacitances_.push_back(cap); @@ -441,14 +439,12 @@ PrimaDelayCalc::findNodeCount() for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) { float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_; ParasiticNode *node1 = parasitics_->node1(capacitor); - if (node1 - && !parasitics_->isExternal(node1)) { + if (node1 && !parasitics_->isExternal(node1)) { size_t node_idx = node_index_map_[node1]; node_capacitances_[node_idx] += cap; } ParasiticNode *node2 = parasitics_->node2(capacitor); - if (node2 - && !parasitics_->isExternal(node2)) { + if (node2 && !parasitics_->isExternal(node2)) { size_t node_idx = node_index_map_[node2]; node_capacitances_[node_idx] += cap; } @@ -496,9 +492,8 @@ PrimaDelayCalc::initCeffIdrvr() const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; ceff_[drvr_idx] = load_cap_; // voltageTime is always for a rising waveform so 0.0v is initial voltage. - drvr_current_[drvr_idx] = - output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), - ceff_[drvr_idx], 0.0); + drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent( + dcalc_arg.inSlewFlt(), ceff_[drvr_idx], 0.0); } } @@ -617,8 +612,7 @@ PrimaDelayCalc::updateCeffIdrvr() double v2 = voltagePrev(node_idx); double dv = v1 - v2; if (drvr_rf_ == RiseFall::rise()) { - if (drvr_current != 0.0 - && dv > 0.0) { + if (drvr_current != 0.0 && dv > 0.0) { double ceff = drvr_current * time_step_ / dv; if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) ceff_[drvr_idx] = ceff; @@ -627,13 +621,11 @@ PrimaDelayCalc::updateCeffIdrvr() // Whoa partner. Head'n for the weeds. drvr_current_[drvr_idx] = 0.0; else - drvr_current_[drvr_idx] = - output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), - ceff_[drvr_idx], v1); + drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent( + dcalc_arg.inSlewFlt(), ceff_[drvr_idx], v1); } else { - if (drvr_current != 0.0 - && dv < 0.0) { + if (drvr_current != 0.0 && dv < 0.0) { double ceff = drvr_current * time_step_ / dv; if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) ceff_[drvr_idx] = ceff; @@ -643,10 +635,8 @@ PrimaDelayCalc::updateCeffIdrvr() drvr_current_[drvr_idx] = 0.0; } else - drvr_current_[drvr_idx] = - output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), - ceff_[drvr_idx], - vdd_ - v1); + drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent( + dcalc_arg.inSlewFlt(), ceff_[drvr_idx], vdd_ - v1); } } } @@ -657,10 +647,8 @@ PrimaDelayCalc::loadWaveformsFinished() for (auto pin_node : pin_node_map_) { size_t node_idx = pin_node.second; double v = voltage(node_idx); - if ((drvr_rf_ == RiseFall::rise() - && v < vh_ + (vdd_ - vh_) * .5) - || (drvr_rf_ == RiseFall::fall() - && (v > vl_ * .5))) { + if ((drvr_rf_ == RiseFall::rise() && v < vh_ + (vdd_ - vh_) * .5) + || (drvr_rf_ == RiseFall::fall() && (v > vl_ * .5))) { return false; } } @@ -678,12 +666,10 @@ PrimaDelayCalc::measureThresholds(double time) double v_prev = voltagePrev(node_idx); for (size_t m = 0; m < measure_threshold_count_; m++) { double th = measure_thresholds_[m]; - if ((v_prev < th && th <= v) - || (v_prev > th && th >= v)) { - double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev); - debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s", - node_idx, - th, + if ((v_prev < th && th <= v) || (v_prev > th && th >= v)) { + double t_cross = + time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev); + debugPrint(debug_, "ccs_measure", 1, "node {} cross {:.2f} {}", node_idx, th, delayAsString(t_cross, this)); threshold_times_[node_idx][m] = t_cross; } @@ -726,10 +712,8 @@ PrimaDelayCalc::dcalcResults() double drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); - debugPrint(debug_, "ccs_dcalc", 2, - "%s gate delay %s slew %s", - network_->pathName(drvr_pin), - delayAsString(gate_delay, this), + debugPrint(debug_, "ccs_dcalc", 2, "{} gate delay {} slew {}", + network_->pathName(drvr_pin), delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); dcalc_result.setLoadCount(load_pin_index_map_->size()); @@ -741,8 +725,7 @@ PrimaDelayCalc::dcalcResults() ThresholdTimes &drvr_times = threshold_times_[drvr_node]; double wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth]; double load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]); - debugPrint(debug_, "ccs_dcalc", 2, - "load %s %s delay %s slew %s", + debugPrint(debug_, "ccs_dcalc", 2, "load {} {} delay {} slew {}", network_->pathName(load_pin), drvr_rf_->shortName(), delayAsString(wire_delay, this), @@ -849,16 +832,18 @@ PrimaDelayCalc::primaReduce2() // Modified Gram-Schmidt orthonormalization for (size_t j = 0; j < k; j++) { - Eigen::MatrixXd H = Vq.block(0, j * port_count_, order_, port_count_).transpose() - * Vq.block(0, k * port_count_, order_, port_count_); + Eigen::MatrixXd H = + Vq.block(0, j * port_count_, order_, port_count_).transpose() + * Vq.block(0, k * port_count_, order_, port_count_); Vq.block(0, k * port_count_, order_, port_count_) = - Vq.block(0, k * port_count_, order_, port_count_) - Vq.block(0, j * port_count_, order_, port_count_) * H; + Vq.block(0, k * port_count_, order_, port_count_) + - Vq.block(0, j * port_count_, order_, port_count_) * H; } Eigen::MatrixXd Vq_k = Vq.block(0, k * port_count_, order_, port_count_); Eigen::HouseholderQR Vq_k_solver(Vq_k); Eigen::MatrixXd VqQ = Vq_k_solver.householderQ(); - Vq.block(0, k * port_count_, order_, port_count_) = - VqQ.block(0, 0, order_, port_count_); + Vq.block(0, k * port_count_, order_, port_count_) = + VqQ.block(0, 0, order_, port_count_); } Vq_.resize(order_, prima_order_); Vq_ = Vq.block(0, 0, order_, prima_order_); @@ -957,8 +942,8 @@ Waveform PrimaDelayCalc::watchWaveform(const Pin *pin) { FloatSeq &voltages = watch_pin_values_[pin]; - TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, - FloatSeq(times_)); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, FloatSeq(times_)); Table waveform(new FloatSeq(voltages), time_axis); return waveform; } @@ -969,7 +954,7 @@ void PrimaDelayCalc::reportMatrix(const char *name, MatrixSd &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportMatrix(matrix); } @@ -977,7 +962,7 @@ void PrimaDelayCalc::reportMatrix(const char *name, Eigen::MatrixXd &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportMatrix(matrix); } @@ -985,7 +970,7 @@ void PrimaDelayCalc::reportMatrix(const char *name, Eigen::VectorXd &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportMatrix(matrix); } @@ -993,22 +978,19 @@ void PrimaDelayCalc::reportVector(const char *name, std::vector &matrix) { - report_->reportLine("%s", name); + report_->report("{}", name); reportVector(matrix); } - + void PrimaDelayCalc::reportMatrix(MatrixSd &matrix) { for (Eigen::Index i = 0; i < matrix.rows(); i++) { std::string line = "| "; - for (Eigen::Index j = 0; j < matrix.cols(); j++) { - std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); - line += entry; - line += " "; - } + for (Eigen::Index j = 0; j < matrix.cols(); j++) + line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " "; line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } } @@ -1017,13 +999,10 @@ PrimaDelayCalc::reportMatrix(Eigen::MatrixXd &matrix) { for (Eigen::Index i = 0; i < matrix.rows(); i++) { std::string line = "| "; - for (Eigen::Index j = 0; j < matrix.cols(); j++) { - std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); - line += entry; - line += " "; - } + for (Eigen::Index j = 0; j < matrix.cols(); j++) + line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " "; line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } } @@ -1032,25 +1011,21 @@ PrimaDelayCalc::reportMatrix(Eigen::VectorXd &matrix) { std::string line = "| "; for (Eigen::Index i = 0; i < matrix.rows(); i++) { - std::string entry = stdstrPrint("%10.3e", matrix.coeff(i)); - line += entry; - line += " "; + std::string entry = + line += sta::format("{:10.3e}", matrix.coeff(i)) + " "; } line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } void PrimaDelayCalc::reportVector(std::vector &matrix) { std::string line = "| "; - for (size_t i = 0; i < matrix.size(); i++) { - std::string entry = stdstrPrint("%10.3e", matrix[i]); - line += entry; - line += " "; - } + for (size_t i = 0; i < matrix.size(); i++) + line += sta::format("{:10.3e}", matrix[i]) + " "; line += "|"; - report_->reportLineString(line); + report_->reportLine(line); } -} // namespace +} // namespace sta diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index 0ac6caa8..83759119 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -24,6 +24,19 @@ This file summarizes STA API changes for each release. +2026/03/12 +---------- + +The Report class used for reporting and error messages now uses std::format +instead of printf. + +sta::format is a wrapper for std::format that will compile on gcc8, which +centos7 uses and does not support std::format. + +stdstrPrint, strintPrint, stringAppend have been removed. Use sta::format. + +reportLineString is now reportLine + Release 3.0.0 2025/01/03 ------------------------ diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 1f1871b9..bebf05b6 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -2,6 +2,7 @@ OpenSTA Timing Analyzer Release Notes ------------------------------------- This file summarizes user visible changes for each release. +See ApiChangeLog.txt for changes to the STA api. Release 3.0.1 2026/03/12 ------------------------ diff --git a/etc/FindMessages.tcl b/etc/FindMessages.tcl index c5fad936..40f00d29 100755 --- a/etc/FindMessages.tcl +++ b/etc/FindMessages.tcl @@ -61,7 +61,7 @@ foreach subdir $subdirs { set files [glob -nocomplain [file join $subdir "*.{cc,hh,yy,ll,i}"]] set files_c [concat $files_c $files] } -set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|libWarn|libError)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")} +set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|warn|error)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")} set files_tcl {} foreach subdir $subdirs { diff --git a/graph/Graph.cc b/graph/Graph.cc index 9878c6ac..022504ca 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -283,7 +283,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin, if (isIsolatedNet(drvrs, loads)) { for (auto drvr_pin : drvrs) { visited_drvrs.insert(drvr_pin); - debugPrint(debug_, "graph", 1, "ignoring isolated driver %s", + debugPrint(debug_, "graph", 1, "ignoring isolated driver {}", network_->pathName(drvr_pin)); } return; diff --git a/graph/Graph.i b/graph/Graph.i index cd8188c9..85cb5124 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -292,7 +292,7 @@ mode_value() return self->timingArcSet()->modeValue().c_str(); } -const char * +std::string latch_d_to_q_en() { if (self->role() == TimingRole::latchDtoQ()) { @@ -308,9 +308,7 @@ latch_d_to_q_en() const RiseFall *enable_rf; lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); if (enable_port) - return stringPrintTmp("%s %s", - enable_port->name(), - enable_rf->shortName()); + return sta::format("{} {}", enable_port->name(), enable_rf->shortName()); } return ""; } diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index 2b9efc5e..06215222 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include "MinMax.hh" #include "RiseFallMinMax.hh" @@ -207,7 +208,7 @@ public: ~ClockEdge(); const RiseFall *transition() const { return rf_; } float time() const { return time_; } - const char *name() const { return name_; } + const std::string &name() const { return name_; } int index() const { return index_; } ClockEdge *opposite() const; // Pulse width if this is the leading edge of the pulse. @@ -221,7 +222,7 @@ private: Clock *clock_; const RiseFall *rf_; - const char *name_; + std::string name_; float time_; int index_; }; diff --git a/include/sta/ClockGroups.hh b/include/sta/ClockGroups.hh index e09cfb91..89159a82 100644 --- a/include/sta/ClockGroups.hh +++ b/include/sta/ClockGroups.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "SdcCmdComment.hh" #include "SdcClass.hh" @@ -32,7 +34,7 @@ namespace sta { class ClockGroups : public SdcCmdComment { public: - ClockGroups(const char *name, + ClockGroups(const std::string &name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, @@ -40,7 +42,7 @@ public: const char *comment); ~ClockGroups(); void makeClockGroup(ClockSet *clks); - const char *name() const { return name_; } + const std::string &name() const { return name_; } ClockGroupSet *groups() { return &groups_; } bool logicallyExclusive() const { return logically_exclusive_; } bool physicallyExclusive() const { return physically_exclusive_; } @@ -49,7 +51,7 @@ public: void removeClock(Clock *clk); private: - const char *name_; + std::string name_; bool logically_exclusive_; bool physically_exclusive_; bool asynchronous_; diff --git a/include/sta/Debug.hh b/include/sta/Debug.hh index 1c21131e..1ff45934 100644 --- a/include/sta/Debug.hh +++ b/include/sta/Debug.hh @@ -25,10 +25,12 @@ #pragma once #include -#include +#include #include #include +#include "Format.hh" +#include "Report.hh" #include "StringUtil.hh" namespace sta { @@ -48,10 +50,16 @@ public: bool check(const char *what, int level) const; int statsLevel() const { return stats_level_; } - void reportLine(const char *what, - const char *fmt, - ...) - __attribute__((format (printf, 3, 4))); + template + void report(const char *what, + std::string_view fmt, + Args &&...args) + { + std::string msg = sta::format("{}: {}", what, + sta::formatRuntime(fmt, std::forward(args)...)); + std::unique_lock lock(buffer_lock_); + report_->reportLine(msg); + } protected: Report *report_; @@ -63,9 +71,9 @@ protected: // Inlining a varargs function would eval the args, which can // be expensive, so use a macro. -#define debugPrint(debug, what, level, ...) \ +#define debugPrint(debug, what, level, fmt, ...) \ if (debug->check(what, level)) { \ - debug->reportLine(what __VA_OPT__(,) __VA_ARGS__); \ + debug->report(what, fmt __VA_OPT__(,) __VA_ARGS__); \ } } // namespace diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index 24b97c60..dae429af 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -155,7 +155,7 @@ public: float delay2) const = 0; virtual Delay div(float delay1, const Delay &delay2) const = 0; - virtual const char *asStringVariance(const Delay &delay, + virtual std::string asStringVariance(const Delay &delay, int digits, const StaState *sta) const = 0; @@ -203,19 +203,19 @@ void delaySetMean(Delay &delay, float mean); -const char * +std::string delayAsString(const Delay &delay, const StaState *sta); -const char * +std::string delayAsString(const Delay &delay, const EarlyLate *early_late, const StaState *sta); -const char * +std::string delayAsString(const Delay &delay, const EarlyLate *early_late, int digits, const StaState *sta); -const char * +std::string delayAsString(const Delay &delay, const EarlyLate *early_late, bool report_variance, diff --git a/include/sta/DelayNormal.hh b/include/sta/DelayNormal.hh index 0d102962..3a25cbf7 100644 --- a/include/sta/DelayNormal.hh +++ b/include/sta/DelayNormal.hh @@ -82,7 +82,7 @@ public: float delay2) const override; Delay div(float delay1, const Delay &delay2) const override; - const char *asStringVariance(const Delay &delay, + std::string asStringVariance(const Delay &delay, int digits, const StaState *sta) const override; }; diff --git a/include/sta/DelayScalar.hh b/include/sta/DelayScalar.hh index c7bd07a9..a413c92d 100644 --- a/include/sta/DelayScalar.hh +++ b/include/sta/DelayScalar.hh @@ -82,7 +82,7 @@ public: float delay2) const override; Delay div(float delay1, const Delay &delay2) const override; - const char *asStringVariance(const Delay &delay, + std::string asStringVariance(const Delay &delay, int digits, const StaState *sta) const override; }; diff --git a/include/sta/DelaySkewNormal.hh b/include/sta/DelaySkewNormal.hh index 090f5701..5922e03e 100644 --- a/include/sta/DelaySkewNormal.hh +++ b/include/sta/DelaySkewNormal.hh @@ -82,7 +82,7 @@ public: float delay2) const override; Delay div(float delay1, const Delay &delay2) const override; - const char *asStringVariance(const Delay &delay, + std::string asStringVariance(const Delay &delay, int digits, const StaState *sta) const override; diff --git a/include/sta/Error.hh b/include/sta/Error.hh index d73775f4..a653024d 100644 --- a/include/sta/Error.hh +++ b/include/sta/Error.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include "Report.hh" @@ -42,7 +43,7 @@ public: class ExceptionMsg : public Exception { public: - ExceptionMsg(const char *msg, + ExceptionMsg(const std::string &msg, const bool suppressed); virtual const char *what() const noexcept; virtual bool suppressed() const { return suppressed_; } @@ -55,11 +56,11 @@ private: class ExceptionLine : public Exception { public: - ExceptionLine(const char *filename, + ExceptionLine(const std::string &filename, int line); protected: - const char *filename_; + std::string filename_; int line_; }; @@ -67,29 +68,31 @@ protected: class FileNotReadable : public Exception { public: - FileNotReadable(const char *filename); + FileNotReadable(std::string filename); virtual const char *what() const noexcept; protected: - const char *filename_; + std::string filename_; + std::string msg_; }; // Failure opening filename for writing. class FileNotWritable : public Exception { public: - FileNotWritable(const char *filename); + FileNotWritable(std::string filename); virtual const char *what() const noexcept; protected: - const char *filename_; + std::string filename_; + std::string msg_; }; // Report an error condition that should not be possible. // The default handler prints msg to stderr and exits. // The msg should NOT include a period or return. -// Only for use in those cases where a Report object is not available. -#define criticalError(id,msg) \ +// Only for use in those cases where a Report object is not available. +#define criticalError(id, msg) \ Report::defaultReport()->fileCritical(id, __FILE__, __LINE__, msg) } // namespace diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index b7f7e655..9e4a555c 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -24,6 +24,7 @@ #pragma once +#include #include #include "Error.hh" @@ -67,7 +68,7 @@ public: virtual bool isGroupPath() const { return false; } virtual bool isFilter() const { return false; } virtual ExceptionPathType type() const = 0; - virtual const char *asString(const Network *network) const; + virtual std::string to_string(const Network *network) const; ExceptionFrom *from() const { return from_; } ExceptionThruSeq *thrus() const { return thrus_; } ExceptionTo *to() const { return to_; } @@ -127,14 +128,14 @@ public: virtual bool useEndClk() const { return false; } virtual int pathMultiplier() const { return 0; } virtual float delay() const { return 0.0; } - virtual const char *name() const { return nullptr; } + virtual std::string name() const { return ""; } virtual bool isDefault() const { return false; } virtual bool ignoreClkLatency() const { return false; } virtual bool breakPath() const { return false; } protected: virtual const char *typeString() const = 0; - const char *fromThruToString(const Network *network) const; + std::string fromThruToString(const Network *network) const; void makeStates(); ExceptionFrom *from_; @@ -209,7 +210,7 @@ public: bool own_pts) override; bool isPathDelay() const override { return true; } ExceptionPathType type() const override { return ExceptionPathType::path_delay; } - const char *asString(const Network *network) const override; + std::string to_string(const Network *network) const override; const char *typeString() const override; bool mergeable(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override; @@ -245,7 +246,7 @@ public: ExceptionPathType type() const override { return ExceptionPathType::multi_cycle; } bool matches(const MinMax *min_max, bool exactly) const override; - const char *asString(const Network *network) const override; + std::string to_string(const Network *network) const override; const char *typeString() const override; bool mergeable(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override; @@ -292,7 +293,7 @@ public: class GroupPath : public ExceptionPath { public: - GroupPath(const char *name, + GroupPath(const std::string &name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -311,11 +312,11 @@ public: bool overrides(ExceptionPath *exception) const override; int typePriority() const override; bool tighterThan(ExceptionPath *exception) const override; - const char *name() const override { return name_; } + std::string name() const override { return name_; } bool isDefault() const override { return is_default_; } protected: - const char *name_; + std::string name_; bool is_default_; }; @@ -343,7 +344,7 @@ public: // All pins and instance/net pins. virtual PinSet allPins(const Network *network) = 0; virtual int typePriority() const = 0; - virtual const char *asString(const Network *network) const = 0; + virtual std::string to_string(const Network *network) const = 0; virtual size_t objectCount() const = 0; virtual void addPin(const Pin *pin, const Network *network) = 0; @@ -367,8 +368,8 @@ protected: // exception merging. size_t hash_; - // Maximum number of objects for asString() to show. - static const int as_string_max_objects_; + // Maximum number of objects for to_string() to show. + static const int to_string_max_objects_; static const size_t hash_clk = 3; static const size_t hash_pin = 5; static const size_t hash_net = 7; @@ -402,7 +403,7 @@ public: const Network *network) const override; void mergeInto(ExceptionPt *pt, const Network *network) override; - const char *asString(const Network *network) const override; + std::string to_string(const Network *network) const override; size_t objectCount() const override; void deleteClock(Clock *clk); void addPin(const Pin *pin, @@ -467,7 +468,7 @@ public: const Network *network); ExceptionTo *clone(const Network *network); bool isTo() const override { return true; } - const char *asString(const Network *network) const override; + std::string to_string(const Network *network) const override; const RiseFallBoth *endTransition() { return end_rf_; } bool intersectsPts(ExceptionTo *to, const Network *network) const; @@ -512,7 +513,7 @@ public: const Network *network); ~ExceptionThru(); ExceptionThru *clone(const Network *network); - const char *asString(const Network *network) const override; + std::string to_string(const Network *network) const override; bool isThru() const override { return true; } PinSet *pins() override { return pins_; } EdgePinsSet *edges() override { return edges_; } diff --git a/include/sta/Format.hh b/include/sta/Format.hh new file mode 100644 index 00000000..1745f482 --- /dev/null +++ b/include/sta/Format.hh @@ -0,0 +1,153 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, 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 . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include +#include + +#include "StaConfig.hh" + +#ifdef ZLIB_FOUND +#include +#endif + +// std::format is not supported in GCC 11 (e.g. Ubuntu 22.04). +// Use fmt library as fallback when __cpp_lib_format is not defined. + +#if defined(__cpp_lib_format) && __cpp_lib_format >= 201907L +#include + +namespace sta { + +template +std::string format(std::format_string fmt, + Args &&...args) { + return std::format(fmt, std::forward(args)...); +} + +template +void print(std::ofstream &stream, + std::format_string fmt, + Args &&...args) { + stream << std::format(fmt, std::forward(args)...); +} + +#ifdef ZLIB_FOUND +template +void print(gzFile stream, + std::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + gzwrite(stream, s.c_str(), s.size()); +} +#endif +template +void print(FILE *stream, + std::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + std::fprintf(stream, "%s", s.c_str()); +} + +inline std::string vformat(std::string_view fmt, + std::format_args args) { + return std::vformat(fmt, args); +} + +template +auto make_format_args(Args &&...args) { + return std::make_format_args(std::forward(args)...); +} + +// Format with runtime format string - captures args to avoid make_format_args +// rvalue reference issues. +template +std::string formatRuntime(std::string_view fmt, + Args &&...args) { + auto args_tuple = std::make_tuple(std::forward(args)...); + return std::apply( + [fmt](auto &...a) { + return std::vformat(fmt, std::make_format_args(a...)); + }, + args_tuple); +} + +} // namespace sta + +#else +#include + +namespace sta { + +template +std::string format(fmt::format_string fmt, + Args &&...args) { + return fmt::format(fmt, std::forward(args)...); +} +template +void print(std::ofstream &stream, + fmt::format_string fmt, + Args &&...args) { + stream << fmt::format(fmt, std::forward(args)...); +} + +#ifdef ZLIB_FOUND +template +void print(gzFile stream, + fmt::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + gzwrite(stream, s.c_str(), s.size()); +} +#endif +template +void print(FILE *stream, + fmt::format_string fmt, + Args &&...args) { + std::string s = sta::format(fmt, std::forward(args)...); + std::fprintf(stream, "%s", s.c_str()); +} + +inline +std::string vformat(std::string_view fmt, + fmt::format_args args) { + return fmt::vformat(fmt, args); +} + +template +auto make_format_args(Args &&...args) { + return fmt::make_format_args(std::forward(args)...); +} + +template +std::string formatRuntime(std::string_view fmt, + Args &&...args) { + return fmt::format(fmt::runtime(fmt), std::forward(args)...); +} + +} // namespace sta +#endif diff --git a/include/sta/ParseBus.hh b/include/sta/ParseBus.hh index d62bd982..9b40063a 100644 --- a/include/sta/ParseBus.hh +++ b/include/sta/ParseBus.hh @@ -25,12 +25,13 @@ #pragma once #include +#include namespace sta { // Return true if name is a bus. bool -isBusName(const char *name, +isBusName(std::string_view name, const char brkt_left, const char brkt_right, char escape); @@ -43,7 +44,7 @@ isBusName(const char *name, // index = bit // Caller must delete returned bus_name string. void -parseBusName(const char *name, +parseBusName(std::string_view name, const char brkt_left, const char brkt_right, char escape, @@ -53,9 +54,9 @@ parseBusName(const char *name, int &index); // Allow multiple different left/right bus brackets. void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, char escape, // Return values. bool &is_bus, @@ -66,7 +67,7 @@ parseBusName(const char *name, // bus_name is set to null if name is not a range. // Caller must delete returned bus_name string. void -parseBusName(const char *name, +parseBusName(std::string_view name, const char brkt_left, const char brkt_right, char escape, @@ -81,9 +82,9 @@ parseBusName(const char *name, // brkt_lefts and brkt_rights are corresponding strings of legal // bus brackets such as "[(<" and "])>". void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, const char escape, // Return values. bool &is_bus, @@ -95,7 +96,7 @@ parseBusName(const char *name, // Insert escapes before ch1 and ch2 in token. std::string -escapeChars(const char *token, +escapeChars(std::string_view token, const char ch1, const char ch2, const char escape); diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index 954bc2cc..d8ba2af6 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -42,7 +42,7 @@ class PathEndVisitor; using PathGroupIterator = PathEndSeq::iterator; using PathGroupClkMap = std::map; -using PathGroupNamedMap = std::map; +using PathGroupNamedMap = std::map; using PathGroupSeq = std::vector; // A collection of PathEnds grouped and sorted for reporting. @@ -140,7 +140,7 @@ public: bool unconstrained_paths, // Return value. PathEndSeq &path_ends); - PathGroup *findPathGroup(const char *name, + PathGroup *findPathGroup(const std::string &name, const MinMax *min_max) const; PathGroup *findPathGroup(const Clock *clock, const MinMax *min_max) const; @@ -191,7 +191,7 @@ protected: bool gated_clk, bool unconstrained, const MinMax *min_max); - bool reportGroup(const char *group_name, + bool reportGroup(const std::string &group_name, StringSet &group_names) const; static GroupPath *groupPathTo(const PathEnd *path_end, const StaState *sta); diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 3ca8ffe0..fb42f00b 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #pragma once @@ -27,15 +27,23 @@ #include #include #include +#include #include #include -#include "Machine.hh" // __attribute__ +#include "Machine.hh" // __attribute__ +#include "Format.hh" struct Tcl_Interp; namespace sta { +// Throws ExceptionMsg - implemented in Report.cc to avoid circular include with +// Error.hh +void +reportThrowExceptionMsg(const std::string &msg, + bool suppressed); + // Output streams used for printing. // This is a wrapper for all printing. It supports logging output to // a file and redirection of command output to a file. @@ -45,74 +53,98 @@ public: Report(); virtual ~Report(); - // Print line with return. - virtual void reportLine(const char *fmt, ...) - __attribute__((format (printf, 2, 3))); - virtual void reportLineString(const char *line); - virtual void reportLineString(const std::string &line); + virtual void reportLine(const std::string &line); virtual void reportBlankLine(); + // Print formatted line using std::format (C++20). + template + void report(std::string_view fmt, + Args &&...args) + { + reportLine(sta::vformat(fmt, sta::make_format_args(args...))); + } + //////////////////////////////////////////////////////////////// // Report warning. - virtual void warn(int id, - const char *fmt, ...) - __attribute__((format (printf, 3, 4))); - virtual void vwarn(int id, - const char *fmt, - va_list args); + template + void warn(int id, + std::string_view fmt, + Args &&...args) + { + if (!isSuppressed(id)) { + reportLine(sta::format( + "Warning {}: {}", id, sta::vformat(fmt, sta::make_format_args(args...)))); + } + } // Report warning in a file. - virtual void fileWarn(int id, - const char *filename, - int line, - const char *fmt, ...) - __attribute__((format (printf, 5, 6))); - virtual void vfileWarn(int id, - const char *filename, - int line, - const char *fmt, - va_list args); + template + void fileWarn(int id, + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + if (!isSuppressed(id)) { + reportLine( + sta::format("Warning {}: {} line {}, {}", id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...)))); + } + } - virtual void error(int id, - const char *fmt, ...) - __attribute__((format (printf, 3, 4))); - virtual void verror(int id, - const char *fmt, - va_list args); + template + void error(int id, + std::string_view fmt, + Args &&...args) + { + std::string msg = sta::vformat(fmt, sta::make_format_args(args...)); + reportThrowExceptionMsg(sta::format("{} {}", id, msg), isSuppressed(id)); + } // Report error in a file. - virtual void fileError(int id, - const char *filename, - int line, - const char *fmt, ...) - __attribute__((format (printf, 5, 6))); - virtual void vfileError(int id, - const char *filename, - int line, - const char *fmt, - va_list args); + template + void fileError(int id, + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + const std::string msg = sta::vformat(fmt, sta::make_format_args(args...)); + reportThrowExceptionMsg(sta::format("{} {} line {}, {}", id, filename, line, msg), + isSuppressed(id)); + } - // Critical. + // Critical. // Report error condition that should not be possible or that prevents execution. // The default handler prints msg to stderr and exits. - virtual void critical(int id, - const char *fmt, - ...) - __attribute__((format (printf, 3, 4))); - virtual void fileCritical(int id, - const char *filename, - int line, - const char *fmt, - ...) - __attribute__((format (printf, 5, 6))); + template + void critical(int id, + std::string_view fmt, + Args &&...args) + { + reportLine(sta::format("Critical {}: {}", id, + sta::vformat(fmt, sta::make_format_args(args...)))); + exit(1); + } + template + void fileCritical(int id, + std::string_view filename, + int line, + std::string_view fmt, + Args &&...args) + { + reportLine(sta::format("Critical {}: {} line {}, {}", id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...)))); + exit(1); + } // Log output to filename until logEnd is called. - virtual void logBegin(const char *filename); + virtual void logBegin(std::string_view filename); virtual void logEnd(); // Redirect output to filename until redirectFileEnd is called. - virtual void redirectFileBegin(const char *filename); + virtual void redirectFileBegin(std::string_view filename); // Redirect append output to filename until redirectFileEnd is called. - virtual void redirectFileAppendBegin(const char *filename); + virtual void redirectFileAppendBegin(std::string_view filename); virtual void redirectFileEnd(); // Redirect output to a string until redirectStringEnd is called. virtual void redirectStringBegin(); @@ -139,9 +171,7 @@ protected: // Return the number of characters written. virtual size_t printConsole(const char *buffer, size_t length); - void printToBuffer(const char *fmt, - ...) - __attribute__((format (printf, 2, 3))); + void printToBuffer(const char *fmt, ...) __attribute__((format(printf, 2, 3))); void printToBuffer(const char *fmt, va_list args); @@ -169,4 +199,4 @@ protected: friend class Debug; }; -} // namespace +} // namespace sta diff --git a/include/sta/ReportTcl.hh b/include/sta/ReportTcl.hh index 127cb23c..5f4a6cd0 100644 --- a/include/sta/ReportTcl.hh +++ b/include/sta/ReportTcl.hh @@ -44,20 +44,20 @@ class ReportTcl : public Report public: ReportTcl(); virtual ~ReportTcl(); - virtual void logBegin(const char *filename); - virtual void logEnd(); - virtual void redirectFileBegin(const char *filename); - virtual void redirectFileAppendBegin(const char *filename); - virtual void redirectFileEnd(); - virtual void redirectStringBegin(); - virtual const char *redirectStringEnd(); + void logBegin(std::string_view filename) override; + void logEnd() override; + void redirectFileBegin(std::string_view filename) override; + void redirectFileAppendBegin(std::string_view filename) override; + void redirectFileEnd() override; + void redirectStringBegin() override; + const char *redirectStringEnd() override; // This must be called after the Tcl interpreter has been constructed. // It makes the encapsulated channels. - virtual void setTclInterp(Tcl_Interp *interp); + void setTclInterp(Tcl_Interp *interp) override; protected: - virtual size_t printConsole(const char *buffer, - size_t length); + size_t printConsole(const char *buffer, + size_t length) override; void flush(); private: diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index 01e07ca7..64a41375 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -179,11 +179,11 @@ using InstDeratingFactorsMap = std::map; using CellDeratingFactorsMap = std::map; using ClockGroupsSet = std::set; using ClockGroupsClkMap = std::map; -using ClockGroupsNameMap = std::map; +using ClockGroupsNameMap = std::map; using ClockSenseMap = std::map; using ClkHpinDisables = std::set; using GroupPathSet = std::set; -using GroupPathMap = std::map; +using GroupPathMap = std::map; using ClockPairSet = std::set; using NetVoltageMap = std::map; @@ -499,7 +499,7 @@ public: Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold); - ClockGroups *makeClockGroups(const char *name, + ClockGroups *makeClockGroups(const std::string &name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, @@ -507,11 +507,13 @@ public: const char *comment); void makeClockGroup(ClockGroups *clk_groups, ClockSet *clks); - void removeClockGroups(const char *name); - // nullptr name removes all. - void removeClockGroupsLogicallyExclusive(const char *name); - void removeClockGroupsPhysicallyExclusive(const char *name); - void removeClockGroupsAsynchronous(const char *name); + void removeClockGroups(const std::string &name); + void removeClockGroupsLogicallyExclusive(); + void removeClockGroupsLogicallyExclusive(const std::string &name); + void removeClockGroupsPhysicallyExclusive(); + void removeClockGroupsPhysicallyExclusive(const std::string &name); + void removeClockGroupsAsynchronous(); + void removeClockGroupsAsynchronous(const std::string &name); bool sameClockGroup(const Clock *clk1, const Clock *clk2) const; // Clocks explicitly excluded by set_clock_group. @@ -756,7 +758,7 @@ public: ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max); - void makeGroupPath(const char *name, + void makeGroupPath(const std::string &name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -1266,7 +1268,7 @@ protected: void makeClkGroupExclusions(ClockGroupSet *groups); void makeClkGroupSame(ClockGroup *group); void clearClkGroupExclusions(); - char *makeClockGroupsName(); + std::string makeClockGroupsName(); void setClockSense(const Pin *pin, const Clock *clk, ClockSense sense); diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index e462af6d..3c2c84be 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -274,7 +274,7 @@ protected: const PatternMatch *pattern, InstanceSeq &matches) const; - const char *staToSdc(const char *sta_name) const; + const char *staToSdc(std::string_view sta_name) const; }; // Encapsulate a network to map names to/from the sdc namespace. diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 4c49b7f3..caffa49d 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -434,19 +434,21 @@ public: const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold, Sdc *sdc); - ClockGroups *makeClockGroups(const char *name, + ClockGroups *makeClockGroups(const std::string &name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, const char *comment, Sdc *sdc); - // nullptr name removes all. - void removeClockGroupsLogicallyExclusive(const char *name, + void removeClockGroupsLogicallyExclusive(Sdc *sdc); + void removeClockGroupsLogicallyExclusive(const std::string &name, Sdc *sdc); - void removeClockGroupsPhysicallyExclusive(const char *name, + void removeClockGroupsPhysicallyExclusive(Sdc *sdc); + void removeClockGroupsPhysicallyExclusive(const std::string &name, Sdc *sdc); - void removeClockGroupsAsynchronous(const char *name, + void removeClockGroupsAsynchronous(Sdc *sdc); + void removeClockGroupsAsynchronous(const std::string &name, Sdc *sdc); void makeClockGroup(ClockGroups *clk_groups, ClockSet *clks, @@ -640,7 +642,7 @@ public: float delay, const char *comment, Sdc *sdc); - void makeGroupPath(const char *name, + void makeGroupPath(const std::string &name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index b12ae00d..c2cfb13d 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -143,14 +143,6 @@ public: char * stringCopy(const char *str); -inline void -stringAppend(char *&str1, - const char *str2) -{ - strcpy(str1, str2); - str1 += strlen(str2); -} - void stringDeleteCheck(const char *str); @@ -164,32 +156,6 @@ stringDelete(const char *str) bool isDigits(const char *str); -// Print to a new string. -// Caller owns returned string. -char * -stringPrint(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); -std::string -stdstrPrint(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); -char * -stringPrintArgs(const char *fmt, - va_list args); -void -stringPrint(std::string &str, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); -// Formated append to std::string. -void -stringAppend(std::string &str, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); - -// Print to a temporary string. -char * -stringPrintTmp(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); - char * makeTmpString(size_t length); char * diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 3390c103..8c3475f5 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -56,9 +56,8 @@ public: void setDigits(int digits); // Does not include suffix. int width() const; - const char *asString(float value) const; - const char *asString(double value) const; - const char *asString(float value, + std::string asString(float value) const; + std::string asString(float value, int digits) const; private: diff --git a/include/sta/VerilogNamespace.hh b/include/sta/VerilogNamespace.hh index 8649c2d7..bcdddb52 100644 --- a/include/sta/VerilogNamespace.hh +++ b/include/sta/VerilogNamespace.hh @@ -29,21 +29,21 @@ namespace sta { std::string -cellVerilogName(const char *sta_name); +cellVerilogName(std::string sta_name); std::string -instanceVerilogName(const char *sta_name); +instanceVerilogName(std::string sta_name); std::string -netVerilogName(const char *sta_name); +netVerilogName(std::string sta_name); std::string -portVerilogName(const char *sta_name); +portVerilogName(std::string sta_name); std::string -moduleVerilogToSta(const std::string *sta_name); +moduleVerilogToSta(std::string sta_name); std::string -instanceVerilogToSta(const std::string *sta_name); +instanceVerilogToSta(std::string sta_name); std::string -netVerilogToSta(const std::string *sta_name); +netVerilogToSta(std::string sta_name); std::string -portVerilogToSta(const std::string *sta_name); +portVerilogToSta(std::string sta_name); } // namespace diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index 35311f6d..64499238 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -25,9 +25,12 @@ #pragma once #include +#include #include #include +#include "Format.hh" +#include "Report.hh" #include "StringUtil.hh" #include "NetworkClass.hh" @@ -59,8 +62,32 @@ class StringRegistry; class VerilogBindingTbl; class VerilogNetNameIterator; class VerilogNetPortRef; -class VerilogError; class LibertyCell; +class VerilogErrorCmp; + +class VerilogError +{ +public: + VerilogError(int id, + std::string_view filename, + int line, + std::string_view msg, + bool warn); + const char *msg() const { return msg_.c_str(); } + const char *filename() const { return filename_.c_str(); } + int id() const { return id_; } + int line() const { return line_; } + bool warn() const { return warn_; } + +private: + int id_; + std::string filename_; + int line_; + std::string msg_; + bool warn_; + + friend class VerilogErrorCmp; +}; using VerilogModuleMap = std::map; using VerilogStmtSeq = std::vector; @@ -148,14 +175,24 @@ public: const char *filename() const { return filename_.c_str(); } void incrLine(); Report *report() const { return report_; } + template void error(int id, - const char *filename, + std::string_view filename, int line, - const char *fmt, ...); + std::string_view fmt, + Args &&...args) + { + report_->fileError(id, filename, line, fmt, std::forward(args)...); + } + template void warn(int id, - const char *filename, + std::string_view filename, int line, - const char *fmt, ...); + std::string_view fmt, + Args &&...args) + { + report_->fileWarn(id, filename, line, fmt, std::forward(args)...); + } const std::string &zeroNetName() const { return zero_net_name_; } const std::string &oneNetName() const { return one_net_name_; } void deleteModules(); @@ -231,16 +268,26 @@ protected: Instance *parent, VerilogBindingTbl *parent_bindings, bool is_leaf); + template void linkWarn(int id, - const char *filename, + std::string_view filename, int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); + std::string_view msg, + Args &&...args) + { + std::string msg_str = sta::formatRuntime(msg, std::forward(args)...); + link_errors_.push_back(new VerilogError(id, filename, line, msg_str, true)); + } + template void linkError(int id, - const char *filename, + std::string_view filename, int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); + std::string_view msg, + Args &&...args) + { + std::string msg_str = sta::formatRuntime(msg, std::forward(args)...); + link_errors_.push_back(new VerilogError(id, filename, line, msg_str, false)); + } bool reportLinkErrors(); bool haveLinkErrors(); Cell *makeBlackBox(VerilogModuleInst *mod_inst, diff --git a/liberty/LibExprReader.cc b/liberty/LibExprReader.cc index c3e1e5f9..1b2fc037 100644 --- a/liberty/LibExprReader.cc +++ b/liberty/LibExprReader.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "FuncExpr.hh" @@ -80,8 +80,7 @@ LibExprReader::makeFuncExprPort(const char *port_name) if (port) expr = FuncExpr::makePort(port); else - report_->warn(1130, "%s references unknown port %s.", - error_msg_, port_name); + report_->warn(1130, "{} references unknown port {}.", error_msg_, port_name); stringDelete(port_name); return expr; } @@ -134,7 +133,7 @@ LibExprReader::setResult(FuncExpr *result) void LibExprReader::parseError(const char *msg) { - report_->error(1131, "%s %s.", error_msg_, msg); + report_->error(1131, "{} {}.", error_msg_, msg); } //////////////////////////////////////////////////////////////// @@ -144,4 +143,4 @@ LibExprScanner::LibExprScanner(std::istringstream &stream) : { } -} // namespace +} // namespace sta diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 1c659ad8..5e4328ad 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -25,6 +25,7 @@ #include "Liberty.hh" #include "ContainerHelpers.hh" +#include "Format.hh" #include "Mutex.hh" #include "EnumNameMap.hh" #include "Report.hh" @@ -775,7 +776,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, port1->setScenePort(port2, ap_index); } else - report->warn(1110, "cell %s/%s port %s not found in cell %s/%s.", + report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.", cell1->library()->name(), cell1->name(), port_name, @@ -801,7 +802,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, } } else - report->warn(1111, "cell %s/%s %s -> %s timing group %s not found in cell %s/%s.", + report->warn(1111, "cell {}/{} {} -> {} timing group {} not found in cell {}/{}.", cell1->library()->name(), cell1->name(), arc_set1->from() ? arc_set1->from()->name() : "", @@ -820,7 +821,7 @@ LibertyLibrary::checkScenes(LibertyCell *cell, for (const Scene *scene : scenes) { for (auto min_max : MinMax::range()) { if (!cell->checkSceneCell(scene, min_max)) - report->error(1112, "Liberty cell %s/%s for corner %s/%s not found.", + report->error(1112, "Liberty cell {}/{} for corner {}/{} not found.", cell->libertyLibrary()->name(), cell->name(), scene->name().c_str(), @@ -1703,7 +1704,7 @@ LibertyCell::makeLatchEnables(Report *report, TimingSense en_sense = en_func->portTimingSense(en); if (en_sense == TimingSense::positive_unate && en_rf != RiseFall::rise()) - report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", + report->warn(1114, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function positive sense.", library_->name(), name(), en->name(), @@ -1711,7 +1712,7 @@ LibertyCell::makeLatchEnables(Report *report, en_rf == RiseFall::rise()?"rising":"falling"); else if (en_sense == TimingSense::negative_unate && en_rf != RiseFall::fall()) - report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", + report->warn(1115, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function negative sense.", library_->name(), name(), en->name(), @@ -1721,7 +1722,7 @@ LibertyCell::makeLatchEnables(Report *report, } } else - report->warn(1121, "cell %s/%s no latch enable found for %s -> %s.", + report->warn(1121, "cell {}/{} no latch enable found for {} -> {}.", library_->name(), name(), d->name(), @@ -1767,7 +1768,7 @@ LibertyCell::findLatchSetup(const LibertyPort *d, for (TimingArc *arc : arc_set->arcs()) { const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); if (from_rf == en_rf) { - report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.", + report->warn(1113, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with {} -> {} setup_{} check.", library_->name(), name(), en->name(), @@ -1824,7 +1825,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d, latch_check_map_[setup_check] = idx; d->setIsLatchData(true); debugPrint(debug, "liberty_latch", 1, - "latch %s -> %s | %s %s -> %s | %s %s -> %s setup", + "latch {} -> {} | {} {} -> {} | {} {} -> {} setup", d->name(), q->name(), en->name(), @@ -2904,7 +2905,7 @@ ModeDef::defineValue(const char *value, const char *sdf_cond) { std::string key = value; - std::string sdf = sdf_cond ? std::string(sdf_cond) : std::string(); + std::string sdf = sdf_cond ? sdf_cond : std::string(); auto [it, inserted] = values_.try_emplace(key, key, cond, std::move(sdf)); return &it->second; } @@ -3202,27 +3203,27 @@ ScaleFactors::report(Report *report) std::string line = " "; for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index; - stringAppend(line, "%10s", scaleFactorPvtName(pvt)); + line += sta::format("{:>10}", scaleFactorPvtName(pvt)); } - report->reportLineString(line); + report->reportLine(line); for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { ScaleFactorType type = (ScaleFactorType) type_index; - stringPrint(line, "%10s ", scaleFactorTypeName(type)); + std::string line = sta::format("{:>10}", scaleFactorTypeName(type)); for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { if (scaleFactorTypeRiseFallSuffix(type) || scaleFactorTypeRiseFallPrefix(type) || scaleFactorTypeLowHighSuffix(type)) { - stringAppend(line, " %.3f,%.3f", + line += sta::format(" {:.3f},{:.3f}", scales_[type_index][pvt_index][RiseFall::riseIndex()], scales_[type_index][pvt_index][RiseFall::fallIndex()]); } else { - stringAppend(line, " %.3f", + line += sta::format(" {:.3f}", scales_[type_index][pvt_index][0]); } } - report->reportLineString(line); + report->reportLine(line); } } diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 2e1814ee..07529526 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -367,16 +367,13 @@ std::string to_string() { return self->to_string(); } const TimingRole *role() { return self->role(); } const char *sdf_cond() { return self->sdfCond().c_str(); } -const char * +std::string full_name() { const char *from = self->from()->name(); const char *to = self->to()->name(); const char *cell_name = self->libertyCell()->name(); - return stringPrintTmp("%s %s -> %s", - cell_name, - from, - to); + return sta::format("{} {} -> {}", cell_name, from, to); } const std::string diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index 72a72b1f..52cd97d5 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -102,12 +102,8 @@ LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, const char *bus_name, int bit_index) { - std::string bit_name; - stringPrint(bit_name, "%s%c%d%c", - bus_name, - library->busBrktLeft(), - bit_index, - library->busBrktRight()); + std::string bit_name = std::string(bus_name) + library->busBrktLeft() + + std::to_string(bit_index) + library->busBrktRight(); LibertyPort *port = makePort(cell, bit_name.c_str(), bit_index); bus_port->addPortBit(port); cell->addPortBit(port); diff --git a/liberty/LibertyParse.yy b/liberty/LibertyParse.yy index 35ca7180..b20b5c6c 100644 --- a/liberty/LibertyParse.yy +++ b/liberty/LibertyParse.yy @@ -45,7 +45,7 @@ sta::LibertyParse::error(const location_type &loc, const std::string &msg) { reader->report()->fileError(164, reader->filename().c_str(), - loc.begin.line, "%s", msg.c_str()); + loc.begin.line, "{}", msg); } %} @@ -169,13 +169,13 @@ attr_value: /* Crafted to avoid conflicts with expr */ volt_expr: FLOAT volt_op FLOAT - { $$ = sta::stdstrPrint("%e%c%e", $1, $2, $3); } + { $$ = sta::format("{}{}{}", $1, $2, $3); } | string volt_op FLOAT - { $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); } + { $$ = sta::format("{}{}{}", $1, $2, $3); } | FLOAT volt_op string - { $$ = sta::stdstrPrint("%e%c%s", $1, $2, $3.c_str()); } + { $$ = sta::format("{}{}{}", $1, $2, $3); } | volt_expr volt_op FLOAT - { $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); } + { $$ = sta::format("{}{}{}", $1, $2, $3); } ; volt_op: @@ -192,7 +192,7 @@ volt_op: expr: expr_term1 | expr_term1 expr_op expr - { $$ = sta::stdstrPrint("%s%c%s", $1.c_str(), $2, $3.c_str()); } + { $$ = sta::format("{}{}{}", $1.c_str(), $2, $3.c_str()); } ; expr_term: diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index 5d16566d..b3cefe9a 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "LibertyParser.hh" @@ -130,10 +130,8 @@ LibertyParser::groupBegin(const std::string type, LibertyAttrValueSeq *params, int line) { - LibertyGroup *group = - new LibertyGroup(std::move(type), - params ? std::move(*params) : LibertyAttrValueSeq(), - line); + LibertyGroup *group = new LibertyGroup( + std::move(type), params ? std::move(*params) : LibertyAttrValueSeq(), line); delete params; LibertyGroup *parent_group = group_stack_.empty() ? nullptr : group_stack_.back(); group_visitor_->begin(group, parent_group); @@ -145,8 +143,7 @@ LibertyParser::groupEnd() { LibertyGroup *group = this->group(); group_stack_.pop_back(); - LibertyGroup *parent = - group_stack_.empty() ? nullptr : group_stack_.back(); + LibertyGroup *parent = group_stack_.empty() ? nullptr : group_stack_.back(); if (parent) parent->addSubgroup(group); group_visitor_->end(group, parent); @@ -170,8 +167,8 @@ LibertyParser::makeSimpleAttr(const std::string name, const LibertyAttrValue *value, int line) { - LibertySimpleAttr *attr = new LibertySimpleAttr(std::move(name), - std::move(*value), line); + LibertySimpleAttr *attr = + new LibertySimpleAttr(std::move(name), std::move(*value), line); delete value; LibertyGroup *group = this->group(); group->addAttr(attr); @@ -191,9 +188,8 @@ LibertyParser::makeComplexAttr(const std::string name, return nullptr; // Define is not a complex attr; already added to group } else { - LibertyComplexAttr *attr = new LibertyComplexAttr(std::move(name), - std::move(*values), - line); + LibertyComplexAttr *attr = + new LibertyComplexAttr(std::move(name), std::move(*values), line); delete values; LibertyGroup *group = this->group(); group->addAttr(attr); @@ -266,7 +262,7 @@ LibertyScanner::includeBegin() } else { report_->fileWarn(25, filename_.c_str(), yylineno, - "cannot open include file %s.", filename.c_str()); + "cannot open include file {}.", filename); delete stream; } } @@ -291,7 +287,7 @@ LibertyScanner::fileEnd() void LibertyScanner::error(const char *msg) { - report_->fileError(1866, filename_.c_str(), lineno(), "%s", msg); + report_->fileError(1866, filename_.c_str(), lineno(), "{}", msg); } //////////////////////////////////////////////////////////////// @@ -305,10 +301,7 @@ LibertyGroup::LibertyGroup(std::string type, { } -LibertyGroup::~LibertyGroup() -{ - clear(); -} +LibertyGroup::~LibertyGroup() { clear(); } void LibertyGroup::clear() @@ -327,19 +320,15 @@ LibertyGroup::clear() bool LibertyGroup::empty() const { - return subgroups_.empty() - && simple_attr_map_.empty() - && complex_attr_map_.empty() - && define_map_.empty(); + return subgroups_.empty() && simple_attr_map_.empty() && complex_attr_map_.empty() + && define_map_.empty(); } bool LibertyGroup::oneGroupOnly() const { - return subgroups_.size() == 1 - && simple_attr_map_.empty() - && complex_attr_map_.empty() - && define_map_.empty(); + return subgroups_.size() == 1 && simple_attr_map_.empty() + && complex_attr_map_.empty() && define_map_.empty(); } void @@ -483,7 +472,7 @@ LibertyGroup::findAttrFloat(const std::string attr_name, const std::string &float_str = attr_value.stringValue(); char *end = nullptr; value = std::strtof(float_str.c_str(), &end); - if (end) { + if (end) { exists = true; return; } @@ -538,10 +527,7 @@ LibertyComplexAttr::LibertyComplexAttr(std::string name, { } -LibertyComplexAttr::~LibertyComplexAttr() -{ - deleteContents(values_); -} +LibertyComplexAttr::~LibertyComplexAttr() { deleteContents(values_); } const LibertyAttrValue * LibertyComplexAttr::firstValue() const @@ -585,9 +571,9 @@ LibertyAttrValue::floatValue() const } void -LibertyAttrValue::floatValue(// Return values. - float &value, - bool &valid) const +LibertyAttrValue::floatValue( // Return values. + float &value, + bool &valid) const { valid = false; if (string_value_.empty()) { @@ -598,8 +584,7 @@ LibertyAttrValue::floatValue(// Return values. // Some floats are enclosed in quotes. char *end; value = strtof(string_value_.c_str(), &end); - if ((*end == '\0' - || isspace(*end)) + if ((*end == '\0' || isspace(*end)) // strtof support INF as a valid float. && string_value_ != "inf") { valid = true; @@ -631,4 +616,4 @@ LibertyVariable::LibertyVariable(std::string var, { } -} // namespace +} // namespace sta diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index e95ed876..a873a881 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -172,12 +172,12 @@ LibertyReader::endCell(const LibertyGroup *cell_group, const char *name = cell_group->firstName(); if (name) { - debugPrint(debug_, "liberty", 1, "cell %s", name); + debugPrint(debug_, "liberty", 1, "cell {}", name); LibertyCell *cell = builder_.makeCell(library_, name, filename_); readCell(cell, cell_group); } else - libWarn(1193, cell_group, "cell missing name."); + warn(1193, cell_group, "cell missing name."); // Delete the cell group and preceding library attributes // and groups so they are not revisited and reduce memory peak. @@ -267,7 +267,7 @@ LibertyReader::makeLibrary(const LibertyGroup *libary_group) if (name) { LibertyLibrary *library = network_->findLiberty(name); if (library) - libWarn(1140, libary_group, "library %s already exists.", name); + warn(1140, libary_group, "library {} already exists.", name); // Make a new library even if a library with the same name exists. // Both libraries may be accessed by min/max analysis points. library_ = network_->makeLibertyLibrary(name, filename_); @@ -296,7 +296,7 @@ LibertyReader::makeLibrary(const LibertyGroup *libary_group) library_->setDelayModelType(DelayModelType::cmos_linear); } else - libError(1141, libary_group, "library missing name."); + error(1141, libary_group, "library missing name."); } // Energy scale is derived from other units. @@ -366,18 +366,18 @@ LibertyReader::readLibraryUnits(const LibertyGroup *library_group) else if (stringEqual(suffix.c_str(), "pf")) cap_scale_ = scale * 1E-12F; else - libWarn(1154, cap_attr, "capacitive_load_units are not ff or pf."); + warn(1154, cap_attr, "capacitive_load_units are not ff or pf."); } else - libWarn(1155, cap_attr, "capacitive_load_units are not a string."); + warn(1155, cap_attr, "capacitive_load_units are not a string."); } else - libWarn(1157, cap_attr, "capacitive_load_units scale is not a float."); + warn(1157, cap_attr, "capacitive_load_units scale is not a float."); } else if (values.size() == 1) - libWarn(1156, cap_attr, "capacitive_load_units missing suffix."); + warn(1156, cap_attr, "capacitive_load_units missing suffix."); else - libWarn(1158, cap_attr, "capacitive_load_units missing scale and suffix."); + warn(1158, cap_attr, "capacitive_load_units missing scale and suffix."); library_->units()->capacitanceUnit()->setScale(cap_scale_); } } @@ -409,7 +409,7 @@ LibertyReader::readUnit(const char *unit_attr_name, else if (unit_mult == "100") mult = 100.0F; else - libWarn(1150, unit_attr, "unknown unit multiplier %s.", unit_mult.c_str()); + warn(1150, unit_attr, "unknown unit multiplier {}.", unit_mult); } else scale_suffix = *units; @@ -432,13 +432,13 @@ LibertyReader::readUnit(const char *unit_attr_name, else if (scale_char == 'f') scale_mult = 1E-15F; else - libWarn(1151, unit_attr, "unknown unit scale %c.", scale_char); + warn(1151, unit_attr, "unknown unit scale {}.", scale_char); } else - libWarn(1152, unit_attr, "unknown unit suffix %s.", suffix.c_str()); + warn(1152, unit_attr, "unknown unit suffix {}.", suffix); } else if (!stringEqual(scale_suffix.c_str(), unit_suffix)) - libWarn(1153, unit_attr, "unknown unit suffix %s.", scale_suffix.c_str()); + warn(1153, unit_attr, "unknown unit suffix {}.", scale_suffix); scale_var = scale_mult * mult; unit->setScale(scale_var); } @@ -456,23 +456,23 @@ LibertyReader::readDelayModel(const LibertyGroup *library_group) library_->setDelayModelType(DelayModelType::cmos_linear); else if (*type_name == "piecewise_cmos") { library_->setDelayModelType(DelayModelType::cmos_pwl); - libWarn(1160, library_group, "delay_model %s not supported.", type_name->c_str()); + warn(1160, library_group, "delay_model {} not supported.", *type_name); } else if (*type_name == "cmos2") { library_->setDelayModelType(DelayModelType::cmos2); - libWarn(1161, library_group, "delay_model %s not supported.", type_name->c_str()); + warn(1161, library_group, "delay_model {} not supported.", *type_name); } else if (*type_name == "polynomial") { library_->setDelayModelType(DelayModelType::polynomial); - libWarn(1162, library_group, "delay_model %s not supported.", type_name->c_str()); + warn(1162, library_group, "delay_model {} not supported.", *type_name); } // Evil IBM garbage. else if (*type_name == "dcm") { library_->setDelayModelType(DelayModelType::dcm); - libWarn(1163, library_group, "delay_model %s not supported..", type_name->c_str()); + warn(1163, library_group, "delay_model {} not supported..", *type_name); } else - libWarn(1164, library_group, "unknown delay_model %s.", type_name->c_str()); + warn(1164, library_group, "unknown delay_model {}.", *type_name); } } @@ -489,7 +489,7 @@ LibertyReader::readBusStyle(const LibertyGroup *library_group) && (*bus_style)[4] == 'd') library_->setBusBrkts((*bus_style)[2], (*bus_style)[5]); else - libWarn(1165, library_group, "unknown bus_naming_style format."); + warn(1165, library_group, "unknown bus_naming_style format."); } } @@ -511,9 +511,9 @@ LibertyReader::readBusTypes(LibertyCell *cell, library_->makeBusDcl(name, from, to); } else if (!from_exists) - libWarn(1179, type_group, "bus type missing bit_from."); + warn(1179, type_group, "bus type missing bit_from."); else if (!to_exists) - libWarn(1180, type_group, "bus type missing bit_to."); + warn(1180, type_group, "bus type missing bit_to."); } } } @@ -539,13 +539,13 @@ LibertyReader::checkThresholds(const LibertyGroup *library_group) const { for (const RiseFall *rf : RiseFall::range()) { if (library_->inputThreshold(rf) == 0.0) - libWarn(1145, library_group, "input_threshold_pct_%s not found.", rf->name()); + warn(1145, library_group, "input_threshold_pct_{} not found.", rf->name()); if (library_->outputThreshold(rf) == 0.0) - libWarn(1146, library_group, "output_threshold_pct_%s not found.", rf->name()); + warn(1146, library_group, "output_threshold_pct_{} not found.", rf->name()); if (library_->slewLowerThreshold(rf) == 0.0) - libWarn(1147, library_group, "slew_lower_threshold_pct_%s not found.", rf->name()); + warn(1147, library_group, "slew_lower_threshold_pct_{} not found.", rf->name()); if (library_->slewUpperThreshold(rf) == 0.0) - libWarn(1148, library_group, "slew_upper_threshold_pct_%s not found.", rf->name()); + warn(1148, library_group, "slew_upper_threshold_pct_{} not found.", rf->name()); } } @@ -579,7 +579,7 @@ LibertyReader::readTableTemplates(const LibertyGroup *library_group, tbl_template->setAxis3(axis3); } else - libWarn(1175, template_group, "table template missing name."); + warn(1175, template_group, "table template missing name."); } } @@ -592,7 +592,7 @@ LibertyReader::makeTableTemplateAxis(const LibertyGroup *template_group, if (var_name) { TableAxisVariable axis_var = stringTableAxisVariable(var_name->c_str()); if (axis_var == TableAxisVariable::unknown) - libWarn(1297, template_group, "axis type %s not supported.", var_name->c_str()); + warn(1297, template_group, "axis type {} not supported.", *var_name); else { std::string index_attr_name = "index_" + std::to_string(axis_index); const LibertyComplexAttr *index_attr = @@ -605,7 +605,7 @@ LibertyReader::makeTableTemplateAxis(const LibertyGroup *template_group, for (size_t i = 1; i < axis_values.size(); i++) { float value = axis_values[i]; if (value <= prev) { - libWarn(1178, template_group, "non-increasing table index values."); + warn(1178, template_group, "non-increasing table index values."); break; } prev = value; @@ -644,7 +644,7 @@ LibertyReader::readVoltateMaps(const LibertyGroup *library_group) if (valid) library_->addSupplyVoltage(volt_name.c_str(), volt); else - libWarn(1166, volt_attr, "voltage_map voltage is not a float."); + warn(1166, volt_attr, "voltage_map voltage is not a float."); } } } @@ -684,8 +684,8 @@ LibertyReader::readOperatingConds(const LibertyGroup *library_group) if (op_cond) library_->setDefaultOperatingConditions(op_cond); else - libWarn(1144, library_group, "default_operating_condition %s not found.", - default_op_cond->c_str()); + warn(1144, library_group, "default_operating_condition {} not found.", + *default_op_cond); } } @@ -774,11 +774,11 @@ LibertyReader::readWireloads(const LibertyGroup *library_group) if (exists) wireload->addFanoutLength(fanout, length); else - libWarn(1185, fanout_attr, "fanout_length is missing length and fanout."); + warn(1185, fanout_attr, "fanout_length is missing length and fanout."); } } else - libWarn(1184, wl_group, "wire_load missing name."); + warn(1184, wl_group, "wire_load missing name."); } } @@ -810,20 +810,20 @@ LibertyReader::readWireloadSelection(const LibertyGroup *library_group) wireload_selection->addWireloadFromArea(min_area, max_area, wireload); else - libWarn(1187, area_attr, "wireload %s not found.", wireload_name.c_str()); + warn(1187, area_attr, "wireload {} not found.", wireload_name); } else - libWarn(1188, area_attr, + warn(1188, area_attr, "wire_load_from_area wireload name not a string."); } else - libWarn(1189, area_attr, "wire_load_from_area min not a float."); + warn(1189, area_attr, "wire_load_from_area min not a float."); } else - libWarn(1190, area_attr, "wire_load_from_area max not a float."); + warn(1190, area_attr, "wire_load_from_area max not a float."); } else - libWarn(1191, area_attr, "wire_load_from_area missing parameters."); + warn(1191, area_attr, "wire_load_from_area missing parameters."); } } } @@ -837,8 +837,8 @@ LibertyReader::readDefaultWireLoad(const LibertyGroup *library_group) if (wireload) library_->setDefaultWireload(wireload); else - libWarn(1142, library_group, "default_wire_load %s not found.", - wireload_name->c_str()); + warn(1142, library_group, "default_wire_load {} not found.", + *wireload_name); } } @@ -852,8 +852,8 @@ LibertyReader::readDefaultWireLoadMode(const LibertyGroup *library_group) if (mode != WireloadMode::unknown) library_->setDefaultWireloadMode(mode); else - libWarn(1174, library_group, "default_wire_load_mode %s not found.", - wire_load_mode->c_str()); + warn(1174, library_group, "default_wire_load_mode {} not found.", + *wire_load_mode); } } @@ -868,8 +868,8 @@ LibertyReader::readDefaultWireLoadSelection(const LibertyGroup *library_group) if (selection) library_->setDefaultWireloadSelection(selection); else - libWarn(1143, library_group, "default_wire_selection %s not found.", - selection_name->c_str()); + warn(1143, library_group, "default_wire_selection {} not found.", + *selection_name); } } @@ -897,11 +897,11 @@ LibertyReader::readModeDefs(LibertyCell *cell, } } else - libWarn(1264, value_group, "mode value missing name."); + warn(1264, value_group, "mode value missing name."); } } else - libWarn(1263, mode_group, "mode definition missing name."); + warn(1263, mode_group, "mode definition missing name."); } } @@ -920,7 +920,7 @@ LibertyReader::readSlewDegradations(const LibertyGroup *library_group) if (LibertyLibrary::checkSlewDegradationAxes(table_model)) library_->setWireSlewDegradationTable(table_model, rf); else - libWarn(1254, degradation_group, "unsupported model axis."); + warn(1254, degradation_group, "unsupported model axis."); } } } @@ -966,9 +966,9 @@ LibertyReader::readLibAttrFloatWarnZero(const LibertyGroup *library_group, if (value == 0.0F) { const LibertySimpleAttr *attr = library_group->findSimpleAttr(attr_name); if (attr) - libWarn(1171, attr, "%s is 0.0.", attr_name); + warn(1171, attr, "{} is 0.0.", attr_name); else - libWarn(1172, library_group, "%s is 0.0.", attr_name); + warn(1172, library_group, "{} is 0.0.", attr_name); } (library_->*set_func)(value * scale); } @@ -1016,7 +1016,7 @@ LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group) if (op_cond_name) { OperatingConditions *op_cond = library_->findOperatingConditions(op_cond_name); if (op_cond) { - debugPrint(debug_, "liberty", 1, "scaled cell %s %s", + debugPrint(debug_, "liberty", 1, "scaled cell {} {}", name, op_cond_name); LibertyCell *scaled_cell = library_->makeScaledCell(name, filename_); readCell(scaled_cell, scaled_cell_group); @@ -1025,17 +1025,17 @@ LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group) owner->addScaledCell(op_cond, scaled_cell); } else - libWarn(1202, scaled_cell_group, "operating conditions %s not found.", + warn(1202, scaled_cell_group, "operating conditions {} not found.", op_cond_name); } else - libWarn(1203, scaled_cell_group, "scaled_cell missing operating condition."); + warn(1203, scaled_cell_group, "scaled_cell missing operating condition."); } else - libWarn(1204, scaled_cell_group, "scaled_cell cell %s has not been defined.", name); + warn(1204, scaled_cell_group, "scaled_cell cell {} has not been defined.", name); } else - libWarn(1205, scaled_cell_group, "scaled_cell missing name."); + warn(1205, scaled_cell_group, "scaled_cell missing name."); } // Minimal check that is not very specific about where the discrepancies are. @@ -1047,20 +1047,20 @@ LibertyReader::checkScaledCell(LibertyCell *scaled_cell, { if (equivCellPorts(scaled_cell, owner)) { if (!equivCellPorts(scaled_cell, owner)) - libWarn(1206, scaled_cell_group, "scaled_cell %s, %s ports do not match cell ports", + warn(1206, scaled_cell_group, "scaled_cell {}, {} ports do not match cell ports", scaled_cell->name(), op_cond_name); if (!equivCellFuncs(scaled_cell, owner)) - libWarn(1206, scaled_cell_group, - "scaled_cell %s, %s port functions do not match cell port functions.", + warn(1206, scaled_cell_group, + "scaled_cell {}, {} port functions do not match cell port functions.", scaled_cell->name(), op_cond_name); } else - libWarn(1207, scaled_cell_group, "scaled_cell ports do not match cell ports."); + warn(1207, scaled_cell_group, "scaled_cell ports do not match cell ports."); if (!equivCellTimingArcSets(scaled_cell, owner)) - libWarn(1208, scaled_cell_group, - "scaled_cell %s, %s timing does not match cell timing.", + warn(1208, scaled_cell_group, + "scaled_cell {}, {} timing does not match cell timing.", scaled_cell->name(), op_cond_name); } @@ -1112,7 +1112,7 @@ LibertyReader::makeBusPort(LibertyCell *cell, if (bus_dcl == nullptr) bus_dcl = library_->findBusDcl(bus_type->c_str()); if (bus_dcl) { - debugPrint(debug_, "liberty", 1, " bus %s", port_name.c_str()); + debugPrint(debug_, "liberty", 1, " bus {}", port_name); LibertyPort *bus_port = makeBusPort(cell, port_name.c_str(), bus_dcl->from(), bus_dcl->to(), bus_dcl); @@ -1121,11 +1121,11 @@ LibertyReader::makeBusPort(LibertyCell *cell, makeBusPinPorts(cell, bus_group, port_group_map); } else - libWarn(1235, bus_type_attr, "bus_type %s not found.", bus_type->c_str()); + warn(1235, bus_type_attr, "bus_type {} not found.", *bus_type); } } else - libWarn(1236, bus_type_attr, "bus_type not found."); + warn(1236, bus_type_attr, "bus_type not found."); } } @@ -1138,7 +1138,7 @@ LibertyReader::makeBusPinPorts(LibertyCell *cell, for (const LibertyAttrValue *param : pin_group->params()) { if (param->isString()) { const std::string pin_name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " bus pin port %s", pin_name.c_str()); + debugPrint(debug_, "liberty", 1, " bus pin port {}", pin_name); // Expand foo[3:0] port names. PortNameBitIterator name_iter(cell, pin_name.c_str(), this, pin_group->line()); while (name_iter.hasNext()) { @@ -1147,11 +1147,11 @@ LibertyReader::makeBusPinPorts(LibertyCell *cell, port_group_map[pin_group].push_back(pin_port); } else - libWarn(1232, pin_group, "pin %s not found.", pin_name.c_str()); + warn(1232, pin_group, "pin {} not found.", pin_name); } } else - libWarn(1233, pin_group, "pin name is not a string."); + warn(1233, pin_group, "pin name is not a string."); } } } @@ -1162,7 +1162,7 @@ LibertyReader::makeBundlePort(LibertyCell *cell, LibertyPortGroupMap &port_group_map) { const std::string &bundle_name = bundle_group->firstName(); - debugPrint(debug_, "liberty", 1, " bundle %s", bundle_name.c_str()); + debugPrint(debug_, "liberty", 1, " bundle {}", bundle_name); const LibertyComplexAttr *member_attr = bundle_group->findComplexAttr("members"); ConcretePortSeq *members = new ConcretePortSeq; @@ -1191,14 +1191,14 @@ LibertyReader::makeBundlePinPorts(LibertyCell *cell, for (LibertyAttrValue *param : pin_group->params()) { if (param->isString()) { const std::string pin_name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " bundle pin port %s", pin_name.c_str()); + debugPrint(debug_, "liberty", 1, " bundle pin port {}", pin_name); LibertyPort *pin_port = cell->findLibertyPort(pin_name.c_str()); if (pin_port == nullptr) pin_port = makePort(cell, pin_name.c_str()); port_group_map[pin_group].push_back(pin_port); } else - libWarn(1234, pin_group, "pin name is not a string."); + warn(1234, pin_group, "pin name is not a string."); } } } @@ -1226,7 +1226,7 @@ LibertyReader::makePgPinPort(LibertyCell *cell, dir = PortDirection::power(); break; case PwrGndType::none: - libError(1291, pg_pin_group, "unknown pg_type."); + error(1291, pg_pin_group, "unknown pg_type."); break; default: break; @@ -1349,10 +1349,10 @@ LibertyReader::readPortAttrBool(const char *attr_name, (port->*set_func)(false); } else - libWarn(1238, attr, "%s attribute is not boolean.", attr_name); + warn(1238, attr, "{} attribute is not boolean.", attr_name); } else - libWarn(1239, attr, "%s attribute is not boolean.", attr_name); + warn(1239, attr, "{} attribute is not boolean.", attr_name); } } @@ -1399,7 +1399,7 @@ LibertyReader::readPulseClock(const LibertyPortSeq &ports, sense = RiseFall::fall(); } else - libWarn(1242, port_group, "pulse_latch unknown pulse type."); + warn(1242, port_group, "pulse_latch unknown pulse type."); if (trigger) { for (LibertyPort *port : ports) port->setPulseClk(trigger, sense); @@ -1437,7 +1437,7 @@ LibertyReader::readSignalType(LibertyCell *cell, else if (*type == "test_scan_out_inverted") signal_type = ScanSignalType::output_inverted; else { - libWarn(1299, port_group, "unknown signal_type %s.", type->c_str()); + warn(1299, port_group, "unknown signal_type {}.", *type); return; } for (LibertyPort *port : ports) @@ -1464,7 +1464,7 @@ LibertyReader::readPortDir(const LibertyPortSeq &ports, else if (*dir == "internal") port_dir = PortDirection::internal(); else - libWarn(1240, dir_attr, "unknown port direction."); + warn(1240, dir_attr, "unknown port direction."); for (LibertyPort *port : ports) port->setDirection(port_dir); } @@ -1522,7 +1522,7 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, port_group->findAttrFloat(attr_name, limit, exists); if (exists) { if (min_max == MinMax::max() && limit == 0.0) - libWarn(1241, port_group, "max_transition is 0.0."); + warn(1241, port_group, "max_transition is 0.0."); port->setSlewLimit(limit * time_scale_, min_max); } } @@ -1604,8 +1604,8 @@ LibertyReader::makePortFuncs(LibertyCell *cell, for (LibertyPort *port : ports) { port->setFunction(func_expr); if (func_expr->checkSize(port)) { - libWarn(1195, func_attr->line(), - "port %s function size does not match port size.", + warn(1195, func_attr->line(), + "port {} function size does not match port size.", port->name()); } } @@ -1694,8 +1694,8 @@ LibertyReader::makeSeqFunc(LibertyCell *cell, if (attr) { expr = parseFunc(attr->c_str(), attr_name, cell, seq_group->line()); if (expr && expr->checkSize(size)) { - libWarn(1196, seq_group, "%s %s bus width mismatch.", - seq_group->type().c_str(), attr_name); + warn(1196, seq_group, "{} {} bus width mismatch.", + seq_group->type(), attr_name); delete expr; expr = nullptr; } @@ -1832,8 +1832,7 @@ LibertyReader::readScaleFactors(LibertyCell *cell, if (scale_factors) cell->setScaleFactors(scale_factors); else - libWarn(1230, cell_group, "scaling_factors %s not found.", - scale_factors_name->c_str()); + warn(1230, cell_group, "scaling_factors {} not found.", *scale_factors_name); } } @@ -1878,10 +1877,10 @@ LibertyReader::readCellAttrBool(const char *attr_name, else if (stringEqual(value.c_str(), "false")) (cell->*set_func)(false); else - libWarn(1279, attr, "%s attribute is not boolean.", attr_name); + warn(1279, attr, "{} attribute is not boolean.", attr_name); } else - libWarn(1280, attr, "%s attribute is not boolean.", attr_name); + warn(1280, attr, "{} attribute is not boolean.", attr_name); } } @@ -1906,18 +1905,18 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, for (LibertyPort *to_port : ports) { if (timing_type == TimingType::combinational && to_port->direction()->isInput()) - libWarn(1209, timing_group, "combinational timing to an input port."); + warn(1209, timing_group, "combinational timing to an input port."); if (related_port_names.size() || related_bus_names.size()) { for (const std::string &from_port_name : related_port_names) { - debugPrint(debug_, "liberty", 2, " timing %s -> %s", - from_port_name.c_str(), to_port->name()); + debugPrint(debug_, "liberty", 2, " timing {} -> {}", + from_port_name, to_port->name()); makeTimingArcs(cell, from_port_name, to_port, related_output_port, true, timing_attrs, timing_group->line()); } for (const std::string &from_port_name : related_bus_names) { - debugPrint(debug_, "liberty", 2, " timing %s -> %s", - from_port_name.c_str(), to_port->name()); + debugPrint(debug_, "liberty", 2, " timing {} -> {}", + from_port_name, to_port->name()); makeTimingArcs(cell, from_port_name, to_port, related_output_port, false, timing_attrs, timing_group->line()); } @@ -1926,7 +1925,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, || timing_type == TimingType::minimum_period || timing_type == TimingType::min_clock_tree_path || timing_type == TimingType::max_clock_tree_path)) - libWarn(1243, timing_group, "timing group missing related_pin/related_bus_pin."); + warn(1243, timing_group, "timing group missing related_pin/related_bus_pin."); else makeTimingArcs(cell, to_port, related_output_port, timing_attrs, timing_group->line()); @@ -1975,7 +1974,7 @@ LibertyReader::readTimingSense(const LibertyGroup *timing_group, else if (*sense_name == "negative_unate") timing_attrs->setTimingSense(TimingSense::negative_unate); else - libWarn(1245, timing_group, "unknown timing_sense %s.", sense_name->c_str()); + warn(1245, timing_group, "unknown timing_sense {}.", *sense_name); } } } @@ -1991,7 +1990,7 @@ LibertyReader::readTimingType(const LibertyGroup *timing_group, if (type_name) { type = findTimingType(type_name->c_str()); if (type == TimingType::unknown) { - libWarn(1244, type_attr, "unknown timing_type %s.", type_name->c_str()); + warn(1244, type_attr, "unknown timing_type {}.", *type_name); type = TimingType::combinational; } } @@ -2046,16 +2045,16 @@ LibertyReader::readTimingMode(const LibertyGroup *timing_group, if (value->isString()) timing_attrs->setModeName(value->stringValue()); else - libWarn(1248, mode_attr, "mode name is not a string."); + warn(1248, mode_attr, "mode name is not a string."); value = mode_values[1]; if (value->isString()) timing_attrs->setModeValue(value->stringValue()); else - libWarn(1246, mode_attr, "mode value is not a string."); + warn(1246, mode_attr, "mode value is not a string."); } else - libWarn(1249, mode_attr, "mode requirees 2 values."); + warn(1249, mode_attr, "mode requirees 2 values."); } } @@ -2161,9 +2160,9 @@ LibertyReader::makeTableModels(LibertyCell *cell, TimingType timing_type = timing_attrs->timingType(); if (isGateTimingType(timing_type)) { if (slew_model == nullptr) - libWarn(1210, timing_group, "missing %s_transition.", rf->name()); + warn(1210, timing_group, "missing {}_transition.", rf->name()); if (delay_model == nullptr) - libWarn(1211, timing_group, "missing cell_%s.", rf->name()); + warn(1211, timing_group, "missing cell_{}.", rf->name()); } found_model = true; } @@ -2189,7 +2188,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, } } if (!found_model) - libWarn(1311, timing_group, "no table models found in timing group."); + warn(1311, timing_group, "no table models found in timing group."); } bool @@ -2343,7 +2342,7 @@ LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, receiver_model->setCapacitanceModel(std::move(*model), index, rf); } else - libWarn(1219, cap_group, "unsupported model axis."); + warn(1219, cap_group, "unsupported model axis."); delete model; } } @@ -2390,13 +2389,13 @@ LibertyReader::readOutputWaveforms(const LibertyGroup *timing_group, output_currents.emplace_back(slew, cap, table1, ref_time); } else - libWarn(1223, vector_group, + warn(1223, vector_group, "vector index_1 and index_2 must have exactly one value."); } delete table; } else - libWarn(1224, vector_group, "vector reference_time not found."); + warn(1224, vector_group, "vector reference_time not found."); } if (!output_currents.empty()) return makeOutputWaveforms(current_group, output_currents, rf); @@ -2448,7 +2447,7 @@ LibertyReader::makeOutputWaveforms(const LibertyGroup *current_group, ref_times[slew_index] = waveform.referenceTime(); } else - libWarn(1221, current_group, "output current waveform %.2e %.2e not found.", + warn(1221, current_group, "output current waveform {:.2e} {:.2e} not found.", waveform.slew(), waveform.cap()); } @@ -2477,13 +2476,13 @@ LibertyReader::readTableModel(const LibertyGroup *table_group, TableModel *table_model = new TableModel(table, tbl_template, scale_factor_type, rf); if (!check_axes(table_model)) { - libWarn(1251, table_group, "unsupported model axis."); + warn(1251, table_group, "unsupported model axis."); } return table_model; } } else - libWarn(1253, table_group, "table template %s not found.", template_name); + warn(1253, table_group, "table template {} not found.", template_name); } return nullptr; } @@ -2522,7 +2521,7 @@ LibertyReader::readTableModel(const LibertyGroup *table_group, } } else - libWarn(1257, table_group, "%s is missing values.", table_group->type().c_str()); + warn(1257, table_group, "{} is missing values.", table_group->type()); return nullptr; } @@ -2535,14 +2534,14 @@ LibertyReader::makeTableAxis(const LibertyGroup *table_group, if (index_attr) { FloatSeq axis_values = readFloatSeq(index_attr, 1.0F); if (axis_values.empty()) - libWarn(1177, index_attr, "missing table index values."); + warn(1177, index_attr, "missing table index values."); else { // Check monotonicity of the values. float prev = axis_values[0]; for (size_t i = 1; i < axis_values.size(); i++) { float value = axis_values[i]; if (value <= prev) - libWarn(1173, index_attr, "non-increasing table index values."); + warn(1173, index_attr, "non-increasing table index values."); prev = value; } @@ -2573,7 +2572,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, if (from_port_iter.hasNext()) { LibertyPort *from_port = from_port_iter.next(); if (from_port->direction()->isOutput()) - libWarn(1212, timing_line, "timing group from output port."); + warn(1212, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, timing_attrs, timing_line); } @@ -2583,7 +2582,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, while (from_port_iter.hasNext()) { LibertyPort *from_port = from_port_iter.next(); if (from_port->direction()->isOutput()) - libWarn(1213, timing_line, "timing group from output port."); + warn(1213, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, timing_attrs, timing_line); } @@ -2593,7 +2592,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, if (from_port_iter.hasNext()) { LibertyPort *from_port = from_port_iter.next(); if (from_port->direction()->isOutput()) - libWarn(1214, timing_line, "timing group from output port."); + warn(1214, timing_line, "timing group from output port."); LibertyPortMemberIterator bit_iter(to_port); while (bit_iter.hasNext()) { LibertyPort *to_port_bit = bit_iter.next(); @@ -2610,8 +2609,8 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, LibertyPortMemberIterator to_port_iter(to_port); // warn about different sizes if (from_size != to_size) - libWarn(1216, timing_line, - "timing port %s and related port %s are different sizes.", + warn(1216, timing_line, + "timing port {} and related port {} are different sizes.", from_port_name.c_str(), to_port->name()); // align to/from iterators for one-to-one mapping @@ -2628,7 +2627,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, LibertyPort *from_port_bit = from_port_iter.next(); LibertyPort *to_port_bit = to_port_iter.next(); if (from_port_bit->direction()->isOutput()) - libWarn(1215, timing_line, "timing group from output port."); + warn(1215, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, related_out_port, timing_attrs, timing_line); @@ -2688,7 +2687,7 @@ LibertyReader::readLeagageGrouops(LibertyCell *cell, cell->makeLeakagePower(related_pg_port, when, power * power_scale_); } else - libWarn(1307, leak_group, "leakage_power missing value."); + warn(1307, leak_group, "leakage_power missing value."); } } @@ -2769,7 +2768,7 @@ LibertyReader::findLibertyPort(LibertyCell *cell, if (port) return port; else - libWarn(1290, attr, "port %s not found.", port_name->c_str()); + warn(1290, attr, "port {} not found.", *port_name); } } return nullptr; @@ -2801,7 +2800,7 @@ LibertyReader::findLibertyPorts(LibertyCell *cell, if (port) ports.push_back(port); else - libWarn(1306, group, "port %s not found.", port_name.c_str()); + warn(1306, group, "port {} not found.", port_name); } return ports; } @@ -2837,12 +2836,12 @@ LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, case TimingSense::negative_unate: break; case TimingSense::non_unate: - libWarn(1200, line, "latch enable function is non-unate for port %s.", + warn(1200, line, "latch enable function is non-unate for port {}.", enable_port->name()); break; case TimingSense::none: case TimingSense::unknown: - libWarn(1201, line, "latch enable function is unknown for port %s.", + warn(1201, line, "latch enable function is unknown for port {}.", enable_port->name()); break; } @@ -2861,19 +2860,19 @@ LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group) TableTemplate *tbl_template = library_->findTableTemplate(template_name, TableTemplateType::delay); if (!tbl_template) { - libWarn(1256, waveform_group, "table template %s not found.", template_name); + warn(1256, waveform_group, "table template {} not found.", template_name); continue; } TablePtr table = readTableModel(waveform_group, tbl_template, time_scale_); if (!table) continue; if (table->axis1()->variable() != TableAxisVariable::input_net_transition) { - libWarn(1265, waveform_group, + warn(1265, waveform_group, "normalized_driver_waveform variable_1 must be input_net_transition"); continue; } if (table->axis2()->variable() != TableAxisVariable::normalized_voltage) { - libWarn(1225, waveform_group, + warn(1225, waveform_group, "normalized_driver_waveform variable_2 must be normalized_voltage"); continue; } @@ -2884,7 +2883,7 @@ LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group) library_->makeDriverWaveform(driver_waveform_name, table); } else - libWarn(1227, waveform_group, "normalized_driver_waveform missing template."); + warn(1227, waveform_group, "normalized_driver_waveform missing template."); } } @@ -2903,7 +2902,7 @@ LibertyReader::readLevelShifterType(LibertyCell *cell, else if (*level_shifter_type == "HL_LH") cell->setLevelShifterType(LevelShifterType::HL_LH); else - libWarn(1228, cell_group, "level_shifter_type must be HL, LH, or HL_LH"); + warn(1228, cell_group, "level_shifter_type must be HL, LH, or HL_LH"); } } @@ -2918,7 +2917,7 @@ LibertyReader::readSwitchCellType(LibertyCell *cell, else if (*switch_cell_type == "fine_grain") cell->setSwitchCellType(SwitchCellType::fine_grain); else - libWarn(1229, cell_group, "switch_cell_type must be coarse_grain or fine_grain"); + warn(1229, cell_group, "switch_cell_type must be coarse_grain or fine_grain"); } } @@ -2934,8 +2933,8 @@ LibertyReader::readCellOcvDerateGroup(LibertyCell *cell, if (derate) cell->setOcvDerate(derate); else - libWarn(1237, cell_group, "OCV derate group named %s not found.", - derate_name->c_str()); + warn(1237, cell_group, "OCV derate group named {} not found.", + *derate_name); } } @@ -2964,25 +2963,25 @@ LibertyReader::readStatetable(LibertyCell *cell, for (const std::string &row : table_rows) { const StringSeq row_groups = parseTokens(row, ':'); if (row_groups.size() != 3) { - libWarn(1300, table_attr, "table row must have 3 groups separated by ':'."); + warn(1300, table_attr, "table row must have 3 groups separated by ':'."); break; } StringSeq inputs = parseTokens(row_groups[0], ' '); if (inputs.size() != input_count) { - libWarn(1301,table_attr,"table row has %zu input values but %zu are required.", + warn(1301, table_attr, "table row has {} input values but {} are required.", inputs.size(), input_count); break; } StringSeq currents = parseTokens(row_groups[1], ' '); if (currents.size() != internal_count) { - libWarn(1302,table_attr, - "table row has %zu current values but %zu are required.", + warn(1302,table_attr, + "table row has {} current values but {} are required.", currents.size(), internal_count); break; } StringSeq nexts = parseTokens(row_groups[2], ' '); if (nexts.size() != internal_count) { - libWarn(1303, table_attr, "table row has %zu next values but %zu are required.", + warn(1303, table_attr, "table row has {} next values but {} are required.", nexts.size(), internal_count); break; } @@ -3002,8 +3001,8 @@ LibertyReader::readStatetable(LibertyCell *cell, if (port) input_port_ptrs.push_back(port); else - libWarn(1298, statetable_group, "statetable input port %s not found.", - input.c_str()); + warn(1298, statetable_group, "statetable input port {} not found.", + input); } LibertyPortSeq internal_port_ptrs; for (const std::string &internal : internal_ports) { @@ -3024,7 +3023,7 @@ LibertyReader::readTestCell(LibertyCell *cell, const LibertyGroup *test_cell_group = cell_group->findSubgroup("test_cell"); if (test_cell_group) { if (cell->testCell()) - libWarn(1262, test_cell_group, "cell %s test_cell redefinition.", cell->name()); + warn(1262, test_cell_group, "cell {} test_cell redefinition.", cell->name()); else { std::string test_cell_name = std::string(cell->name()) + "/test_cell"; TestCell *test_cell = new TestCell(cell->libertyLibrary(), @@ -3131,8 +3130,8 @@ LibertyReader::parseStateInputValues(StringSeq &inputs, StateInputValue value; state_input_value_name_map.find(input.c_str(), value, exists); if (!exists) { - libWarn(1304, attr, "table input value '%s' not recognized.", - input.c_str()); + warn(1304, attr, "table input value '{}' not recognized.", + input); value = StateInputValue::dont_care; } input_values.push_back(value); @@ -3150,8 +3149,8 @@ LibertyReader::parseStateInternalValues(StringSeq &states, StateInternalValue value; state_internal_value_name_map.find(state.c_str(), value, exists); if (!exists) { - libWarn(1305, attr, "table internal value '%s' not recognized.", - state.c_str()); + warn(1305, attr, "table internal value '{}' not recognized.", + state); value = StateInternalValue::unknown; } state_values.push_back(value); @@ -3178,11 +3177,11 @@ LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr, else if (value->isFloat()) row.push_back(value->floatValue() * scale); else - libWarn(1258, values_attr, "%s is not a list of floats.", - values_attr->name().c_str()); + warn(1258, values_attr, "{} is not a list of floats.", + values_attr->name()); if (row.size() != cols) { - libWarn(1259, values_attr, "%s row has %zu columns but axis has %zu.", - table_group->type().c_str(), + warn(1259, values_attr, "{} row has {} columns but axis has {}.", + table_group->type(), row.size(), cols); for (size_t c = row.size(); c < cols; c++) @@ -3192,11 +3191,11 @@ LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr, } if (table.size() != rows) { if (rows == 0) - libWarn(1260, values_attr, "%s missing axis values.", - table_group->type().c_str()); + warn(1260, values_attr, "{} missing axis values.", + table_group->type()); else - libWarn(1261, values_attr, "%s has %zu rows but axis has %zu.", - table_group->type().c_str(), + warn(1261, values_attr, "{} has {} rows but axis has {}.", + table_group->type(), table.size(), rows); for (size_t r = table.size(); r < rows; r++) { @@ -3224,7 +3223,7 @@ LibertyReader::getAttrInt(const LibertySimpleAttr *attr, exists = true; } else - libWarn(1268, attr, "%s attribute is not an integer.",attr->name().c_str()); + warn(1268, attr, "{} attribute is not an integer.", attr->name()); } // Get two floats in a complex attribute. @@ -3242,15 +3241,15 @@ LibertyReader::getAttrFloat2(const LibertyComplexAttr *attr, LibertyAttrValue *value = values[0]; getAttrFloat(attr, value, value1, exists); if (!exists) - libWarn(1272, attr, "%s is not a float.", attr->name().c_str()); + warn(1272, attr, "{} is not a float.", attr->name()); value = values[1]; getAttrFloat(attr, value, value2, exists); if (!exists) - libWarn(1273, attr, "%s is not a float.", attr->name().c_str()); + warn(1273, attr, "{} is not a float.", attr->name()); } else - libWarn(1274, attr, "%s requires 2 valules.", attr->name().c_str()); + warn(1274, attr, "{} requires 2 valules.", attr->name()); } void @@ -3272,9 +3271,9 @@ LibertyReader::getAttrFloat(const LibertyComplexAttr *attr, value = strtof(str.c_str(), &end); if ((*end && !isspace(*end)) || str == "inf") - libWarn(1183, attr->line(), "%s value %s is not a float.", - attr->name().c_str(), - str.c_str()); + warn(1183, attr->line(), "{} value {} is not a float.", + attr->name(), + str); valid = true; } } @@ -3308,7 +3307,7 @@ LibertyReader::parseStringFloatList(const std::string &float_list, for (const char *t = token; t <= end; t++) token_end += *t; } - libWarn(1310, attr, "%s is not a float.", token_end.c_str()); + warn(1310, attr, "{} is not a float.", token_end); token += token_end.size(); } else { @@ -3345,7 +3344,7 @@ LibertyReader::parseStringFloatList(const std::string &float_list, for (const char *t = token; t <= end; t++) token_end += *t; } - libWarn(1275, attr, "%s is not a float.", token_end.c_str()); + warn(1275, attr, "{} is not a float.", token_end); token += token_end.size(); } else { @@ -3373,7 +3372,7 @@ LibertyReader::readFloatSeq(const LibertyComplexAttr *attr, values.push_back(value->floatValue() * scale); } else - libWarn(1276, attr, "%s is missing values.", attr->name().c_str()); + warn(1276, attr, "{} is missing values.", attr->name()); } else if (attr_values.size() > 1) { for (LibertyAttrValue *val : attr_values) { @@ -3386,7 +3385,7 @@ LibertyReader::readFloatSeq(const LibertyComplexAttr *attr, } } else - libWarn(1277, attr, "%s has no values.", attr->name().c_str()); + warn(1277, attr, "{} has no values.", attr->name()); return values; } @@ -3411,10 +3410,10 @@ LibertyReader::getAttrBool(const LibertySimpleAttr *attr, exists = true; } else - libWarn(1288, attr, "%s attribute is not boolean.", attr->name().c_str()); + warn(1288, attr, "{} attribute is not boolean.", attr->name()); } else - libWarn(1289, attr, "%s attribute is not boolean.", attr->name().c_str()); + warn(1289, attr, "{} attribute is not boolean.", attr->name()); } // Read L/H/X string attribute values as bool. @@ -3430,8 +3429,8 @@ LibertyReader::getAttrLogicValue(const LibertySimpleAttr *attr) else if (*str == "X") return LogicValue::unknown; else - libWarn(1282, attr, "attribute %s value %s not recognized.", - attr->name().c_str(), str->c_str()); + warn(1282, attr, "attribute {} value {} not recognized.", + attr->name(), *str); // fall thru } return LogicValue::unknown; @@ -3448,7 +3447,7 @@ LibertyReader::getAttrEarlyLate(const LibertySimpleAttr *attr) else if (*value == "early_and_late") return EarlyLateAll::all(); else { - libWarn(1283, attr, "unknown early/late value."); + warn(1283, attr, "unknown early/late value."); return EarlyLateAll::all(); } } @@ -3461,11 +3460,10 @@ LibertyReader::parseFunc(const char *func, const LibertyCell *cell, int line) { - std::string error_msg; - stringPrint(error_msg, "%s, line %d %s", - filename_, - line, - attr_name); + std::string error_msg = format("{}, line {}{}", + filename_, + line, + attr_name); return parseFuncExpr(func, cell, error_msg.c_str(), report_); } @@ -3491,92 +3489,6 @@ LibertyReader::variableValue(const char *var, //////////////////////////////////////////////////////////////// -void -LibertyReader::libWarn(int id, - const LibertyGroup *group, - const char *fmt, - ...) const -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, group->line(), fmt, args); - va_end(args); -} - -void -LibertyReader::libWarn(int id, - const LibertySimpleAttr *attr, - const char *fmt, - ...) const -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, attr->line(), fmt, args); - va_end(args); -} - -void -LibertyReader::libWarn(int id, - const LibertyComplexAttr *attr, - const char *fmt, - ...) const -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, attr->line(), fmt, args); - va_end(args); -} - -void -LibertyReader::libWarn(int id, - int line, - const char *fmt, - ...) const -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, line, fmt, args); - va_end(args); -} - -void -LibertyReader::libError(int id, - const LibertyGroup *group, - const char *fmt, - ...) const -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename_, group->line(), fmt, args); - va_end(args); -} - -void -LibertyReader::libError(int id, - const LibertySimpleAttr *attr, - const char *fmt, - ...) const -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename_, attr->line(), fmt, args); - va_end(args); -} - -void -LibertyReader::libError(int id, - const LibertyComplexAttr *attr, - const char *fmt, - ...) const -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename_, attr->line(), fmt, args); - va_end(args); -} - -//////////////////////////////////////////////////////////////// - void LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group) { @@ -3587,8 +3499,8 @@ LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group) if (derate) library_->setDefaultOcvDerate(derate); else - libWarn(1284, library_group, "OCV derate group named %s not found.", - derate_name->c_str()); + warn(1284, library_group, "OCV derate group named {} not found.", + *derate_name); } } @@ -3616,7 +3528,7 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, else if (*rf_attr == "rise_and_fall") rf_type = RiseFallBoth::riseFall(); else - libError(1286, factors_group, "unknown rise/fall."); + error(1286, factors_group, "unknown rise/fall."); } const EarlyLateAll *derate_type = EarlyLateAll::all(); @@ -3629,7 +3541,7 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, else if (*derate_attr == "early_and_late") derate_type = EarlyLateAll::all(); else { - libWarn(1309, factors_group, "unknown early/late value."); + warn(1309, factors_group, "unknown early/late value."); } } @@ -3643,7 +3555,7 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, else if (*path_attr == "clock_and_data") path_type = PathType::clk_and_data; else - libWarn(1287, factors_group, "unknown derate type."); + warn(1287, factors_group, "unknown derate type."); } const char *template_name = factors_group->firstName(); @@ -3666,12 +3578,12 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, } } else - libWarn(1308, factors_group, "table template %s not found.", template_name); + warn(1308, factors_group, "table template {} not found.", template_name); } } } else - libWarn(1285, ocv_derate_group, "ocv_derate missing name."); + warn(1285, ocv_derate_group, "ocv_derate missing name."); } } @@ -3725,13 +3637,13 @@ PortNameBitIterator::init(const char *port_name) range_bit_ = from; } else - visitor_->libWarn(1292, line_, "port %s subscript out of range.", + visitor_->warn(1292, line_, "port {} subscript out of range.", port_name); } else - visitor_->libWarn(1293, line_, "port range %s of non-bus port %s.", + visitor_->warn(1293, line_, "port range {} of non-bus port {}.", port_name, - bus_name.c_str()); + bus_name); } else { range_bus_name_ = bus_name; @@ -3743,7 +3655,7 @@ PortNameBitIterator::init(const char *port_name) size_ = std::abs(from - to) + 1; } else - visitor_->libWarn(1294, line_, "port %s not found.", port_name); + visitor_->warn(1294, line_, "port {} not found.", port_name); } } @@ -3809,7 +3721,7 @@ PortNameBitIterator::findRangeBusNameNext() range_bit_++; } else - visitor_->libWarn(1295, line_, "port %s not found.", bus_bit_name.c_str()); + visitor_->warn(1295, line_, "port {} not found.", bus_bit_name); } else range_name_next_ = nullptr; diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 070b28e7..e1307b7d 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ #include "LibertyParser.hh" #include "LibertyReader.hh" #include "LibertyBuilder.hh" +#include "Report.hh" namespace sta { @@ -451,38 +453,65 @@ protected: const char *attr_name, const LibertyCell *cell, int line); - void libWarn(int id, + template + void warn(int id, const LibertyGroup *group, - const char *fmt, - ...) const - __attribute__((format (printf, 4, 5))); - void libWarn(int id, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, group->line(), fmt, std::forward(args)...); + } + template + void warn(int id, const LibertySimpleAttr *attr, - const char *fmt, - ...) const - __attribute__((format (printf, 4, 5))); - void libWarn(int id, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, attr->line(), fmt, std::forward(args)...); + } + template + void warn(int id, const LibertyComplexAttr *attr, - const char *fmt, - ...) const - __attribute__((format (printf, 4, 5))); - void libWarn(int id, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, attr->line(), fmt, std::forward(args)...); + } + template + void warn(int id, int line, - const char *fmt, - ...) const - __attribute__((format (printf, 4, 5))); - void libError(int id, + std::string_view fmt, + Args &&...args) const + { + report_->fileWarn(id, filename_, line, fmt, std::forward(args)...); + } + template + void error(int id, const LibertyGroup *group, - const char *fmt, ...) const - __attribute__((format (printf, 4, 5))); - void libError(int id, + std::string_view fmt, + Args &&...args) const + { + report_->fileError(id, filename_, group->line(), fmt, + std::forward(args)...); + } + template + void error(int id, const LibertySimpleAttr *attr, - const char *fmt, ...) const - __attribute__((format (printf, 4, 5))); - void libError(int id, + std::string_view fmt, + Args &&...args) const + { + report_->fileError(id, filename_, attr->line(), fmt, + std::forward(args)...); + } + template + void error(int id, const LibertyComplexAttr *attr, - const char *fmt, ...) const - __attribute__((format (printf, 4, 5))); + std::string_view fmt, + Args &&...args) const + { + report_->fileError(id, filename_, attr->line(), fmt, + std::forward(args)...); + } const char *filename_; bool infer_latches_; diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index ada26b09..5e22ce6d 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -1,32 +1,34 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "LibertyWriter.hh" #include #include +#include +#include "Format.hh" #include "Units.hh" #include "FuncExpr.hh" #include "PortDirection.hh" @@ -44,7 +46,7 @@ class LibertyWriter public: LibertyWriter(const LibertyLibrary *lib, const char *filename, - FILE *stream, + std::ofstream &stream, Report *report); void writeLibrary(); @@ -80,7 +82,7 @@ protected: const LibertyLibrary *library_; const char *filename_; - FILE *stream_; + std::ofstream &stream_; Report *report_; const Unit *time_unit_; const Unit *cap_unit_; @@ -91,11 +93,10 @@ writeLiberty(LibertyLibrary *lib, const char *filename, StaState *sta) { - FILE *stream = fopen(filename, "w"); - if (stream) { + std::ofstream stream(filename); + if (stream.is_open()) { LibertyWriter writer(lib, filename, stream, sta->report()); writer.writeLibrary(); - fclose(stream); } else throw FileNotWritable(filename); @@ -103,7 +104,7 @@ writeLiberty(LibertyLibrary *lib, LibertyWriter::LibertyWriter(const LibertyLibrary *lib, const char *filename, - FILE *stream, + std::ofstream &stream, Report *report) : library_(lib), filename_(filename), @@ -118,87 +119,87 @@ void LibertyWriter::writeLibrary() { writeHeader(); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeTableTemplates(); writeBusDcls(); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeCells(); writeFooter(); } - + void LibertyWriter::writeHeader() { - fprintf(stream_, "library (%s) {\n", library_->name()); - fprintf(stream_, " comment : \"\";\n"); - fprintf(stream_, " delay_model : table_lookup;\n"); - fprintf(stream_, " simulation : false;\n"); + sta::print(stream_, "library ({}) {{\n", library_->name()); + sta::print(stream_, " comment : \"\";\n"); + sta::print(stream_, " delay_model : table_lookup;\n"); + sta::print(stream_, " simulation : false;\n"); const Unit *cap_unit = library_->units()->capacitanceUnit(); - fprintf(stream_, " capacitive_load_unit (1,%s);\n", - cap_unit->scaleAbbrevSuffix().c_str()); - fprintf(stream_, " leakage_power_unit : 1pW;\n"); + sta::print(stream_, " capacitive_load_unit (1,{});\n", + cap_unit->scaleAbbrevSuffix()); + sta::print(stream_, " leakage_power_unit : 1pW;\n"); const Unit *current_unit = library_->units()->currentUnit(); - fprintf(stream_, " current_unit : \"1%s\";\n", - current_unit->scaleAbbrevSuffix().c_str()); + sta::print(stream_, " current_unit : \"1{}\";\n", + current_unit->scaleAbbrevSuffix()); const Unit *res_unit = library_->units()->resistanceUnit(); - fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n", - res_unit->scaleAbbrevSuffix().c_str()); + sta::print(stream_, " pulling_resistance_unit : \"1{}\";\n", + res_unit->scaleAbbrevSuffix()); const Unit *time_unit = library_->units()->timeUnit(); - fprintf(stream_, " time_unit : \"1%s\";\n", - time_unit->scaleAbbrevSuffix().c_str()); + sta::print(stream_, " time_unit : \"1{}\";\n", + time_unit->scaleAbbrevSuffix()); const Unit *volt_unit = library_->units()->voltageUnit(); - fprintf(stream_, " voltage_unit : \"1%s\";\n", - volt_unit->scaleAbbrevSuffix().c_str()); - fprintf(stream_, " library_features(report_delay_calculation);\n"); - fprintf(stream_, "\n"); + sta::print(stream_, " voltage_unit : \"1{}\";\n", + volt_unit->scaleAbbrevSuffix()); + sta::print(stream_, " library_features(report_delay_calculation);\n"); + sta::print(stream_, "\n"); - fprintf(stream_, " input_threshold_pct_rise : %.0f;\n", - library_->inputThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " input_threshold_pct_fall : %.0f;\n", - library_->inputThreshold(RiseFall::fall()) * 100); - fprintf(stream_, " output_threshold_pct_rise : %.0f;\n", - library_->inputThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " output_threshold_pct_fall : %.0f;\n", - library_->inputThreshold(RiseFall::fall()) * 100); - fprintf(stream_, " slew_lower_threshold_pct_rise : %.0f;\n", - library_->slewLowerThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " slew_lower_threshold_pct_fall : %.0f;\n", - library_->slewLowerThreshold(RiseFall::fall()) * 100); - fprintf(stream_, " slew_upper_threshold_pct_rise : %.0f;\n", - library_->slewUpperThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " slew_upper_threshold_pct_fall : %.0f;\n", - library_->slewUpperThreshold(RiseFall::rise()) * 100); - fprintf(stream_, " slew_derate_from_library : %.1f;\n", - library_->slewDerateFromLibrary()); - fprintf(stream_, "\n"); + sta::print(stream_, " input_threshold_pct_rise : {:.0f};\n", + library_->inputThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " input_threshold_pct_fall : {:.0f};\n", + library_->inputThreshold(RiseFall::fall()) * 100); + sta::print(stream_, " output_threshold_pct_rise : {:.0f};\n", + library_->inputThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " output_threshold_pct_fall : {:.0f};\n", + library_->inputThreshold(RiseFall::fall()) * 100); + sta::print(stream_, " slew_lower_threshold_pct_rise : {:.0f};\n", + library_->slewLowerThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " slew_lower_threshold_pct_fall : {:.0f};\n", + library_->slewLowerThreshold(RiseFall::fall()) * 100); + sta::print(stream_, " slew_upper_threshold_pct_rise : {:.0f};\n", + library_->slewUpperThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " slew_upper_threshold_pct_fall : {:.0f};\n", + library_->slewUpperThreshold(RiseFall::rise()) * 100); + sta::print(stream_, " slew_derate_from_library : {:.1f};\n", + library_->slewDerateFromLibrary()); + sta::print(stream_, "\n"); bool exists; float max_fanout; library_->defaultFanoutLoad(max_fanout, exists); if (exists) - fprintf(stream_, " default_max_fanout : %.0f;\n", max_fanout); + sta::print(stream_, " default_max_fanout : {:.0f};\n", max_fanout); float max_slew; library_->defaultMaxSlew(max_slew, exists); if (exists) - fprintf(stream_, " default_max_transition : %s;\n", - time_unit_->asString(max_slew, 3)); + sta::print(stream_, " default_max_transition : {};\n", + time_unit_->asString(max_slew, 3)); float max_cap; library_->defaultMaxCapacitance(max_cap, exists); if (exists) - fprintf(stream_, " default_max_capacitance : %s;\n", - cap_unit_->asString(max_cap, 3)); + sta::print(stream_, " default_max_capacitance : {};\n", + cap_unit_->asString(max_cap, 3)); float fanout_load; library_->defaultFanoutLoad(fanout_load, exists); if (exists) - fprintf(stream_, " default_fanout_load : %.2f;\n", fanout_load); - fprintf(stream_, "\n"); + sta::print(stream_, " default_fanout_load : {:.2f};\n", fanout_load); + sta::print(stream_, "\n"); - fprintf(stream_, " nom_process : %.1f;\n", - library_->nominalProcess()); - fprintf(stream_, " nom_temperature : %.1f;\n", - library_->nominalTemperature()); - fprintf(stream_, " nom_voltage : %.2f;\n", - library_->nominalVoltage()); + sta::print(stream_, " nom_process : {:.1f};\n", + library_->nominalProcess()); + sta::print(stream_, " nom_temperature : {:.1f};\n", + library_->nominalTemperature()); + sta::print(stream_, " nom_voltage : {:.2f};\n", + library_->nominalVoltage()); } void @@ -216,22 +217,22 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template) const TableAxis *axis3 = tbl_template->axis3(); // skip scalar templates if (axis1) { - fprintf(stream_, " lu_table_template(%s) {\n", tbl_template->name().c_str()); - fprintf(stream_, " variable_1 : %s;\n", - tableVariableString(axis1->variable())); + sta::print(stream_, " lu_table_template({}) {{\n", tbl_template->name()); + sta::print(stream_, " variable_1 : {};\n", + tableVariableString(axis1->variable())); if (axis2) - fprintf(stream_, " variable_2 : %s;\n", - tableVariableString(axis2->variable())); + sta::print(stream_, " variable_2 : {};\n", + tableVariableString(axis2->variable())); if (axis3) - fprintf(stream_, " variable_3 : %s;\n", - tableVariableString(axis3->variable())); + sta::print(stream_, " variable_3 : {};\n", + tableVariableString(axis3->variable())); if (axis1 && !axis1->values().empty()) writeTableAxis4(axis1, 1); if (axis2 && !axis2->values().empty()) writeTableAxis4(axis2, 2); if (axis3 && !axis3->values().empty()) writeTableAxis4(axis3, 3); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } } @@ -240,16 +241,16 @@ void LibertyWriter::writeTableAxis4(const TableAxis *axis, int index) { - fprintf(stream_, " index_%d(\"", index); + sta::print(stream_, " index_{}(\"", index); const Unit *unit = tableVariableUnit(axis->variable(), library_->units()); bool first = true; for (size_t i = 0; i < axis->size(); i++) { if (!first) - fprintf(stream_, ", "); - fprintf(stream_, "%s", unit->asString(axis->axisValue(i), 5)); + sta::print(stream_, ", "); + sta::print(stream_, "{}", unit->asString(axis->axisValue(i), 5)); first = false; } - fprintf(stream_, "\");\n"); + sta::print(stream_, "\");\n"); } // indent 10 @@ -257,7 +258,7 @@ void LibertyWriter::writeTableAxis10(const TableAxis *axis, int index) { - fprintf(stream_, " "); + sta::print(stream_, " "); writeTableAxis4(axis, index); } @@ -266,13 +267,13 @@ LibertyWriter::writeBusDcls() { BusDclSeq dcls = library_->busDcls(); for (BusDcl *dcl : dcls) { - fprintf(stream_, " type (\"%s\") {\n", dcl->name().c_str()); - fprintf(stream_, " base_type : array;\n"); - fprintf(stream_, " data_type : bit;\n"); - fprintf(stream_, " bit_width : %d;\n", std::abs(dcl->from() - dcl->to() + 1)); - fprintf(stream_, " bit_from : %d;\n", dcl->from()); - fprintf(stream_, " bit_to : %d;\n", dcl->to()); - fprintf(stream_, " }\n"); + sta::print(stream_, " type (\"{}\") {{\n", dcl->name()); + sta::print(stream_, " base_type : array;\n"); + sta::print(stream_, " data_type : bit;\n"); + sta::print(stream_, " bit_width : {};\n", std::abs(dcl->from() - dcl->to() + 1)); + sta::print(stream_, " bit_from : {};\n", dcl->from()); + sta::print(stream_, " bit_to : {};\n", dcl->to()); + sta::print(stream_, " }}\n"); } } @@ -289,21 +290,20 @@ LibertyWriter::writeCells() void LibertyWriter::writeCell(const LibertyCell *cell) { - fprintf(stream_, " cell (\"%s\") {\n", cell->name()); + sta::print(stream_, " cell (\"{}\") {{\n", cell->name()); float area = cell->area(); if (area > 0.0) - fprintf(stream_, " area : %.3f \n", area); + sta::print(stream_, " area : {:.3f} \n", area); if (cell->isMacro()) - fprintf(stream_, " is_macro_cell : true;\n"); + sta::print(stream_, " is_macro_cell : true;\n"); if (cell->interfaceTiming()) - fprintf(stream_, " interface_timing : true;\n"); + sta::print(stream_, " interface_timing : true;\n"); const char *footprint = cell->footprint(); if (footprint) - fprintf(stream_, " cell_footprint : \"%s\";\n", footprint); + sta::print(stream_, " cell_footprint : \"{}\";\n", footprint); const char *user_function_class = cell->userFunctionClass(); if (user_function_class) - fprintf(stream_, " user_function_class : \"%s\";\n", - user_function_class); + sta::print(stream_, " user_function_class : \"{}\";\n", user_function_class); LibertyCellPortIterator port_iter(cell); while (port_iter.hasNext()) { @@ -314,24 +314,23 @@ LibertyWriter::writeCell(const LibertyCell *cell) else if (port->isBus()) writeBusPort(port); else if (port->isBundle()) - report_->error(1340, "%s/%s bundled ports not supported.", - library_->name(), + report_->error(1340, "{}/{} bundled ports not supported.", library_->name(), cell->name()); else writePort(port); } } - fprintf(stream_, " }\n"); - fprintf(stream_, "\n"); + sta::print(stream_, " }}\n"); + sta::print(stream_, "\n"); } void LibertyWriter::writeBusPort(const LibertyPort *port) { - fprintf(stream_, " bus(\"%s\") {\n", port->name()); + sta::print(stream_, " bus(\"{}\") {{\n", port->name()); if (port->busDcl()) - fprintf(stream_, " bus_type : %s;\n", port->busDcl()->name().c_str()); + sta::print(stream_, " bus_type : {};\n", port->busDcl()->name()); writePortAttrs(port); LibertyPortMemberIterator member_iter(port); @@ -339,56 +338,53 @@ LibertyWriter::writeBusPort(const LibertyPort *port) LibertyPort *member = member_iter.next(); writePort(member); } - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } void LibertyWriter::writePort(const LibertyPort *port) { - fprintf(stream_, " pin(\"%s\") {\n", port->name()); + sta::print(stream_, " pin(\"{}\") {{\n", port->name()); writePortAttrs(port); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } void LibertyWriter::writePortAttrs(const LibertyPort *port) { - fprintf(stream_, " direction : %s;\n" , asString(port->direction())); + sta::print(stream_, " direction : {};\n", asString(port->direction())); auto func = port->function(); if (func // cannot ref internal ports until sequentials are written - && !(func->port() - && func->port()->direction()->isInternal())) - fprintf(stream_, " function : \"%s\";\n", func->to_string().c_str()); + && !(func->port() && func->port()->direction()->isInternal())) + sta::print(stream_, " function : \"{}\";\n", func->to_string()); auto tristate_enable = port->tristateEnable(); if (tristate_enable) { if (tristate_enable->op() == FuncExpr::Op::not_) { FuncExpr *three_state = tristate_enable->left(); - fprintf(stream_, " three_state : \"%s\";\n", - three_state->to_string().c_str()); + sta::print(stream_, " three_state : \"{}\";\n", + three_state->to_string()); } else { FuncExpr *three_state = tristate_enable->copy()->invert(); - fprintf(stream_, " three_state : \"%s\";\n", - three_state->to_string().c_str()); + sta::print(stream_, " three_state : \"{}\";\n", + three_state->to_string()); delete three_state; } } if (port->isClock()) - fprintf(stream_, " clock : true;\n"); - fprintf(stream_, " capacitance : %s;\n", - cap_unit_->asString(port->capacitance(), 4)); - + sta::print(stream_, " clock : true;\n"); + sta::print(stream_, " capacitance : {};\n", + cap_unit_->asString(port->capacitance(), 4)); + float limit; bool exists; port->slewLimit(MinMax::max(), limit, exists); if (exists) - fprintf(stream_, " max_transition : %s;\n", - time_unit_->asString(limit, 3)); + sta::print(stream_, " max_transition : {};\n", time_unit_->asString(limit, 3)); port->capacitanceLimit(MinMax::max(), limit, exists); if (exists) - fprintf(stream_, " max_capacitance : %s;\n", - cap_unit_->asString(limit, 3)); + sta::print(stream_, " max_capacitance : {};\n", cap_unit_->asString(limit, 3)); for (TimingArcSet *arc_set : port->libertyCell()->timingArcSetsTo(port)) { if (!isAutoWidthArc(port, arc_set)) @@ -399,10 +395,10 @@ LibertyWriter::writePortAttrs(const LibertyPort *port) void LibertyWriter::writePwrGndPort(const LibertyPort *port) { - fprintf(stream_, " pg_pin(\"%s\") {\n", port->name()); - fprintf(stream_, " pg_type : \"%s\";\n", pwrGndTypeName(port->pwrGndType())); - fprintf(stream_, " voltage_name : \"%s\";\n", port->voltageName()); - fprintf(stream_, " }\n"); + sta::print(stream_, " pg_pin(\"{}\") {{\n", port->name()); + sta::print(stream_, " pg_type : \"{}\";\n", pwrGndTypeName(port->pwrGndType())); + sta::print(stream_, " voltage_name : \"{}\";\n", port->voltageName()); + sta::print(stream_, " }}\n"); } // Check if arc is added for port min_pulse_width_high/low attribute. @@ -423,30 +419,27 @@ LibertyWriter::isAutoWidthArc(const LibertyPort *port, void LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set) { - fprintf(stream_, " timing() {\n"); + sta::print(stream_, " timing() {{\n"); if (arc_set->from()) - fprintf(stream_, " related_pin : \"%s\";\n", arc_set->from()->name()); + sta::print(stream_, " related_pin : \"{}\";\n", arc_set->from()->name()); TimingSense sense = arc_set->sense(); - if (sense != TimingSense::unknown - && sense != TimingSense::non_unate) - fprintf(stream_, " timing_sense : %s;\n", - to_string(sense)); + if (sense != TimingSense::unknown && sense != TimingSense::non_unate) + sta::print(stream_, " timing_sense : {};\n", to_string(sense)); const char *timing_type = timingTypeString(arc_set); if (timing_type) - fprintf(stream_, " timing_type : %s;\n", timing_type); + sta::print(stream_, " timing_type : {};\n", timing_type); for (const RiseFall *rf : RiseFall::range()) { TimingArc *arc = arc_set->arcTo(rf); if (arc) { // Min pulse width arcs are wrt to the leading edge of the pulse. - const RiseFall *model_rf = (arc_set->role() == TimingRole::width()) - ? rf->opposite() - : rf; + const RiseFall *model_rf = + (arc_set->role() == TimingRole::width()) ? rf->opposite() : rf; writeTimingModels(arc, model_rf); } } - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } void @@ -454,54 +447,53 @@ LibertyWriter::writeTimingModels(const TimingArc *arc, const RiseFall *rf) { TimingModel *model = arc->model(); - const GateTableModel *gate_model = dynamic_cast(model); - const CheckTableModel *check_model = dynamic_cast(model); + const GateTableModel *gate_model = dynamic_cast(model); + const CheckTableModel *check_model = dynamic_cast(model); if (gate_model) { const TableModel *delay_model = gate_model->delayModel(); const std::string &template_name = delay_model->tblTemplate()->name(); - fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name.c_str()); + sta::print(stream_, " cell_{}({}) {{\n", rf->name(), template_name); writeTableModel(delay_model); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); const TableModel *slew_model = gate_model->slewModel(); if (slew_model) { const std::string &slew_template_name = slew_model->tblTemplate()->name(); - fprintf(stream_, " %s_transition(%s) {\n", rf->name(), - slew_template_name.c_str()); + sta::print(stream_, " {}_transition({}) {{\n", rf->name(), + slew_template_name); writeTableModel(slew_model); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } } else if (check_model) { const TableModel *model = check_model->checkModel(); const std::string &template_name = model->tblTemplate()->name(); - fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name.c_str()); + sta::print(stream_, " {}_constraint({}) {{\n", rf->name(), + template_name); writeTableModel(model); - fprintf(stream_, " }\n"); + sta::print(stream_, " }}\n"); } else - report_->error(1341, "%s/%s/%s timing model not supported.", - library_->name(), - arc->from()->libertyCell()->name(), - arc->from()->name()); + report_->error(1341, "{}/{}/{} timing model not supported.", library_->name(), + arc->from()->libertyCell()->name(), arc->from()->name()); } void LibertyWriter::writeTableModel(const TableModel *model) { switch (model->order()) { - case 0: - writeTableModel0(model); - break; - case 1: - writeTableModel1(model); - break; - case 2: - writeTableModel2(model); - break; - case 3: - report_->error(1342, "3 axis table models not supported."); - break; + case 0: + writeTableModel0(model); + break; + case 1: + writeTableModel1(model); + break; + case 2: + writeTableModel2(model); + break; + case 3: + report_->error(1342, "3 axis table models not supported."); + break; } } @@ -509,24 +501,23 @@ void LibertyWriter::writeTableModel0(const TableModel *model) { float value = model->value(0, 0, 0); - fprintf(stream_, " values(\"%s\");\n", - time_unit_->asString(value, 5)); + sta::print(stream_, " values(\"{}\");\n", time_unit_->asString(value, 5)); } void LibertyWriter::writeTableModel1(const TableModel *model) { writeTableAxis10(model->axis1(), 1); - fprintf(stream_, " values(\""); + sta::print(stream_, " values(\""); bool first_col = true; for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) { float value = model->value(index1, 0, 0); if (!first_col) - fprintf(stream_, ","); - fprintf(stream_, "%s", time_unit_->asString(value, 5)); + sta::print(stream_, ","); + sta::print(stream_, "{}", time_unit_->asString(value, 5)); first_col = false; } - fprintf(stream_, "\");\n"); + sta::print(stream_, "\");\n"); } void @@ -534,31 +525,31 @@ LibertyWriter::writeTableModel2(const TableModel *model) { writeTableAxis10(model->axis1(), 1); writeTableAxis10(model->axis2(), 2); - fprintf(stream_, " values(\""); + sta::print(stream_, " values(\""); bool first_row = true; for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) { if (!first_row) { - fprintf(stream_, "\\\n"); - fprintf(stream_, " \""); + sta::print(stream_, "\\\n"); + sta::print(stream_, " \""); } bool first_col = true; for (size_t index2 = 0; index2 < model->axis2()->size(); index2++) { float value = model->value(index1, index2, 0); if (!first_col) - fprintf(stream_, ","); - fprintf(stream_, "%s", time_unit_->asString(value, 5)); + sta::print(stream_, ","); + sta::print(stream_, "{}", time_unit_->asString(value, 5)); first_col = false; } - fprintf(stream_, "\""); + sta::print(stream_, "\""); first_row = false; } - fprintf(stream_, ");\n"); + sta::print(stream_, ");\n"); } void LibertyWriter::writeFooter() { - fprintf(stream_, "}\n"); + sta::print(stream_, "}}\n"); } const char * @@ -572,15 +563,13 @@ LibertyWriter::asString(const PortDirection *dir) { if (dir == PortDirection::input()) return "input"; - else if (dir == PortDirection::output() - || (dir == PortDirection::tristate())) + else if (dir == PortDirection::output() || (dir == PortDirection::tristate())) return "output"; else if (dir == PortDirection::internal()) return "internal"; else if (dir == PortDirection::bidirect()) return "inout"; - else if (dir == PortDirection::ground() - || dir == PortDirection::power()) + else if (dir == PortDirection::ground() || dir == PortDirection::power()) return "input"; return "unknown"; } @@ -595,8 +584,7 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set) return "three_state_disable"; else if (role == TimingRole::tristateEnable()) return "three_state_enable"; - else if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { + else if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) { const TimingArc *arc = arc_set->arcs()[0]; if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) return "rising_edge"; @@ -612,16 +600,14 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set) else return "clear"; } - else if (role == TimingRole::setup() - || role == TimingRole::recovery()) { + else if (role == TimingRole::setup() || role == TimingRole::recovery()) { const TimingArc *arc = arc_set->arcs()[0]; if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) return "setup_rising"; else return "setup_falling"; } - else if (role == TimingRole::hold() - || role == TimingRole::removal()) { + else if (role == TimingRole::hold() || role == TimingRole::removal()) { const TimingArc *arc = arc_set->arcs()[0]; if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) return "hold_rising"; @@ -649,13 +635,11 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set) else if (role == TimingRole::width()) return "min_pulse_width"; else { - report_->error(1343, "%s/%s/%s timing arc type %s not supported.", - library_->name(), - arc_set->to()->libertyCell()->name(), - arc_set->to()->name(), - role->to_string().c_str()); + report_->error(1343, "{}/{}/{} timing arc type {} not supported.", + library_->name(), arc_set->to()->libertyCell()->name(), + arc_set->to()->name(), role->to_string()); return nullptr; } } -} // namespace +} // namespace sta diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index a235dcb4..46dd6d6b 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "TableModel.hh" @@ -250,12 +250,12 @@ GateTableModel::reportTableLookup(const char *result_name, { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(model, in_slew, load_cap, related_out_cap, - axis_value1, axis_value2, axis_value3); + findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, + axis_value2, axis_value3); const LibertyLibrary *library = cell_->libertyLibrary(); return model->reportValue(result_name, cell_, pvt, axis_value1, nullptr, - axis_value2, axis_value3, - library->units()->timeUnit(), digits); + axis_value2, axis_value3, library->units()->timeUnit(), + digits); } return ""; } @@ -269,8 +269,8 @@ GateTableModel::findValue(const Pvt *pvt, { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(model, in_slew, load_cap, related_out_cap, - axis_value1, axis_value2, axis_value3); + findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1, + axis_value2, axis_value3); return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else @@ -288,37 +288,31 @@ GateTableModel::findAxisValues(const TableModel *model, float &axis_value3) const { switch (model->order()) { - case 0: - axis_value1 = 0.0; - axis_value2 = 0.0; - axis_value3 = 0.0; - break; - case 1: - axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_cap); - axis_value2 = 0.0; - axis_value3 = 0.0; - break; - case 2: - axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_cap); - axis_value2 = axisValue(model->axis2(), in_slew, load_cap, - related_out_cap); - axis_value3 = 0.0; - break; - case 3: - axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_cap); - axis_value2 = axisValue(model->axis2(), in_slew, load_cap, - related_out_cap); - axis_value3 = axisValue(model->axis3(), in_slew, load_cap, - related_out_cap); - break; - default: - axis_value1 = 0.0; - axis_value2 = 0.0; - axis_value3 = 0.0; - criticalError(239, "unsupported table order"); + case 0: + axis_value1 = 0.0; + axis_value2 = 0.0; + axis_value3 = 0.0; + break; + case 1: + axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); + axis_value2 = 0.0; + axis_value3 = 0.0; + break; + case 2: + axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); + axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap); + axis_value3 = 0.0; + break; + case 3: + axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap); + axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap); + axis_value3 = axisValue(model->axis3(), in_slew, load_cap, related_out_cap); + break; + default: + axis_value1 = 0.0; + axis_value2 = 0.0; + axis_value3 = 0.0; + criticalError(239, "unsupported table order"); } } @@ -348,12 +342,12 @@ GateTableModel::maxCapSlew(float in_slew, slew = findValue(pvt, model, in_slew, cap, 0.0); } else if (axis2 - && axis2->variable()==TableAxisVariable::total_output_net_capacitance) { + && axis2->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis2->axisValue(axis2->size() - 1); slew = findValue(pvt, model, in_slew, cap, 0.0); } else if (axis3 - && axis3->variable()==TableAxisVariable::total_output_net_capacitance) { + && axis3->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis3->axisValue(axis3->size() - 1); slew = findValue(pvt, model, in_slew, cap, 0.0); } @@ -408,9 +402,9 @@ GateTableModel::checkAxis(const TableAxis *axis) { TableAxisVariable var = axis->variable(); return var == TableAxisVariable::total_output_net_capacitance - || var == TableAxisVariable::input_transition_time - || var == TableAxisVariable::input_net_transition - || var == TableAxisVariable::related_out_total_output_net_capacitance; + || var == TableAxisVariable::input_transition_time + || var == TableAxisVariable::input_net_transition + || var == TableAxisVariable::related_out_total_output_net_capacitance; } //////////////////////////////////////////////////////////////// @@ -435,12 +429,13 @@ ReceiverModel::checkAxes(const TableModel *table) const TableAxis *axis2 = table->axis2(); const TableAxis *axis3 = table->axis3(); return (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2 == nullptr + && axis2 == nullptr && axis3 == nullptr) + || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition + && axis2 + && axis2->variable() == TableAxisVariable::total_output_net_capacitance && axis3 == nullptr) - || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2 && axis2->variable() == TableAxisVariable::total_output_net_capacitance - && axis3 == nullptr) - || (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance + || (axis1 + && axis1->variable() == TableAxisVariable::total_output_net_capacitance && axis2 && axis2->variable() == TableAxisVariable::input_net_transition && axis3 == nullptr); } @@ -488,7 +483,8 @@ CheckTableModel::checkDelay(const Pvt *pvt, if (std_dev_model == nullptr) std_dev_model = check_models_->sigma(min_max); if (std_dev_model) { - float std_dev = findValue(pvt, std_dev_model, from_slew, to_slew, related_out_cap); + float std_dev = findValue(pvt, std_dev_model, from_slew, + to_slew, related_out_cap); check_delay.setStdDev(std_dev); } break; @@ -529,8 +525,8 @@ CheckTableModel::findValue(const Pvt *pvt, { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(from_slew, to_slew, related_out_cap, - axis_value1, axis_value2, axis_value3); + findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, + axis_value3); return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else @@ -580,8 +576,8 @@ CheckTableModel::reportTableDelay(const char *result_name, { if (model) { float axis_value1, axis_value2, axis_value3; - findAxisValues(from_slew, to_slew, related_out_cap, - axis_value1, axis_value2, axis_value3); + findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, + axis_value3); std::string result = reportPvt(cell_, pvt, digits); const Unit *time_unit = cell_->libertyLibrary()->units()->timeUnit(); result += check_models_->model()->reportValue(result_name, cell_, pvt, @@ -675,8 +671,8 @@ CheckTableModel::checkAxis(const TableAxis *axis) { TableAxisVariable var = axis->variable(); return var == TableAxisVariable::constrained_pin_transition - || var == TableAxisVariable::related_pin_transition - || var == TableAxisVariable::related_out_total_output_net_capacitance; + || var == TableAxisVariable::related_pin_transition + || var == TableAxisVariable::related_out_total_output_net_capacitance; } //////////////////////////////////////////////////////////////// @@ -837,7 +833,7 @@ TableModel::findValue(const LibertyCell *cell, float axis_value3) const { return table_->findValue(axis_value1, axis_value2, axis_value3) - * scaleFactor(cell, pvt); + * scaleFactor(cell, pvt); } float @@ -849,8 +845,8 @@ TableModel::scaleFactor(const LibertyCell *cell, // nominal pvt. return 1.0F; else - return cell->libertyLibrary()->scaleFactor(static_cast(scale_factor_type_), - rf_index_, cell, pvt); + return cell->libertyLibrary()->scaleFactor( + static_cast(scale_factor_type_), rf_index_, cell, pvt); } std::string @@ -864,14 +860,16 @@ TableModel::reportValue(const char *result_name, const Unit *table_unit, int digits) const { - std::string result = table_->reportValue("Table value", cell, pvt, value1, - comment1, value2, value3, table_unit, digits); + std::string result = + table_->reportValue("Table value", cell, pvt, value1, comment1, value2, value3, + table_unit, digits); result += reportPvtScaleFactor(cell, pvt, digits); result += result_name; result += " = "; - result += table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits); + result += + table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits); result += '\n'; return result; } @@ -884,14 +882,11 @@ reportPvt(const LibertyCell *cell, const LibertyLibrary *library = cell->libertyLibrary(); if (pvt == nullptr) pvt = library->defaultOperatingConditions(); - if (pvt) { - std::string result; - stringPrint(result, "P = %.*f V = %.*f T = %.*f\n", - digits, pvt->process(), - digits, pvt->voltage(), - digits, pvt->temperature()); - return result; - } + if (pvt) + return sta::format("P = {:.{}f} V = {:.{}f} T = {:.{}f}\n", + pvt->process(), digits, + pvt->voltage(), digits, + pvt->temperature(), digits); return ""; } @@ -902,13 +897,9 @@ TableModel::reportPvtScaleFactor(const LibertyCell *cell, { if (pvt == nullptr) pvt = cell->libertyLibrary()->defaultOperatingConditions(); - if (pvt) { - std::string result; - stringPrint(result, "PVT scale factor = %.*f\n", - digits, - scaleFactor(cell, pvt)); - return result; - } + if (pvt) + return sta::formatRuntime("PVT scale factor = {:.{}f}\n", + scaleFactor(cell, pvt), digits); return ""; } @@ -1189,13 +1180,10 @@ Table::findValueOrder3(float axis_value1, } return (1 - dx1) * (1 - dx2) * (1 - dx3) * y000 - + (1 - dx1) * (1 - dx2) * dx3 * y001 - + (1 - dx1) * dx2 * (1 - dx3) * y010 - + (1 - dx1) * dx2 * dx3 * y011 - + dx1 * (1 - dx2) * (1 - dx3) * y100 - + dx1 * (1 - dx2) * dx3 * y101 - + dx1 * dx2 * (1 - dx3) * y110 - + dx1 * dx2 * dx3 * y111; + + (1 - dx1) * (1 - dx2) * dx3 * y001 + (1 - dx1) * dx2 * (1 - dx3) * y010 + + (1 - dx1) * dx2 * dx3 * y011 + dx1 * (1 - dx2) * (1 - dx3) * y100 + + dx1 * (1 - dx2) * dx3 * y101 + dx1 * dx2 * (1 - dx3) * y110 + + dx1 * dx2 * dx3 * y111; } void @@ -1279,14 +1267,14 @@ Table::reportValue(const char *result_name, case 0: return reportValueOrder0(result_name, comment1, table_unit, digits); case 1: - return reportValueOrder1(result_name, cell, value1, comment1, - value2, value3, table_unit, digits); + return reportValueOrder1(result_name, cell, value1, comment1, value2, value3, + table_unit, digits); case 2: - return reportValueOrder2(result_name, cell, value1, comment1, - value2, value3, table_unit, digits); + return reportValueOrder2(result_name, cell, value1, comment1, value2, value3, + table_unit, digits); case 3: - return reportValueOrder3(result_name, cell, value1, comment1, - value2, value3, table_unit, digits); + return reportValueOrder3(result_name, cell, value1, comment1, value2, value3, + table_unit, digits); default: return ""; } @@ -1453,12 +1441,12 @@ Table::reportValueOrder3(const char *result_name, result += " "; result += unit1->asString(axis1_->axisValue(axis_index1 + 1), digits); result += " v / "; - result += table_unit->asString(value(axis_index1 + 1, axis_index2, - axis_index3), digits); + result += table_unit->asString(value(axis_index1 + 1, axis_index2, axis_index3), + digits); if (axis3_->size() != 1) { result += " "; - result += table_unit->asString(value(axis_index1 + 1, axis_index2, - axis_index3 + 1), digits); + result += table_unit->asString( + value(axis_index1 + 1, axis_index2, axis_index3 + 1), digits); } } else { @@ -1470,22 +1458,22 @@ Table::reportValueOrder3(const char *result_name, result += " "; result += unit2->asString(axis2_->axisValue(axis_index2), digits); result += " | "; - result += table_unit->asString(value(axis_index1, axis_index2, - axis_index3), digits); + result += + table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits); if (axis3_->size() != 1) { result += " "; - result += table_unit->asString(value(axis_index1, axis_index2, - axis_index3 + 1), digits); + result += table_unit->asString(value(axis_index1, axis_index2, axis_index3 + 1), + digits); } result += '\n'; result += " |/ "; if (axis1_->size() != 1 && axis2_->size() != 1) { - result += table_unit->asString(value(axis_index1 + 1, axis_index2 + 1, - axis_index3), digits); + result += table_unit->asString( + value(axis_index1 + 1, axis_index2 + 1, axis_index3), digits); if (axis3_->size() != 1) { result += " "; - result += table_unit->asString(value(axis_index1 + 1, axis_index2 + 1, - axis_index3 + 1), digits); + result += table_unit->asString( + value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1), digits); } } result += '\n'; @@ -1493,12 +1481,12 @@ Table::reportValueOrder3(const char *result_name, result += unit2->asString(axis2_->axisValue(axis_index2 + 1), digits); result += " | "; if (axis2_->size() != 1) { - result += table_unit->asString(value(axis_index1, axis_index2 + 1, - axis_index3), digits); + result += table_unit->asString(value(axis_index1, axis_index2 + 1, axis_index3), + digits); if (axis3_->size() != 1) { result += " "; - result += table_unit->asString(value(axis_index1, axis_index2 + 1, - axis_index3 + 1), digits); + result += table_unit->asString( + value(axis_index1, axis_index2 + 1, axis_index3 + 1), digits); } } result += '\n'; @@ -1516,38 +1504,38 @@ Table::report(const Units *units, int digits = 4; const Unit *table_unit = units->timeUnit(); if (order_ == 0) { - report->reportLine("%s", table_unit->asString(value_, digits)); + report->report("{}", table_unit->asString(value_, digits)); return; } if (order_ == 1) { const Unit *unit1 = axis1_->unit(units); - report->reportLine("%s", tableVariableString(axis1_->variable())); - report->reportLine("------------------------------"); + report->report("{}", tableVariableString(axis1_->variable())); + report->report("------------------------------"); std::string line; for (size_t index1 = 0; index1 < axis1_->size(); index1++) { line += unit1->asString(axis1_->axisValue(index1), digits); line += " "; } - report->reportLineString(line); + report->reportLine(line); line.clear(); for (size_t index1 = 0; index1 < axis1_->size(); index1++) { line += table_unit->asString(value(index1), digits); line += " "; } - report->reportLineString(line); + report->reportLine(line); return; } if (order_ == 2) { const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); - report->reportLine("%s", tableVariableString(axis2_->variable())); - report->reportLine(" ------------------------------"); + report->report("{}", tableVariableString(axis2_->variable())); + report->report(" ------------------------------"); std::string line = " "; for (size_t index2 = 0; index2 < axis2_->size(); index2++) { line += unit2->asString(axis2_->axisValue(index2), digits); line += " "; } - report->reportLineString(line); + report->reportLine(line); for (size_t index1 = 0; index1 < axis1_->size(); index1++) { line = unit1->asString(axis1_->axisValue(index1), digits); line += " |"; @@ -1555,7 +1543,7 @@ Table::report(const Units *units, line += table_unit->asString(value(index1, index2), digits); line += " "; } - report->reportLineString(line); + report->reportLine(line); } return; } @@ -1564,24 +1552,25 @@ Table::report(const Units *units, const Unit *unit2 = axis2_->unit(units); const Unit *unit3 = axis3_->unit(units); for (size_t axis_index1 = 0; axis_index1 < axis1_->size(); axis_index1++) { - report->reportLine("%s %s", tableVariableString(axis1_->variable()), - unit1->asString(axis1_->axisValue(axis_index1), digits)); - report->reportLine("%s", tableVariableString(axis3_->variable())); - report->reportLine(" ------------------------------"); + report->report("{} {}", tableVariableString(axis1_->variable()), + unit1->asString(axis1_->axisValue(axis_index1), digits)); + report->report("{}", tableVariableString(axis3_->variable())); + report->report(" ------------------------------"); std::string line = " "; for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) { line += unit3->asString(axis3_->axisValue(axis_index3), digits); line += " "; } - report->reportLineString(line); + report->reportLine(line); for (size_t axis_index2 = 0; axis_index2 < axis2_->size(); axis_index2++) { line = unit2->asString(axis2_->axisValue(axis_index2), digits); line += " |"; for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) { - line += table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits); + line += table_unit->asString(value(axis_index1, axis_index2, axis_index3), + digits); line += " "; } - report->reportLineString(line); + report->reportLine(line); } } } @@ -1626,9 +1615,7 @@ bool TableAxis::inBounds(float value) const { size_t size = values_.size(); - return size > 1 - && value >= values_[0] - && value <= values_[size - 1]; + return size > 1 && value >= values_[0] && value <= values_[size - 1]; } size_t @@ -1670,9 +1657,7 @@ TableAxis::findAxisIndex(float value, bool &exists) const { size_t size = values_.size(); - if (size != 0 - && value >= values_[0] - && value <= values_[size - 1]) { + if (size != 0 && value >= values_[0] && value <= values_[size - 1]) { int lower = -1; int upper = size; while (upper - lower > 1) { @@ -1730,27 +1715,28 @@ TableAxis::unit(const Units *units) //////////////////////////////////////////////////////////////// -static EnumNameMap table_axis_variable_map = - {{TableAxisVariable::total_output_net_capacitance, "total_output_net_capacitance"}, - {TableAxisVariable::equal_or_opposite_output_net_capacitance, "equal_or_opposite_output_net_capacitance"}, - {TableAxisVariable::input_net_transition, "input_net_transition"}, - {TableAxisVariable::input_transition_time, "input_transition_time"}, - {TableAxisVariable::related_pin_transition, "related_pin_transition"}, - {TableAxisVariable::constrained_pin_transition, "constrained_pin_transition"}, - {TableAxisVariable::output_pin_transition, "output_pin_transition"}, - {TableAxisVariable::connect_delay, "connect_delay"}, - {TableAxisVariable::related_out_total_output_net_capacitance, - "related_out_total_output_net_capacitance"}, - {TableAxisVariable::time, "time"}, - {TableAxisVariable::iv_output_voltage, "iv_output_voltage"}, - {TableAxisVariable::input_noise_width, "input_noise_width"}, - {TableAxisVariable::input_noise_height, "input_noise_height"}, - {TableAxisVariable::input_voltage, "input_voltage"}, - {TableAxisVariable::output_voltage, "output_voltage"}, - {TableAxisVariable::path_depth, "path_depth"}, - {TableAxisVariable::path_distance, "path_distance"}, - {TableAxisVariable::normalized_voltage, "normalized_voltage"} - }; +static EnumNameMap table_axis_variable_map = { + {TableAxisVariable::total_output_net_capacitance, + "total_output_net_capacitance"}, + {TableAxisVariable::equal_or_opposite_output_net_capacitance, + "equal_or_opposite_output_net_capacitance"}, + {TableAxisVariable::input_net_transition, "input_net_transition"}, + {TableAxisVariable::input_transition_time, "input_transition_time"}, + {TableAxisVariable::related_pin_transition, "related_pin_transition"}, + {TableAxisVariable::constrained_pin_transition, "constrained_pin_transition"}, + {TableAxisVariable::output_pin_transition, "output_pin_transition"}, + {TableAxisVariable::connect_delay, "connect_delay"}, + {TableAxisVariable::related_out_total_output_net_capacitance, + "related_out_total_output_net_capacitance"}, + {TableAxisVariable::time, "time"}, + {TableAxisVariable::iv_output_voltage, "iv_output_voltage"}, + {TableAxisVariable::input_noise_width, "input_noise_width"}, + {TableAxisVariable::input_noise_height, "input_noise_height"}, + {TableAxisVariable::input_voltage, "input_voltage"}, + {TableAxisVariable::output_voltage, "output_voltage"}, + {TableAxisVariable::path_depth, "path_depth"}, + {TableAxisVariable::path_distance, "path_distance"}, + {TableAxisVariable::normalized_voltage, "normalized_voltage"}}; TableAxisVariable stringTableAxisVariable(const char *variable) @@ -1769,30 +1755,30 @@ tableVariableUnit(TableAxisVariable variable, const Units *units) { switch (variable) { - case TableAxisVariable::total_output_net_capacitance: - case TableAxisVariable::related_out_total_output_net_capacitance: - case TableAxisVariable::equal_or_opposite_output_net_capacitance: - return units->capacitanceUnit(); - case TableAxisVariable::input_net_transition: - case TableAxisVariable::input_transition_time: - case TableAxisVariable::related_pin_transition: - case TableAxisVariable::constrained_pin_transition: - case TableAxisVariable::output_pin_transition: - case TableAxisVariable::connect_delay: - case TableAxisVariable::time: - case TableAxisVariable::input_noise_height: - return units->timeUnit(); - case TableAxisVariable::input_voltage: - case TableAxisVariable::output_voltage: - case TableAxisVariable::iv_output_voltage: - case TableAxisVariable::input_noise_width: - return units->voltageUnit(); - case TableAxisVariable::path_distance: - return units->distanceUnit(); - case TableAxisVariable::path_depth: - case TableAxisVariable::normalized_voltage: - case TableAxisVariable::unknown: - return units->scalarUnit(); + case TableAxisVariable::total_output_net_capacitance: + case TableAxisVariable::related_out_total_output_net_capacitance: + case TableAxisVariable::equal_or_opposite_output_net_capacitance: + return units->capacitanceUnit(); + case TableAxisVariable::input_net_transition: + case TableAxisVariable::input_transition_time: + case TableAxisVariable::related_pin_transition: + case TableAxisVariable::constrained_pin_transition: + case TableAxisVariable::output_pin_transition: + case TableAxisVariable::connect_delay: + case TableAxisVariable::time: + case TableAxisVariable::input_noise_height: + return units->timeUnit(); + case TableAxisVariable::input_voltage: + case TableAxisVariable::output_voltage: + case TableAxisVariable::iv_output_voltage: + case TableAxisVariable::input_noise_width: + return units->voltageUnit(); + case TableAxisVariable::path_distance: + return units->distanceUnit(); + case TableAxisVariable::path_depth: + case TableAxisVariable::normalized_voltage: + case TableAxisVariable::unknown: + return units->scalarUnit(); } // Prevent warnings from lame compilers. return nullptr; @@ -1828,12 +1814,13 @@ OutputWaveforms::checkAxes(const TableTemplate *tbl_template) const TableAxis *axis2 = tbl_template->axis2(); const TableAxis *axis3 = tbl_template->axis3(); return (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2->variable() == TableAxisVariable::time - && axis3 == nullptr) - || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2 && axis2->variable() == TableAxisVariable::total_output_net_capacitance + && axis2->variable() == TableAxisVariable::time && axis3 == nullptr) + || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition + && axis2 + && axis2->variable() == TableAxisVariable::total_output_net_capacitance && axis3->variable() == TableAxisVariable::time) - || (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance + || (axis1 + && axis1->variable() == TableAxisVariable::total_output_net_capacitance && axis2 && axis2->variable() == TableAxisVariable::input_net_transition && axis3->variable() == TableAxisVariable::time); } @@ -1886,8 +1873,8 @@ OutputWaveforms::findVoltages(size_t wave_index, // Make voltage -> current table. FloatSeq axis_volts = volts; - TableAxisPtr volt_axis = - std::make_shared(TableAxisVariable::input_voltage, std::move(axis_volts)); + TableAxisPtr volt_axis = std::make_shared( + TableAxisVariable::input_voltage, std::move(axis_volts)); FloatSeq *currents1 = new FloatSeq(*currents->values()); Table *volt_currents = new Table(currents1, volt_axis); voltage_currents_[wave_index] = volt_currents; @@ -1906,7 +1893,8 @@ OutputWaveforms::currentWaveform(float slew, times->push_back(time); currents->push_back(current); } - TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, std::move(*times)); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, std::move(*times)); delete times; return Table(currents, time_axis); } @@ -1988,11 +1976,8 @@ OutputWaveforms::voltageTime1(double volt, double y01 = voltageTime2(volt, wave_index01); double y10 = voltageTime2(volt, wave_index10); double y11 = voltageTime2(volt, wave_index11); - double time - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + double time = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; return time; } @@ -2057,11 +2042,8 @@ OutputWaveforms::waveformValue(float slew, double y01 = waveform01->findValueClip(axis_value); double y10 = waveform10->findValueClip(axis_value); double y11 = waveform11->findValueClip(axis_value); - double wave_value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + double wave_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; return wave_value; } @@ -2083,8 +2065,8 @@ OutputWaveforms::voltageWaveform(float slew, times.push_back(time); volts.push_back(volt); } - TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, - std::move(times)); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, std::move(times)); return Table(std::move(volts), time_axis); } @@ -2101,8 +2083,8 @@ OutputWaveforms::voltageWaveformRaw(float slew, float OutputWaveforms::voltageTime(float slew, - float cap, - float volt) + float cap, + float volt) { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); @@ -2187,11 +2169,8 @@ OutputWaveforms::beginEndTime(float slew, y11 = waveform11->axis1()->max(); } - float wave_value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + float wave_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; return wave_value; } @@ -2207,8 +2186,8 @@ OutputWaveforms::voltageCurrentWaveform(float slew, volts->push_back(volt); currents->push_back(current); } - TableAxisPtr volt_axis = - std::make_shared(TableAxisVariable::input_voltage, std::move(*volts)); + TableAxisPtr volt_axis = std::make_shared( + TableAxisVariable::input_voltage, std::move(*volts)); delete volts; return Table(currents, volt_axis); } @@ -2250,11 +2229,11 @@ DriverWaveform::waveform(float slew) time_values->push_back(time); volt_values->push_back(volt); } - TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, - std::move(*time_values)); + TableAxisPtr time_axis = + std::make_shared(TableAxisVariable::time, std::move(*time_values)); delete time_values; Table waveform(volt_values, time_axis); return waveform; } -} // namespace +} // namespace sta diff --git a/liberty/Units.cc b/liberty/Units.cc index 98cad0aa..fb3fcdb6 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -26,6 +26,7 @@ #include // abs +#include "Format.hh" #include "StringUtil.hh" #include "MinMax.hh" // INF #include "Fuzzy.hh" @@ -127,7 +128,7 @@ Unit::scaleString() const else if (fuzzyEqual(scale_, 1E-15)) return "1f"; else - return stdstrPrint("%.1e", scale_); + return sta::format("{:.1e}", scale_); } std::string @@ -155,19 +156,13 @@ Unit::width() const return digits_ + 2; } -const char * +std::string Unit::asString(float value) const { return asString(value, digits_); } -const char * -Unit::asString(double value) const -{ - return asString(static_cast(value), digits_); -} - -const char * +std::string Unit::asString(float value, int digits) const { @@ -179,7 +174,7 @@ Unit::asString(float value, // prevent "-0.00" on slowaris if (std::abs(scaled_value) < 1E-6) scaled_value = 0.0; - return stringPrintTmp("%.*f", digits, scaled_value); + return sta::formatRuntime("{:.{}f}", scaled_value, digits); } } diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 50bc62b2..1d18d24d 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -222,12 +222,10 @@ ConcreteCell::makeBusPortBit(ConcretePort *bus_port, const char *bus_name, int bit_index) { - std::string bit_name; - stringPrint(bit_name, "%s%c%d%c", - bus_name, - library_->busBrktLeft(), - bit_index, - library_->busBrktRight()); + std::string bit_name = std::string(bus_name) + + library_->busBrktLeft() + + std::to_string(bit_index) + + library_->busBrktRight(); ConcretePort *port = makePort(bit_name.c_str(), bit_index); bus_port->addPortBit(port); addPortBit(port); @@ -465,12 +463,13 @@ ConcretePort::busName() const { if (is_bus_) { ConcreteLibrary *lib = cell_->library(); - return stringPrintTmp("%s%c%d:%d%c", - name(), - lib->busBrktLeft(), - from_index_, - to_index_, - lib->busBrktRight()); + std::string bus_name = sta::format("{}{}{}:{}{}", + name(), + lib->busBrktLeft(), + from_index_, + to_index_, + lib->busBrktRight()); + return makeTmpString(bus_name); } else return name(); diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index de4b0464..6291d5bc 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -2003,7 +2003,7 @@ ConcreteNetwork::linkNetwork(const char *top_cell_name, return top_instance_ != nullptr; } else { - report->error(1000, "cell type %s can not be linked.", top_cell_name); + report->error(1000, "cell type {} can not be linked.", top_cell_name); return false; } } diff --git a/network/HpinDrvrLoad.cc b/network/HpinDrvrLoad.cc index 2c98c5fa..ab76900d 100644 --- a/network/HpinDrvrLoad.cc +++ b/network/HpinDrvrLoad.cc @@ -293,15 +293,16 @@ HpinDrvrLoad::~HpinDrvrLoad() void HpinDrvrLoad::report(const Network *network) { - printf("%s -> %s: ", - drvr_ ? network->pathName(drvr_) : "-", - load_ ? network->pathName(load_) : "-"); + Report *report = network->report(); + std::string line = sta::format("{} -> {}: ", + drvr_ ? network->pathName(drvr_) : "-", + load_ ? network->pathName(load_) : "-"); for (const Pin *pin : *hpins_from_drvr_) - printf("%s ", network->pathName(pin)); - printf("* "); + line += sta::format("{} ", network->pathName(pin)); + line += "* "; for (const Pin *pin : *hpins_to_load_) - printf("%s ", network->pathName(pin)); - printf("\n"); + line += sta::format("{} ", network->pathName(pin)); + report->report(line); } void diff --git a/network/Network.cc b/network/Network.cc index fe9a9c1d..b0a7d635 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -262,24 +262,15 @@ Network::pathName(const Instance *instance) const { InstanceSeq inst_path; path(instance, inst_path); - size_t name_length = 0; - for (const Instance *inst : inst_path) - name_length += strlen(name(inst)) + 1; - char *path_name = makeTmpString(name_length + 1); - char *path_ptr = path_name; - // Top instance has null string name, so terminate the string here. - *path_name = '\0'; + std::string path_name; while (inst_path.size()) { const Instance *inst = inst_path.back(); - const char *inst_name = name(inst); - strcpy(path_ptr, inst_name); - path_ptr += strlen(inst_name); + path_name += name(inst); inst_path.pop_back(); - if (inst_path.size()) - *path_ptr++ = pathDivider(); - *path_ptr = '\0'; + if (!inst_path.empty()) + path_name += pathDivider(); } - return path_name; + return makeTmpString(path_name); } bool @@ -376,18 +367,10 @@ Network::pathName(const Pin *pin) const { const Instance *inst = instance(pin); if (inst && inst != topInstance()) { - const char *inst_name = pathName(inst); - size_t inst_name_length = strlen(inst_name); - const char *port_name = portName(pin); - size_t port_name_length = strlen(port_name); - size_t path_name_length = inst_name_length + port_name_length + 2; - char *path_name = makeTmpString(path_name_length); - char *path_ptr = path_name; - strcpy(path_ptr, inst_name); - path_ptr += inst_name_length; - *path_ptr++ = pathDivider(); - strcpy(path_ptr, port_name); - return path_name; + std::string path_name = pathName(inst); + path_name += pathDivider(); + path_name += portName(pin); + return makeTmpString(path_name); } else return portName(pin); @@ -464,18 +447,10 @@ Network::pathName(const Net *net) const { const Instance *inst = instance(net); if (inst && inst != topInstance()) { - const char *inst_name = pathName(inst); - size_t inst_name_length = strlen(inst_name); - const char *net_name = name(net); - size_t net_name_length = strlen(net_name); - size_t path_name_length = inst_name_length + net_name_length + 2; - char *path_name = makeTmpString(path_name_length); - char *path_ptr = path_name; - strcpy(path_ptr, inst_name); - path_ptr += inst_name_length; - *path_ptr++ = pathDivider(); - strcpy(path_ptr, net_name); - return path_name; + std::string path_name = pathName(inst); + path_name += pathDivider(); + path_name += name(net); + return makeTmpString(path_name); } else return name(net); diff --git a/network/Network.i b/network/Network.i index 6495ec8b..870399df 100644 --- a/network/Network.i +++ b/network/Network.i @@ -511,7 +511,7 @@ net_pins(Net *net) return pins; } -const char * +std::string pin_location(const Pin *pin) { Network *network = Sta::sta()->ensureLinked(); @@ -520,12 +520,12 @@ pin_location(const Pin *pin) network->location(pin, x, y, exists); // return x/y as tcl list if (exists) - return sta::stringPrintTmp("%f %f", x, y); + return std::format("{} {}", x, y); else return ""; } -const char * +std::string port_location(const Port *port) { Network *network = Sta::sta()->ensureLinked(); diff --git a/network/ParseBus.cc b/network/ParseBus.cc index 83f097b3..7bbab7a0 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -24,35 +24,34 @@ #include "ParseBus.hh" -#include -#include #include +#include #include "StringUtil.hh" namespace sta { bool -isBusName(const char *name, +isBusName(std::string_view name, const char brkt_left, const char brkt_right, char escape) { - size_t len = strlen(name); + size_t len = name.size(); // Shortest bus name is a[0]. if (len >= 4 // Escaped bus brackets are not buses. && name[len - 2] != escape && name[len - 1] == brkt_right) { - const char *left = strrchr(name, brkt_left); - return left != nullptr; + size_t left = name.rfind(brkt_left); + return left != std::string_view::npos; } else return false; } void -parseBusName(const char *name, +parseBusName(std::string_view name, const char brkt_left, const char brkt_right, const char escape, @@ -61,16 +60,15 @@ parseBusName(const char *name, std::string &bus_name, int &index) { - const char brkts_left[2] = {brkt_left, '\0'}; - const char brkts_right[2] = {brkt_right, '\0'}; - parseBusName(name, brkts_left, brkts_right, escape, + parseBusName(name, std::string_view(&brkt_left, 1), + std::string_view(&brkt_right, 1), escape, is_bus, bus_name, index); } void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, char escape, // Return values. bool &is_bus, @@ -78,30 +76,28 @@ parseBusName(const char *name, int &index) { is_bus = false; - size_t len = strlen(name); + size_t len = name.size(); // Shortest bus name is a[0]. if (len >= 4 // Escaped bus brackets are not buses. && name[len - 2] != escape) { char last_ch = name[len - 1]; - const char *brkt_right_ptr = strchr(brkts_right, last_ch); - if (brkt_right_ptr) { - size_t brkt_index = brkt_right_ptr - brkts_right; - char brkt_left = brkts_left[brkt_index]; - const char *left = strrchr(name, brkt_left); - if (left) { + size_t brkt_index = brkts_right.find(last_ch); + if (brkt_index != std::string_view::npos) { + char brkt_left_ch = brkts_left[brkt_index]; + size_t left = name.rfind(brkt_left_ch); + if (left != std::string_view::npos) { is_bus = true; - size_t bus_name_len = left - name; - bus_name.append(name, bus_name_len); + bus_name.append(name.data(), left); // Simple bus subscript. - index = atoi(left + 1); + index = std::stoi(std::string(name.substr(left + 1))); } } } } void -parseBusName(const char *name, +parseBusName(std::string_view name, const char brkt_left, const char brkt_right, char escape, @@ -113,16 +109,15 @@ parseBusName(const char *name, int &to, bool &subscript_wild) { - const char brkts_left[2] = {brkt_left, '\0'}; - const char brkts_right[2] = {brkt_right, '\0'}; - parseBusName(name, brkts_left, brkts_right, escape, + parseBusName(name, std::string_view(&brkt_left, 1), + std::string_view(&brkt_right, 1), escape, is_bus, is_range, bus_name, from, to, subscript_wild); } void -parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, +parseBusName(std::string_view name, + std::string_view brkts_left, + std::string_view brkts_right, char escape, // Return values. bool &is_bus, @@ -135,36 +130,31 @@ parseBusName(const char *name, is_bus = false; is_range = false; subscript_wild = false; - size_t len = strlen(name); + size_t len = name.size(); // Shortest bus is a[0]. if (len >= 4 // Escaped bus brackets are not buses. && name[len - 2] != escape) { char last_ch = name[len - 1]; - const char *brkt_right_ptr = strchr(brkts_right, last_ch); - if (brkt_right_ptr) { - size_t brkt_index = brkt_right_ptr - brkts_right; - char brkt_left = brkts_left[brkt_index]; - const char *left = strrchr(name, brkt_left); - if (left) { + size_t brkt_index = brkts_right.find(last_ch); + if (brkt_index != std::string_view::npos) { + char brkt_left_ch = brkts_left[brkt_index]; + size_t left = name.rfind(brkt_left_ch); + if (left != std::string_view::npos) { is_bus = true; + bus_name.append(name.data(), left); // Check for bus range. - const char range_sep = ':'; - const char *range = strchr(name, range_sep); - if (range) { + size_t range = name.find(':', left); + if (range != std::string_view::npos) { is_range = true; - bus_name.append(name, left - name); - // No need to terminate bus subscript because atoi stops - // scanning at first non-digit character. - from = atoi(left + 1); - to = atoi(range + 1); + from = std::stoi(std::string(name.substr(left + 1))); + to = std::stoi(std::string(name.substr(range + 1))); } else { - bus_name.append(name, left - name); - if (left[1] == '*') + if (left + 1 < len && name[left + 1] == '*') subscript_wild = true; else - from = to = atoi(left + 1); + from = to = std::stoi(std::string(name.substr(left + 1))); } } } @@ -172,22 +162,23 @@ parseBusName(const char *name, } std::string -escapeChars(const char *token, +escapeChars(std::string_view token, const char ch1, const char ch2, const char escape) { std::string escaped; - for (const char *s = token; *s; s++) { - char ch = *s; + escaped.reserve(token.size()); + for (size_t i = 0; i < token.size(); i++) { + char ch = token[i]; if (ch == escape) { - char next_ch = s[1]; - // Make sure we don't skip the null if escape is the last char. - if (next_ch != '\0') { + if (i + 1 < token.size()) { escaped += ch; - escaped += next_ch; - s++; + escaped += token[i + 1]; + i++; } + else + escaped += ch; } else if (ch == ch1 || ch == ch2) { escaped += escape; diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 5200367e..4cdef63e 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -640,28 +640,27 @@ SdcNetwork::SdcNetwork(Network *network) : // Translate sta namespace to sdc namespace. // Remove all escapes. const char * -SdcNetwork::staToSdc(const char *sta_name) const +SdcNetwork::staToSdc(std::string_view sta_name) const { char escape = pathEscape(); - char *sdc_name = makeTmpString(strlen(sta_name) + 1); - char *d = sdc_name; - for (const char *s = sta_name; *s; s++) { - char ch = s[0]; + size_t sta_length = sta_name.length(); + std::string sdc_name; + for (size_t i = 0; i < sta_length; i++) { + char ch = sta_name[i]; if (ch == escape) { - char next_ch = s[1]; + char next_ch = sta_name[i + 1]; // Escaped escape. if (next_ch == escape) { - *d++ = ch; - *d++ = next_ch; - s++; + sdc_name += ch; + sdc_name += next_ch; + i++; } } else // Non escape. - *d++ = ch; + sdc_name += ch; } - *d++ = '\0'; - return sdc_name; + return makeTmpString(sdc_name); } Port * @@ -680,11 +679,11 @@ SdcNetwork::findPort(const Cell *cell, port = network_->findPort(cell, escaped1.c_str()); if (port == nullptr) { // Try escaping base foo\[0\][1] - std::string escaped2; std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); - stringPrint(escaped2, "%s[%d]", - escaped_bus_name.c_str(), - index); + std::string escaped2 = escaped_bus_name + + '[' + + std::to_string(index) + + ']'; port = network_->findPort(cell, escaped2.c_str()); } } @@ -966,8 +965,10 @@ SdcNetwork::findPin(const Instance *instance, if (pin == nullptr) { // Try escaping base foo\[0\][1] std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); - std::string escaped2; - stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); + std::string escaped2 = escaped_bus_name + + '[' + + std::to_string(index) + + ']'; pin = network_->findPin(instance, escaped2.c_str()); } } @@ -1149,46 +1150,39 @@ SdcNetwork::parsePath(const char *path, const char *&path_tail) const { Instance *parent = topInstance(); + std::string inst_path; // Leave room to escape all the dividers and '\0'. - int inst_path_length = path_length + divider_count + 1; - char *inst_path = new char[inst_path_length]; + inst_path.reserve(path_length + divider_count + 1); inst = nullptr; path_tail = path; - char *p = inst_path; for (const char *s = path; *s; s++) { char ch = *s; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. if (s[1] != '\0') { - *p++ = ch; - *p++ = s[1]; + inst_path += ch; + inst_path += s[1]; s++; } } else if (ch == divider_) { - // Terminate the sub-path up to this divider. - *p = '\0'; - Instance *child = findChild(parent, inst_path); + Instance *child = findChild(parent, inst_path.c_str()); if (child) { // Found an instance for the sub-path up to this divider. parent = inst = child; // Reset the instance path. - p = inst_path; + inst_path.clear(); path_tail = s + 1; } else { // No match for sub-path. Escape the divider and keep looking. - *p++ = escape_; - *p++ = divider_; + inst_path += escape_; + inst_path += divider_; } } else - *p++ = ch; - if (p - inst_path + 1 > inst_path_length) - report_->critical(1500, "inst path std::string lenth estimate busted"); + inst_path += ch; } - *p = '\0'; - stringDelete(inst_path); } // Helper to visit instance path matches. @@ -1207,11 +1201,9 @@ SdcNetwork::visitMatches(const Instance *parent, { int divider_count, path_length; scanPath(pattern->pattern(), divider_count, path_length); - + std::string inst_path; // Leave room to escape all the dividers and '\0'. - int inst_path_length = path_length + divider_count + 1; - char *inst_path = new char[inst_path_length]; - char *p = inst_path; + inst_path.reserve(path_length + divider_count + 1); bool has_brkts = false; bool found_match = false; for (const char *s = pattern->pattern(); *s; s++) { @@ -1219,20 +1211,18 @@ SdcNetwork::visitMatches(const Instance *parent, if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. if (s[1] != '\0') { - *p++ = ch; - *p++ = s[1]; + inst_path += ch; + inst_path += s[1]; s++; } } else if (ch == divider_) { - // Terminate the sub-path up to this divider. - *p = '\0'; - PatternMatch matcher(inst_path, pattern); + PatternMatch matcher(inst_path.c_str(), pattern); InstanceSeq matches; network_->findChildrenMatching(parent, &matcher, matches); if (has_brkts && matches.empty()) { // Look for matches after escaping brackets. - std::string escaped_brkts = escapeBrackets(inst_path, this); + std::string escaped_brkts = escapeBrackets(inst_path.c_str(), this); const PatternMatch escaped_pattern(escaped_brkts, pattern); network_->findChildrenMatching(parent, &escaped_pattern, matches); } @@ -1245,29 +1235,25 @@ SdcNetwork::visitMatches(const Instance *parent, found_match |= visitMatches(match, &tail_pattern, visit_tail); } // Escape the divider and keep looking. - *p++ = escape_; - *p++ = divider_; + inst_path += escape_; + inst_path += divider_; } else { if (ch == '[' || ch == ']') has_brkts = true; - *p++ = ch; + inst_path += ch; } - if (p - inst_path + 1 > inst_path_length) - report_->critical(1501, "inst path std::string lenth estimate exceeded"); } - *p = '\0'; if (!found_match) { - PatternMatch tail_pattern(inst_path, pattern); + PatternMatch tail_pattern(inst_path.c_str(), pattern); found_match |= visit_tail(parent, &tail_pattern); if (!found_match && has_brkts) { // Look for matches after escaping brackets. - std::string escaped_path = escapeBrackets(inst_path, this); + std::string escaped_path = escapeBrackets(inst_path.c_str(), this); const PatternMatch escaped_tail(escaped_path, pattern); found_match |= visit_tail(parent, &escaped_tail); } } - stringDelete(inst_path); return found_match; } diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index a018782e..702ca6d2 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -34,35 +34,34 @@ namespace sta { constexpr char verilog_escape = '\\'; static std::string -staToVerilog(const char *sta_name); +staToVerilog(std::string sta_name); static std::string -staToVerilog2(const char *sta_name); +staToVerilog2(std::string sta_name); static std::string -verilogToSta(const std::string *verilog_name); +verilogToSta(const std::string verilog_name); std::string -cellVerilogName(const char *sta_name) +cellVerilogName(std::string sta_name) { return staToVerilog(sta_name); } std::string -instanceVerilogName(const char *sta_name) +instanceVerilogName(std::string sta_name) { return staToVerilog(sta_name); } std::string -netVerilogName(const char *sta_name) +netVerilogName(std::string sta_name) { bool is_bus; std::string bus_name; int index; - parseBusName(sta_name, '[', ']', verilog_escape, is_bus, bus_name, index); + parseBusName(sta_name.c_str(), '[', ']', verilog_escape, is_bus, bus_name, index); if (is_bus) { std::string bus_vname = staToVerilog(bus_name.c_str()); - std::string vname; - stringPrint(vname, "%s[%d]", bus_vname.c_str(), index); + std::string vname = bus_vname + '[' + std::to_string(index) + ']'; return vname; } else @@ -70,27 +69,28 @@ netVerilogName(const char *sta_name) } std::string -portVerilogName(const char *sta_name) +portVerilogName(std::string sta_name) { return staToVerilog2(sta_name); } static std::string -staToVerilog(const char *sta_name) +staToVerilog(std::string sta_name) { // Leave room for leading escape and trailing space if the name // needs to be escaped. // Assume the name has to be escaped and start copying while scanning. std::string escaped_name = "\\"; bool escaped = false; - for (const char *s = sta_name; *s ; s++) { - char ch = s[0]; + size_t sta_length = sta_name.size(); + for (size_t i = 0; i < sta_length; i++) { + char ch = sta_name[i]; if (ch == verilog_escape) { escaped = true; - char next_ch = s[1]; + char next_ch = sta_name[i + 1]; if (next_ch == verilog_escape) { escaped_name += next_ch; - s++; + i++; } } else { @@ -105,11 +105,11 @@ staToVerilog(const char *sta_name) return escaped_name; } else - return std::string(sta_name); + return sta_name; } static std::string -staToVerilog2(const char *sta_name) +staToVerilog2(std::string sta_name) { constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; @@ -118,14 +118,15 @@ staToVerilog2(const char *sta_name) std::string escaped_name = "\\"; // Assume the name has to be escaped and start copying while scanning. bool escaped = false; - for (const char *s = sta_name; *s ; s++) { - char ch = s[0]; + size_t sta_length = sta_name.size(); + for (size_t i = 0; i < sta_length; i++) { + char ch = sta_name[i]; if (ch == verilog_escape) { escaped = true; - char next_ch = s[1]; + char next_ch = sta_name[i + 1]; if (next_ch == verilog_escape) { escaped_name += next_ch; - s++; + i++; } } else { @@ -142,50 +143,50 @@ staToVerilog2(const char *sta_name) return escaped_name; } else - return std::string(sta_name); + return sta_name; } //////////////////////////////////////////////////////////////// std::string -moduleVerilogToSta(const std::string *module_name) +moduleVerilogToSta(std::string module_name) { return verilogToSta(module_name); } std::string -instanceVerilogToSta(const std::string *inst_name) +instanceVerilogToSta(std::string inst_name) { return verilogToSta(inst_name); } std::string -netVerilogToSta(const std::string *net_name) +netVerilogToSta(std::string net_name) { return verilogToSta(net_name); } std::string -portVerilogToSta(const std::string *port_name) +portVerilogToSta(std::string port_name) { return verilogToSta(port_name); } static std::string -verilogToSta(const std::string *verilog_name) +verilogToSta(std::string verilog_name) { - if (verilog_name->front() == '\\') { + if (verilog_name.front() == '\\') { constexpr char divider = '/'; constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; - size_t verilog_name_length = verilog_name->size(); - if (isspace(verilog_name->back())) + size_t verilog_name_length = verilog_name.size(); + if (isspace(verilog_name.back())) verilog_name_length--; std::string sta_name; // Ignore leading '\'. for (size_t i = 1; i < verilog_name_length; i++) { - char ch = verilog_name->at(i); + char ch = verilog_name[i]; if (ch == bus_brkt_left || ch == bus_brkt_right || ch == divider @@ -197,7 +198,7 @@ verilogToSta(const std::string *verilog_name) return sta_name; } else - return std::string(*verilog_name); + return verilog_name; } } // namespace diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index db291c53..e85036ee 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -410,8 +410,10 @@ const char * ConcreteParasiticNode::name(const Network *network) const { if (is_net_) { - const char *net_name = network->pathName(net_pin_.net_); - return stringPrintTmp("%s:%d", net_name, id_); + std::string name = std::string(network->pathName(net_pin_.net_)) + + ':' + + std::to_string(id_); + return makeTmpString(name); } else return network->pathName(net_pin_.pin_); diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index 8aaf5fc0..ab304bda 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Parasitics.hh" @@ -49,38 +49,29 @@ Parasitics::report(const Parasitic *parasitic) const { if (isParasiticNetwork(parasitic)) { const Unit *cap_unit = units_->capacitanceUnit(); - report_->reportLine("Net %s %s", - network_->pathName(net(parasitic)), - cap_unit->asString(capacitance(parasitic))); - report_->reportLine("Nodes:"); + report_->report("Net {} {}", network_->pathName(net(parasitic)), + cap_unit->asString(capacitance(parasitic))); + report_->report("Nodes:"); for (ParasiticNode *node : nodes(parasitic)) - report_->reportLine("%s%s %s", - name(node), - isExternal(node) ? " (ext)" : "", - cap_unit->asString(nodeGndCap(node))); - report_->reportLine("Resistors:"); + report_->report("{}{} {}", name(node), isExternal(node) ? " (ext)" : "", + cap_unit->asString(nodeGndCap(node))); + report_->report("Resistors:"); for (ParasiticResistor *res : resistors(parasitic)) { ParasiticNode *node1 = this->node1(res); ParasiticNode *node2 = this->node2(res); - report_->reportLine("%zu %s%s %s%s %s", - id(res), - name(node1), - isExternal(node1) ? " (ext)" : "", - name(node2), - isExternal(node2) ? " (ext)" : "", - units_->resistanceUnit()->asString(value(res))); + report_->report("{} {}{} {}{} {}", id(res), name(node1), + isExternal(node1) ? " (ext)" : "", name(node2), + isExternal(node2) ? " (ext)" : "", + units_->resistanceUnit()->asString(value(res))); } - report_->reportLine("Coupling Capacitors:"); + report_->report("Coupling Capacitors:"); for (ParasiticCapacitor *cap : capacitors(parasitic)) { ParasiticNode *node1 = this->node1(cap); ParasiticNode *node2 = this->node2(cap); - report_->reportLine("%zu %s%s %s%s %s", - id(cap), - name(node1), - isExternal(node1) ? " (ext)" : "", - name(node2), - isExternal(node2) ? " (ext)" : "", - cap_unit->asString(value(cap))); + report_->report("{} {}{} {}{} {}", id(cap), name(node1), + isExternal(node1) ? " (ext)" : "", name(node2), + isExternal(node2) ? " (ext)" : "", + cap_unit->asString(value(cap))); } } } @@ -138,10 +129,10 @@ Parasitics::parasiticNodeResistorMap(const Parasitic *parasitic) const return resistor_map; } -ParasiticNodeCapacitorMap +ParasiticNodeCapacitorMap Parasitics::parasiticNodeCapacitorMap(const Parasitic *parasitic) const { - ParasiticNodeCapacitorMap capacitor_map; + ParasiticNodeCapacitorMap capacitor_map; for (ParasiticCapacitor *capacitor : capacitors(parasitic)) { ParasiticNode *n1 = node1(capacitor); ParasiticNode *n2 = node2(capacitor); @@ -186,9 +177,8 @@ Parasitics::reduceToPiElmore(const Parasitic *parasitic, const Scene *scene, const MinMax *min_max) { - return sta::reduceToPiElmore(parasitic, drvr_pin, rf, - coupling_cap_factor_, - scene, min_max, this); + return sta::reduceToPiElmore(parasitic, drvr_pin, rf, coupling_cap_factor_, scene, + min_max, this); } Parasitic * @@ -198,8 +188,7 @@ Parasitics::reduceToPiPoleResidue2(const Parasitic *parasitic, const Scene *scene, const MinMax *min_max) { - return sta::reduceToPiPoleResidue2(parasitic, drvr_pin, rf, - coupling_cap_factor_, + return sta::reduceToPiPoleResidue2(parasitic, drvr_pin, rf, coupling_cap_factor_, scene, min_max, this); } @@ -217,10 +206,9 @@ Parasitics::estimatePiElmore(const Pin *drvr_pin, EstimateParasitics estimate(this); float c2, rpi, c1, elmore_res, elmore_cap; bool elmore_use_load_cap; - estimate.estimatePiElmore(drvr_pin, rf, wireload, fanout, net_pin_cap, - scene, min_max, - c2, rpi, c1, - elmore_res, elmore_cap, elmore_use_load_cap); + estimate.estimatePiElmore(drvr_pin, rf, wireload, fanout, net_pin_cap, scene, + min_max, c2, rpi, c1, elmore_res, elmore_cap, + elmore_use_load_cap); if (c1 > 0.0 || c2 > 0.0) { Parasitic *parasitic = makePiElmore(drvr_pin, rf, min_max, c2, rpi, c1); @@ -265,19 +253,19 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, if (op_cond) tree = op_cond->wireloadTree(); switch (tree) { - case WireloadTree::worst_case: - makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, - wireload_res, fanout); - break; - case WireloadTree::balanced: - makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, - wireload_res, fanout); - break; - case WireloadTree::best_case: - case WireloadTree::unknown: - makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, - wireload_res, fanout); - break; + case WireloadTree::worst_case: + makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, + wireload_res, fanout); + break; + case WireloadTree::balanced: + makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, wireload_res, + fanout); + break; + case WireloadTree::best_case: + case WireloadTree::unknown: + makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, wireload_res, + fanout); + break; } } return parasitic; @@ -298,12 +286,10 @@ Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic, ParasiticNode *load_node = ensureParasiticNode(parasitic, net, 0, network_); makeResistor(parasitic, resistor_index++, wireload_res, drvr_node, load_node); incrCap(load_node, wireload_cap); - PinConnectedPinIterator *load_iter = - network_->connectedPinIterator(drvr_pin); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); makeResistor(parasitic, resistor_index++, 0.0, load_node, load_node1); } @@ -320,13 +306,11 @@ Parasitics::makeWireloadNetworkBest(Parasitic *parasitic, { ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); incrCap(drvr_node, wireload_cap); - PinConnectedPinIterator *load_iter = - network_->connectedPinIterator(drvr_pin); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); size_t resistor_index = 1; while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); makeResistor(parasitic, resistor_index++, 0.0, drvr_node, load_node1); } @@ -345,15 +329,13 @@ Parasitics::makeWireloadNetworkBalanced(Parasitic *parasitic, float fanout_cap = wireload_cap / fanout; float fanout_res = wireload_res / fanout; ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); - PinConnectedPinIterator *load_iter = - network_->connectedPinIterator(drvr_pin); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); size_t resistor_index = 1; while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); - makeResistor(parasitic, resistor_index++, fanout_res, drvr_node, load_node1); + makeResistor(parasitic, resistor_index++, fanout_res, drvr_node, load_node1); incrCap(load_node1, fanout_cap); } } @@ -391,12 +373,10 @@ ParasiticNodeLess::operator()(const ParasiticNode *node1, unsigned id1 = parasitics_->netId(node1); unsigned id2 = parasitics_->netId(node2); return (pin1 == nullptr && pin2) - || (pin1 && pin2 - && network_->id(pin1) < network_->id(pin2)) - || (pin1 == nullptr && pin2 == nullptr - && (network_->id(net1) < network_->id(net2) - || (net1 == net2 - && id1 < id2))); + || (pin1 && pin2 && network_->id(pin1) < network_->id(pin2)) + || (pin1 == nullptr && pin2 == nullptr + && (network_->id(net1) < network_->id(net2) + || (net1 == net2 && id1 < id2))); } -} // namespace +} // namespace sta diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index 6dbab37d..4282a7f4 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -147,7 +147,7 @@ ReduceToPi::reduceToPi(const Parasitic *parasitic_network, rpi = -y3 * y3 / (y2 * y2 * y2); } debugPrint(debug_, "parasitic_reduce", 2, - " Pi model c2=%.3g rpi=%.3g c1=%.3g max_r=%.3g", + " Pi model c2={:.3g} rpi={:.3g} c1={:.3g} max_r={:.3g}", c2, rpi, c1, max_resistance); } @@ -185,7 +185,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin, && resistor != from_res) { if (isVisited(onode)) { // Resistor loop. - debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %zu", + debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor {}", parasitics_->id(resistor)); markLoopResistor(resistor); } @@ -208,7 +208,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin, setDownstreamCap(node, dwn_cap); leave(node); debugPrint(debug_, "parasitic_reduce", 3, - " node %s y1=%.3g y2=%.3g y3=%.3g cap=%.3g", + " node {} y1={:.3g} y2={:.3g} y3={:.3g} cap={:.3g}", parasitics_->name(node), y1, y2, y3, dwn_cap); } @@ -309,10 +309,10 @@ reduceToPiElmore(const Parasitic *parasitic_network, ParasiticNode *drvr_node = parasitics->findParasiticNode(parasitic_network, drvr_pin); if (drvr_node) { - debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s %s %s", + debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver {} {} {}", sta->network()->pathName(drvr_pin), rf->shortName(), - min_max->to_string().c_str()); + min_max->to_string()); ReduceToPiElmore reducer(sta); return reducer.makePiElmore(parasitic_network, drvr_pin, drvr_node, coupling_cap_factor, rf, scene, min_max); @@ -356,7 +356,7 @@ ReduceToPiElmore::reduceElmoreDfs(const Pin *drvr_pin, const Pin *pin = parasitics_->pin(node); if (from_res && pin) { if (network_->isLoad(pin)) { - debugPrint(debug_, "parasitic_reduce", 2, " Load %s elmore=%.3g", + debugPrint(debug_, "parasitic_reduce", 2, " Load {} elmore={:.3g}", network_->pathName(pin), elmore); parasitics_->setElmore(pi_elmore, pin, elmore); @@ -456,7 +456,7 @@ reduceToPiPoleResidue2(const Parasitic *parasitic_network, ParasiticNode *drvr_node = parasitics->findParasiticNode(parasitic_network, drvr_pin); if (drvr_node) { - debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s", + debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver {}", sta->network()->pathName(drvr_pin)); ReduceToPiPoleResidue2 reducer(sta); return reducer.makePiPoleResidue2(parasitic_network, drvr_pin, drvr_node, @@ -564,7 +564,7 @@ ReduceToPiPoleResidue2::findBranchCurrents(const Pin *drvr_pin, leave(node); if (from_res) { setCurrent(from_res, branch_i); - debugPrint(debug_, "parasitic_reduce", 3, " res i=%.3g", branch_i); + debugPrint(debug_, "parasitic_reduce", 3, " res i={:.3g}", branch_i); } return branch_i; } @@ -589,7 +589,7 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, double r_volt = r * current(resistor); double onode_volt = from_volt - r_volt; setMoment(onode, onode_volt, moment_index); - debugPrint(debug_, "parasitic_reduce", 3, " moment %s %d %.3g", + debugPrint(debug_, "parasitic_reduce", 3, " moment {} {} {:.3g}", parasitics_->name(onode), moment_index, onode_volt); @@ -655,7 +655,7 @@ ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, || m1 / m2 == m2 / m3) { double p1 = -1.0 / m1; double k1 = 1.0; - debugPrint(debug_, "parasitic_reduce", 3, " load %s p1=%.3g k1=%.3g", + debugPrint(debug_, "parasitic_reduce", 3, " load {} p1={:.3g} k1={:.3g}", network_->pathName(load_pin), p1, k1); ComplexFloatSeq *poles = new ComplexFloatSeq(1); ComplexFloatSeq *residues = new ComplexFloatSeq(1); @@ -675,7 +675,7 @@ ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, k1 = k; } debugPrint(debug_, "parasitic_reduce", 3, - " load %s p1=%.3g p2=%.3g k1=%.3g k2=%.3g", + " load {} p1={:.3g} p2={:.3g} k1={:.3g} k2={:.3g}", network_->pathName(load_pin), p1, p2, k1, k2); ComplexFloatSeq *poles = new ComplexFloatSeq(2); diff --git a/parasitics/ReportParasiticAnnotation.cc b/parasitics/ReportParasiticAnnotation.cc index a6726911..f91db926 100644 --- a/parasitics/ReportParasiticAnnotation.cc +++ b/parasitics/ReportParasiticAnnotation.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "ReportParasiticAnnotation.hh" @@ -65,8 +65,8 @@ reportParasiticAnnotation(Parasitics *parasitics, const Scene *scene, StaState *sta) { - ReportParasiticAnnotation report_annotation(parasitics, report_unannotated, - scene, sta); + ReportParasiticAnnotation report_annotation(parasitics, report_unannotated, scene, + sta); report_annotation.report(); } @@ -92,25 +92,26 @@ ReportParasiticAnnotation::report() void ReportParasiticAnnotation::reportAnnotationCounts() { - report_->reportLine("Found %zu unannotated drivers.", unannotated_.size()); + report_->report("Found {} unannotated drivers.", unannotated_.size()); if (report_unannotated_) { sort(unannotated_, PinPathNameLess(network_)); for (const Pin *drvr_pin : unannotated_) - report_->reportLine(" %s", network_->pathName(drvr_pin)); + report_->report(" {}", network_->pathName(drvr_pin)); } - report_->reportLine("Found %zu partially unannotated drivers.", - partially_annotated_.size()); + report_->report("Found {} partially unannotated drivers.", + partially_annotated_.size()); if (report_unannotated_) { sort(partially_annotated_, PinPathNameLess(network_)); for (const Pin *drvr_pin : partially_annotated_) { - report_->reportLine(" %s", network_->pathName(drvr_pin)); + report_->report(" {}", network_->pathName(drvr_pin)); Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin); if (parasitic) { - PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, drvr_pin); + PinSet unannotated_loads = + parasitics_->unannotatedLoads(parasitic, drvr_pin); for (const Pin *load_pin : unannotated_loads) - report_->reportLine(" %s", network_->pathName(load_pin)); + report_->report(" {}", network_->pathName(load_pin)); } } } @@ -124,21 +125,20 @@ ReportParasiticAnnotation::findCounts() Vertex *vertex = vertex_iter.next(); Pin *pin = vertex->pin(); PortDirection *dir = network_->direction(pin); - if (vertex->isDriver(network_) - && !dir->isInternal()) { + if (vertex->isDriver(network_) && !dir->isInternal()) { Parasitic *parasitic = parasitics_->findParasiticNetwork(pin); if (parasitic == nullptr) - parasitic = arc_delay_calc_->findParasitic(pin, RiseFall::rise(), - scene_, min_max_); + parasitic = + arc_delay_calc_->findParasitic(pin, RiseFall::rise(), scene_, min_max_); if (parasitic) { PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, pin); if (unannotated_loads.size() > 0) partially_annotated_.push_back(pin); } - else + else unannotated_.push_back(pin); } } } -} // namespace +} // namespace sta diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index 42983f0b..a88e2d26 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -41,8 +41,7 @@ void sta::SpefParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164,reader->filename().c_str(), - loc.begin.line,"%s",msg.c_str()); + reader->report()->fileError(1670,reader->filename(), loc.begin. line, "{}", msg); } %} @@ -831,7 +830,7 @@ pos_integer: INTEGER { int value = $1; if (value < 0) - reader->warn(1525, "%d is not positive.", value); + reader->warn(1525, "{} is not positive.", value); $$ = value; } ; @@ -840,13 +839,13 @@ pos_number: INTEGER { float value = static_cast($1); if (value < 0) - reader->warn(1526, "%.4f is not positive.", value); + reader->warn(1526, "{:.4f} is not positive.", value); $$ = value; } | FLOAT { float value = static_cast($1); if (value < 0) - reader->warn(1527, "%.4f is not positive.", value); + reader->warn(1527, "{:.4f} is not positive.", value); $$ = value; } ; diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 99c53633..2bc516ae 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "SpefReader.hh" @@ -55,9 +55,8 @@ readSpefFile(const std::string &filename, Parasitics *parasitics, StaState *sta) { - SpefReader reader(filename, instance, pin_cap_included, - keep_coupling_caps, coupling_cap_factor, - reduce, scene, min_max, parasitics, sta); + SpefReader reader(filename, instance, pin_cap_included, keep_coupling_caps, + coupling_cap_factor, reduce, scene, min_max, parasitics, sta); bool success = reader.read(); return success; } @@ -98,9 +97,7 @@ SpefReader::SpefReader(const std::string &filename, parasitics->setCouplingCapFactor(coupling_cap_factor); } -SpefReader::~SpefReader() -{ -} +SpefReader::~SpefReader() {} bool SpefReader::read() @@ -112,13 +109,13 @@ SpefReader::read() SpefScanner scanner(&stream, filename_, this, report_); scanner_ = &scanner; SpefParse parser(&scanner, this); - //parser.set_debug_level(1); - // yyparse returns 0 on success. + // parser.set_debug_level(1); + // yyparse returns 0 on success. success = (parser.parse() == 0); stats.report("Read spef"); } else - throw FileNotReadable(filename_.c_str()); + throw FileNotReadable(filename_); return success; } @@ -138,12 +135,9 @@ void SpefReader::setBusBrackets(char left, char right) { - if (!((left == '[' && right == ']') - || (left == '{' && right == '}') - || (left == '(' && right == ')') - || (left == '<' && right == '>') - || (left == ':' && right == '\0') - || (left == '.' && right == '\0'))) + if (!((left == '[' && right == ']') || (left == '{' && right == '}') + || (left == '(' && right == ')') || (left == '<' && right == '>') + || (left == ':' && right == '\0') || (left == '.' && right == '\0'))) warn(1640, "illegal bus delimiters."); bus_brkt_left_ = left; bus_brkt_right_ = right; @@ -181,19 +175,16 @@ SpefReader::findPortPinRelative(const char *name) char * SpefReader::translated(const char *token) { - return spefToSta(token, divider_, network_->pathDivider(), - network_->pathEscape()); + return spefToSta(token, divider_, network_->pathDivider(), network_->pathEscape()); } -void -SpefReader::warn(int id, const char *fmt, ...) +int +SpefReader::warnLine() const { - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_.c_str(), scanner_->line(), fmt, args); - va_end(args); + return scanner_->line(); } + void SpefReader::setTimeScale(float scale, const char *units) @@ -203,7 +194,7 @@ SpefReader::setTimeScale(float scale, else if (stringEq(units, "PS")) time_scale_ = scale * 1E-12F; else - warn(1641, "unknown units %s.", units); + warn(1641, "unknown units {}.", units); stringDelete(units); } @@ -216,7 +207,7 @@ SpefReader::setCapScale(float scale, else if (stringEq(units, "FF")) cap_scale_ = scale * 1E-15F; else - warn(1642, "unknown units %s.", units); + warn(1642, "unknown units {}.", units); stringDelete(units); } @@ -229,7 +220,7 @@ SpefReader::setResScale(float scale, else if (stringEq(units, "KOHM")) res_scale_ = scale * 1E+3F; else - warn(1643, "unknown units %s.", units); + warn(1643, "unknown units {}.", units); stringDelete(units); } @@ -244,7 +235,7 @@ SpefReader::setInductScale(float scale, else if (stringEq(units, "UH")) induct_scale_ = scale * 1E-6F; else - warn(1644, "unknown units %s.", units); + warn(1644, "unknown units {}.", units); stringDelete(units); } @@ -267,7 +258,7 @@ SpefReader::nameMapLookup(const char *name) if (itr != name_map_.end()) return itr->second.c_str(); else { - warn(1645, "no name map entry for %d.", index); + warn(1645, "no name map entry for {}.", index); return nullptr; } } @@ -286,7 +277,7 @@ SpefReader::portDirection(char *spef_dir) else if (stringEq(spef_dir, "B")) direction = PortDirection::bidirect(); else - warn(1646, "unknown port direction %s.", spef_dir); + warn(1646, "unknown port direction {}.", spef_dir); return direction; } @@ -314,16 +305,16 @@ SpefReader::findPin(char *name) if (inst) { pin = network_->findPin(inst, port_name); if (pin == nullptr) - warn(1647, "pin %s not found.", name1); + warn(1647, "pin {} not found.", name1); } else - warn(1648, "instance %s not found.", name1); + warn(1648, "instance {} not found.", name1); } } else { pin = findPortPinRelative(name); if (pin == nullptr) - warn(1649, "pin %s not found.", name); + warn(1649, "pin {} not found.", name); } } return pin; @@ -337,7 +328,7 @@ SpefReader::findNet(const char *name) if (name1) { net = findNetRelative(name1); if (net == nullptr) - warn(1650, "net %s not found.", name1); + warn(1650, "net {} not found.", name1); } return net; } @@ -366,9 +357,7 @@ SpefReader::rspfDrvrBegin(Pin *drvr_pin, float rpi = pi->r1()->value(triple_index_) * res_scale_; float c1 = pi->c1()->value(triple_index_) * cap_scale_; // Only one parasitic, save it under rise transition. - parasitic_ = parasitics_->makePiElmore(drvr_pin, - RiseFall::rise(), - MinMax::max(), + parasitic_ = parasitics_->makePiElmore(drvr_pin, RiseFall::rise(), MinMax::max(), c2, rpi, c1); } delete pi; @@ -412,8 +401,8 @@ SpefReader::dspfBegin(Net *net, delete term_iter; parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner); if (parasitic_ == nullptr) - parasitic_ = parasitics_->makeParasiticNetwork(parasitic_owner, - pin_cap_included_); + parasitic_ = + parasitics_->makeParasiticNetwork(parasitic_owner, pin_cap_included_); } net_ = net; } @@ -451,16 +440,15 @@ SpefReader::findParasiticNode(char *name, // : Pin *pin = network_->findPin(inst, name2); if (pin) { - if (local_only - && !network_->isConnected(net_, pin)) - warn(1651, "%s not connected to net %s.", - name1, sdc_network_->pathName(net_)); + if (local_only && !network_->isConnected(net_, pin)) + warn(1651, "{} not connected to net {}.", name1, + sdc_network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } else { // Replace delimiter for error message. *delim = delimiter_; - warn(1652, "pin %s not found.", name1); + warn(1652, "pin {} not found.", name1); } } else { @@ -472,15 +460,13 @@ SpefReader::findParasiticNode(char *name, const char *id_str = delim + 1; if (isDigits(id_str)) { int id = atoi(id_str); - if (local_only - && !network_->isConnected(net, net_)) - warn(1653, "%s not connected to net %s.", - name1, + if (local_only && !network_->isConnected(net, net_)) + warn(1653, "{} not connected to net {}.", name1, network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, net, id, network_); } else - warn(1654, "node %s not a pin or net:number", name1); + warn(1654, "node {} not a pin or net:number", name1); } } } @@ -491,23 +477,24 @@ SpefReader::findParasiticNode(char *name, if (name1) { Pin *pin = findPortPinRelative(name1); if (pin) { - if (local_only - && !network_->isConnected(net_, pin)) - warn(1655, "%s not connected to net %s.", name1, network_->pathName(net_)); + if (local_only && !network_->isConnected(net_, pin)) + warn(1655, "{} not connected to net {}.", name1, + network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } else - warn(1656, "pin %s not found.", name1); + warn(1656, "pin {} not found.", name1); } else - warn(1657, "pin %s not found.", name); + warn(1657, "pin {} not found.", name); } } return nullptr; } void -SpefReader::makeCapacitor(int, char *node_name, +SpefReader::makeCapacitor(int, + char *node_name, SpefTriple *cap) { ParasiticNode *node = findParasiticNode(node_name, true); @@ -622,7 +609,7 @@ SpefScanner::SpefScanner(std::istream *stream, void SpefScanner::error(const char *msg) { - report_->fileError(1867, filename_.c_str(), lineno(), "%s", msg); + report_->fileError(1658, filename_.c_str(), lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index cf58eb51..7b01fda6 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include "Zlib.hh" #include "StringUtil.hh" @@ -65,10 +66,6 @@ public: const std::string &filename() const { return filename_; } // Translate from spf/spef namespace to sta namespace. char *translated(const char *token); - void warn(int id, - const char *fmt, - ...) - __attribute__((format (printf, 3, 4))); void setBusBrackets(char left, char right); void setTimeScale(float scale, @@ -108,6 +105,15 @@ public: char *node_name2, SpefTriple *res); PortDirection *portDirection(char *spef_dir); + int warnLine() const; + template + void warn(int id, + std::string_view fmt, + Args &&...args) + { + report_->fileWarn(id, filename_, warnLine(), fmt, + std::forward(args)...); + } private: Pin *findPinRelative(const char *name); diff --git a/power/Power.cc b/power/Power.cc index 7a3de2af..d4dd239c 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1,31 +1,31 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Power.hh" -#include // max -#include // abs +#include // max +#include // abs #include "cudd.h" #include "ContainerHelpers.hh" @@ -74,26 +74,28 @@ namespace sta { static bool isPositiveUnate(const LibertyCell *cell, - const LibertyPort *from, - const LibertyPort *to); + const LibertyPort *from, + const LibertyPort *to); -static EnumNameMap pwr_activity_origin_map = - {{PwrActivityOrigin::global, "global"}, - {PwrActivityOrigin::input, "input"}, - {PwrActivityOrigin::user, "user"}, - {PwrActivityOrigin::vcd, "vcd"}, - {PwrActivityOrigin::saif, "saif"}, - {PwrActivityOrigin::propagated, "propagated"}, - {PwrActivityOrigin::clock, "clock"}, - {PwrActivityOrigin::constant, "constant"}, - {PwrActivityOrigin::unknown, "unknown"}}; +static EnumNameMap pwr_activity_origin_map = { + {PwrActivityOrigin::global, "global"}, + {PwrActivityOrigin::input, "input"}, + {PwrActivityOrigin::user, "user"}, + {PwrActivityOrigin::vcd, "vcd"}, + {PwrActivityOrigin::saif, "saif"}, + {PwrActivityOrigin::propagated, "propagated"}, + {PwrActivityOrigin::clock, "clock"}, + {PwrActivityOrigin::constant, "constant"}, + {PwrActivityOrigin::unknown, "unknown"}}; Power::Power(StaState *sta) : StaState(sta), scene_(nullptr), global_activity_(), - input_activity_(), // default set in ensureActivities. - seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()), + input_activity_(), // default set in ensureActivities. + seq_activity_map_(100, + SeqPinHash(network_), + SeqPinEqual()), activities_valid_(false), bdd_(sta), instance_powers_(InstanceIdLess(network_)), @@ -123,12 +125,12 @@ Power::activitiesInvalid() void Power::setGlobalActivity(float density, - float duty) + float duty) { global_activity_.set(density, duty, PwrActivityOrigin::global); activitiesInvalid(); } - + void Power::unsetGlobalActivity() { @@ -138,7 +140,7 @@ Power::unsetGlobalActivity() void Power::setInputActivity(float density, - float duty) + float duty) { input_activity_.set(density, duty, PwrActivityOrigin::input); activitiesInvalid(); @@ -153,8 +155,8 @@ Power::unsetInputActivity() void Power::setInputPortActivity(const Port *input_port, - float density, - float duty) + float density, + float duty) { Instance *top_inst = network_->topInstance(); const Pin *pin = network_->findPin(top_inst, input_port); @@ -206,12 +208,10 @@ Power::hasUserActivity(const Pin *pin) void Power::setActivity(const Pin *pin, - PwrActivity &activity) + PwrActivity &activity) { - debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f %s", - network_->pathName(pin), - activity.density(), - activity.duty(), + debugPrint(debug_, "power_activity", 3, "set {} {:.2e} {:.2f} {}", + network_->pathName(pin), activity.density(), activity.duty(), pwr_activity_origin_map.find(activity.origin())); activity_map_[pin] = activity; } @@ -232,8 +232,8 @@ Power::hasActivity(const Pin *pin) // activities are stored by instance/liberty_port pairs. void Power::setSeqActivity(const Instance *reg, - LibertyPort *output, - PwrActivity &activity) + LibertyPort *output, + PwrActivity &activity) { seq_activity_map_[SeqPin(reg, output)] = activity; activitiesInvalid(); @@ -241,14 +241,14 @@ Power::setSeqActivity(const Instance *reg, bool Power::hasSeqActivity(const Instance *reg, - LibertyPort *output) + LibertyPort *output) { return seq_activity_map_.contains(SeqPin(reg, output)); } PwrActivity & Power::seqActivity(const Instance *reg, - LibertyPort *output) + LibertyPort *output) { return seq_activity_map_[SeqPin(reg, output)]; } @@ -261,18 +261,17 @@ SeqPinHash::SeqPinHash(const Network *network) : size_t SeqPinHash::operator()(const SeqPin &pin) const { - const auto& [inst, port] = pin; + const auto &[inst, port] = pin; return hashSum(network_->id(inst), port->id()); } bool SeqPinEqual::operator()(const SeqPin &pin1, - const SeqPin &pin2) const + const SeqPin &pin2) const { - const auto& [inst1, port1] = pin1; - const auto& [inst2, port2] = pin2; - return inst1 == inst2 - && port1 == port2; + const auto &[inst1, port1] = pin1; + const auto &[inst2, port2] = pin2; + return inst1 == inst2 && port1 == port2; } //////////////////////////////////////////////////////////////// @@ -284,7 +283,8 @@ Power::reportDesign(const Scene *scene, PowerResult total, sequential, combinational, clock, macro, pad; power(scene, total, sequential, combinational, clock, macro, pad); ReportPower report_power(this); - report_power.reportDesign(total, sequential, combinational, clock, macro, pad, digits); + report_power.reportDesign(total, sequential, combinational, clock, macro, pad, + digits); } void @@ -313,15 +313,15 @@ Power::reportDesignJson(const Scene *scene, { PowerResult total, sequential, combinational, clock, macro, pad; power(scene, total, sequential, combinational, clock, macro, pad); - - report_->reportLine("{"); + + report_->report("{{"); reportPowerRowJson("Sequential", sequential, digits, ","); reportPowerRowJson("Combinational", combinational, digits, ","); reportPowerRowJson("Clock", clock, digits, ","); reportPowerRowJson("Macro", macro, digits, ","); reportPowerRowJson("Pad", pad, digits, ","); reportPowerRowJson("Total", total, digits, ""); - report_->reportLine("}"); + report_->report("}}"); } void @@ -330,17 +330,17 @@ Power::reportInstsJson(const InstanceSeq &insts, int digits) { InstPowers inst_pwrs = sortInstsByPower(insts, scene); - - report_->reportLine("["); + + report_->report("["); bool first = true; for (const InstPower &inst_pwr : inst_pwrs) { if (!first) { - report_->reportLine(","); + report_->report(","); } first = false; reportPowerInstJson(inst_pwr.first, inst_pwr.second, digits); } - report_->reportLine("]"); + report_->report("]"); } void @@ -353,16 +353,16 @@ Power::reportPowerRowJson(const char *name, float switching = power.switching(); float leakage = power.leakage(); float total = power.total(); - - report_->reportLine(" \"%s\": {", name); - report_->reportLine(" \"internal\": %.*e,", digits, internal); - report_->reportLine(" \"switching\": %.*e,", digits, switching); - report_->reportLine(" \"leakage\": %.*e,", digits, leakage); - report_->reportLine(" \"total\": %.*e", digits, total); + + report_->report(" \"{}\": {{", name); + report_->report(" \"internal\": {:.{}e},", internal, digits); + report_->report(" \"switching\": {:.{}e},", switching, digits); + report_->report(" \"leakage\": {:.{}e},", leakage, digits); + report_->report(" \"total\": {:.{}e}", total, digits); std::string line = " }"; if (separator && separator[0] != '\0') line += separator; - report_->reportLineString(line); + report_->reportLine(line); } void @@ -374,15 +374,15 @@ Power::reportPowerInstJson(const Instance *inst, float switching = power.switching(); float leakage = power.leakage(); float total = power.total(); - + const char *inst_name = network_->pathName(inst); - report_->reportLine("{"); - report_->reportLine(" \"name\": \"%s\",", inst_name); - report_->reportLine(" \"internal\": %.*e,", digits, internal); - report_->reportLine(" \"switching\": %.*e,", digits, switching); - report_->reportLine(" \"leakage\": %.*e,", digits, leakage); - report_->reportLine(" \"total\": %.*e", digits, total); - report_->reportLine("}"); + report_->report("{{"); + report_->report(" \"name\": \"{}\",", inst_name); + report_->report(" \"internal\": {:.{}e},", internal, digits); + report_->report(" \"switching\": {:.{}e},", switching, digits); + report_->report(" \"leakage\": {:.{}e},", leakage, digits); + report_->report(" \"total\": {:.{}e}", total, digits); + report_->report("}}"); } static bool @@ -402,7 +402,7 @@ Power::sortInstsByPower(const InstanceSeq &insts, PowerResult inst_power = power(inst, scene); inst_pwrs.push_back(std::make_pair(inst, inst_power)); } - + // Sort by total power (descending) sort(inst_pwrs, instPowerGreater); return inst_pwrs; @@ -412,13 +412,13 @@ Power::sortInstsByPower(const InstanceSeq &insts, void Power::power(const Scene *scene, - // Return values. - PowerResult &total, - PowerResult &sequential, - PowerResult &combinational, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, PowerResult &clock, - PowerResult ¯o, - PowerResult &pad) + PowerResult ¯o, + PowerResult &pad) { total.clear(); sequential.clear(); @@ -433,18 +433,16 @@ Power::power(const Scene *scene, for (auto [inst, inst_power] : instance_powers_) { LibertyCell *cell = network_->libertyCell(inst); if (cell) { - if (cell->isMacro() - || cell->isMemory() - || cell->interfaceTiming()) - macro.incr(inst_power); + if (cell->isMacro() || cell->isMemory() || cell->interfaceTiming()) + macro.incr(inst_power); else if (cell->isPad()) - pad.incr(inst_power); + pad.incr(inst_power); else if (inClockNetwork(inst, clk_network)) - clock.incr(inst_power); + clock.incr(inst_power); else if (cell->hasSequentials()) - sequential.incr(inst_power); + sequential.incr(inst_power); else - combinational.incr(inst_power); + combinational.incr(inst_power); total.incr(inst_power); } } @@ -457,8 +455,7 @@ Power::inClockNetwork(const Instance *inst, InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyOutput() - && !clk_network->isClock(pin)) { + if (network_->direction(pin)->isAnyOutput() && !clk_network->isClock(pin)) { delete pin_iter; return false; } @@ -553,12 +550,9 @@ ActivitySrchPred::searchThru(Edge *edge, { const Sdc *sdc = mode->sdc(); const TimingRole *role = edge->role(); - return !(edge->role()->isTimingCheck() - || sdc->isDisabledConstraint(edge) - || sdc->isDisabledCondDefault(edge) - || edge->isBidirectInstPath() - || edge->isDisabledLoop() - || role == TimingRole::regClkToQ() + return !(edge->role()->isTimingCheck() || sdc->isDisabledConstraint(edge) + || sdc->isDisabledCondDefault(edge) || edge->isBidirectInstPath() + || edge->isDisabledLoop() || role == TimingRole::regClkToQ() || role->isLatchDtoQ()); } @@ -576,7 +570,7 @@ class PropActivityVisitor : public VertexVisitor, StaState public: PropActivityVisitor(Power *power, const Mode *mode, - BfsFwdIterator *bfs); + BfsFwdIterator *bfs); virtual VertexVisitor *copy() const; virtual void visit(Vertex *vertex); InstanceSet &visitedRegs() { return visited_regs_; } @@ -599,7 +593,7 @@ private: PropActivityVisitor::PropActivityVisitor(Power *power, const Mode *mode, - BfsFwdIterator *bfs) : + BfsFwdIterator *bfs) : StaState(power), visited_regs_(network_), max_change_(0.0), @@ -628,8 +622,8 @@ PropActivityVisitor::visit(Vertex *vertex) { Pin *pin = vertex->pin(); Instance *inst = network_->instance(pin); - debugPrint(debug_, "power_activity", 3, "visit %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "power_activity", 3, "visit {}", + vertex->to_string(this)); bool changed = false; if (power_->hasUserActivity(pin)) { PwrActivity &activity = power_->userActivity(pin); @@ -639,22 +633,21 @@ PropActivityVisitor::visit(Vertex *vertex) if (network_->isLoad(pin)) { VertexInEdgeIterator edge_iter(vertex, graph_); if (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->isWire()) { - Vertex *from_vertex = edge->from(graph_); + Edge *edge = edge_iter.next(); + if (edge->isWire()) { + Vertex *from_vertex = edge->from(graph_); const Pin *from_pin = from_vertex->pin(); - PwrActivity &from_activity = power_->activity(from_pin); - PwrActivity to_activity(from_activity.density(), - from_activity.duty(), - PwrActivityOrigin::propagated); - changed = setActivityCheck(pin, to_activity); - } + PwrActivity &from_activity = power_->activity(from_pin); + PwrActivity to_activity(from_activity.density(), from_activity.duty(), + PwrActivityOrigin::propagated); + changed = setActivityCheck(pin, to_activity); + } } } if (network_->isDriver(pin)) { LibertyPort *port = network_->libertyPort(pin); if (port) { - FuncExpr *func = port->function(); + FuncExpr *func = port->function(); if (func == nullptr) { LibertyCell *test_cell = port->libertyCell()->testCell(); if (test_cell) { @@ -663,10 +656,10 @@ PropActivityVisitor::visit(Vertex *vertex) func = port->function(); } } - if (func) { + if (func) { PwrActivity activity = power_->evalActivity(func, inst); - changed = setActivityCheck(pin, activity); - } + changed = setActivityCheck(pin, activity); + } if (port->isClockGateOut()) { const Pin *enable, *clk, *gclk; power_->clockGatePins(inst, enable, clk, gclk); @@ -676,12 +669,10 @@ PropActivityVisitor::visit(Vertex *vertex) float p1 = activity1.duty(); float p2 = activity2.duty(); PwrActivity activity(activity1.density() * p2 + activity2.density() * p1, - p1 * p2, - PwrActivityOrigin::propagated); + p1 * p2, PwrActivityOrigin::propagated); changed = setActivityCheck(gclk, activity); - debugPrint(debug_, "power_activity", 3, "gated_clk %s %.2e %.2f", - network_->pathName(gclk), - activity.density(), + debugPrint(debug_, "power_activity", 3, "gated_clk {} {:.2e} {:.2f}", + network_->pathName(gclk), activity.density(), activity.duty()); } } @@ -693,22 +684,20 @@ PropActivityVisitor::visit(Vertex *vertex) if (cell) { LibertyCell *test_cell = cell->libertyCell()->testCell(); if (network_->isLoad(pin)) { - if (cell->hasSequentials() - || (test_cell - && test_cell->hasSequentials())) { - debugPrint(debug_, "power_activity", 3, "pending seq %s", - network_->pathName(inst)); - visited_regs_.insert(inst); - } - // Gated clock cells latch the enable so there is no EN->GCLK timing arc. - if (cell->isClockGate()) { - const Pin *enable, *clk, *gclk; - power_->clockGatePins(inst, enable, clk, gclk); - if (gclk) { - Vertex *gclk_vertex = graph_->pinDrvrVertex(gclk); - bfs_->enqueue(gclk_vertex); - } - } + if (cell->hasSequentials() || (test_cell && test_cell->hasSequentials())) { + debugPrint(debug_, "power_activity", 3, "pending seq {}", + network_->pathName(inst)); + visited_regs_.insert(inst); + } + // Gated clock cells latch the enable so there is no EN->GCLK timing arc. + if (cell->isClockGate()) { + const Pin *enable, *clk, *gclk; + power_->clockGatePins(inst, enable, clk, gclk); + if (gclk) { + Vertex *gclk_vertex = graph_->pinDrvrVertex(gclk); + bfs_->enqueue(gclk_vertex); + } + } } bfs_->enqueueAdjacentVertices(vertex, mode_); } @@ -739,8 +728,7 @@ PropActivityVisitor::setActivityCheck(const Pin *pin, if (activity.density() > max_density) activity.setDensity(max_density); PwrActivity &prev_activity = power_->activity(pin); - float density_delta = percentChange(activity.density(), - prev_activity.density()); + float density_delta = percentChange(activity.density(), prev_activity.density()); float duty_delta = percentChange(activity.duty(), prev_activity.duty()); if (density_delta > max_change_) { max_change_ = density_delta; @@ -750,9 +738,9 @@ PropActivityVisitor::setActivityCheck(const Pin *pin, max_change_ = duty_delta; max_change_pin_ = pin; } - bool changed = density_delta > change_tolerance_ - || duty_delta > change_tolerance_ - || activity.origin() != prev_activity.origin();; + bool changed = density_delta > change_tolerance_ || duty_delta > change_tolerance_ + || activity.origin() != prev_activity.origin(); + ; power_->setActivity(pin, activity); return changed; } @@ -787,10 +775,10 @@ Power::clockGatePins(const Instance *inst, PwrActivity Power::evalActivity(FuncExpr *expr, - const Instance *inst) + const Instance *inst) { LibertyPort *func_port = expr->port(); - if (func_port && func_port->direction()->isInternal()) + if (func_port && func_port->direction()->isInternal()) return findSeqActivity(inst, func_port); else { DdNode *bdd = bdd_.funcBdd(expr); @@ -843,7 +831,7 @@ Power::evalBddDuty(DdNode *bdd, int var_index = Cudd_ReadPerm(bdd_.cuddMgr(), index); const LibertyPort *port = bdd_.varIndexPort(var_index); if (port->direction()->isInternal()) - return findSeqActivity(inst, const_cast(port)).duty(); + return findSeqActivity(inst, const_cast(port)).duty(); else { const Pin *pin = findLinkPin(inst, port); if (pin) { @@ -878,10 +866,8 @@ Power::evalBddActivity(DdNode *bdd, Cudd_RecursiveDeref(bdd_.cuddMgr(), diff); float var_density = var_activity.density() * diff_duty; density += var_density; - debugPrint(debug_, "power_activity", 3, "%s %.3e * %.3f = %.3e", - network_->pathName(pin), - var_activity.density(), - diff_duty, + debugPrint(debug_, "power_activity", 3, "{} {:.3e} * {:.3f} = {:.3e}", + network_->pathName(pin), var_activity.density(), diff_duty, var_density); } } @@ -911,9 +897,8 @@ Power::ensureActivities(const Scene *scene) // unless it has been set by command. if (input_activity_.origin() == PwrActivityOrigin::unknown) { float min_period = clockMinPeriod(scene_->mode()->sdc()); - float density = 0.1 / (min_period != 0.0 - ? min_period - : units_->timeUnit()->scale()); + float density = + 0.1 / (min_period != 0.0 ? min_period : units_->timeUnit()->scale()); input_activity_.set(density, 0.5, PwrActivityOrigin::input); } ActivitySrchPred activity_srch_pred(this); @@ -927,17 +912,15 @@ Power::ensureActivities(const Scene *scene) int pass = 1; while (!regs.empty() && pass < max_activity_passes_) { visitor.init(); - for (const Instance *reg : regs) - // Propagate activiities across register D->Q. - seedRegOutputActivities(reg, bfs); - // Propagate register output activities through - // combinational logic. - bfs.visit(levelize_->maxLevel(), &visitor); + for (const Instance *reg : regs) + // Propagate activiities across register D->Q. + seedRegOutputActivities(reg, bfs); + // Propagate register output activities through + // combinational logic. + bfs.visit(levelize_->maxLevel(), &visitor); regs = std::move(visitor.visitedRegs()); - debugPrint(debug_, "power_activity", 1, "Pass %d change %.2f %s", - pass, - visitor.maxChange(), - network_->pathName(visitor.maxChangePin())); + debugPrint(debug_, "power_activity", 1, "Pass {} change {:.2f} {}", pass, + visitor.maxChange(), network_->pathName(visitor.maxChangePin())); pass++; } } @@ -953,14 +936,14 @@ Power::seedActivities(BfsFwdIterator &bfs) const Pin *pin = vertex->pin(); // Clock activities are baked in. if (!scene_->mode()->sdc()->isLeafPinClock(pin) - && !network_->direction(pin)->isInternal()) { - debugPrint(debug_, "power_activity", 3, "seed %s", - vertex->to_string(this).c_str()); + && !network_->direction(pin)->isInternal()) { + debugPrint(debug_, "power_activity", 3, "seed {}", + vertex->to_string(this)); if (hasUserActivity(pin)) - setActivity(pin, userActivity(pin)); + setActivity(pin, userActivity(pin)); else - // Default inputs without explicit activities to the input default. - setActivity(pin, input_activity_); + // Default inputs without explicit activities to the input default. + setActivity(pin, input_activity_); Vertex *vertex = graph_->pinDrvrVertex(pin); bfs.enqueueAdjacentVertices(vertex, scene_->mode()); } @@ -969,7 +952,7 @@ Power::seedActivities(BfsFwdIterator &bfs) void Power::seedRegOutputActivities(const Instance *inst, - BfsFwdIterator &bfs) + BfsFwdIterator &bfs) { LibertyCell *cell = network_->libertyCell(inst); const SequentialSeq &seqs = cell->sequentials(); @@ -1004,12 +987,10 @@ Power::seedRegOutputActivities(const Instance *inst, if (port) { FuncExpr *func = port->function(); Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex - && func - && (func->port() == seq.output() - || func->port() == seq.outputInv())) { - debugPrint(debug_, "power_reg", 1, "enqueue reg output %s", - vertex->to_string(this).c_str()); + if (vertex && func + && (func->port() == seq.output() || func->port() == seq.outputInv())) { + debugPrint(debug_, "power_reg", 1, "enqueue reg output {}", + vertex->to_string(this)); bfs.enqueue(vertex); } } @@ -1020,9 +1001,9 @@ Power::seedRegOutputActivities(const Instance *inst, void Power::seedRegOutputActivities(const Instance *reg, - const Sequential &seq, - LibertyPort *output, - bool invert) + const Sequential &seq, + LibertyPort *output, + bool invert) { const Pin *out_pin = network_->findPin(reg, output); if (!hasUserActivity(out_pin)) { @@ -1041,9 +1022,8 @@ Power::seedRegOutputActivities(const Instance *reg, PwrActivity clk_activity = evalActivity(seq.clock(), reg); float clk_duty = clk_activity.duty(); FuncExpr *clk_func = seq.clock(); - bool clk_invert = clk_func - && clk_func->op() == FuncExpr::Op::not_ - && clk_func->left()->op() == FuncExpr::Op::port; + bool clk_invert = clk_func && clk_func->op() == FuncExpr::Op::not_ + && clk_func->left()->op() == FuncExpr::Op::port; if (clk_invert) out_density = in_density * (1 - clk_duty); else @@ -1087,10 +1067,10 @@ Power::findInstPowers() PowerResult Power::power(const Instance *inst, - LibertyCell *cell, + LibertyCell *cell, const Scene *scene) { - debugPrint(debug_, "power", 2, "find power %s", sdc_network_->pathName(inst)); + debugPrint(debug_, "power", 2, "find power {}", sdc_network_->pathName(inst)); PowerResult result; findInternalPower(inst, cell, scene, result); findSwitchingPower(inst, cell, scene, result); @@ -1128,15 +1108,15 @@ Power::findInternalPower(const Instance *inst, LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { float load_cap = to_port->direction()->isAnyOutput() - ? graph_delay_calc_->loadCap(to_pin, scene, MinMax::max()) - : 0.0; + ? graph_delay_calc_->loadCap(to_pin, scene, MinMax::max()) + : 0.0; PwrActivity activity = findActivity(to_pin); if (to_port->direction()->isAnyOutput()) - findOutputInternalPower(to_port, inst, cell, activity, - load_cap, scene, result); + findOutputInternalPower(to_port, inst, cell, activity, load_cap, scene, + result); if (to_port->direction()->isAnyInput()) - findInputInternalPower(to_pin, to_port, inst, cell, activity, - load_cap, scene, result); + findInputInternalPower(to_pin, to_port, inst, cell, activity, load_cap, + scene, result); } } delete pin_iter; @@ -1144,24 +1124,24 @@ Power::findInternalPower(const Instance *inst, void Power::findInputInternalPower(const Pin *pin, - LibertyPort *port, - const Instance *inst, - LibertyCell *cell, - PwrActivity &activity, - float load_cap, + LibertyPort *port, + const Instance *inst, + LibertyCell *cell, + PwrActivity &activity, + float load_cap, const Scene *scene, - // Return values. - PowerResult &result) + // Return values. + PowerResult &result) { const MinMax *min_max = MinMax::max(); LibertyCell *scene_cell = cell->sceneCell(scene, min_max); const LibertyPort *scene_port = port->scenePort(scene, min_max); if (scene_cell && scene_port) { - const InternalPowerPtrSeq &internal_pwrs = scene_cell->internalPowers(scene_port); + const InternalPowerPtrSeq &internal_pwrs = + scene_cell->internalPowers(scene_port); if (!internal_pwrs.empty()) { - debugPrint(debug_, "power", 2, "internal input %s/%s cap %s", - network_->pathName(inst), - port->name(), + debugPrint(debug_, "power", 2, "internal input {}/{} cap {}", + network_->pathName(inst), port->name(), units_->capacitanceUnit()->asString(load_cap)); debugPrint(debug_, "power", 2, " when act/ns duty energy power"); const Pvt *pvt = scene->sdc()->operatingConditions(MinMax::max()); @@ -1180,8 +1160,8 @@ Power::findInputInternalPower(const Pin *pin, } } if (rf_count) - energy /= rf_count; // average non-inf energies - float duty = 1.0; // fallback default + energy /= rf_count; // average non-inf energies + float duty = 1.0; // fallback default FuncExpr *when = pwr->when(); if (when) { const LibertyPort *out_scene_port = findExprOutPort(when); @@ -1199,13 +1179,9 @@ Power::findInputInternalPower(const Pin *pin, duty = evalActivity(when, inst).duty(); } float port_internal = energy * duty * activity.density(); - debugPrint(debug_, "power", 2, " %3s %6s %.2f %.2f %9.2e %9.2e %s", - port->name(), - when ? when->to_string().c_str() : "", - activity.density() * 1e-9, - duty, - energy, - port_internal, + debugPrint(debug_, "power", 2, " {} {} {:.2f} {:.2f} {:9.2e} {:9.2e} {}", + port->name(), when ? when->to_string() : "", + activity.density() * 1e-9, duty, energy, port_internal, related_pg_pin ? related_pg_pin->name() : "no pg_pin"); internal += port_internal; } @@ -1219,7 +1195,6 @@ Power::getSlew(Vertex *vertex, const RiseFall *rf, const Scene *scene) { - const MinMax *min_max = MinMax::max(); const Pin *pin = vertex->pin(); const ClkNetwork *clk_network = scene->mode()->clkNetwork(); @@ -1259,46 +1234,45 @@ Power::findExprOutPort(FuncExpr *expr) { LibertyPort *port; switch (expr->op()) { - case FuncExpr::Op::port: - port = expr->port(); - if (port && port->direction()->isAnyOutput()) - return expr->port(); - return nullptr; - case FuncExpr::Op::not_: - port = findExprOutPort(expr->left()); - if (port) - return port; - return nullptr; - case FuncExpr::Op::or_: - case FuncExpr::Op::and_: - case FuncExpr::Op::xor_: - port = findExprOutPort(expr->left()); - if (port) - return port; - port = findExprOutPort(expr->right()); - if (port) - return port; - return nullptr; - case FuncExpr::Op::one: - case FuncExpr::Op::zero: - return nullptr; + case FuncExpr::Op::port: + port = expr->port(); + if (port && port->direction()->isAnyOutput()) + return expr->port(); + return nullptr; + case FuncExpr::Op::not_: + port = findExprOutPort(expr->left()); + if (port) + return port; + return nullptr; + case FuncExpr::Op::or_: + case FuncExpr::Op::and_: + case FuncExpr::Op::xor_: + port = findExprOutPort(expr->left()); + if (port) + return port; + port = findExprOutPort(expr->right()); + if (port) + return port; + return nullptr; + case FuncExpr::Op::one: + case FuncExpr::Op::zero: + return nullptr; } return nullptr; } void Power::findOutputInternalPower(const LibertyPort *to_port, - const Instance *inst, - LibertyCell *cell, - PwrActivity &to_activity, - float load_cap, + const Instance *inst, + LibertyCell *cell, + PwrActivity &to_activity, + float load_cap, const Scene *scene, - // Return values. - PowerResult &result) + // Return values. + PowerResult &result) { - debugPrint(debug_, "power", 2, "internal output %s/%s cap %s", - network_->pathName(inst), - to_port->name(), + debugPrint(debug_, "power", 2, "internal output {}/{} cap {}", + network_->pathName(inst), to_port->name(), units_->capacitanceUnit()->asString(load_cap)); const MinMax *min_max = MinMax::max(); const Pvt *pvt = scene->sdc()->operatingConditions(min_max); @@ -1306,7 +1280,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, const LibertyPort *to_scene_port = to_port->scenePort(scene, min_max); FuncExpr *func = to_port->function(); - std::map pg_duty_sum; + std::map pg_duty_sum; for (const InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { const LibertyPort *from_scene_port = pwr->relatedPort(); if (from_scene_port) { @@ -1334,7 +1308,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, positive_unate = isPositiveUnate(scene_cell, from_scene_port, to_scene_port); from_pin = findLinkPin(inst, from_scene_port); if (from_pin) - from_vertex = graph_->pinLoadVertex(from_pin); + from_vertex = graph_->pinLoadVertex(from_pin); } float energy = 0.0; int rf_count = 0; @@ -1351,26 +1325,21 @@ Power::findOutputInternalPower(const LibertyPort *to_port, } } if (rf_count) - energy /= rf_count; // average non-inf energies + energy /= rf_count; // average non-inf energies auto duty_sum_iter = pg_duty_sum.find(related_pg_pin); float weight = 0.0; if (duty_sum_iter != pg_duty_sum.end()) { float duty_sum = duty_sum_iter->second; if (duty_sum != 0.0 && from_pin) { float from_density = findActivity(from_pin).density(); - weight = from_density * duty / duty_sum; + weight = from_density * duty / duty_sum; } } float port_internal = weight * energy * to_activity.density(); - debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.3f %.3f %.3f %9.2e %9.2e %s", - from_scene_port ? from_scene_port->name() : "-" , - to_port->name(), - when ? when->to_string().c_str() : "", - to_activity.density() * 1e-9, - duty, - weight, - energy, - port_internal, + debugPrint(debug_, "power", 2, "{} -> {} {} {:.3f} {:.3f} {:.3f} {:9.2e} {:9.2e} {}", + from_scene_port ? from_scene_port->name() : "-", to_port->name(), + when ? when->to_string() : "", to_activity.density() * 1e-9, + duty, weight, energy, port_internal, related_pg_pin ? related_pg_pin->name() : "no pg_pin"); internal += port_internal; } @@ -1384,20 +1353,20 @@ Power::findInputDuty(const Instance *inst, { const LibertyPort *from_scene_port = pwr->relatedPort(); if (from_scene_port) { - LibertyPort *from_port = findLinkPort(network_->libertyCell(inst), - from_scene_port); + LibertyPort *from_port = + findLinkPort(network_->libertyCell(inst), from_scene_port); const Pin *from_pin = network_->findPin(inst, from_port); if (from_pin) { FuncExpr *when = pwr->when(); Vertex *from_vertex = graph_->pinLoadVertex(from_pin); if (func && func->hasPort(from_port)) { - float duty = evalDiffDuty(func, from_port, inst); - return duty; + float duty = evalDiffDuty(func, from_port, inst); + return duty; } else if (when) - return evalActivity(when, inst).duty(); + return evalActivity(when, inst).duty(); else if (scene_->mode()->clkNetwork()->isClock(from_vertex->pin())) - return 0.5; + return 0.5; return 0.5; } } @@ -1423,14 +1392,13 @@ Power::findLinkPin(const Instance *inst, static bool isPositiveUnate(const LibertyCell *cell, - const LibertyPort *from, - const LibertyPort *to) + const LibertyPort *from, + const LibertyPort *to) { const TimingArcSetSeq &arc_sets = cell->timingArcSets(from, to); if (!arc_sets.empty()) { TimingSense sense = arc_sets[0]->sense(); - return sense == TimingSense::positive_unate - || sense == TimingSense::non_unate; + return sense == TimingSense::positive_unate || sense == TimingSense::non_unate; } // default return true; @@ -1452,18 +1420,15 @@ Power::findSwitchingPower(const Instance *inst, const LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { float load_cap = to_port->direction()->isAnyOutput() - ? graph_delay_calc_->loadCap(to_pin, scene, MinMax::max()) - : 0.0; + ? graph_delay_calc_->loadCap(to_pin, scene, MinMax::max()) + : 0.0; PwrActivity activity = findActivity(to_pin); if (to_port->direction()->isAnyOutput()) { float volt = portVoltage(scene_cell, to_port, scene, MinMax::max()); float switching = .5 * load_cap * volt * volt * activity.density(); - debugPrint(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e", - cell->name(), - to_port->name(), - activity.density(), - volt, - switching); + debugPrint(debug_, "power", 2, + "switching {}/{} activity = {:.2e} volt = {:.2f} {:.3e}", cell->name(), + to_port->name(), activity.density(), volt, switching); result.incrSwitching(switching); } } @@ -1473,7 +1438,6 @@ Power::findSwitchingPower(const Instance *inst, //////////////////////////////////////////////////////////////// - // Leakage totals for one power/gnd pin. class LeakageSummary { @@ -1502,41 +1466,34 @@ LeakageSummary::LeakageSummary() : void Power::findLeakagePower(const Instance *inst, - LibertyCell *cell, + LibertyCell *cell, const Scene *scene, - // Return values. - PowerResult &result) + // Return values. + PowerResult &result) { LibertyCell *scene_cell = cell->sceneCell(scene, MinMax::max()); - std::map leakage_summaries; + std::map leakage_summaries; Sim *sim = scene->mode()->sim(); for (const LeakagePower &pwr : scene_cell->leakagePowers()) { LibertyPort *pg_port = pwr.relatedPgPort(); - if (pg_port == nullptr - || pg_port->pwrGndType() == PwrGndType::primary_power) { + if (pg_port == nullptr || pg_port->pwrGndType() == PwrGndType::primary_power) { LeakageSummary &sum = leakage_summaries[pg_port]; float leakage = pwr.power(); FuncExpr *when = pwr.when(); if (when) { LogicValue when_value = sim->evalExpr(when, inst); if (when_value == LogicValue::one) { - debugPrint(debug_, "power", 2, "leakage %s/%s %s=1 %.3e", - cell->name(), - pg_port->name(), - when->to_string().c_str(), - leakage); + debugPrint(debug_, "power", 2, "leakage {}/{} {}=1 {:.3e}", cell->name(), + pg_port->name(), when->to_string(), leakage); sum.cond_true_leakage = leakage; sum.cond_true_exists = true; } else { PwrActivity cond_activity = evalActivity(when, inst); float cond_duty = cond_activity.duty(); - debugPrint(debug_, "power", 2, "leakage %s %s %s %.3e * %.2f", - cell->name(), - pg_port->name(), - when->to_string().c_str(), - leakage, - cond_duty); + debugPrint(debug_, "power", 2, "leakage {} {} {} {:.3e} * {:.2f}", + cell->name(), pg_port->name(), when->to_string(), + leakage, cond_duty); // Leakage power average weighted by duty. sum.cond_leakage += leakage * cond_duty; if (leakage > 0.0) @@ -1545,10 +1502,8 @@ Power::findLeakagePower(const Instance *inst, } } else { - debugPrint(debug_, "power", 2, "leakage %s %s -- %.3e", - cell->name(), - pg_port->name(), - leakage); + debugPrint(debug_, "power", 2, "leakage {} {} -- {:.3e}", cell->name(), + pg_port->name(), leakage); sum.uncond_leakage = leakage; sum.uncond_exists = true; } @@ -1574,10 +1529,8 @@ Power::findLeakagePower(const Instance *inst, // Ignore unconditional leakage unless there are no conditional leakage groups. else if (sum.uncond_exists) leakage = sum.uncond_leakage; - debugPrint(debug_, "power", 2, "leakage %s/%s %.3e", - cell->name(), - pg_port->name(), - leakage); + debugPrint(debug_, "power", 2, "leakage {}/{} {:.3e}", cell->name(), + pg_port->name(), leakage); result.incrLeakage(leakage); } } @@ -1601,8 +1554,7 @@ Power::findActivity(const Pin *pin) Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex && mode->clkNetwork()->isClock(pin)) { PwrActivity *activity = findKeyValuePtr(activity_map_, pin); - if (activity - && activity->origin() != PwrActivityOrigin::unknown) + if (activity && activity->origin() != PwrActivityOrigin::unknown) return *activity; const Clock *clk = findClk(pin); float duty = clockDuty(clk); @@ -1612,8 +1564,7 @@ Power::findActivity(const Pin *pin) return global_activity_; else { PwrActivity *activity = findKeyValuePtr(activity_map_, pin); - if (activity - && activity->origin() != PwrActivityOrigin::unknown) + if (activity && activity->origin() != PwrActivityOrigin::unknown) return *activity; } return PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown); @@ -1625,7 +1576,7 @@ Power::clockDuty(const Clock *clk) if (clk->isGenerated()) { const Clock *master = clk->masterClk(); if (master == nullptr) - return 0.5; // punt + return 0.5; // punt else return clockDuty(master); } @@ -1640,7 +1591,7 @@ Power::clockDuty(const Clock *clk) PwrActivity Power::findSeqActivity(const Instance *inst, - LibertyPort *port) + LibertyPort *port) { if (global_activity_.isSet()) return global_activity_; @@ -1653,7 +1604,7 @@ Power::findSeqActivity(const Instance *inst, float Power::portVoltage(LibertyCell *cell, - const LibertyPort *port, + const LibertyPort *port, const Scene *scene, const MinMax *min_max) { @@ -1662,7 +1613,7 @@ Power::portVoltage(LibertyCell *cell, float Power::pgNameVoltage(LibertyCell *cell, - const char *pg_port_name, + const char *pg_port_name, const Scene *scene, const MinMax *min_max) { @@ -1675,7 +1626,7 @@ Power::pgNameVoltage(LibertyCell *cell, bool exists; library->supplyVoltage(volt_name, voltage, exists); if (exists) - return voltage; + return voltage; } } @@ -1698,10 +1649,8 @@ Power::findClk(const Pin *to_pin) while (path_iter.hasNext()) { Path *path = path_iter.next(); const Clock *path_clk = path->clock(this); - if (path_clk - && (clk == nullptr - || path_clk->period() < clk->period())) - clk = path_clk; + if (path_clk && (clk == nullptr || path_clk->period() < clk->period())) + clk = path_clk; } } return clk; @@ -1716,45 +1665,43 @@ Power::reportActivityAnnotation(bool report_unannotated, size_t vcd_count = 0; size_t saif_count = 0; size_t input_count = 0; - for (auto const& [pin, activity] : user_activity_map_) { + for (auto const &[pin, activity] : user_activity_map_) { PwrActivityOrigin origin = activity.origin(); switch (origin) { - case PwrActivityOrigin::vcd: - vcd_count++; - break; - case PwrActivityOrigin::saif: - saif_count++; - break; - case PwrActivityOrigin::user: - input_count++; - break; - default: - break; + case PwrActivityOrigin::vcd: + vcd_count++; + break; + case PwrActivityOrigin::saif: + saif_count++; + break; + case PwrActivityOrigin::user: + input_count++; + break; + default: + break; } } if (vcd_count > 0) - report_->reportLine("vcd %5zu", vcd_count); + report_->report("vcd {:>5}", vcd_count); if (saif_count > 0) - report_->reportLine("saif %5zu", saif_count); + report_->report("saif {:>5}", saif_count); if (input_count > 0) - report_->reportLine("input %5zu", input_count); + report_->report("input {:>5}", input_count); size_t pin_count = pinCount(); size_t unannotated_count = pin_count - vcd_count - saif_count - input_count; - report_->reportLine("unannotated %5zu", unannotated_count); + report_->report("unannotated {:>5}", unannotated_count); if (report_annotated) { PinSeq annotated_pins; - for (auto const& [pin, activity] : user_activity_map_) + for (auto const &[pin, activity] : user_activity_map_) annotated_pins.push_back(pin); sort(annotated_pins, PinPathNameLess(sdc_network_)); - report_->reportLine("Annotated pins:"); + report_->report("Annotated pins:"); for (const Pin *pin : annotated_pins) { const PwrActivity &activity = user_activity_map_[pin]; PwrActivityOrigin origin = activity.origin(); const char *origin_name = pwr_activity_origin_map.find(origin); - report_->reportLine("%5s %s", - origin_name, - sdc_network_->pathName(pin)); + report_->report("{:>5} {}", origin_name, sdc_network_->pathName(pin)); } } if (report_unannotated) { @@ -1768,9 +1715,9 @@ Power::reportActivityAnnotation(bool report_unannotated, delete inst_iter; sort(unannotated_pins, PinPathNameLess(sdc_network_)); - report_->reportLine("Unannotated pins:"); + report_->report("Unannotated pins:"); for (const Pin *pin : unannotated_pins) { - report_->reportLine(" %s", sdc_network_->pathName(pin)); + report_->report(" {}", sdc_network_->pathName(pin)); } } } @@ -1784,8 +1731,8 @@ Power::findUnannotatedPins(const Instance *inst, const Pin *pin = pin_iter->next(); LibertyPort *liberty_port = sdc_network_->libertyPort(pin); if (!network_->direction(pin)->isInternal() - && !network_->direction(pin)->isPowerGround() - && !(liberty_port && liberty_port->isPwrGnd()) + && !network_->direction(pin)->isPowerGround() + && !(liberty_port && liberty_port->isPwrGnd()) && !user_activity_map_.contains(pin)) unannotated_pins.push_back(pin); } @@ -1805,8 +1752,8 @@ Power::pinCount() const Pin *pin = pin_iter->next(); LibertyPort *liberty_port = sdc_network_->libertyPort(pin); if (!network_->direction(pin)->isInternal() - && !network_->direction(pin)->isPowerGround() - && !(liberty_port && liberty_port->isPwrGnd())) + && !network_->direction(pin)->isPowerGround() + && !(liberty_port && liberty_port->isPwrGnd())) count++; } delete pin_iter; @@ -1855,7 +1802,7 @@ PowerResult::PowerResult() : } void -PowerResult::clear() +PowerResult::clear() { internal_ = 0.0; switching_ = 0.0; @@ -1897,8 +1844,8 @@ PowerResult::incr(PowerResult &result) //////////////////////////////////////////////////////////////// PwrActivity::PwrActivity(float density, - float duty, - PwrActivityOrigin origin) : + float duty, + PwrActivityOrigin origin) : density_(density), duty_(duty), origin_(origin) @@ -1941,8 +1888,8 @@ PwrActivity::init() void PwrActivity::set(float density, - float duty, - PwrActivityOrigin origin) + float duty, + PwrActivityOrigin origin) { density_ = density; duty_ = duty; @@ -1972,4 +1919,4 @@ PwrActivity::originName() const return pwr_activity_origin_map.find(origin_); } -} // namespace +} // namespace sta diff --git a/power/ReportPower.cc b/power/ReportPower.cc index 3f0d0d76..cf4803b7 100644 --- a/power/ReportPower.cc +++ b/power/ReportPower.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "ReportPower.hh" @@ -29,7 +29,7 @@ #include "Report.hh" #include "Network.hh" -#include "StringUtil.hh" +#include "Format.hh" namespace sta { @@ -51,32 +51,31 @@ ReportPower::reportDesign(PowerResult &total, float design_switching = total.switching(); float design_leakage = total.leakage(); float design_total = total.total(); - + int field_width = std::max(digits + 6, 10); - - reportTitle5("Group", "Internal", "Switching", "Leakage", "Total", - field_width); + + reportTitle5("Group", "Internal", "Switching", "Leakage", "Total", field_width); reportTitle5Units(" ", "Power", "Power", "Power", "Power", "(Watts)", field_width); reportTitleDashes5(field_width); - + reportRow("Sequential", sequential, design_total, field_width, digits); reportRow("Combinational", combinational, design_total, field_width, digits); reportRow("Clock", clock, design_total, field_width, digits); reportRow("Macro", macro, design_total, field_width, digits); reportRow("Pad", pad, design_total, field_width, digits); - + reportTitleDashes5(field_width); - + // Report total row using the totals PowerResult reportRow("Total", total, design_total, field_width, digits); - + // Report percentage line - std::string percent_line = stdstrPrint("%-20s", ""); + std::string percent_line = sta::format("{:<20}", ""); percent_line += powerColPercent(design_internal, design_total, field_width); percent_line += powerColPercent(design_switching, design_total, field_width); percent_line += powerColPercent(design_leakage, design_total, field_width); - report_->reportLineString(percent_line); + report_->reportLine(percent_line); } void @@ -84,13 +83,11 @@ ReportPower::reportInsts(const InstPowers &inst_pwrs, int digits) { int field_width = std::max(digits + 6, 10); - - reportTitle4("Internal", "Switching", "Leakage", "Total", - field_width); - reportTitle4Units("Power", "Power", "Power", "Power", "(Watts)", - field_width); + + reportTitle4("Internal", "Switching", "Leakage", "Total", field_width); + reportTitle4Units("Power", "Power", "Power", "Power", "(Watts)", field_width); reportTitleDashes4(field_width); - + for (const InstPower &inst_pwr : inst_pwrs) { reportInst(inst_pwr.first, inst_pwr.second, field_width, digits); } @@ -106,14 +103,14 @@ ReportPower::reportInst(const Instance *inst, float switching = power.switching(); float leakage = power.leakage(); float total = power.total(); - + std::string line = powerCol(internal, field_width, digits); line += powerCol(switching, field_width, digits); line += powerCol(leakage, field_width, digits); line += powerCol(total, field_width, digits); line += " "; line += network_->pathName(inst); - report_->reportLineString(line); + report_->reportLine(line); } std::string @@ -122,9 +119,9 @@ ReportPower::powerCol(float pwr, int digits) { if (std::isnan(pwr)) - return stdstrPrint(" %*s", field_width, "NaN"); + return sta::format(" {:>{}}", "NaN", field_width); else - return stdstrPrint(" %*.*e", field_width, digits, pwr); + return sta::format(" {:{}.{}e}", pwr, field_width, digits); } std::string @@ -136,41 +133,33 @@ ReportPower::powerColPercent(float col_total, if (total != 0.0 && !std::isnan(total)) { percent = col_total / total * 100.0; } - return stdstrPrint("%*.*f%%", field_width, 1, percent); + return sta::format("{:{}.1f}%", percent, field_width); } void ReportPower::reportTitle5(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *title5, - int field_width) + const char *title2, + const char *title3, + const char *title4, + const char *title5, + int field_width) { - report_->reportLine("%-20s %*s %*s %*s %*s", - title1, - field_width, title2, - field_width, title3, - field_width, title4, - field_width, title5); + report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}}", title1, title2, field_width, + title3, field_width, title4, field_width, title5, field_width); } void ReportPower::reportTitle5Units(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *title5, - const char *units, - int field_width) + const char *title2, + const char *title3, + const char *title4, + const char *title5, + const char *units, + int field_width) { - report_->reportLine("%-20s %*s %*s %*s %*s %s", - title1, - field_width, title2, - field_width, title3, - field_width, title4, - field_width, title5, - units); + report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, title2, + field_width, title3, field_width, title4, field_width, title5, + field_width, units); } void @@ -178,7 +167,7 @@ ReportPower::reportTitleDashes5(int field_width) { int count = 20 + (field_width + 1) * 4; std::string dashes(count, '-'); - report_->reportLineString(dashes); + report_->reportLine(dashes); } void @@ -192,18 +181,18 @@ ReportPower::reportRow(const char *type, float switching = power.switching(); float leakage = power.leakage(); float total = power.total(); - + float percent = 0.0; if (design_total != 0.0 && !std::isnan(design_total)) percent = total / design_total * 100.0; - - std::string line = stdstrPrint("%-20s", type); + + std::string line = sta::format("{:<20}", type); line += powerCol(internal, field_width, digits); line += powerCol(switching, field_width, digits); line += powerCol(leakage, field_width, digits); line += powerCol(total, field_width, digits); - line += stdstrPrint(" %5.1f%%", percent); - report_->reportLineString(line); + line += sta::format(" {:5.1f}%", percent); + report_->reportLine(line); } void @@ -213,11 +202,8 @@ ReportPower::reportTitle4(const char *title1, const char *title4, int field_width) { - report_->reportLine(" %*s %*s %*s %*s", - field_width, title1, - field_width, title2, - field_width, title3, - field_width, title4); + report_->report(" {:>{}} {:>{}} {:>{}} {:>{}}", title1, field_width, title2, + field_width, title3, field_width, title4, field_width); } void @@ -228,12 +214,8 @@ ReportPower::reportTitle4Units(const char *title1, const char *units, int field_width) { - report_->reportLine(" %*s %*s %*s %*s %s", - field_width, title1, - field_width, title2, - field_width, title3, - field_width, title4, - units); + report_->report(" {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, field_width, title2, + field_width, title3, field_width, title4, field_width, units); } void @@ -241,7 +223,7 @@ ReportPower::reportTitleDashes4(int field_width) { int count = (field_width + 1) * 4; std::string dashes(count, '-'); - report_->reportLineString(dashes); + report_->reportLine(dashes); } -} // namespace +} // namespace sta diff --git a/power/SaifParse.yy b/power/SaifParse.yy index be4f723f..6012d59a 100644 --- a/power/SaifParse.yy +++ b/power/SaifParse.yy @@ -42,7 +42,7 @@ void sta::SaifParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(169,reader->filename(),loc.begin.line,"%s",msg.c_str()); + reader->report()->fileError(169,reader->filename(),loc.begin.line,{}, msg); } %} diff --git a/power/SaifReader.cc b/power/SaifReader.cc index 17345229..c06cb512 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "power/SaifReader.hh" @@ -61,7 +61,7 @@ SaifReader::SaifReader(const char *filename, scope_(scope), divider_('/'), escape_('\\'), - timescale_(1.0E-9F), // default units of ns + timescale_(1.0E-9F), // default units of ns duration_(0.0), in_scope_level_(0), power_(sta->power()) @@ -78,7 +78,7 @@ SaifReader::read() SaifParse parser(&scanner, this); // yyparse returns 0 on success. bool success = (parser.parse() == 0); - report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size()); + report_->report("Annotated {} pin activities.", annotated_pins_.size()); return success; } else @@ -95,9 +95,7 @@ void SaifReader::setTimescale(uint64_t multiplier, const char *units) { - if (multiplier == 1 - || multiplier == 10 - || multiplier == 100) { + if (multiplier == 1 || multiplier == 10 || multiplier == 100) { if (stringEq(units, "us")) timescale_ = multiplier * 1E-6; else if (stringEq(units, "ns")) @@ -107,10 +105,10 @@ SaifReader::setTimescale(uint64_t multiplier, else if (stringEq(units, "fs")) timescale_ = multiplier * 1E-15; else - report_->error(180, "SAIF TIMESCALE units not us, ns, or ps."); + report_->error(1861, "SAIF TIMESCALE units not us, ns, or ps."); } else - report_->error(181, "SAIF TIMESCALE multiplier not 1, 10, or 100."); + report_->error(1862, "SAIF TIMESCALE multiplier not 1, 10, or 100."); stringDelete(units); } @@ -168,8 +166,7 @@ SaifReader::setNetDurations(const char *net_name, std::string unescaped_name = unescaped(net_name); const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str()); LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr; - if (pin - && !sdc_network_->isHierarchical(pin) + if (pin && !sdc_network_->isHierarchical(pin) && !sdc_network_->direction(pin)->isInternal() && !(liberty_port && liberty_port->isPwrGnd())) { double t1 = durations[static_cast(SaifState::T1)]; @@ -177,13 +174,8 @@ SaifReader::setNetDurations(const char *net_name, double tc = durations[static_cast(SaifState::TC)]; float density = tc / (duration_ * timescale_); debugPrint(debug_, "read_saif", 2, - "%s duty %.0f / %" PRIu64 " = %.2f tc %.0f density %.2f", - sdc_network_->pathName(pin), - t1, - duration_, - duty, - tc, - density); + "{} duty {:.0f} / {} = {:.2f} tc {:.0f} density {:.2f}", + sdc_network_->pathName(pin), t1, duration_, duty, tc, density); power_->setUserActivity(pin, density, duty, PwrActivityOrigin::saif); annotated_pins_.insert(pin); } @@ -202,7 +194,7 @@ SaifReader::unescaped(const char *token) // Just the normal noises. unescaped += ch; } - debugPrint(debug_, "saif_name", 1, "token %s -> %s", token, unescaped.c_str()); + debugPrint(debug_, "saif_name", 1, "token {} -> {}", token, unescaped); return unescaped; } @@ -222,7 +214,7 @@ SaifScanner::SaifScanner(std::istream *stream, void SaifScanner::error(const char *msg) { - report_->fileError(1868, filename_.c_str(), lineno(), "%s", msg); + report_->fileError(1860, filename_.c_str(), lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/power/VcdParse.cc b/power/VcdParse.cc index 1227e1b2..c82423cf 100644 --- a/power/VcdParse.cc +++ b/power/VcdParse.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "VcdParse.hh" @@ -80,16 +80,14 @@ VcdParse::read(const char *filename, else if (token[0] == '#') { try { time_ = stoll(token.substr(1)); + } catch (std::invalid_argument &error) { + report_->fileError(805, filename_, file_line_, "invalid time {}", + token.substr(1)); + } catch (std::out_of_range &error) { + report_->fileError(806, filename_, file_line_, "time out of range {}", + token.substr(1)); } - catch (std::invalid_argument &error) { - report_->fileError(805, filename_, file_line_, "invalid time %s", - token.substr(1).c_str()); - } - catch (std::out_of_range &error) { - report_->fileError(806, filename_, file_line_, "time out of range %s", - token.substr(1).c_str()); - } - reader_->setTimeMin(time_); + reader_->setTimeMin(time_); prev_time_ = time_; } else if (token[0] == '$') @@ -151,37 +149,34 @@ VcdParse::setTimeUnit(const std::string &time_unit, reader_->setTimeUnit(time_unit, time_unit_scale, time_scale); } -static EnumNameMap vcd_var_type_map = - {{VcdVarType::wire, "wire"}, - {VcdVarType::reg, "reg"}, - {VcdVarType::parameter, "parameter"}, - {VcdVarType::integer, "integer"}, - {VcdVarType::real, "real"}, - {VcdVarType::supply0, "supply0"}, - {VcdVarType::supply1, "supply1"}, - {VcdVarType::time, "time"}, - {VcdVarType::tri, "tri"}, - {VcdVarType::triand, "triand"}, - {VcdVarType::trior, "trior"}, - {VcdVarType::trireg, "trireg"}, - {VcdVarType::tri0, "tri0"}, - {VcdVarType::tri1, "tri1"}, - {VcdVarType::wand, "wand"}, - {VcdVarType::wor, "wor"} - }; +static EnumNameMap vcd_var_type_map = { + {VcdVarType::wire, "wire"}, + {VcdVarType::reg, "reg"}, + {VcdVarType::parameter, "parameter"}, + {VcdVarType::integer, "integer"}, + {VcdVarType::real, "real"}, + {VcdVarType::supply0, "supply0"}, + {VcdVarType::supply1, "supply1"}, + {VcdVarType::time, "time"}, + {VcdVarType::tri, "tri"}, + {VcdVarType::triand, "triand"}, + {VcdVarType::trior, "trior"}, + {VcdVarType::trireg, "trireg"}, + {VcdVarType::tri0, "tri0"}, + {VcdVarType::tri1, "tri1"}, + {VcdVarType::wand, "wand"}, + {VcdVarType::wor, "wor"}}; void VcdParse::parseVar() { std::vector tokens = readStmtTokens(); - if (tokens.size() == 4 - || tokens.size() == 5) { + if (tokens.size() == 4 || tokens.size() == 5) { std::string type_name = tokens[0]; VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown); if (type == VcdVarType::unknown) - report_->fileWarn(1370, filename_, file_line_, - "Unknown variable type %s.", - type_name.c_str()); + report_->fileWarn(809, filename_, file_line_, "Unknown variable type {}.", + type_name); else { size_t width = std::stoi(tokens[1]); std::string &id = tokens[2]; @@ -229,23 +224,18 @@ VcdParse::parseVarValues() if (time_ > prev_time_) reader_->varMinDeltaTime(time_ - prev_time_); } - else if (char0 == '0' - || char0 == '1' - || char0 == 'X' - || char0 == 'U' + else if (char0 == '0' || char0 == '1' || char0 == 'X' || char0 == 'U' || char0 == 'Z') { std::string id = token.substr(1); if (!reader_->varIdValid(id)) - report_->fileError(805, filename_, file_line_, - "unknown variable %s", id.c_str()); + report_->fileError(808, filename_, file_line_, "unknown variable {}", id); reader_->varAppendValue(id, time_, char0); } else if (char0 == 'B') { std::string bus_value = token.substr(1); std::string id = getToken(); if (!reader_->varIdValid(id)) - report_->fileError(807, filename_, file_line_, - "unknown variable %s", id.c_str()); + report_->fileError(807, filename_, file_line_, "unknown variable {}", id); else { // Reverse the bus value to match the bit order in the VCD file. std::reverse(bus_value.begin(), bus_value.end()); @@ -327,4 +317,4 @@ VcdValue::setValue(VcdTime time, value_ = value; } -} // namespace +} // namespace sta diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 7a1b312a..fd5ed1cd 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "VcdReader.hh" @@ -89,12 +89,10 @@ VcdCount::incrCounts(VcdTime time, if (prev_value_ == '1') high_time_ += time - prev_time_; if (value != prev_value_) - transition_count_ += (value == 'X' - || value == 'Z' - || prev_value_ == 'X' - || prev_value_ == 'Z') - ? .5 - : 1.0; + transition_count_ += + (value == 'X' || value == 'Z' || prev_value_ == 'X' || prev_value_ == 'Z') + ? .5 + : 1.0; } prev_time_ = time; prev_value_ = value; @@ -216,8 +214,7 @@ VcdCountReader::makeVar(const VcdScope &scope, size_t width, const std::string &id) { - if (type == VcdVarType::wire - || type == VcdVarType::reg) { + if (type == VcdVarType::wire || type == VcdVarType::reg) { std::string path_name; bool first = true; for (const std::string &context : scope) { @@ -228,24 +225,23 @@ VcdCountReader::makeVar(const VcdScope &scope, } size_t scope_length = scope_.size(); // string::starts_with in c++20 - if (scope_length == 0 - || path_name.substr(0, scope_length) == scope_) { + if (scope_length == 0 || path_name.substr(0, scope_length) == scope_) { path_name += '/'; path_name += name; // Strip the scope from the name. std::string var_scoped = path_name.substr(scope_length + 1); if (width == 1) { - std::string pin_name = netVerilogToSta(&var_scoped); + std::string pin_name = netVerilogToSta(var_scoped); addVarPin(pin_name, id, width, 0); } else { bool is_bus, is_range, subscript_wild; std::string bus_name; int from, to; - parseBusName(var_scoped.c_str(), '[', ']', '\\', - is_bus, is_range, bus_name, from, to, subscript_wild); + parseBusName(var_scoped.c_str(), '[', ']', '\\', is_bus, is_range, bus_name, + from, to, subscript_wild); if (is_bus) { - std::string sta_bus_name = netVerilogToSta(&bus_name); + std::string sta_bus_name = netVerilogToSta(bus_name); int bit_idx = 0; if (to < from) { for (int bus_bit = to; bus_bit <= from; bus_bit++) { @@ -269,7 +265,7 @@ VcdCountReader::makeVar(const VcdScope &scope, } } else - report_->warn(1451, "problem parsing bus %s.", var_scoped.c_str()); + report_->warn(1451, "problem parsing bus {}.", var_scoped); } } } @@ -283,17 +279,14 @@ VcdCountReader::addVarPin(const std::string &pin_name, { const Pin *pin = sdc_network_->findPin(pin_name.c_str()); LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr; - if (pin - && !sdc_network_->isHierarchical(pin) + if (pin && !sdc_network_->isHierarchical(pin) && !sdc_network_->direction(pin)->isInternal() && !sdc_network_->direction(pin)->isPowerGround() && !(liberty_port && liberty_port->isPwrGnd())) { VcdCounts &vcd_counts = vcd_count_map_[id]; vcd_counts.resize(width); vcd_counts[bit_idx].addPin(pin); - debugPrint(debug_, "read_vcd", 2, "id %s pin %s", - id.c_str(), - pin_name.c_str()); + debugPrint(debug_, "read_vcd", 2, "id {} pin {}", id, pin_name); } } @@ -309,10 +302,8 @@ VcdCountReader::varAppendValue(const std::string &id, for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) { VcdCount &vcd_count = vcd_counts[bit_idx]; for (const Pin *pin : vcd_count.pins()) { - debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c", - sdc_network_->pathName(pin), - time, - value); + debugPrint(debug_, "read_vcd", 3, "{} time {} value {}", + sdc_network_->pathName(pin), time, value); } } } @@ -335,7 +326,7 @@ VcdCountReader::varAppendBusValue(const std::string &id, char bit_value; if (bus_value.size() == 1) bit_value = bus_value[0]; - else if (bit_idx < bus_value.size()) + else if (bit_idx < bus_value.size()) bit_value = bus_value[bit_idx]; else bit_value = '0'; @@ -343,10 +334,8 @@ VcdCountReader::varAppendBusValue(const std::string &id, vcd_count.incrCounts(time, bit_value); if (debug_->check("read_vcd", 3)) { for (const Pin *pin : vcd_count.pins()) { - debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c", - sdc_network_->pathName(pin), - time, - bit_value); + debugPrint(debug_, "read_vcd", 3, "{} time {} value {}", + sdc_network_->pathName(pin), time, bit_value); } } } @@ -371,7 +360,7 @@ private: const std::string filename_; - std::set annotated_pins_; + std::set annotated_pins_; VcdCountReader vcd_reader_; VcdParse vcd_parse_; const Sdc *sdc_; @@ -398,8 +387,12 @@ ReadVcdActivities::ReadVcdActivities(const std::string &filename, Sta *sta) : StaState(sta), filename_(filename), - vcd_reader_(scope, sdc_network_, report_, debug_), - vcd_parse_(report_, debug_), + vcd_reader_(scope, + sdc_network_, + report_, + debug_), + vcd_parse_(report_, + debug_), sdc_(sdc), power_(sta->power()) { @@ -418,7 +411,7 @@ ReadVcdActivities::readActivities() setActivities(); else report_->warn(1450, "VCD max time is zero."); - report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size()); + report_->report("Annotated {} pin activities.", annotated_pins_.size()); } void @@ -428,7 +421,7 @@ ReadVcdActivities::setActivities() VcdTime time_max = vcd_reader_.timeMax(); VcdTime time_delta = time_max - time_min; double time_scale = vcd_reader_.timeScale(); - for (auto& [id, vcd_counts] : vcd_reader_.countMap()) { + for (auto &[id, vcd_counts] : vcd_reader_.countMap()) { for (const VcdCount &vcd_count : vcd_counts) { double transition_count = vcd_count.transitionCount(); VcdTime high_time = vcd_count.highTime(time_max); @@ -437,11 +430,8 @@ ReadVcdActivities::setActivities() if (debug_->check("read_vcd", 1)) { for (const Pin *pin : vcd_count.pins()) { debugPrint(debug_, "read_vcd", 1, - "%s transitions %.1f activity %.2f duty %.2f", - sdc_network_->pathName(pin), - transition_count, - density, - duty); + "{} transitions {:.1f} activity {:.2f} duty {:.2f}", + sdc_network_->pathName(pin), transition_count, density, duty); } } for (const Pin *pin : vcd_count.pins()) { @@ -463,23 +453,24 @@ ReadVcdActivities::checkClkPeriod(const Pin *pin, VcdTime time_max = vcd_reader_.timeMax(); VcdTime time_min = vcd_reader_.timeMin(); double time_scale = vcd_reader_.timeScale(); - double sim_period = (time_max - time_min) * time_scale / (transition_count / 2.0); + double sim_period = + (time_max - time_min) * time_scale / (transition_count / 2.0); for (Clock *clk : *clks) { if (transition_count == 0) - report_->warn(1453, "clock %s pin %s has no vcd transitions.", - clk->name(), + report_->warn(1453, "clock {} pin {} has no vcd transitions.", clk->name(), sdc_network_->pathName(pin)); else { double clk_period = clk->period(); - if (std::abs((clk_period - sim_period) / clk_period) > sim_clk_period_tolerance_) + if (std::abs((clk_period - sim_period) / clk_period) + > sim_clk_period_tolerance_) // Warn if sim clock period differs from SDC by more than 10%. - report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s", - clk->name(), - delayAsString(sim_period, this), + report_->warn(1452, + "clock {} vcd period {} differs from SDC clock period {}", + clk->name(), delayAsString(sim_period, this), delayAsString(clk_period, this)); } } } } -} // namespace +} // namespace sta diff --git a/sdc/Clock.cc b/sdc/Clock.cc index b54f4302..df161fc2 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -28,6 +28,7 @@ #include "ContainerHelpers.hh" #include "Error.hh" +#include "Format.hh" #include "StringUtil.hh" #include "MinMax.hh" #include "Transition.hh" @@ -531,7 +532,7 @@ ClockEdge::ClockEdge(Clock *clock, const RiseFall *rf) : clock_(clock), rf_(rf), - name_(stringPrint("%s %s", clock_->name(), rf_->shortName())), + name_(sta::format("{} {}", clock_->name(), rf_->shortName())), time_(0.0), index_(clock_->index() * RiseFall::index_count + rf_->index()) { @@ -539,7 +540,6 @@ ClockEdge::ClockEdge(Clock *clock, ClockEdge::~ClockEdge() { - stringDelete(name_); } void diff --git a/sdc/ClockGroups.cc b/sdc/ClockGroups.cc index 9729a15c..0bf76c38 100644 --- a/sdc/ClockGroups.cc +++ b/sdc/ClockGroups.cc @@ -29,14 +29,14 @@ namespace sta { -ClockGroups::ClockGroups(const char *name, +ClockGroups::ClockGroups(const std::string &name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, const char *comment) : SdcCmdComment(comment), - name_(stringCopy(name)), + name_(name), logically_exclusive_(logically_exclusive), physically_exclusive_(physically_exclusive), asynchronous_(asynchronous), @@ -46,7 +46,6 @@ ClockGroups::ClockGroups(const char *name, ClockGroups::~ClockGroups() { - stringDelete(name_); deleteContents(groups_); } diff --git a/sdc/CycleAccting.cc b/sdc/CycleAccting.cc index 3c674e05..a3baf716 100644 --- a/sdc/CycleAccting.cc +++ b/sdc/CycleAccting.cc @@ -93,7 +93,7 @@ CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report) ClockPair clk_pair2(tgt_clk, src_clk); if (!clk_warnings.contains(clk_pair1) && !clk_warnings.contains(clk_pair2)) { - report->warn(1010, "No common period was found between clocks %s and %s.", + report->warn(1010, "No common period was found between clocks {} and {}.", src_clk->name(), tgt_clk->name()); clk_warnings.insert(clk_pair1); @@ -126,7 +126,7 @@ CycleAccting::findDelays(StaState *sta) { Debug *debug = sta->debug(); const Unit *time_unit = sta->units()->timeUnit(); - debugPrint(debug, "cycle_acct", 1, "%s -> %s", + debugPrint(debug, "cycle_acct", 1, "{} -> {}", src_->name(), tgt_->name()); const int setup_index = TimingRole::setup()->index(); @@ -167,14 +167,14 @@ CycleAccting::findDelays(StaState *sta) if (tgt_past_src && src_past_tgt // Synchronicity achieved. && fuzzyEqual(src_cycle_start, tgt_cycle_start)) { - debugPrint(debug, "cycle_acct", 1, " setup = %s, required = %s", + debugPrint(debug, "cycle_acct", 1, " setup = {}, required = {}", time_unit->asString(delay_[setup_index]), time_unit->asString(required_[setup_index])); - debugPrint(debug, "cycle_acct", 1, " hold = %s, required = %s", + debugPrint(debug, "cycle_acct", 1, " hold = {}, required = {}", time_unit->asString(delay_[hold_index]), time_unit->asString(required_[hold_index])); debugPrint(debug, "cycle_acct", 1, - " converged at src cycles = %d tgt cycles = %d", + " converged at src cycles = {} tgt cycles = {}", src_cycle, tgt_cycle); return; } @@ -182,13 +182,13 @@ CycleAccting::findDelays(StaState *sta) if (fuzzyGreater(src_cycle_start, tgt_cycle_start + tgt_period) && src_past_tgt) break; - debugPrint(debug, "cycle_acct", 2, " %s src cycle %d %s + %s = %s", + debugPrint(debug, "cycle_acct", 2, " {} src cycle {} {} + {} = {}", src_->name(), src_cycle, time_unit->asString(src_cycle_start), time_unit->asString(src_->time()), time_unit->asString(src_time)); - debugPrint(debug, "cycle_acct", 2, " %s tgt cycle %d %s + %s = %s", + debugPrint(debug, "cycle_acct", 2, " {} tgt cycle {} {} + {} = {}", tgt_->name(), tgt_cycle, time_unit->asString(tgt_cycle_start), @@ -203,7 +203,7 @@ CycleAccting::findDelays(StaState *sta) double required = tgt_time - src_cycle_start; setSetupAccting(src_cycle, tgt_cycle, delay, required); debugPrint(debug, "cycle_acct", 2, - " setup min delay = %s, required = %s", + " setup min delay = {}, required = {}", time_unit->asString(delay_[setup_index]), time_unit->asString(required_[setup_index])); } @@ -239,7 +239,7 @@ CycleAccting::findDelays(StaState *sta) setAccting(TimingRole::latchSetup(), src_cycle, latch_tgt_cycle, delay, required); debugPrint(debug, "cycle_acct", 2, - " latch setup min delay = %s, required = %s", + " latch setup min delay = {}, required = {}", time_unit->asString(delay_[latch_setup_index]), time_unit->asString(required_[latch_setup_index])); } @@ -253,7 +253,7 @@ CycleAccting::findDelays(StaState *sta) double required = tgt_time - src_cycle_start; setHoldAccting(src_cycle, tgt_cycle, delay, required); debugPrint(debug, "cycle_acct", 2, - " hold min delay = %s, required = %s", + " hold min delay = {}, required = {}", time_unit->asString(delay_[hold_index]), time_unit->asString(required_[hold_index])); } @@ -268,7 +268,7 @@ CycleAccting::findDelays(StaState *sta) setAccting(TimingRole::gatedClockHold(), src_cycle, tgt_cycle, delay, required); debugPrint(debug, "cycle_acct", 2, - " gated clk hold min delay = %s, required = %s", + " gated clk hold min delay = {}, required = {}", time_unit->asString(delay_[gclk_hold_index]), time_unit->asString(required_[gclk_hold_index])); } @@ -278,7 +278,7 @@ CycleAccting::findDelays(StaState *sta) } max_cycles_exceeded_ = true; debugPrint(debug, "cycle_acct", 1, - " max cycles exceeded after %d src cycles, %d tgt_cycles", + " max cycles exceeded after {} src cycles, {} tgt_cycles", src_cycle, tgt_cycle); } else if (tgt_period > 0.0) diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 4ca26e4e..83bb6055 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -26,6 +26,7 @@ #include +#include "Format.hh" #include "ContainerHelpers.hh" #include "MinMax.hh" #include "TimingRole.hh" @@ -121,17 +122,10 @@ ExceptionPath::~ExceptionPath() } } -const char * -ExceptionPath::asString(const Network *network) const +std::string +ExceptionPath::to_string(const Network *network) const { - const char *from_thru_to = fromThruToString(network); - const char *type = typeString(); - size_t length = strlen(type) + strlen(from_thru_to) + 1; - char *result = makeTmpString(length); - char *r = result; - stringAppend(r, type); - stringAppend(r, from_thru_to); - return result; + return sta::format("{}{}", typeString(), fromThruToString(network)); } void @@ -321,7 +315,7 @@ ExceptionPath::intersectsPts(ExceptionPath *exception, return false; } -const char * +std::string ExceptionPath::fromThruToString(const Network *network) const { std::string str; @@ -331,7 +325,7 @@ ExceptionPath::fromThruToString(const Network *network) const } if (from_) - str += from_->asString(network); + str += from_->to_string(network); if (thrus_) { str += " -thru"; @@ -341,7 +335,7 @@ ExceptionPath::fromThruToString(const Network *network) const if (!first_thru) str += " &&"; str += " {"; - str += thru->asString(network); + str += thru->to_string(network); str += "}"; first_thru = false; } @@ -349,11 +343,9 @@ ExceptionPath::fromThruToString(const Network *network) const } if (to_) - str += to_->asString(network); + str += to_->to_string(network); - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } ExceptionState * @@ -524,14 +516,12 @@ PathDelay::tighterThan(ExceptionPath *exception) const return delay_ < exception->delay(); } -const char * -PathDelay::asString(const Network *network) const +std::string +PathDelay::to_string(const Network *network) const { - const char *from_thru_to = fromThruToString(network); - const char *result = stringPrintTmp("PathDelay %.3fns%s", - delay_ * 1E+9F, - from_thru_to); - return result; + return sta::format("PathDelay {:.3f}ns{}", + delay_ * 1E+9F, + fromThruToString(network)); } const char * @@ -728,15 +718,13 @@ MultiCyclePath::matches(const MinMax *min_max, || (!exactly && min_max == MinMax::min()); } -const char * -MultiCyclePath::asString(const Network *network) const +std::string +MultiCyclePath::to_string(const Network *network) const { - const char *from_thru_to = fromThruToString(network); - const char *result = stringPrintTmp("Multicycle %s %d%s", - (use_end_clk_) ? "-end" : "-start", - path_multiplier_, - from_thru_to); - return result; + return sta::format("Multicycle {} {}{}", + (use_end_clk_) ? "-end" : "-start", + path_multiplier_, + fromThruToString(network)); } const char * @@ -826,7 +814,7 @@ FilterPath::resetMatch(ExceptionFrom *, //////////////////////////////////////////////////////////////// -GroupPath::GroupPath(const char *name, +GroupPath::GroupPath(const std::string &name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -836,14 +824,13 @@ GroupPath::GroupPath(const char *name, ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, groupPathPriority() + fromThruToPriority(from, thrus, to), comment), - name_(stringCopy(name)), + name_(name), is_default_(is_default) { } GroupPath::~GroupPath() { - stringDelete(name_); } const char * @@ -877,7 +864,7 @@ GroupPath::tighterThan(ExceptionPath *) const bool GroupPath::mergeable(ExceptionPath *exception) const { - return stringEqIf(name_, exception->name()) + return name_ == exception->name() && ExceptionPath::mergeable(exception) && overrides(exception); } @@ -887,12 +874,12 @@ GroupPath::overrides(ExceptionPath *exception) const { return exception->isGroupPath() && is_default_ == exception->isDefault() - && stringEqIf(name_, exception->name()); + && name_ == exception->name(); } //////////////////////////////////////////////////////////////// -const int ExceptionPt::as_string_max_objects_ = 20; +const int ExceptionPt::to_string_max_objects_ = 20; ExceptionPt::ExceptionPt(const RiseFallBoth *rf, bool own_pts) : @@ -1181,8 +1168,8 @@ ExceptionFromTo::deletePinBefore(const Pin *pin, deletePin(pin, network); } -const char * -ExceptionFromTo::asString(const Network *network) const +std::string +ExceptionFromTo::to_string(const Network *network) const { std::string str; str += " "; @@ -1199,7 +1186,7 @@ ExceptionFromTo::asString(const Network *network) const str += network->pathName(pin); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1211,7 +1198,7 @@ ExceptionFromTo::asString(const Network *network) const str += clk->name(); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1223,18 +1210,16 @@ ExceptionFromTo::asString(const Network *network) const str += network->pathName(inst); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } - if (obj_count == as_string_max_objects_) + if (obj_count == to_string_max_objects_) str += ", ..."; str += "}"; - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } size_t @@ -1355,19 +1340,17 @@ ExceptionTo::clone(const Network *network) return new ExceptionTo(pins, clks, insts, rf_, end_rf_, true, network); } -const char * -ExceptionTo::asString(const Network *network) const +std::string +ExceptionTo::to_string(const Network *network) const { std::string str; if (hasObjects()) - str += ExceptionFromTo::asString(network); + str += ExceptionFromTo::to_string(network); if (end_rf_ != RiseFallBoth::riseFall()) str += (end_rf_ == RiseFallBoth::rise()) ? " -rise" : " -fall"; - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } bool @@ -1674,8 +1657,8 @@ ExceptionThru::~ExceptionThru() } } -const char * -ExceptionThru::asString(const Network *network) const +std::string +ExceptionThru::to_string(const Network *network) const { std::string str; bool first = true; @@ -1688,7 +1671,7 @@ ExceptionThru::asString(const Network *network) const str += network->pathName(pin); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1700,7 +1683,7 @@ ExceptionThru::asString(const Network *network) const str += network->pathName(net); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } @@ -1712,20 +1695,18 @@ ExceptionThru::asString(const Network *network) const str += network->pathName(inst); first = false; obj_count++; - if (obj_count > as_string_max_objects_) + if (obj_count > to_string_max_objects_) break; } } - if (obj_count == as_string_max_objects_) + if (obj_count == to_string_max_objects_) str += ", ..."; if (rf_ == RiseFallBoth::rise()) str += " rise"; else if (rf_ == RiseFallBoth::fall()) str += " fall"; - char *result = makeTmpString(str.size() + 1); - strcpy(result, str.c_str()); - return result; + return str; } ExceptionThruSeq * diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 6d035b05..8889f177 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -1941,40 +1941,38 @@ ClockInsertionkLess::operator()(const ClockInsertion *insert1, //////////////////////////////////////////////////////////////// ClockGroups * -Sdc::makeClockGroups(const char *name, +Sdc::makeClockGroups(const std::string &name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, const char *comment) { - char *gen_name = nullptr; - if (name == nullptr - || name[0] == '\0') - name = gen_name = makeClockGroupsName(); + std::string group_name; + if (name.empty()) + group_name = makeClockGroupsName(); else { - ClockGroups *groups = findKey(clk_groups_name_map_, name); + group_name = name; + ClockGroups *groups = findKey(clk_groups_name_map_, group_name); if (groups) removeClockGroups(groups); } - ClockGroups *groups = new ClockGroups(name, logically_exclusive, + ClockGroups *groups = new ClockGroups(group_name, logically_exclusive, physically_exclusive, asynchronous, allow_paths, comment); clk_groups_name_map_[groups->name()] = groups; - stringDelete(gen_name); return groups; } // Generate a name for the clock group. -char * +std::string Sdc::makeClockGroupsName() { - char *name = nullptr; + std::string name; int i = 0; do { i++; - stringDelete(name); - name = stringPrint("group%d", i); + name = sta::format("group{}", i); } while (clk_groups_name_map_.contains(name)); return name; } @@ -1990,7 +1988,7 @@ void Sdc::ensureClkGroupExclusions() { if (clk_group_exclusions_.empty()) { - for (const auto [name, clk_groups] : clk_groups_name_map_) + for (const auto &[name, clk_groups] : clk_groups_name_map_) makeClkGroupExclusions(clk_groups); } } @@ -2087,7 +2085,7 @@ Sdc::sameClockGroupExplicit(const Clock *clk1, } void -Sdc::removeClockGroups(const char *name) +Sdc::removeClockGroups(const std::string &name) { ClockGroups *clk_groups = findKey(clk_groups_name_map_, name); if (clk_groups) @@ -2095,51 +2093,55 @@ Sdc::removeClockGroups(const char *name) } void -Sdc::removeClockGroupsLogicallyExclusive(const char *name) +Sdc::removeClockGroupsLogicallyExclusive() { - if (name) { - ClockGroups *groups = findKey(clk_groups_name_map_, name); - if (groups && groups->logicallyExclusive()) + + for (const auto &[name, groups] : clk_groups_name_map_) { + if (groups->logicallyExclusive()) removeClockGroups(groups); } - else { - for (const auto [name, groups] : clk_groups_name_map_) { - if (groups->logicallyExclusive()) - removeClockGroups(groups); - } - } } void -Sdc::removeClockGroupsPhysicallyExclusive(const char *name) +Sdc::removeClockGroupsLogicallyExclusive(const std::string &name) { - if (name) { - ClockGroups *groups = findKey(clk_groups_name_map_, name); - if (groups && groups->physicallyExclusive()) + ClockGroups *groups = findKey(clk_groups_name_map_, name); + if (groups && groups->logicallyExclusive()) + removeClockGroups(groups); +} + +void +Sdc::removeClockGroupsPhysicallyExclusive() +{ + for (const auto &[name, groups] : clk_groups_name_map_) { + if (groups->physicallyExclusive()) removeClockGroups(groups); } - else { - for (const auto [name, groups] : clk_groups_name_map_) { - if (groups->physicallyExclusive()) - removeClockGroups(groups); - } - } } void -Sdc::removeClockGroupsAsynchronous(const char *name) +Sdc::removeClockGroupsPhysicallyExclusive(const std::string &name) { - if (name) { - ClockGroups *groups = findKey(clk_groups_name_map_, name); - if (groups && groups->asynchronous()) + ClockGroups *groups = findKey(clk_groups_name_map_, name); + if (groups && groups->physicallyExclusive()) + removeClockGroups(groups); +} + +void +Sdc::removeClockGroupsAsynchronous() +{ + for (const auto &[name, groups] : clk_groups_name_map_) { + if (groups->asynchronous()) removeClockGroups(groups); } - else { - for (const auto [name, groups] : clk_groups_name_map_) { - if (groups->asynchronous()) - removeClockGroups(groups); - } - } +} + +void +Sdc::removeClockGroupsAsynchronous(const std::string &name) +{ + ClockGroups *groups = findKey(clk_groups_name_map_, name); + if (groups && groups->asynchronous()) + removeClockGroups(groups); } void @@ -2155,7 +2157,7 @@ Sdc::removeClockGroups(ClockGroups *groups) void Sdc::clockGroupsDeleteClkRefs(Clock *clk) { - for (const auto [name, groups] : clk_groups_name_map_) + for (const auto &[name, groups] : clk_groups_name_map_) groups->removeClock(clk); clearClkGroupExclusions(); } @@ -4080,9 +4082,7 @@ void Sdc::clearGroupPathMap() { // GroupPath exceptions are deleted with other exceptions. - // Delete group_path name strings. for (auto [name, groups] : group_path_map_) { - stringDelete(name); deleteContents(*groups); delete groups; } @@ -4090,7 +4090,7 @@ Sdc::clearGroupPathMap() } void -Sdc::makeGroupPath(const char *name, +Sdc::makeGroupPath(const std::string &name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -4098,9 +4098,9 @@ Sdc::makeGroupPath(const char *name, const char *comment) { checkFromThrusTo(from, thrus, to); - if (name && is_default) + if (!name.empty() && is_default) report_->critical(1490, "group path name and is_default are mutually exclusive."); - else if (name) { + else if (!name.empty()) { GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to, true, comment); // Clone the group_path because it may get merged and hence deleted @@ -4115,7 +4115,7 @@ Sdc::makeGroupPath(const char *name, GroupPathSet *groups = findKey(group_path_map_, name); if (groups == nullptr) { groups = new GroupPathSet(network_); - group_path_map_[stringCopy(name)] = groups; + group_path_map_[name] = groups; } if (groups->contains(group_path)) // Exact copy of existing group path. @@ -4226,7 +4226,7 @@ void Sdc::makeLoopExceptionThru(const Pin *pin, ExceptionThruSeq *thrus) { - debugPrint(debug_, "levelize", 2, " %s", network_->pathName(pin)); + debugPrint(debug_, "levelize", 2, " {}", network_->pathName(pin)); PinSet *pins = new PinSet(network_); pins->insert(pin); ExceptionThru *thru = makeExceptionThru(pins, nullptr, nullptr, @@ -4254,8 +4254,8 @@ Sdc::deleteLoopExceptions() void Sdc::addException(ExceptionPath *exception) { - debugPrint(debug_, "exception_merge", 1, "add exception for %s", - exception->asString(network_)); + debugPrint(debug_, "exception_merge", 1, "add exception for {}", + exception->to_string(network_)); if (exception->isPathDelay()) { recordPathDelayInternalFrom(exception); @@ -4282,8 +4282,8 @@ Sdc::addException(ExceptionPath *exception) ExceptionTo *to = exception->to(); ExceptionTo *to1 = to ? to->clone(network_) : nullptr; ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception1->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception1->to_string(network_)); addException1(exception1); ClockSet *clks2 = new ClockSet(*from->clks()); @@ -4292,8 +4292,8 @@ Sdc::addException(ExceptionPath *exception) ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); ExceptionTo *to2 = to ? to->clone(network_) : nullptr; ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception2->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception2->to_string(network_)); addException1(exception2); delete exception; @@ -4316,8 +4316,8 @@ Sdc::addException1(ExceptionPath *exception) ExceptionTo *to1 = new ExceptionTo(pins1, nullptr, insts1, to->transition(), to->endTransition(), true, network_); ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception1->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception1->to_string(network_)); addException2(exception1); ExceptionFrom *from2 = exception->from() ? exception->from()->clone(network_):nullptr; @@ -4326,8 +4326,8 @@ Sdc::addException1(ExceptionPath *exception) ExceptionTo *to2 = new ExceptionTo(nullptr, clks2, nullptr, to->transition(), to->endTransition(), true, network_); ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); - debugPrint(debug_, "exception_merge", 1, " split exception for %s", - exception2->asString(network_)); + debugPrint(debug_, "exception_merge", 1, " split exception for {}", + exception2->to_string(network_)); addException2(exception2); delete exception; @@ -4389,8 +4389,8 @@ Sdc::addException2(ExceptionPath *exception) void Sdc::deleteMatchingExceptions(ExceptionPath *exception) { - debugPrint(debug_, "exception_merge", 1, "find matches for %s", - exception->asString(network_)); + debugPrint(debug_, "exception_merge", 1, "find matches for {}", + exception->to_string(network_)); ExceptionPathSet matches; findMatchingExceptions(exception, matches); @@ -4676,10 +4676,10 @@ Sdc::recordMergeHash(ExceptionPath *exception, { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, - "record merge hash %zu %s missing %s", + "record merge hash {} {} missing {}", hash, - exception->asString(network_), - missing_pt->asString(network_)); + exception->to_string(network_), + missing_pt->to_string(network_)); ExceptionPathSet &set = exception_merge_hash_[hash]; set.insert(exception); } @@ -4860,10 +4860,10 @@ Sdc::findMergeMatch(ExceptionPath *exception) // search at the endpoint. && exception->mergeable(match) && match->mergeablePts(exception, missing_pt, match_missing_pt)) { - debugPrint(debug_, "exception_merge", 1, "merge %s", - exception->asString(network_)); - debugPrint(debug_, "exception_merge", 1, " with %s", - match->asString(network_)); + debugPrint(debug_, "exception_merge", 1, "merge {}", + exception->to_string(network_)); + debugPrint(debug_, "exception_merge", 1, " with {}", + match->to_string(network_)); // Unrecord the exception that is being merged away. unrecordException(exception); unrecordMergeHashes(match); @@ -4965,8 +4965,8 @@ Sdc::deleteExceptionsReferencing(Clock *clk) void Sdc::deleteException(ExceptionPath *exception) { - debugPrint(debug_, "exception_merge", 2, "delete %s", - exception->asString(network_)); + debugPrint(debug_, "exception_merge", 2, "delete {}", + exception->to_string(network_)); unrecordException(exception); delete exception; } @@ -4996,10 +4996,10 @@ Sdc::unrecordMergeHash(ExceptionPath *exception, { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, - "unrecord merge hash %zu %s missing %s", + "unrecord merge hash {} {} missing {}", hash, - exception->asString(network_), - missing_pt->asString(network_)); + exception->to_string(network_), + missing_pt->to_string(network_)); auto itr = exception_merge_hash_.find(hash); if (itr != exception_merge_hash_.end()) { ExceptionPathSet &matches = itr->second; @@ -5214,8 +5214,8 @@ Sdc::resetPath(ExceptionFrom *from, for (auto itr = exceptions_.begin(); itr != exceptions_.end(); ) { ExceptionPath *match = *itr; if (match->resetMatch(from, thrus, to, min_max, network_)) { - debugPrint(debug_, "exception_match", 3, "reset match %s", - match->asString(network_)); + debugPrint(debug_, "exception_match", 3, "reset match {}", + match->to_string(network_)); ExceptionPathSet expansions; expandException(match, expansions); itr = exceptions_.erase(itr); diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 1347ac25..0b0251f1 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -860,8 +860,6 @@ make_group_path(const char *name, { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); - if (name[0] == '\0') - name = nullptr; sta->makeGroupPath(name, is_default, from, thrus, to, comment, sdc); } @@ -975,6 +973,14 @@ clock_groups_make_group(ClockGroups *clk_groups, sta->makeClockGroup(clk_groups, clks, sdc); } +void +unset_clock_groups_logically_exclusive() +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsLogicallyExclusive(sdc); +} + void unset_clock_groups_logically_exclusive(const char *name) { @@ -983,6 +989,14 @@ unset_clock_groups_logically_exclusive(const char *name) sta->removeClockGroupsLogicallyExclusive(name, sdc); } +void +unset_clock_groups_physically_exclusive() +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsPhysicallyExclusive(sdc); +} + void unset_clock_groups_physically_exclusive(const char *name) { @@ -991,6 +1005,14 @@ unset_clock_groups_physically_exclusive(const char *name) sta->removeClockGroupsPhysicallyExclusive(name, sdc); } +void +unset_clock_groups_asynchronous() +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsAsynchronous(sdc); +} + void unset_clock_groups_asynchronous(const char *name) { diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index 50f49e95..26196d2f 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -1435,11 +1435,11 @@ proc unset_clk_groups_cmd { cmd cmd_args } { if { $all } { if { $logically_exclusive } { - unset_clock_groups_logically_exclusive "NULL" + unset_clock_groups_logically_exclusive_all } elseif { $physically_exclusive } { - unset_clock_groups_physically_exclusive "NULL" + unset_clock_groups_physically_exclusive_all } elseif { $asynchronous } { - unset_clock_groups_asynchronous "NULL" + unset_clock_groups_asynchronous_all } } else { foreach name $names { diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index cd58f136..7688fc68 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -32,6 +32,7 @@ #include #include "ContainerHelpers.hh" +#include "Format.hh" #include "Zlib.hh" #include "Report.hh" #include "Error.hh" @@ -148,7 +149,7 @@ void WriteGetPinAndClkKey::write() const { writer_->writeClockKey(clk_); - gzprintf(writer_->stream(), " "); + sta::print(writer_->stream(), " "); writer_->writeGetPin(pin_, map_hpin_to_drvr_); } @@ -356,18 +357,18 @@ void WriteSdc::writeHeader() const { writeCommentSeparator(); - gzprintf(stream_, "# Created by %s\n", creator_); + sta::print(stream_, "# Created by {}\n", creator_); if (!no_timestamp_) { time_t now; time(&now); char *time_str = ctime(&now); // Remove trailing \n. time_str[strlen(time_str) - 1] = '\0'; - gzprintf(stream_, "# %s\n", time_str); + sta::print(stream_, "# {}\n", time_str); } writeCommentSeparator(); - gzprintf(stream_, "current_design %s\n", sdc_network_->name(cell_)); + sta::print(stream_, "current_design {}\n", sdc_network_->name(cell_)); } //////////////////////////////////////////////////////////////// @@ -404,9 +405,9 @@ WriteSdc::writeClocks() const writeClockSlews(clk); writeClockUncertainty(clk); if (clk->isPropagated()) { - gzprintf(stream_, "set_propagated_clock "); + sta::print(stream_, "set_propagated_clock "); writeGetClock(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -414,69 +415,69 @@ WriteSdc::writeClocks() const void WriteSdc::writeClock(Clock *clk) const { - gzprintf(stream_, "create_clock -name %s", - clk->name()); + sta::print(stream_, "create_clock -name {}", + clk->name()); if (clk->addToPins()) - gzprintf(stream_, " -add"); - gzprintf(stream_, " -period "); + sta::print(stream_, " -add"); + sta::print(stream_, " -period "); float period = clk->period(); writeTime(period); FloatSeq *waveform = clk->waveform(); if (!(waveform->size() == 2 && (*waveform)[0] == 0.0 && fuzzyEqual((*waveform)[1], period / 2.0))) { - gzprintf(stream_, " -waveform "); + sta::print(stream_, " -waveform "); writeFloatSeq(waveform, scaleTime(1.0)); } writeCmdComment(clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeClockPins(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeGeneratedClock(Clock *clk) const { - gzprintf(stream_, "create_generated_clock -name %s", - clk->name()); + sta::print(stream_, "create_generated_clock -name {}", + clk->name()); if (clk->addToPins()) - gzprintf(stream_, " -add"); - gzprintf(stream_, " -source "); + sta::print(stream_, " -add"); + sta::print(stream_, " -source "); writeGetPin(clk->srcPin(), true); Clock *master = clk->masterClk(); if (master && !clk->masterClkInfered()) { - gzprintf(stream_, " -master_clock "); + sta::print(stream_, " -master_clock "); writeGetClock(master); } if (clk->combinational()) - gzprintf(stream_, " -combinational"); + sta::print(stream_, " -combinational"); int divide_by = clk->divideBy(); if (divide_by != 0) - gzprintf(stream_, " -divide_by %d", divide_by); + sta::print(stream_, " -divide_by {}", divide_by); int multiply_by = clk->multiplyBy(); if (multiply_by != 0) - gzprintf(stream_, " -multiply_by %d", multiply_by); + sta::print(stream_, " -multiply_by {}", multiply_by); float duty_cycle = clk->dutyCycle(); if (duty_cycle != 0.0) { - gzprintf(stream_, " -duty_cycle "); + sta::print(stream_, " -duty_cycle "); writeFloat(duty_cycle); } if (clk->invert()) - gzprintf(stream_, " -invert"); + sta::print(stream_, " -invert"); IntSeq *edges = clk->edges(); if (edges && !edges->empty()) { - gzprintf(stream_, " -edges "); + sta::print(stream_, " -edges "); writeIntSeq(edges); FloatSeq *edge_shifts = clk->edgeShifts(); if (edge_shifts && !edge_shifts->empty()) { - gzprintf(stream_, " -edge_shift "); + sta::print(stream_, " -edge_shift "); writeFloatSeq(edge_shifts, scaleTime(1.0)); } } writeCmdComment(clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeClockPins(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -485,7 +486,7 @@ WriteSdc::writeClockPins(const Clock *clk) const const PinSet &pins = clk->pins(); if (!pins.empty()) { if (pins.size() > 1) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPins(&pins, true); } } @@ -523,9 +524,9 @@ WriteSdc::writeClockUncertainty(const Clock *clk, const char *setup_hold, float value) const { - gzprintf(stream_, "set_clock_uncertainty %s", setup_hold); + sta::print(stream_, "set_clock_uncertainty {}", setup_hold); writeTime(value); - gzprintf(stream_, " %s\n", clk->name()); + sta::print(stream_, " {}\n", clk->name()); } void @@ -560,11 +561,11 @@ WriteSdc::writeClockUncertaintyPin(const Pin *pin, const char *setup_hold, float value) const { - gzprintf(stream_, "set_clock_uncertainty %s", setup_hold); + sta::print(stream_, "set_clock_uncertainty {}", setup_hold); writeTime(value); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPin(pin, true); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -633,9 +634,9 @@ void WriteSdc::writePropagatedClkPins() const { for (const Pin *pin : sdc_->propagated_clk_pins_) { - gzprintf(stream_, "set_propagated_clock "); + sta::print(stream_, "set_propagated_clock "); writeGetPin(pin, true); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -659,13 +660,13 @@ writeInterClockUncertainty(InterClockUncertainty *uncertainty) const float value; if (src_rise->equal(src_fall) && src_rise->isOneValue(value)) { - gzprintf(stream_, "set_clock_uncertainty -from "); + sta::print(stream_, "set_clock_uncertainty -from "); writeGetClock(src_clk); - gzprintf(stream_, " -to "); + sta::print(stream_, " -to "); writeGetClock(tgt_clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeTime(value); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } else { for (auto src_rf : RiseFall::range()) { @@ -676,16 +677,16 @@ writeInterClockUncertainty(InterClockUncertainty *uncertainty) const sdc_->clockUncertainty(src_clk, src_rf, tgt_clk, tgt_rf, setup_hold, value, exists); if (exists) { - gzprintf(stream_, "set_clock_uncertainty -%s_from ", - src_rf == RiseFall::rise() ? "rise" : "fall"); + sta::print(stream_, "set_clock_uncertainty -{}_from ", + src_rf == RiseFall::rise() ? "rise" : "fall"); writeGetClock(uncertainty->src()); - gzprintf(stream_, " -%s_to ", - tgt_rf == RiseFall::rise() ? "rise" : "fall"); + sta::print(stream_, " -{}_to ", + tgt_rf == RiseFall::rise() ? "rise" : "fall"); writeGetClock(uncertainty->target()); - gzprintf(stream_, " %s ", - setupHoldFlag(setup_hold)); + sta::print(stream_, " {} ", + setupHoldFlag(setup_hold)); writeTime(value); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -794,25 +795,25 @@ WriteSdc::writePortDelay(PortDelay *port_delay, const MinMaxAll *min_max, const char *sdc_cmd) const { - gzprintf(stream_, "%s ", sdc_cmd); + sta::print(stream_, "{} ", sdc_cmd); writeTime(delay); const ClockEdge *clk_edge = port_delay->clkEdge(); if (clk_edge) { writeClockKey(clk_edge->clock()); if (clk_edge->transition() == RiseFall::fall()) - gzprintf(stream_, " -clock_fall"); + sta::print(stream_, " -clock_fall"); } - gzprintf(stream_, "%s%s -add_delay ", - transRiseFallFlag(rf), - minMaxFlag(min_max)); + sta::print(stream_, "{}{} -add_delay ", + transRiseFallFlag(rf), + minMaxFlag(min_max)); const Pin *ref_pin = port_delay->refPin(); if (ref_pin) { - gzprintf(stream_, "-reference_pin "); + sta::print(stream_, "-reference_pin "); writeGetPin(ref_pin, true); - gzprintf(stream_, " "); + sta::print(stream_, " "); } writeGetPin(port_delay->pin(), is_input_delay); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } class PinClockPairNameLess @@ -876,15 +877,15 @@ WriteSdc::writeClockSense(PinClockPair &pin_clk, flag = "-negative"; else if (sense == ClockSense::stop) flag = "-stop_propagation"; - gzprintf(stream_, "set_sense -type clock %s ", flag); + sta::print(stream_, "set_sense -type clock {} ", flag); const Clock *clk = pin_clk.second; if (clk) { - gzprintf(stream_, "-clock "); + sta::print(stream_, "-clock "); writeGetClock(clk); - gzprintf(stream_, " "); + sta::print(stream_, " "); } writeGetPin(pin_clk.first, true); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } class ClockGroupLess @@ -935,22 +936,22 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1, void WriteSdc::writeClockGroups() const { - for (const auto [name, clk_groups] : sdc_->clk_groups_name_map_) + for (const auto &[name, clk_groups] : sdc_->clk_groups_name_map_) writeClockGroups(clk_groups); } void WriteSdc::writeClockGroups(ClockGroups *clk_groups) const { - gzprintf(stream_, "set_clock_groups -name %s ", clk_groups->name()); + sta::print(stream_, "set_clock_groups -name {} ", clk_groups->name()); if (clk_groups->logicallyExclusive()) - gzprintf(stream_, "-logically_exclusive \\\n"); + sta::print(stream_, "-logically_exclusive \\\n"); else if (clk_groups->physicallyExclusive()) - gzprintf(stream_, "-physically_exclusive \\\n"); + sta::print(stream_, "-physically_exclusive \\\n"); else if (clk_groups->asynchronous()) - gzprintf(stream_, "-asynchronous \\\n"); + sta::print(stream_, "-asynchronous \\\n"); if (clk_groups->allowPaths()) - gzprintf(stream_, "-allow_paths \\\n"); + sta::print(stream_, "-allow_paths \\\n"); std::vector groups; for (ClockGroup *clk_group : *clk_groups->groups()) groups.push_back(clk_group); @@ -958,13 +959,13 @@ WriteSdc::writeClockGroups(ClockGroups *clk_groups) const bool first = true; for (ClockGroup *clk_group : groups) { if (!first) - gzprintf(stream_, "\\\n"); - gzprintf(stream_, " -group "); + sta::print(stream_, "\\\n"); + sta::print(stream_, " -group "); writeGetClocks(clk_group); first = false; } writeCmdComment(clk_groups); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -987,46 +988,46 @@ WriteSdc::writeDisabledCells() const for (const DisabledCellPorts *disable : disables) { const LibertyCell *cell = disable->cell(); if (disable->all()) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetLibCell(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } if (disable->fromTo()) { LibertyPortPairSeq from_tos = sortByName(disable->fromTo()); for (const LibertyPortPair &from_to : from_tos) { const LibertyPort *from = from_to.first; const LibertyPort *to = from_to.second; - gzprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", - from->name(), - to->name()); + sta::print(stream_, "set_disable_timing -from {{{}}} -to {{{}}} ", + from->name(), + to->name()); writeGetLibCell(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } if (disable->from()) { LibertyPortSeq from = sortByName(disable->from()); for (const LibertyPort *from_port : from) { - gzprintf(stream_, "set_disable_timing -from {%s} ", - from_port->name()); + sta::print(stream_, "set_disable_timing -from {{{}}} ", + from_port->name()); writeGetLibCell(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } if (disable->to()) { LibertyPortSeq to = sortByName(disable->to()); for (const LibertyPort *to_port : to) { - gzprintf(stream_, "set_disable_timing -to {%s} ", - to_port->name()); + sta::print(stream_, "set_disable_timing -to {{{}}} ", + to_port->name()); writeGetLibCell(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } if (disable->timingArcSets()) { // The only syntax to disable timing arc sets disables all of the // cell's timing arc sets. - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetTimingArcsOfOjbects(cell); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -1036,9 +1037,9 @@ WriteSdc::writeDisabledPorts() const { const PortSeq ports = sortByName(sdc_->disabledPorts(), sdc_network_); for (const Port *port : ports) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -1047,9 +1048,9 @@ WriteSdc::writeDisabledLibPorts() const { LibertyPortSeq ports = sortByName(sdc_->disabledLibPorts()); for (LibertyPort *port : ports) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetLibPin(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -1061,38 +1062,38 @@ WriteSdc::writeDisabledInstances() const for (DisabledInstancePorts *disable : disables) { Instance *inst = disable->instance(); if (disable->all()) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } else if (disable->fromTo()) { LibertyPortPairSeq from_tos = sortByName(disable->fromTo()); for (LibertyPortPair &from_to : from_tos) { const LibertyPort *from_port = from_to.first; const LibertyPort *to_port = from_to.second; - gzprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", - from_port->name(), - to_port->name()); + sta::print(stream_, "set_disable_timing -from {{{}}} -to {{{}}} ", + from_port->name(), + to_port->name()); writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } if (disable->from()) { LibertyPortSeq from = sortByName(disable->from()); for (const LibertyPort *from_port : from) { - gzprintf(stream_, "set_disable_timing -from {%s} ", - from_port->name()); + sta::print(stream_, "set_disable_timing -from {{{}}} ", + from_port->name()); writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } if (disable->to()) { LibertyPortSeq to = sortByName(disable->to()); for (const LibertyPort *to_port : to) { - gzprintf(stream_, "set_disable_timing -to {%s} ", - to_port->name()); + sta::print(stream_, "set_disable_timing -to {{{}}} ", + to_port->name()); writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -1103,9 +1104,9 @@ WriteSdc::writeDisabledPins() const { PinSeq pins = sortByPathName(sdc_->disabledPins(), sdc_network_); for (const Pin *pin : pins) { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -1156,20 +1157,19 @@ WriteSdc::edgeSenseIsUnique(Edge *edge, void WriteSdc::writeDisabledEdge(Edge *edge) const { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); writeGetTimingArcs(edge); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeDisabledEdgeSense(Edge *edge) const { - gzprintf(stream_, "set_disable_timing "); + sta::print(stream_, "set_disable_timing "); const char *sense = to_string(edge->sense()); - std::string filter; - stringPrint(filter, "sense == %s", sense); + std::string filter = sta::format("sense == {}", sense); writeGetTimingArcs(edge, filter.c_str()); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -1202,44 +1202,44 @@ WriteSdc::writeException(ExceptionPath *exception) const writeExceptionTo(exception->to()); writeExceptionValue(exception); writeCmdComment(exception); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeExceptionCmd(ExceptionPath *exception) const { if (exception->isFalse()) { - gzprintf(stream_, "set_false_path"); + sta::print(stream_, "set_false_path"); writeSetupHoldFlag(exception->minMax()); } else if (exception->isMultiCycle()) { - gzprintf(stream_, "set_multicycle_path"); + sta::print(stream_, "set_multicycle_path"); const MinMaxAll *min_max = exception->minMax(); writeSetupHoldFlag(min_max); if (min_max == MinMaxAll::min()) { // For hold MCPs default is -start. if (exception->useEndClk()) - gzprintf(stream_, " -end"); + sta::print(stream_, " -end"); } else { // For setup MCPs default is -end. if (!exception->useEndClk()) - gzprintf(stream_, " -start"); + sta::print(stream_, " -start"); } } else if (exception->isPathDelay()) { if (exception->minMax() == MinMaxAll::max()) - gzprintf(stream_, "set_max_delay"); + sta::print(stream_, "set_max_delay"); else - gzprintf(stream_, "set_min_delay"); + sta::print(stream_, "set_min_delay"); if (exception->ignoreClkLatency()) - gzprintf(stream_, " -ignore_clock_latency"); + sta::print(stream_, " -ignore_clock_latency"); } else if (exception->isGroupPath()) { if (exception->isDefault()) - gzprintf(stream_, "group_path -default"); + sta::print(stream_, "group_path -default"); else - gzprintf(stream_, "group_path -name %s", exception->name()); + sta::print(stream_, "group_path -name {}", exception->name()); } else report_->critical(1620, "unknown exception type"); @@ -1249,10 +1249,10 @@ void WriteSdc::writeExceptionValue(ExceptionPath *exception) const { if (exception->isMultiCycle()) - gzprintf(stream_, " %d", - exception->pathMultiplier()); + sta::print(stream_, " {}", + exception->pathMultiplier()); else if (exception->isPathDelay()) { - gzprintf(stream_, " "); + sta::print(stream_, " "); writeTime(exception->delay()); } } @@ -1268,7 +1268,7 @@ WriteSdc::writeExceptionTo(ExceptionTo *to) const { const RiseFallBoth *end_rf = to->endTransition(); if (end_rf != RiseFallBoth::riseFall()) - gzprintf(stream_, "%s ", transRiseFallFlag(end_rf)); + sta::print(stream_, "{} ", transRiseFallFlag(end_rf)); if (to->hasObjects()) writeExceptionFromTo(to, "to", false); } @@ -1284,19 +1284,19 @@ WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, rf_prefix = "-rise_"; else if (rf == RiseFallBoth::fall()) rf_prefix = "-fall_"; - gzprintf(stream_, "\\\n %s%s ", rf_prefix, from_to_key); + sta::print(stream_, "\\\n {}{} ", rf_prefix, from_to_key); bool multi_objs = ((from_to->pins() ? from_to->pins()->size() : 0) + (from_to->clks() ? from_to->clks()->size() : 0) + (from_to->instances() ? from_to->instances()->size() : 0)) > 1; if (multi_objs) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); bool first = true; if (from_to->pins()) { PinSeq pins = sortByPathName(from_to->pins(), sdc_network_); for (const Pin *pin : pins) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPin(pin, map_hpin_to_drvr); first = false; } @@ -1307,13 +1307,13 @@ WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, InstanceSeq insts = sortByPathName(from_to->instances(), sdc_network_); for (const Instance *inst : insts) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetInstance(inst); first = false; } } if (multi_objs) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void @@ -1325,7 +1325,7 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const rf_prefix = "-rise_"; else if (rf == RiseFallBoth::fall()) rf_prefix = "-fall_"; - gzprintf(stream_, "\\\n %sthrough ", rf_prefix); + sta::print(stream_, "\\\n {}through ", rf_prefix); PinSeq pins; mapThruHpins(thru, pins); bool multi_objs = @@ -1333,12 +1333,12 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const + (thru->nets() ? thru->nets()->size() : 0) + (thru->instances() ? thru->instances()->size() : 0)) > 1; if (multi_objs) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); bool first = true; sort(pins, PinPathNameLess(network_)); for (const Pin *pin : pins) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPin(pin); first = false; } @@ -1347,7 +1347,7 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const NetSeq nets = sortByPathName(thru->nets(), sdc_network_); for (const Net *net : nets) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetNet(net); first = false; } @@ -1356,13 +1356,13 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const InstanceSeq insts = sortByPathName(thru->instances(), sdc_network_); for (const Instance *inst : insts) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetInstance(inst); first = false; } } if (multi_objs) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void @@ -1446,19 +1446,19 @@ WriteSdc::writeDataCheck(DataCheck *check, from_key = "-rise_from"; else if (from_rf == RiseFallBoth::fall()) from_key = "-fall_from"; - gzprintf(stream_, "set_data_check %s ", from_key); + sta::print(stream_, "set_data_check {} ", from_key); writeGetPin(check->from(), true); const char *to_key = "-to"; if (to_rf == RiseFallBoth::rise()) to_key = "-rise_to"; else if (to_rf == RiseFallBoth::fall()) to_key = "-fall_to"; - gzprintf(stream_, " %s ", to_key); + sta::print(stream_, " {} ", to_key); writeGetPin(check->to(), false); - gzprintf(stream_, "%s ", - setupHoldFlag(setup_hold)); + sta::print(stream_, "{} ", + setupHoldFlag(setup_hold)); writeTime(margin); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -1486,7 +1486,7 @@ WriteSdc::writeOperatingConditions() const { OperatingConditions *cond = sdc_->operatingConditions(MinMax::max()); if (cond) - gzprintf(stream_, "set_operating_conditions %s\n", cond->name()); + sta::print(stream_, "set_operating_conditions {}\n", cond->name()); } void @@ -1494,8 +1494,8 @@ WriteSdc::writeWireload() const { WireloadMode wireload_mode = sdc_->wireloadMode(); if (wireload_mode != WireloadMode::unknown) - gzprintf(stream_, "set_wire_load_mode \"%s\"\n", - wireloadModeString(wireload_mode)); + sta::print(stream_, "set_wire_load_mode \"{}\"\n", + wireloadModeString(wireload_mode)); } void @@ -1523,12 +1523,12 @@ WriteSdc::writeNetLoad(const Net *net, const MinMaxAll *min_max, float cap) const { - gzprintf(stream_, "set_load "); - gzprintf(stream_, "%s ", minMaxFlag(min_max)); + sta::print(stream_, "set_load "); + sta::print(stream_, "{} ", minMaxFlag(min_max)); writeCapacitance(cap); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetNet(net); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -1572,12 +1572,12 @@ WriteSdc::writeDriveResistances() const float res; bool exists; drive->driveResistance(rf, MinMax::max(), res, exists); - gzprintf(stream_, "set_drive %s ", - transRiseFallFlag(rf)); + sta::print(stream_, "set_drive {} ", + transRiseFallFlag(rf)); writeResistance(res); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } else { for (auto min_max : MinMax::range()) { @@ -1585,13 +1585,13 @@ WriteSdc::writeDriveResistances() const bool exists; drive->driveResistance(rf, min_max, res, exists); if (exists) { - gzprintf(stream_, "set_drive %s %s ", - transRiseFallFlag(rf), - minMaxFlag(min_max)); + sta::print(stream_, "set_drive {} {} ", + transRiseFallFlag(rf), + minMaxFlag(min_max)); writeResistance(res); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -1668,27 +1668,27 @@ WriteSdc::writeDrivingCell(Port *port, const LibertyPort *to_port = drive_cell->toPort(); float *from_slews = drive_cell->fromSlews(); const LibertyLibrary *lib = drive_cell->library(); - gzprintf(stream_, "set_driving_cell"); + sta::print(stream_, "set_driving_cell"); if (rf) - gzprintf(stream_, " %s", transRiseFallFlag(rf)); + sta::print(stream_, " {}", transRiseFallFlag(rf)); if (min_max) - gzprintf(stream_, " %s", minMaxFlag(min_max)); + sta::print(stream_, " {}", minMaxFlag(min_max)); // Only write -library if it was specified in the sdc. if (lib) - gzprintf(stream_, " -library %s", lib->name()); - gzprintf(stream_, " -lib_cell %s", cell->name()); + sta::print(stream_, " -library {}", lib->name()); + sta::print(stream_, " -lib_cell {}", cell->name()); if (from_port) - gzprintf(stream_, " -from_pin {%s}", - from_port->name()); - gzprintf(stream_, - " -pin {%s} -input_transition_rise ", - to_port->name()); + sta::print(stream_, " -from_pin {{{}}}", + from_port->name()); + sta::print(stream_, + " -pin {{{}}} -input_transition_rise ", + to_port->name()); writeTime(from_slews[RiseFall::riseIndex()]); - gzprintf(stream_, " -input_transition_fall "); + sta::print(stream_, " -input_transition_fall "); writeTime(from_slews[RiseFall::fallIndex()]); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -1736,11 +1736,11 @@ WriteSdc::writeNetResistance(const Net *net, const MinMaxAll *min_max, float res) const { - gzprintf(stream_, "set_resistance "); + sta::print(stream_, "set_resistance "); writeResistance(res); - gzprintf(stream_, "%s ", minMaxFlag(min_max)); + sta::print(stream_, "{} ", minMaxFlag(min_max)); writeGetNet(net); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -1756,9 +1756,9 @@ void WriteSdc::writeConstant(const Pin *pin) const { const char *cmd = setConstantCmd(pin); - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } const char * @@ -1796,9 +1796,9 @@ void WriteSdc::writeCaseAnalysis(const Pin *pin) const { const char *value_str = caseAnalysisValueStr(pin); - gzprintf(stream_, "set_case_analysis %s ", value_str); + sta::print(stream_, "set_case_analysis {} ", value_str); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } const char * @@ -1882,9 +1882,9 @@ WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const && (!cell_check_factors->hasValue() || (check_is_one_value && check_value == 1.0))) { if (delay_value != 1.0) { - gzprintf(stream_, "set_timing_derate %s ", earlyLateFlag(early_late)); + sta::print(stream_, "set_timing_derate {} ", earlyLateFlag(early_late)); writeFloat(delay_value); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } else { @@ -1923,15 +1923,15 @@ WriteSdc::writeDerating(DeratingFactors *factors, factors->isOneValue(early_late, is_one_value, value); if (is_one_value) { if (value != 1.0) { - gzprintf(stream_, "set_timing_derate %s %s ", - type_key, - earlyLateFlag(early_late)); + sta::print(stream_, "set_timing_derate {} {} ", + type_key, + earlyLateFlag(early_late)); writeFloat(value); if (write_obj) { - gzprintf(stream_, " "); + sta::print(stream_, " "); write_obj->write(); } - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } else { @@ -1944,16 +1944,16 @@ WriteSdc::writeDerating(DeratingFactors *factors, factors->isOneValue(clk_data, early_late, is_one_value, value); if (is_one_value) { if (value != 1.0) { - gzprintf(stream_, "set_timing_derate %s %s %s ", - type_key, - earlyLateFlag(early_late), - clk_data_key); + sta::print(stream_, "set_timing_derate {} {} {} ", + type_key, + earlyLateFlag(early_late), + clk_data_key); writeFloat(value); if (write_obj) { - gzprintf(stream_, " "); + sta::print(stream_, " "); write_obj->write(); } - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } else { @@ -1962,17 +1962,17 @@ WriteSdc::writeDerating(DeratingFactors *factors, bool exists; factors->factor(clk_data, rf, early_late, factor, exists); if (exists) { - gzprintf(stream_, "set_timing_derate %s %s %s %s ", - type_key, - clk_data_key, - transRiseFallFlag(rf), - earlyLateFlag(early_late)); + sta::print(stream_, "set_timing_derate {} {} {} {} ", + type_key, + clk_data_key, + transRiseFallFlag(rf), + earlyLateFlag(early_late)); writeFloat(factor); if (write_obj) { - gzprintf(stream_, " "); + sta::print(stream_, " "); write_obj->write(); } - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -1998,11 +1998,11 @@ WriteSdc::writeVoltages() const if (exists_max) { sdc_->voltage(MinMax::min(), voltage_min, exists_min); if (exists_min) - gzprintf(stream_, "set_voltage -min %.3f %.3f\n", - voltage_min, - voltage_max); + sta::print(stream_, "set_voltage -min {:.3f} {:.3f}\n", + voltage_min, + voltage_max); else - gzprintf(stream_, "set_voltage %.3f\n", voltage_max); + sta::print(stream_, "set_voltage {:.3f}\n", voltage_max); } for (const auto& [net, volts] : sdc_->net_voltage_map_) { @@ -2010,14 +2010,14 @@ WriteSdc::writeVoltages() const if (exists_max) { volts.value(MinMax::min(), voltage_min, exists_min); if (exists_min) - gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n", - sdc_network_->pathName(net), - voltage_min, - voltage_max); + sta::print(stream_, "set_voltage -object_list {} -min {:.3f} {:.3f}\n", + sdc_network_->pathName(net), + voltage_min, + voltage_max); else - gzprintf(stream_, "set_voltage -object_list %s %.3f\n", - sdc_network_->pathName(net), - voltage_max); + sta::print(stream_, "set_voltage -object_list {} {:.3f}\n", + sdc_network_->pathName(net), + voltage_max); } } } @@ -2079,11 +2079,11 @@ WriteSdc::writeMinPulseWidth(const char *hi_low, float value, WriteSdcObject &write_obj) const { - gzprintf(stream_, "set_min_pulse_width %s", hi_low); + sta::print(stream_, "set_min_pulse_width {}", hi_low); writeTime(value); - gzprintf(stream_, " "); + sta::print(stream_, " "); write_obj.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -2092,27 +2092,27 @@ void WriteSdc::writeLatchBorowLimits() const { for (const auto [pin, limit] : sdc_->pin_latch_borrow_limit_map_) { - gzprintf(stream_, "set_max_time_borrow "); + sta::print(stream_, "set_max_time_borrow "); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } for (const auto [inst, limit] : sdc_->inst_latch_borrow_limit_map_) { - gzprintf(stream_, "set_max_time_borrow "); + sta::print(stream_, "set_max_time_borrow "); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetInstance(inst); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } for (const auto [clk, limit] : sdc_->clk_latch_borrow_limit_map_) { - gzprintf(stream_, "set_max_time_borrow "); + sta::print(stream_, "set_max_time_borrow "); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetClock(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -2126,9 +2126,9 @@ WriteSdc::writeSlewLimits() const bool exists; sdc_->slewLimit(cell_, min_max, slew, exists); if (exists) { - gzprintf(stream_, "set_max_transition "); + sta::print(stream_, "set_max_transition "); writeTime(slew); - gzprintf(stream_, " [current_design]\n"); + sta::print(stream_, " [current_design]\n"); } CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); @@ -2136,11 +2136,11 @@ WriteSdc::writeSlewLimits() const Port *port = port_iter->next(); sdc_->slewLimit(port, min_max, slew, exists); if (exists) { - gzprintf(stream_, "set_max_transition "); + sta::print(stream_, "set_max_transition "); writeTime(slew); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } delete port_iter; @@ -2200,11 +2200,11 @@ WriteSdc::writeClkSlewLimit(const char *clk_data, const Clock *clk, float limit) const { - gzprintf(stream_, "set_max_transition %s%s", clk_data, rise_fall); + sta::print(stream_, "set_max_transition {}{}", clk_data, rise_fall); writeTime(limit); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetClock(clk); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -2222,9 +2222,9 @@ WriteSdc::writeCapLimits(const MinMax *min_max, bool exists; sdc_->capacitanceLimit(cell_, min_max, cap, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeCapacitance(cap); - gzprintf(stream_, " [current_design]\n"); + sta::print(stream_, " [current_design]\n"); } for (const auto [port, limits] : sdc_->port_cap_limit_map_) { @@ -2232,11 +2232,11 @@ WriteSdc::writeCapLimits(const MinMax *min_max, bool exists; limits.value(min_max, cap, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeCapacitance(cap); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -2245,11 +2245,11 @@ WriteSdc::writeCapLimits(const MinMax *min_max, bool exists; limits.value(min_max, cap, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeCapacitance(cap); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPin(pin, false); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } } @@ -2259,9 +2259,9 @@ WriteSdc::writeMaxArea() const { float max_area = sdc_->maxArea(); if (max_area > 0.0) { - gzprintf(stream_, "set_max_area "); + sta::print(stream_, "set_max_area "); writeFloat(max_area); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } @@ -2280,9 +2280,9 @@ WriteSdc::writeFanoutLimits(const MinMax *min_max, bool exists; sdc_->fanoutLimit(cell_, min_max, fanout, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeFloat(fanout); - gzprintf(stream_, " [current_design]\n"); + sta::print(stream_, " [current_design]\n"); } else { CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); @@ -2290,11 +2290,11 @@ WriteSdc::writeFanoutLimits(const MinMax *min_max, Port *port = port_iter->next(); sdc_->fanoutLimit(port, min_max, fanout, exists); if (exists) { - gzprintf(stream_, "%s ", cmd); + sta::print(stream_, "{} ", cmd); writeFloat(fanout); - gzprintf(stream_, " "); + sta::print(stream_, " "); writeGetPort(port); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } } delete port_iter; @@ -2308,15 +2308,15 @@ WriteSdc::writeVariables() const { if (variables_->propagateAllClocks()) { if (native_) - gzprintf(stream_, "set sta_propagate_all_clocks 1\n"); + sta::print(stream_, "set sta_propagate_all_clocks 1\n"); else - gzprintf(stream_, "set timing_all_clocks_propagated true\n"); + sta::print(stream_, "set timing_all_clocks_propagated true\n"); } if (variables_->presetClrArcsEnabled()) { if (native_) - gzprintf(stream_, "set sta_preset_clear_arcs_enabled 1\n"); + sta::print(stream_, "set sta_preset_clear_arcs_enabled 1\n"); else - gzprintf(stream_, "set timing_enable_preset_clear_arcs true\n"); + sta::print(stream_, "set timing_enable_preset_clear_arcs true\n"); } } @@ -2325,9 +2325,9 @@ WriteSdc::writeVariables() const void WriteSdc::writeGetTimingArcsOfOjbects(const LibertyCell *cell) const { - gzprintf(stream_, "[%s -of_objects ", getTimingArcsCmd()); + sta::print(stream_, "[{} -of_objects ", getTimingArcsCmd()); writeGetLibCell(cell); - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void @@ -2340,15 +2340,15 @@ void WriteSdc::writeGetTimingArcs(Edge *edge, const char *filter) const { - gzprintf(stream_, "[%s -from ", getTimingArcsCmd()); + sta::print(stream_, "[{} -from ", getTimingArcsCmd()); Vertex *from_vertex = edge->from(graph_); writeGetPin(from_vertex->pin(), true); - gzprintf(stream_, " -to "); + sta::print(stream_, " -to "); Vertex *to_vertex = edge->to(graph_); writeGetPin(to_vertex->pin(), false); if (filter) - gzprintf(stream_, " -filter {%s}", filter); - gzprintf(stream_, "]"); + sta::print(stream_, " -filter {{{}}}", filter); + sta::print(stream_, "]"); } const char * @@ -2362,7 +2362,7 @@ WriteSdc::getTimingArcsCmd() const void WriteSdc::writeGetLibCell(const LibertyCell *cell) const { - gzprintf(stream_, "[get_lib_cells {%s/%s}]", + sta::print(stream_, "[get_lib_cells {{{}/{}}}]", cell->libertyLibrary()->name(), cell->name()); } @@ -2372,10 +2372,10 @@ WriteSdc::writeGetLibPin(const LibertyPort *port) const { LibertyCell *cell = port->libertyCell(); LibertyLibrary *lib = cell->libertyLibrary(); - gzprintf(stream_, "[get_lib_pins {%s/%s/%s}]", - lib->name(), - cell->name(), - port->name()); + sta::print(stream_, "[get_lib_pins {{{}/{}/{}}}]", + lib->name(), + cell->name(), + port->name()); } void @@ -2384,10 +2384,10 @@ WriteSdc::writeGetClocks(ClockSet *clks) const bool first = true; bool multiple = clks->size() > 1; if (multiple) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); writeGetClocks(clks, multiple, first); if (multiple) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void @@ -2398,7 +2398,7 @@ WriteSdc::writeGetClocks(ClockSet *clks, ClockSeq clks1 = sortByName(clks); for (const Clock *clk : clks1) { if (multiple && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetClock(clk); first = false; } @@ -2407,14 +2407,14 @@ WriteSdc::writeGetClocks(ClockSet *clks, void WriteSdc::writeGetClock(const Clock *clk) const { - gzprintf(stream_, "[get_clocks {%s}]", - clk->name()); + sta::print(stream_, "[get_clocks {{{}}}]", + clk->name()); } void WriteSdc::writeGetPort(const Port *port) const { - gzprintf(stream_, "[get_ports {%s}]", sdc_network_->name(port)); + sta::print(stream_, "[get_ports {{{}}}]", sdc_network_->name(port)); } void @@ -2447,25 +2447,25 @@ WriteSdc::writeGetPins1(PinSeq *pins) const { bool multiple = pins->size() > 1; if (multiple) - gzprintf(stream_, "[list "); + sta::print(stream_, "[list "); bool first = true; for (const Pin *pin : *pins) { if (multiple && !first) - gzprintf(stream_, "\\\n "); + sta::print(stream_, "\\\n "); writeGetPin(pin); first = false; } if (multiple) - gzprintf(stream_, "]"); + sta::print(stream_, "]"); } void WriteSdc::writeGetPin(const Pin *pin) const { if (sdc_network_->instance(pin) == instance_) - gzprintf(stream_, "[get_ports {%s}]", sdc_network_->portName(pin)); + sta::print(stream_, "[get_ports {{{}}}]", sdc_network_->portName(pin)); else - gzprintf(stream_, "[get_pins {%s}]", pathName(pin)); + sta::print(stream_, "[get_pins {{{}}}]", pathName(pin)); } void @@ -2484,13 +2484,13 @@ WriteSdc::writeGetPin(const Pin *pin, void WriteSdc::writeGetNet(const Net *net) const { - gzprintf(stream_, "[get_nets {%s}]", pathName(net)); + sta::print(stream_, "[get_nets {{{}}}]", pathName(net)); } void WriteSdc::writeGetInstance(const Instance *inst) const { - gzprintf(stream_, "[get_cells {%s}]", pathName(inst)); + sta::print(stream_, "[get_cells {{{}}}]", pathName(inst)); } const char * @@ -2527,14 +2527,14 @@ void WriteSdc::writeCommentSection(const char *line) const { writeCommentSeparator(); - gzprintf(stream_, "# %s\n", line); + sta::print(stream_, "# {}\n", line); writeCommentSeparator(); } void WriteSdc::writeCommentSeparator() const { - gzprintf(stream_, "###############################################################################\n"); + sta::print(stream_, "###############################################################################\n"); } //////////////////////////////////////////////////////////////// @@ -2632,20 +2632,20 @@ WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, const MinMaxAll *min_max, WriteSdcObject &write_object) const { - gzprintf(stream_, "%s%s%s ", - sdc_cmd, - transRiseFallFlag(rf), - minMaxFlag(min_max)); + sta::print(stream_, "{}{}{} ", + sdc_cmd, + transRiseFallFlag(rf), + minMaxFlag(min_max)); writeFloat(value / scale); - gzprintf(stream_, " "); + sta::print(stream_, " "); write_object.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void WriteSdc::writeClockKey(const Clock *clk) const { - gzprintf(stream_, " -clock "); + sta::print(stream_, " -clock "); writeGetClock(clk); } @@ -2681,13 +2681,13 @@ WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd, const MinMaxAll *min_max, WriteSdcObject &write_object) const { - gzprintf(stream_, "%s%s ", - sdc_cmd, - minMaxFlag(min_max)); + sta::print(stream_, "{}{} ", + sdc_cmd, + minMaxFlag(min_max)); writeFloat(value / scale); - gzprintf(stream_, " "); + sta::print(stream_, " "); write_object.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } void @@ -2718,12 +2718,12 @@ WriteSdc::writeMinMaxIntCmd(const char *sdc_cmd, const MinMaxAll *min_max, WriteSdcObject &write_object) const { - gzprintf(stream_, "%s%s ", - sdc_cmd, - minMaxFlag(min_max)); - gzprintf(stream_, "%d ", value); + sta::print(stream_, "{}{} ", + sdc_cmd, + minMaxFlag(min_max)); + sta::print(stream_, "{} ", value); write_object.write(); - gzprintf(stream_, "\n"); + sta::print(stream_, "\n"); } //////////////////////////////////////////////////////////////// @@ -2749,54 +2749,54 @@ WriteSdc::scaleResistance(float res) const void WriteSdc::writeFloat(float value) const { - gzprintf(stream_, "%.*f", digits_, value); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", value, digits_)); } void WriteSdc::writeTime(float time) const { - gzprintf(stream_, "%.*f", digits_, scaleTime(time)); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", scaleTime(time), digits_)); } void WriteSdc::writeCapacitance(float cap) const { - gzprintf(stream_, "%.*f", digits_, scaleCapacitance(cap)); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", scaleCapacitance(cap), digits_)); } void WriteSdc::writeResistance(float res) const { - gzprintf(stream_, "%.*f", digits_, scaleResistance(res)); + sta::print(stream_, "{}", sta::formatRuntime("{:.{}f}", scaleResistance(res), digits_)); } void WriteSdc::writeFloatSeq(FloatSeq *floats, float scale) const { - gzprintf(stream_, "{"); + sta::print(stream_, "{{"); bool first = true; for (float flt : *floats) { if (!first) - gzprintf(stream_, " "); + sta::print(stream_, " "); writeFloat(flt * scale); first = false; } - gzprintf(stream_, "}"); + sta::print(stream_, "}}"); } void WriteSdc::writeIntSeq(IntSeq *ints) const { - gzprintf(stream_, "{"); + sta::print(stream_, "{{"); bool first = true; for (int i : *ints) { if (!first) - gzprintf(stream_, " "); - gzprintf(stream_, "%d", i); + sta::print(stream_, " "); + sta::print(stream_, "{}", i); first = false; } - gzprintf(stream_, "}"); + sta::print(stream_, "}}"); } @@ -2846,9 +2846,9 @@ void WriteSdc::writeSetupHoldFlag(const MinMaxAll *min_max) const { if (min_max == MinMaxAll::min()) - gzprintf(stream_, " -hold"); + sta::print(stream_, " -hold"); else if (min_max == MinMaxAll::max()) - gzprintf(stream_, " -setup"); + sta::print(stream_, " -setup"); } static const char * @@ -2862,7 +2862,7 @@ WriteSdc::writeCmdComment(SdcCmdComment *cmd) const { const char *comment = cmd->comment(); if (comment) { - gzprintf(stream_, " -comment {%s}", comment); + sta::print(stream_, " -comment {{{}}}", comment); } } diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index 0e516e0e..8f667b71 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "sdf/ReportAnnotation.hh" @@ -72,7 +72,8 @@ protected: count_input_net, count_output_net, }; - static const int count_index_max = static_cast(CountIndex::count_output_net) + 1; + static const int count_index_max = + static_cast(CountIndex::count_output_net) + 1; static int count_delay; void init(); @@ -81,7 +82,7 @@ protected: void reportDelayCounts(); void reportCheckCounts(); void reportArcs(); - void reportArcs(const char *header, + void reportArcs(const std::string &header, bool report_annotated, PinSet &pins); void reportArcs(Vertex *vertex, @@ -117,7 +118,6 @@ protected: PinSet annotated_pins_; }; - int ReportAnnotated::count_delay; void @@ -132,10 +132,9 @@ reportAnnotatedDelay(const Scene *scene, bool report_constant_arcs, StaState *sta) { - ReportAnnotated report(scene, report_cells, report_nets, - from_in_ports, to_out_ports, - max_lines, report_annotated, report_unannotated, - report_constant_arcs, sta); + ReportAnnotated report(scene, report_cells, report_nets, from_in_ports, + to_out_ports, max_lines, report_annotated, + report_unannotated, report_constant_arcs, sta); report.reportDelayAnnotation(); } @@ -176,24 +175,27 @@ ReportAnnotated::reportDelayAnnotation() void ReportAnnotated::reportDelayCounts() { - report_->reportLine(" Not "); - report_->reportLine("Delay type Total Annotated Annotated"); - report_->reportLine("----------------------------------------------------------------"); + report_->report( + " Not "); + report_->report( + "Delay type Total Annotated Annotated"); + report_->report( + "----------------------------------------------------------------"); int total = 0; int annotated_total = 0; reportCount("cell arcs", count_delay, total, annotated_total); - reportCount("internal net arcs", static_cast(CountIndex::count_internal_net), total, annotated_total); - reportCount("net arcs from primary inputs", static_cast(CountIndex::count_input_net), - total, annotated_total); - reportCount("net arcs to primary outputs", static_cast(CountIndex::count_output_net), + reportCount("internal net arcs", static_cast(CountIndex::count_internal_net), total, annotated_total); - report_->reportLine("----------------------------------------------------------------"); - report_->reportLine("%-28s %10u %10u %10u", - " ", - total, - annotated_total, - total - annotated_total); + reportCount("net arcs from primary inputs", + static_cast(CountIndex::count_input_net), total, annotated_total); + reportCount("net arcs to primary outputs", + static_cast(CountIndex::count_output_net), total, + annotated_total); + report_->report( + "----------------------------------------------------------------"); + report_->report("{:<28} {:10} {:10} {:10}", " ", total, annotated_total, + total - annotated_total); } //////////////////////////////////////////////////////////////// @@ -215,12 +217,10 @@ reportAnnotatedCheck(const Scene *scene, StaState *sta) { - ReportAnnotated report(scene, report_setup, report_hold, - report_recovery, report_removal, - report_nochange, report_width, - report_period, report_max_skew, - max_lines, report_annotated, report_unannotated, - report_constant_arcs, sta); + ReportAnnotated report(scene, report_setup, report_hold, report_recovery, + report_removal, report_nochange, report_width, + report_period, report_max_skew, max_lines, report_annotated, + report_unannotated, report_constant_arcs, sta); report.reportCheckAnnotation(); } @@ -269,9 +269,12 @@ ReportAnnotated::reportCheckAnnotation() void ReportAnnotated::reportCheckCounts() { - report_->reportLine(" Not "); - report_->reportLine("Check type Total Annotated Annotated"); - report_->reportLine("----------------------------------------------------------------"); + report_->report( + " Not "); + report_->report( + "Check type Total Annotated Annotated"); + report_->report( + "----------------------------------------------------------------"); int total = 0; int annotated_total = 0; @@ -284,12 +287,10 @@ ReportAnnotated::reportCheckCounts() reportCheckCount(TimingRole::period(), total, annotated_total); reportCheckCount(TimingRole::skew(), total, annotated_total); - report_->reportLine("----------------------------------------------------------------"); - report_->reportLine("%-28s %10u %10u %10u", - " ", - total, - annotated_total, - total - annotated_total); + report_->report( + "----------------------------------------------------------------"); + report_->report("{:<28} {:10} {:10} {:10}", " ", total, annotated_total, + total - annotated_total); } void @@ -299,8 +300,7 @@ ReportAnnotated::reportCheckCount(const TimingRole *role, { int index = role->index(); if (edge_count_[index] > 0) { - std::string title; - stringPrint(title, "cell %s arcs", role->to_string().c_str()); + std::string title = sta::format("cell {} arcs", role->to_string()); reportCount(title.c_str(), index, total, annotated_total); } } @@ -330,8 +330,7 @@ ReportAnnotated::findCounts() Pin *from_pin = from_vertex->pin(); LogicValue from_logic_value; bool from_logic_value_exists; - sdc->logicValue(from_pin, from_logic_value, - from_logic_value_exists); + sdc->logicValue(from_pin, from_logic_value, from_logic_value_exists); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -341,8 +340,7 @@ ReportAnnotated::findCounts() int index = roleIndex(role, from_pin, to_pin); LogicValue to_logic_value; bool to_logic_value_exists; - sdc->logicValue(to_pin, to_logic_value, - to_logic_value_exists); + sdc->logicValue(to_pin, to_logic_value, to_logic_value_exists); edge_count_[index]++; @@ -374,7 +372,7 @@ ReportAnnotated::delayAnnotated(Edge *edge) for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max); if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) - return false; + return false; } } return true; @@ -397,8 +395,7 @@ ReportAnnotated::roleIndex(const TimingRole *role, return count_delay; else { if (role->isTimingCheck() - && (role == TimingRole::latchSetup() - || role == TimingRole::latchHold())) + && (role == TimingRole::latchSetup() || role == TimingRole::latchHold())) role = role->genericRole(); return role->index(); } @@ -443,19 +440,13 @@ ReportAnnotated::reportCount(const char *title, if (report_role_[index]) { int count = edge_count_[index]; int annotated_count = edge_annotated_count_[index]; - report_->reportLine("%-28s %10u %10u %10u", - title, - count, - annotated_count, - count - annotated_count); + report_->report("{:<28} {:10} {:10} {:10}", title, count, annotated_count, + count - annotated_count); if (report_constant_arcs_) { int const_count = edge_constant_count_[index]; int const_annotated_count = edge_constant_annotated_count_[index]; - report_->reportLine("%-28s %10s %10u %10u", - "constant arcs", - "", - const_annotated_count, - const_count - const_annotated_count); + report_->report("{:<28} {:10} {:10} {:10}", "constant arcs", "", + const_annotated_count, const_count - const_annotated_count); } total += count; annotated_total += annotated_count; @@ -472,12 +463,12 @@ ReportAnnotated::reportArcs() } void -ReportAnnotated::reportArcs(const char *header, +ReportAnnotated::reportArcs(const std::string &header, bool report_annotated, PinSet &pins) { report_->reportBlankLine(); - report_->reportLineString(header); + report_->reportLine(header); PinSeq pins1 = sortByPathName(&pins, network_); int i = 0; for (const Pin *pin : pins1) { @@ -499,8 +490,7 @@ ReportAnnotated::reportArcs(Vertex *vertex, { const Pin *from_pin = vertex->pin(); VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext() - && (max_lines_ == 0 || i < max_lines_)) { + while (edge_iter.hasNext() && (max_lines_ == 0 || i < max_lines_)) { Edge *edge = edge_iter.next(); const TimingRole *role = edge->role(); const Pin *to_pin = edge->to(graph_)->pin(); @@ -520,11 +510,8 @@ ReportAnnotated::reportArcs(Vertex *vertex, else role_name = "delay"; const std::string &cond = edge->timingArcSet()->sdfCond(); - report_->reportLine(" %-18s %s -> %s %s", - role_name, - network_->pathName(from_pin), - network_->pathName(to_pin), - cond.c_str()); + report_->report(" {:<18} {} -> {} {}", role_name, network_->pathName(from_pin), + network_->pathName(to_pin), cond); i++; } } @@ -539,8 +526,7 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin, if (port) { DcalcAPIndex ap_index = 0; int period_index = TimingRole::period()->index(); - if (report_role_[period_index] - && (max_lines_ == 0 || i < max_lines_)) { + if (report_role_[period_index] && (max_lines_ == 0 || i < max_lines_)) { float value; bool exists, annotated; port->minPeriod(value, exists); @@ -548,9 +534,7 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin, edge_count_[period_index]++; graph_->periodCheckAnnotation(pin, ap_index, value, annotated); if (annotated == report_annotated) { - report_->reportLine(" %-18s %s", - "period", - network_->pathName(pin)); + report_->report(" {:<18} {}", "period", network_->pathName(pin)); i++; } } @@ -558,4 +542,4 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin, } } -} // namespace +} // namespace sta diff --git a/sdf/SdfParse.yy b/sdf/SdfParse.yy index 60e8b7ef..6c9c3efb 100644 --- a/sdf/SdfParse.yy +++ b/sdf/SdfParse.yy @@ -38,8 +38,7 @@ void sta::SdfParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164,reader->filename().c_str(), - loc.begin.line,"%s",msg.c_str()); + reader->report()->fileError(170, reader->filename(), loc.begin.line,"{}",msg); } %} diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 81818127..65a2bb8e 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "sdf/SdfReader.hh" @@ -74,7 +74,7 @@ public: private: const Transition *tr_; const std::string *port_; - const std::string *cond_; // timing checks only + const std::string *cond_; // timing checks only }; bool @@ -88,11 +88,9 @@ readSdf(const char *filename, { int arc_min_index = scene->dcalcAnalysisPtIndex(MinMax::min()); int arc_max_index = scene->dcalcAnalysisPtIndex(MinMax::max()); - SdfReader reader(filename, path, - arc_min_index, arc_max_index, - scene->sdc()->analysisType(), - unescaped_dividers, incremental_only, - cond_use, sta); + SdfReader reader(filename, path, arc_min_index, arc_max_index, + scene->sdc()->analysisType(), unescaped_dividers, + incremental_only, cond_use, sta); bool success = reader.read(); return success; } @@ -123,7 +121,7 @@ SdfReader::SdfReader(const char *filename, cell_name_(nullptr), in_timing_check_(false), in_incremental_(false), - timescale_(1.0E-9F) // default units of ns + timescale_(1.0E-9F) // default units of ns { if (unescaped_dividers) network_ = makeSdcNetwork(network_); @@ -149,7 +147,7 @@ SdfReader::read() return success; } else - throw FileNotReadable(filename_.c_str()); + throw FileNotReadable(filename_); } void @@ -162,9 +160,7 @@ void SdfReader::setTimescale(float multiplier, const std::string *units) { - if (multiplier == 1.0 - || multiplier == 10.0 - || multiplier == 100.0) { + if (multiplier == 1.0 || multiplier == 10.0 || multiplier == 100.0) { if (*units == "us") timescale_ = multiplier * 1E-6F; else if (*units == "ns") @@ -172,10 +168,10 @@ SdfReader::setTimescale(float multiplier, else if (*units == "ps") timescale_ = multiplier * 1E-12F; else - sdfError(180, "TIMESCALE units not us, ns, or ps."); + error(180, "TIMESCALE units not us, ns, or ps."); } else - sdfError(181, "TIMESCALE multiplier not 1, 10, or 100."); + error(181, "TIMESCALE multiplier not 1, 10, or 100."); delete units; } @@ -198,23 +194,20 @@ SdfReader::interconnect(const std::string *from_pin_name, bool to_is_hier = network_->isHierarchical(to_pin); if (from_is_hier || to_is_hier) { if (from_is_hier) - sdfError(182, "pin %s is a hierarchical pin.", - from_pin_name->c_str()); + error(182, "pin {} is a hierarchical pin.", *from_pin_name); if (to_is_hier) - sdfError(183, "pin %s is a hierarchical pin.", - to_pin_name->c_str()); + error(183, "pin {} is a hierarchical pin.", *to_pin_name); } else - sdfWarn(184, "INTERCONNECT from %s to %s not found.", - from_pin_name->c_str(), - to_pin_name->c_str()); + warn(184, "INTERCONNECT from {} to {} not found.", + *from_pin_name, *to_pin_name); } } else { if (from_pin == nullptr) - sdfWarn(185, "pin %s not found.", from_pin_name->c_str()); + warn(185, "pin {} not found.", *from_pin_name); if (to_pin == nullptr) - sdfWarn(186, "pin %s not found.", to_pin_name->c_str()); + warn(186, "pin {} not found.", *to_pin_name); } } delete from_pin_name; @@ -229,10 +222,10 @@ SdfReader::port(const std::string *to_pin_name, // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_)) { Pin *to_pin = (instance_) - ? network_->findPinRelative(instance_, to_pin_name->c_str()) - : network_->findPin(to_pin_name->c_str()); + ? network_->findPinRelative(instance_, to_pin_name->c_str()) + : network_->findPin(to_pin_name->c_str()); if (to_pin == nullptr) - sdfWarn(187, "pin %s not found.", to_pin_name->c_str()); + warn(187, "pin {} not found.", *to_pin_name); else { Vertex *vertex = graph_->pinLoadVertex(to_pin); VertexInEdgeIterator edge_iter(vertex, graph_); @@ -259,8 +252,7 @@ SdfReader::findWireEdge(Pin *from_pin, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); const TimingRole *edge_role = edge->role(); - if (edge->from(graph_)->pin() == from_pin - && edge_role->sdfRole()->isWire()) + if (edge->from(graph_)->pin() == from_pin && edge_role->sdfRole()->isWire()) return edge; } } @@ -274,8 +266,7 @@ SdfReader::setEdgeDelays(Edge *edge, { // Rise/fall triples. size_t triple_count = triples->size(); - if (triple_count == 1 - || triple_count == 2) { + if (triple_count == 1 || triple_count == 2) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { size_t triple_index; @@ -288,9 +279,9 @@ SdfReader::setEdgeDelays(Edge *edge, } } else if (triple_count == 0) - sdfError(188, "%s with no triples.", sdf_cmd); + error(188, "{} with no triples.", sdf_cmd); else - sdfError(189, "%s with more than 2 triples.", sdf_cmd); + error(189, "{} with more than 2 triples.", sdf_cmd); } void @@ -313,10 +304,8 @@ SdfReader::setInstance(const std::string *instance_name) Cell *inst_cell = network_->cell(instance_); const char *inst_cell_name = network_->name(inst_cell); if (cell_name_ && !stringEq(inst_cell_name, cell_name_->c_str())) - sdfWarn(190, "instance %s cell %s does not match enclosing cell %s.", - instance_name->c_str(), - inst_cell_name, - cell_name_->c_str()); + warn(190, "instance {} cell {} does not match enclosing cell {}.", + *instance_name, inst_cell_name, *cell_name_); } } } @@ -372,7 +361,7 @@ SdfReader::iopath(SdfPortSpec *from_edge, const std::string &lib_cond = arc_set->sdfCond(); const TimingRole *edge_role = arc_set->role(); bool cond_use_flag = cond_use_ && cond && lib_cond.empty() - && !(!is_incremental_only_ && in_incremental_); + && !(!is_incremental_only_ && in_incremental_); if (edge->from(graph_)->pin() == from_pin && edge_role->sdfRole() == TimingRole::sdfIopath() && (cond_use_flag @@ -402,10 +391,9 @@ SdfReader::iopath(SdfPortSpec *from_edge, } } if (!matched) - sdfWarn(191, "cell %s IOPATH %s -> %s not found.", - network_->cellName(instance_), - from_port_name->c_str(), - to_port_name->c_str()); + warn(191, "cell {} IOPATH {} -> {} not found.", + network_->cellName(instance_), *from_port_name, + *to_port_name); } } } @@ -422,9 +410,8 @@ SdfReader::findPort(const Cell *cell, { Port *port = network_->findPort(cell, port_name->c_str()); if (port == nullptr) - sdfWarn(194, "instance %s port %s not found.", - network_->pathName(instance_), - port_name->c_str()); + warn(194, "instance {} port {} not found.", network_->pathName(instance_), + *port_name); return port; } @@ -457,8 +444,7 @@ SdfReader::timingCheck1(const TimingRole *role, SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { Pin *data_pin = network_->findPin(instance_, data_port); Pin *clk_pin = network_->findPin(instance_, clk_port); if (data_pin && clk_pin) { @@ -468,36 +454,32 @@ SdfReader::timingCheck1(const TimingRole *role, float *value_max = values[triple_max_index_]; if (value_min && value_max) { switch (analysis_type_) { - case AnalysisType::single: - break; - case AnalysisType::bc_wc: - if (role->genericRole() == TimingRole::setup()) + case AnalysisType::single: + break; + case AnalysisType::bc_wc: + if (role->genericRole() == TimingRole::setup()) + *value_min = *value_max; + else + *value_max = *value_min; + break; + case AnalysisType::ocv: *value_min = *value_max; - else - *value_max = *value_min; - break; - case AnalysisType::ocv: - *value_min = *value_max; - break; + break; } } - bool matched = annotateCheckEdges(data_pin, data_edge, - clk_pin, clk_edge, role, + bool matched = annotateCheckEdges(data_pin, data_edge, clk_pin, clk_edge, role, triple, false); // Liberty setup/hold checks on preset/clear pins can be translated // into recovery/removal checks, so be flexible about matching. if (!matched) - matched = annotateCheckEdges(data_pin, data_edge, - clk_pin, clk_edge, role, + matched = annotateCheckEdges(data_pin, data_edge, clk_pin, clk_edge, role, triple, true); if (!matched // Only warn when non-null values are present. && triple->hasValue()) - sdfWarn(192, "cell %s %s -> %s %s check not found.", - network_->cellName(instance_), - network_->name(data_port), - network_->name(clk_port), - role->to_string().c_str()); + warn(192, "cell {} {} -> {} {} check not found.", + network_->cellName(instance_), network_->name(data_port), + network_->name(clk_port), role->to_string()); } } } @@ -526,11 +508,10 @@ SdfReader::annotateCheckEdges(Pin *data_pin, const TimingRole *edge_role = arc_set->role(); const std::string &lib_cond_start = arc_set->sdfCondStart(); const std::string &lib_cond_end = arc_set->sdfCondEnd(); - bool cond_matches = condMatch(cond_start, lib_cond_start) - && condMatch(cond_end, lib_cond_end); + bool cond_matches = + condMatch(cond_start, lib_cond_start) && condMatch(cond_end, lib_cond_end); if (((!match_generic && edge_role->sdfRole() == sdf_role) - || (match_generic - && edge_role->genericRole() == sdf_role->genericRole())) + || (match_generic && edge_role->genericRole() == sdf_role->genericRole())) && cond_matches) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { @@ -553,8 +534,7 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge, SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { const std::string *port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); @@ -622,8 +602,7 @@ SdfReader::timingCheckPeriod(SdfPortSpec *edge, SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { const std::string *port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); @@ -668,8 +647,7 @@ void SdfReader::device(SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { InstancePinIterator *pin_iter = network_->pinIterator(instance_); while (pin_iter->hasNext()) { Pin *to_pin = pin_iter->next(); @@ -685,8 +663,7 @@ SdfReader::device(const std::string *to_port_name, SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. - if (!(is_incremental_only_ && !in_incremental_) - && instance_) { + if (!(is_incremental_only_ && !in_incremental_) && instance_) { Cell *cell = network_->cell(instance_); Port *to_port = findPort(cell, to_port_name); if (to_port) { @@ -780,8 +757,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, int arc_delay_index, const MinMax *min_max) { - if (value - && triple_index != null_index_) { + if (value && triple_index != null_index_) { ArcDelay delay(*value); if (!is_incremental_only_ && in_incremental_) delay = delaySum(graph_->arcDelay(edge, arc, arc_delay_index), *value, this); @@ -844,9 +820,7 @@ SdfReader::makeCondPortSpec(const std::string *cond_port) auto cond_end = cond_port1.find_last_not_of(" ", port_idx); if (cond_end != cond_port1.npos) { std::string *cond1 = new std::string(cond_port1.substr(0, cond_end + 1)); - SdfPortSpec *port_spec = new SdfPortSpec(Transition::riseFall(), - port1, - cond1); + SdfPortSpec *port_spec = new SdfPortSpec(Transition::riseFall(), port1, cond1); delete cond_port; return port_spec; } @@ -887,9 +861,12 @@ SdfReader::makeTriple(float *min, float *typ, float *max) { - if (min) *min *= timescale_; - if (typ) *typ *= timescale_; - if (max) *max *= timescale_; + if (min) + *min *= timescale_; + if (typ) + *typ *= timescale_; + if (max) + *max *= timescale_; return new SdfTriple(min, typ, max); } @@ -929,9 +906,7 @@ SdfReader::unescaped(const std::string *token) // Translate sdf divider to network divider. *unescaped += path_divider; } - else if (next_ch == '[' - || next_ch == ']' - || next_ch == escape_) { + else if (next_ch == '[' || next_ch == ']' || next_ch == escape_) { // Escaped bus bracket or escape. // Translate sdf escape to network escape. *unescaped += path_escape; @@ -946,9 +921,8 @@ SdfReader::unescaped(const std::string *token) // Just the normal noises. *unescaped += ch; } - debugPrint(debug_, "sdf_name", 1, "unescape %s -> %s", - token->c_str(), - unescaped->c_str()); + debugPrint(debug_, "sdf_name", 1, "unescape {} -> {}", *token, + *unescaped); delete token; return unescaped; } @@ -980,27 +954,13 @@ SdfReader::makeBusName(std::string *base_name, void SdfReader::notSupported(const char *feature) { - sdfError(193, "%s not supported.", feature); + error(193, "{} not supported.", feature); } -void -SdfReader::sdfWarn(int id, - const char *fmt, ...) +int +SdfReader::sdfLine() const { - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_.c_str(), scanner_->lineno(), fmt, args); - va_end(args); -} - -void -SdfReader::sdfError(int id, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename_.c_str(), scanner_->lineno(), fmt, args); - va_end(args); + return scanner_->lineno(); } Pin * @@ -1030,7 +990,7 @@ SdfReader::findInstance(const std::string *name) inst_name = *name; Instance *inst = network_->findInstance(inst_name.c_str()); if (inst == nullptr) - sdfWarn(195, "instance %s not found.", inst_name.c_str()); + warn(195, "instance {} not found.", inst_name); return inst; } @@ -1067,9 +1027,12 @@ SdfTriple::~SdfTriple() if (values_[0] == values_[1] && values_[0] == values_[2]) delete values_[0]; else { - if (values_[0]) delete values_[0]; - if (values_[1]) delete values_[1]; - if (values_[2]) delete values_[2]; + if (values_[0]) + delete values_[0]; + if (values_[1]) + delete values_[1]; + if (values_[2]) + delete values_[2]; } } @@ -1095,7 +1058,7 @@ SdfScanner::SdfScanner(std::istream *stream, void SdfScanner::error(const char *msg) { - report_->fileError(1869, filename_.c_str(), lineno(), "%s", msg); + report_->fileError(196, filename_.c_str(), lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index 3b140a4e..4ae883ba 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -24,6 +24,7 @@ #pragma once +#include #include #include "TimingRole.hh" @@ -31,6 +32,7 @@ #include "LibertyClass.hh" #include "NetworkClass.hh" #include "GraphClass.hh" +#include "Report.hh" #include "SdcClass.hh" #include "StaState.hh" @@ -148,11 +150,23 @@ public: std::string *makeBusName(std::string *bus_name, int index); const std::string &filename() const { return filename_; } - void sdfWarn(int id, - const char *fmt, ...); - void sdfError(int id, - const char *fmt, - ...); + int sdfLine() const; + template + void warn(int id, + std::string_view fmt, + Args &&...args) + { + report_->fileWarn(id, filename_, sdfLine(), fmt, + std::forward(args)...); + } + template + void error(int id, + std::string_view fmt, + Args &&...args) + { + report_->fileError(id, filename_, sdfLine(), fmt, + std::forward(args)...); + } void notSupported(const char *feature); private: diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index db7af6b0..fa79139f 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -27,6 +27,7 @@ #include #include +#include "Format.hh" #include "Zlib.hh" #include "StaConfig.hh" // STA_VERSION #include "Fuzzy.hh" @@ -49,7 +50,6 @@ class SdfWriter : public StaState { public: SdfWriter(StaState *sta); - ~SdfWriter(); void write(const char *filename, const Scene *scene, char sdf_divider, @@ -118,7 +118,7 @@ private: char sdf_escape_; char network_escape_; - char *delay_format_; + int digits_; gzFile stream_; const Scene *scene_; @@ -145,16 +145,10 @@ writeSdf(const char *filename, SdfWriter::SdfWriter(StaState *sta) : StaState(sta), sdf_escape_('\\'), - network_escape_(network_->pathEscape()), - delay_format_(nullptr) + network_escape_(network_->pathEscape()) { } -SdfWriter::~SdfWriter() -{ - stringDelete(delay_format_); -} - void SdfWriter::write(const char *filename, const Scene *scene, @@ -167,8 +161,7 @@ SdfWriter::write(const char *filename, { sdf_divider_ = sdf_divider; include_typ_ = include_typ; - if (delay_format_ == nullptr) - delay_format_ = stringPrint("%%.%df", digits); + digits_ = digits; LibertyLibrary *default_lib = network_->defaultLibertyLibrary(); timescale_ = default_lib->units()->timeUnit()->scale(); @@ -195,25 +188,25 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib, bool no_timestamp, bool no_version) { - gzprintf(stream_, "(DELAYFILE\n"); - gzprintf(stream_, " (SDFVERSION \"3.0\")\n"); - gzprintf(stream_, " (DESIGN \"%s\")\n", - network_->cellName(network_->topInstance())); - + sta::print(stream_, "(DELAYFILE\n"); + sta::print(stream_, " (SDFVERSION \"3.0\")\n"); + sta::print(stream_, " (DESIGN \"{}\")\n", + network_->cellName(network_->topInstance())); + if (!no_timestamp) { time_t now; time(&now); char *time_str = ctime(&now); // Remove trailing \n. time_str[strlen(time_str) - 1] = '\0'; - gzprintf(stream_, " (DATE \"%s\")\n", time_str); + sta::print(stream_, " (DATE \"{}\")\n", time_str); } - gzprintf(stream_, " (VENDOR \"Parallax\")\n"); - gzprintf(stream_, " (PROGRAM \"STA\")\n"); + sta::print(stream_, " (VENDOR \"Parallax\")\n"); + sta::print(stream_, " (PROGRAM \"STA\")\n"); if (!no_version) - gzprintf(stream_, " (VERSION \"%s\")\n", STA_VERSION); - gzprintf(stream_, " (DIVIDER %c)\n", sdf_divider_); + sta::print(stream_, " (VERSION \"{}\")\n", STA_VERSION); + sta::print(stream_, " (DIVIDER {:c})\n", sdf_divider_); LibertyLibrary *lib_min = default_lib; const LibertySeq &libs_min = scene_->libertyLibraries(MinMax::min()); @@ -227,15 +220,15 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib, OperatingConditions *cond_min = lib_min->defaultOperatingConditions(); OperatingConditions *cond_max = lib_max->defaultOperatingConditions(); if (cond_min && cond_max) { - gzprintf(stream_, " (VOLTAGE %.3f::%.3f)\n", - cond_min->voltage(), - cond_max->voltage()); - gzprintf(stream_, " (PROCESS \"%.3f::%.3f\")\n", - cond_min->process(), - cond_max->process()); - gzprintf(stream_, " (TEMPERATURE %.3f::%.3f)\n", - cond_min->temperature(), - cond_max->temperature()); + sta::print(stream_, " (VOLTAGE {:.3f}::{:.3f})\n", + cond_min->voltage(), + cond_max->voltage()); + sta::print(stream_, " (PROCESS \"{:.3f}::{:.3f}\")\n", + cond_min->process(), + cond_max->process()); + sta::print(stream_, " (TEMPERATURE {:.3f}::{:.3f})\n", + cond_min->temperature(), + cond_max->temperature()); } const char *sdf_timescale = nullptr; @@ -258,24 +251,24 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib, else if (fuzzyEqual(timescale_, 100e-12)) sdf_timescale = "100ps"; if (sdf_timescale) - gzprintf(stream_, " (TIMESCALE %s)\n", sdf_timescale); + sta::print(stream_, " (TIMESCALE {})\n", sdf_timescale); } void SdfWriter::writeTrailer() { - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } void SdfWriter::writeInterconnects() { - gzprintf(stream_, " (CELL\n"); - gzprintf(stream_, " (CELLTYPE \"%s\")\n", - network_->cellName(network_->topInstance())); - gzprintf(stream_, " (INSTANCE)\n"); - gzprintf(stream_, " (DELAY\n"); - gzprintf(stream_, " (ABSOLUTE\n"); + sta::print(stream_, " (CELL\n"); + sta::print(stream_, " (CELLTYPE \"{}\")\n", + network_->cellName(network_->topInstance())); + sta::print(stream_, " (INSTANCE)\n"); + sta::print(stream_, " (DELAY\n"); + sta::print(stream_, " (ABSOLUTE\n"); writeInstInterconnects(network_->topInstance()); @@ -286,9 +279,9 @@ SdfWriter::writeInterconnects() } delete inst_iter; - gzprintf(stream_, " )\n"); - gzprintf(stream_, " )\n"); - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); + sta::print(stream_, " )\n"); + sta::print(stream_, " )\n"); } void @@ -315,11 +308,11 @@ SdfWriter::writeInterconnectFromPin(Pin *drvr_pin) Pin *load_pin = edge->to(graph_)->pin(); std::string drvr_pin_name = sdfPathName(drvr_pin); std::string load_pin_name = sdfPathName(load_pin); - gzprintf(stream_, " (INTERCONNECT %s %s ", - drvr_pin_name.c_str(), - load_pin_name.c_str()); + sta::print(stream_, " (INTERCONNECT {} {} ", + drvr_pin_name, + load_pin_name); writeArcDelays(edge); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } } } @@ -343,16 +336,16 @@ SdfWriter::writeInstances() void SdfWriter::writeInstHeader(const Instance *inst) { - gzprintf(stream_, " (CELL\n"); - gzprintf(stream_, " (CELLTYPE \"%s\")\n", network_->cellName(inst)); + sta::print(stream_, " (CELL\n"); + sta::print(stream_, " (CELLTYPE \"{}\")\n", network_->cellName(inst)); std::string inst_name = sdfPathName(inst); - gzprintf(stream_, " (INSTANCE %s)\n", inst_name.c_str()); + sta::print(stream_, " (INSTANCE {})\n", inst_name); } void SdfWriter::writeInstTrailer() { - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); } void @@ -387,18 +380,18 @@ SdfWriter::writeIopaths(const Instance *inst, } const std::string &sdf_cond = edge->timingArcSet()->sdfCond(); if (!sdf_cond.empty()) { - gzprintf(stream_, " (COND %s\n", sdf_cond.c_str()); - gzprintf(stream_, " "); + sta::print(stream_, " (COND {}\n", sdf_cond); + sta::print(stream_, " "); } std::string from_pin_name = sdfPortName(from_pin); std::string to_pin_name = sdfPortName(to_pin); - gzprintf(stream_, " (IOPATH %s %s ", - from_pin_name.c_str(), - to_pin_name.c_str()); + sta::print(stream_, " (IOPATH {} {} ", + from_pin_name, + to_pin_name); writeArcDelays(edge); if (!sdf_cond.empty()) - gzprintf(stream_, ")"); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")"); + sta::print(stream_, ")\n"); } } } @@ -412,15 +405,15 @@ SdfWriter::writeIopaths(const Instance *inst, void SdfWriter::writeIopathHeader() { - gzprintf(stream_, " (DELAY\n"); - gzprintf(stream_, " (ABSOLUTE\n"); + sta::print(stream_, " (DELAY\n"); + sta::print(stream_, " (ABSOLUTE\n"); } void SdfWriter::writeIopathTrailer() { - gzprintf(stream_, " )\n"); - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); + sta::print(stream_, " )\n"); } void @@ -446,7 +439,7 @@ SdfWriter::writeArcDelays(Edge *edge) delays.value(RiseFall::fall(), MinMax::min())) && fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()), delays.value(RiseFall::fall(),MinMax::max())))) { - gzprintf(stream_, " "); + sta::print(stream_, " "); writeSdfTriple(delays, RiseFall::fall()); } } @@ -455,7 +448,7 @@ SdfWriter::writeArcDelays(Edge *edge) writeSdfTriple(delays, RiseFall::rise()); else if (delays.hasValue(RiseFall::fall(), MinMax::min())) { // Fall only. - gzprintf(stream_, "() "); + sta::print(stream_, "() "); writeSdfTriple(delays, RiseFall::fall()); } } @@ -473,23 +466,24 @@ void SdfWriter::writeSdfTriple(float min, float max) { - gzprintf(stream_, "("); + sta::print(stream_, "("); writeSdfDelay(min); if (include_typ_) { - gzprintf(stream_, ":"); + sta::print(stream_, ":"); writeSdfDelay((min + max) / 2.0); - gzprintf(stream_, ":"); + sta::print(stream_, ":"); } else - gzprintf(stream_, "::"); + sta::print(stream_, "::"); writeSdfDelay(max); - gzprintf(stream_, ")"); + sta::print(stream_, ")"); } void SdfWriter::writeSdfDelay(double delay) { - gzprintf(stream_, delay_format_, delay / timescale_); + std::string str = sta::formatRuntime("{:.{}f}", delay / timescale_, digits_); + sta::print(stream_, "{}", str); } void @@ -568,13 +562,13 @@ SdfWriter::ensureTimingCheckheaders(bool &check_header, void SdfWriter::writeTimingCheckHeader() { - gzprintf(stream_, " (TIMINGCHECK\n"); + sta::print(stream_, " (TIMINGCHECK\n"); } void SdfWriter::writeTimingCheckTrailer() { - gzprintf(stream_, " )\n"); + sta::print(stream_, " )\n"); } void @@ -663,40 +657,40 @@ SdfWriter::writeCheck(Edge *edge, const std::string &sdf_cond_start = arc_set->sdfCondStart(); const std::string &sdf_cond_end = arc_set->sdfCondEnd(); - gzprintf(stream_, " (%s ", sdf_check); + sta::print(stream_, " ({} ", sdf_check); if (!sdf_cond_start.empty()) - gzprintf(stream_, "(COND %s ", sdf_cond_start.c_str()); + sta::print(stream_, "(COND {} ", sdf_cond_start); std::string to_pin_name = sdfPortName(to_pin); if (use_data_edge) { - gzprintf(stream_, "(%s %s)", - sdfEdge(arc->toEdge()), - to_pin_name.c_str()); + sta::print(stream_, "({} {})", + sdfEdge(arc->toEdge()), + to_pin_name); } else - gzprintf(stream_, "%s", to_pin_name.c_str()); + sta::print(stream_, "{}", to_pin_name); if (!sdf_cond_start.empty()) - gzprintf(stream_, ")"); + sta::print(stream_, ")"); - gzprintf(stream_, " "); + sta::print(stream_, " "); if (!sdf_cond_end.empty()) - gzprintf(stream_, "(COND %s ", sdf_cond_end.c_str()); + sta::print(stream_, "(COND {} ", sdf_cond_end); std::string from_pin_name = sdfPortName(from_pin); if (use_clk_edge) - gzprintf(stream_, "(%s %s)", - sdfEdge(arc->fromEdge()), - from_pin_name.c_str()); + sta::print(stream_, "({} {})", + sdfEdge(arc->fromEdge()), + from_pin_name); else - gzprintf(stream_, "%s", from_pin_name.c_str()); + sta::print(stream_, "{}", from_pin_name); if (!sdf_cond_end.empty()) - gzprintf(stream_, ")"); + sta::print(stream_, ")"); - gzprintf(stream_, " "); + sta::print(stream_, " "); float min_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_), MinMax::min(), this); @@ -704,7 +698,7 @@ SdfWriter::writeCheck(Edge *edge, MinMax::max(), this); writeSdfTriple(min_delay, max_delay); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } void @@ -714,11 +708,11 @@ SdfWriter::writeWidthCheck(const Pin *pin, float max_width) { std::string pin_name = sdfPortName(pin); - gzprintf(stream_, " (WIDTH (%s %s) ", - sdfEdge(hi_low->asTransition()), - pin_name.c_str()); + sta::print(stream_, " (WIDTH ({} {}) ", + sdfEdge(hi_low->asTransition()), + pin_name); writeSdfTriple(min_width, max_width); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } void @@ -726,9 +720,9 @@ SdfWriter::writePeriodCheck(const Pin *pin, float min_period) { std::string pin_name = sdfPortName(pin); - gzprintf(stream_, " (PERIOD %s ", pin_name.c_str()); + sta::print(stream_, " (PERIOD {} ", pin_name); writeSdfTriple(min_period, min_period); - gzprintf(stream_, ")\n"); + sta::print(stream_, ")\n"); } const char * diff --git a/search/Bfs.cc b/search/Bfs.cc index 9de9be6c..559cabb7 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Bfs.hh" @@ -37,10 +37,10 @@ namespace sta { BfsIterator::BfsIterator(BfsIndex bfs_index, - Level level_min, - Level level_max, - SearchPred *search_pred, - StaState *sta) : + Level level_min, + Level level_max, + SearchPred *search_pred, + StaState *sta) : StaState(sta), bfs_index_(bfs_index), level_min_(level_min), @@ -68,9 +68,7 @@ BfsIterator::ensureSize() } } -BfsIterator::~BfsIterator() -{ -} +BfsIterator::~BfsIterator() {} void BfsIterator::clear() @@ -80,7 +78,7 @@ BfsIterator::clear() VertexSeq &level_vertices = queue_[level]; for (Vertex *vertex : level_vertices) { if (vertex) - vertex->setBfsInQueue(bfs_index_, false); + vertex->setBfsInQueue(bfs_index_, false); } level_vertices.clear(); incrLevel(level); @@ -91,18 +89,18 @@ BfsIterator::clear() void BfsIterator::reportEntries() const { - for (Level level=first_level_; levelLessOrEqual(level, last_level_);incrLevel(level)){ + for (Level level = first_level_; levelLessOrEqual(level, last_level_); + incrLevel(level)) { const VertexSeq &level_vertices = queue_[level]; if (!level_vertices.empty()) { - report_->reportLine("Level %d", level); + report_->report("Level {}", level); for (Vertex *vertex : level_vertices) - report_->reportLine(" %s", - vertex ? vertex->to_string(this).c_str() : "NULL"); + report_->report(" {}", vertex ? vertex->to_string(this) : "NULL"); } } } -void +void BfsIterator::deleteEntries(Level level) { VertexSeq &level_vertices = queue_[level]; @@ -134,11 +132,11 @@ BfsIterator::enqueueAdjacentVertices(Vertex *vertex, int BfsIterator::visit(Level to_level, - VertexVisitor *visitor) + VertexVisitor *visitor) { int visit_count = 0; while (levelLessOrEqual(first_level_, last_level_) - && levelLessOrEqual(first_level_, to_level)) { + && levelLessOrEqual(first_level_, to_level)) { Level level = first_level_; VertexSeq &level_vertices = queue_[level]; incrLevel(first_level_); @@ -162,7 +160,7 @@ BfsIterator::visit(Level to_level, int BfsIterator::visitParallel(Level to_level, - VertexVisitor *visitor) + VertexVisitor *visitor) { size_t thread_count = thread_count_; int visit_count = 0; @@ -170,15 +168,15 @@ BfsIterator::visitParallel(Level to_level, if (thread_count == 1) visit_count = visit(to_level, visitor); else { - std::vector visitors; + std::vector visitors; for (int k = 0; k < thread_count_; k++) - visitors.push_back(visitor->copy()); + visitors.push_back(visitor->copy()); while (levelLessOrEqual(first_level_, last_level_) - && levelLessOrEqual(first_level_, to_level)) { - VertexSeq &level_vertices = queue_[first_level_]; + && levelLessOrEqual(first_level_, to_level)) { + VertexSeq &level_vertices = queue_[first_level_]; Level level = first_level_; - incrLevel(first_level_); - if (!level_vertices.empty()) { + incrLevel(first_level_); + if (!level_vertices.empty()) { size_t vertex_count = level_vertices.size(); if (vertex_count < thread_count) { for (Vertex *vertex : level_vertices) { @@ -196,7 +194,7 @@ BfsIterator::visitParallel(Level to_level, for (size_t k = 0; k < thread_count; k++) { // Last thread gets the left overs. size_t to = (k == thread_count - 1) ? vertex_count : from + chunk_size; - dispatch_queue_->dispatch( [=, this](int) { + dispatch_queue_->dispatch([=, this](int) { for (size_t i = from; i < to; i++) { Vertex *vertex = level_vertices[i]; if (vertex) { @@ -210,13 +208,13 @@ BfsIterator::visitParallel(Level to_level, } dispatch_queue_->finishTasks(); } - visitor->levelFinished(); - level_vertices.clear(); + visitor->levelFinished(); + level_vertices.clear(); visit_count += vertex_count; - } + } } for (VertexVisitor *visitor : visitors) - delete visitor; + delete visitor; } } return visit_count; @@ -233,7 +231,7 @@ BfsIterator::hasNext(Level to_level) { findNext(to_level); return levelLessOrEqual(first_level_, last_level_) - && !queue_[first_level_].empty(); + && !queue_[first_level_].empty(); } Vertex * @@ -250,16 +248,16 @@ void BfsIterator::findNext(Level to_level) { while (levelLessOrEqual(first_level_, last_level_) - && levelLessOrEqual(first_level_, to_level)) { + && levelLessOrEqual(first_level_, to_level)) { VertexSeq &level_vertices = queue_[first_level_]; // Skip null entries from deleted vertices. while (!level_vertices.empty()) { Vertex *vertex = level_vertices.back(); if (vertex == nullptr) - level_vertices.pop_back(); + level_vertices.pop_back(); else { checkLevel(vertex, first_level_); - return; + return; } } incrLevel(first_level_); @@ -269,8 +267,7 @@ BfsIterator::findNext(Level to_level) void BfsIterator::enqueue(Vertex *vertex) { - debugPrint(debug_, "bfs", 2, "enqueue %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "bfs", 2, "enqueue {}", vertex->to_string(this)); if (!vertex->bfsInQueue(bfs_index_)) { Level level = vertex->level(); LockGuard lock(queue_lock_); @@ -279,9 +276,9 @@ BfsIterator::enqueue(Vertex *vertex) queue_[level].push_back(vertex); if (levelLess(last_level_, level)) - last_level_ = level; + last_level_ = level; if (levelLess(level, first_level_)) - first_level_ = level; + first_level_ = level; } } } @@ -300,17 +297,15 @@ BfsIterator::checkInQueue(Vertex *vertex) if (static_cast(queue_.size()) > level) { for (Vertex *v : queue_[level]) { if (v == vertex) { - if (vertex->bfsInQueue(bfs_index_)) - return; - else - debugPrint(debug_, "bfs", 1, "extra %s", - vertex->to_string(this).c_str()); + if (vertex->bfsInQueue(bfs_index_)) + return; + else + debugPrint(debug_, "bfs", 1, "extra {}", vertex->to_string(this)); } } } if (vertex->bfsInQueue(bfs_index_)) - debugPrint(debug_, "brs", 1, "missing %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "brs", 1, "missing {}", vertex->to_string(this)); } void @@ -318,10 +313,8 @@ BfsIterator::checkLevel(Vertex *vertex, Level level) { if (vertex->level() != level) - report_->error(2300, "vertex %s level %d != bfs level %d", - vertex->to_string(this).c_str(), - vertex->level(), - level); + report_->error(2300, "vertex {} level {} != bfs level {}", + vertex->to_string(this), vertex->level(), level); } void @@ -336,14 +329,12 @@ BfsIterator::remove(Vertex *vertex) { // If the iterator has not been inited the queue will be empty. Level level = vertex->level(); - if (vertex->bfsInQueue(bfs_index_) - && static_cast(queue_.size()) > level) { - debugPrint(debug_, "bfs", 2, "remove %s", - vertex->to_string(this).c_str()); + if (vertex->bfsInQueue(bfs_index_) && static_cast(queue_.size()) > level) { + debugPrint(debug_, "bfs", 2, "remove {}", vertex->to_string(this)); for (Vertex *&v : queue_[level]) { if (v == vertex) { - v = nullptr; - vertex->setBfsInQueue(bfs_index_, false); + v = nullptr; + vertex->setBfsInQueue(bfs_index_, false); break; } } @@ -353,9 +344,13 @@ BfsIterator::remove(Vertex *vertex) //////////////////////////////////////////////////////////////// BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index, - SearchPred *search_pred, - StaState *sta) : - BfsIterator(bfs_index, 0, level_max, search_pred, sta) + SearchPred *search_pred, + StaState *sta) : + BfsIterator(bfs_index, + 0, + level_max, + search_pred, + sta) { } @@ -374,14 +369,14 @@ BfsFwdIterator::incrLevel(Level &level) const bool BfsFwdIterator::levelLessOrEqual(Level level1, - Level level2) const + Level level2) const { return level1 <= level2; } bool BfsFwdIterator::levelLess(Level level1, - Level level2) const + Level level2) const { return level1 < level2; } @@ -395,9 +390,8 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); - if (search_pred->searchThru(edge) - && search_pred->searchTo(to_vertex)) - enqueue(to_vertex); + if (search_pred->searchThru(edge) && search_pred->searchTo(to_vertex)) + enqueue(to_vertex); } } } @@ -422,9 +416,13 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, //////////////////////////////////////////////////////////////// BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index, - SearchPred *search_pred, - StaState *sta) : - BfsIterator(bfs_index, level_max, 0, search_pred, sta) + SearchPred *search_pred, + StaState *sta) : + BfsIterator(bfs_index, + level_max, + 0, + search_pred, + sta) { } @@ -443,14 +441,14 @@ BfsBkwdIterator::incrLevel(Level &level) const bool BfsBkwdIterator::levelLessOrEqual(Level level1, - Level level2) const + Level level2) const { return level1 >= level2; } bool BfsBkwdIterator::levelLess(Level level1, - Level level2) const + Level level2) const { return level1 > level2; } @@ -464,9 +462,8 @@ BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); - if (search_pred->searchFrom(from_vertex) - && search_pred->searchThru(edge)) - enqueue(from_vertex); + if (search_pred->searchFrom(from_vertex) && search_pred->searchThru(edge)) + enqueue(from_vertex); } } } @@ -488,4 +485,4 @@ BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, } } -} // namespace +} // namespace sta diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index 12c795ea..cad8319e 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -140,8 +140,8 @@ CheckMinPulseWidths::checkVertex(Vertex *vertex, Path *close_path = check.closePath(sta_); // Don't bother visiting if nobody is home. if (close_path) { - debugPrint(debug, "mpw", 2, "%s %s %s", - path_vertex->to_string(sta_).c_str(), + debugPrint(debug, "mpw", 2, "{} {} {}", + path_vertex->to_string(sta_), path->transition(sta_) == RiseFall::rise() ? "(high)" : "(low)", delayAsString(check.slack(sta_), sta_)); if (violators) { @@ -219,17 +219,17 @@ MinPulseWidthCheck::closePath(const StaState *sta) const open_tag->isSegmentStart(), open_tag->states(), false); - debugPrint(sta->debug(), "mpw", 3, " open %s", - open_tag->to_string(sta).c_str()); - debugPrint(sta->debug(), "mpw", 3, " close %s", - close_tag.to_string(sta).c_str()); + debugPrint(sta->debug(), "mpw", 3, " open {}", + open_tag->to_string(sta)); + debugPrint(sta->debug(), "mpw", 3, " close {}", + close_tag.to_string(sta)); VertexPathIterator close_iter(open_path_->vertex(sta), scene, close_min_max, close_rf, sta); while (close_iter.hasNext()) { Path *close_path = close_iter.next(); if (Tag::matchNoPathAp(close_path->tag(sta), &close_tag)) { - debugPrint(sta->debug(), "mpw", 3, " match %s", - close_path->tag(sta)->to_string(sta).c_str()); + debugPrint(sta->debug(), "mpw", 3, " match {}", + close_path->tag(sta)->to_string(sta)); return close_path; } } diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index de4ad8b4..9b83593f 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -123,8 +123,7 @@ CheckTiming::checkNoInputDelay() } } delete pin_iter; - pushPinErrors("Warning: There %is %d input port%s missing set_input_delay.", - no_arrival); + pushPinErrors("Warning: There {} {} input port{} missing set_input_delay.",no_arrival); } void @@ -132,7 +131,7 @@ CheckTiming::checkNoOutputDelay() { PinSet no_departure(network_); checkNoOutputDelay(no_departure); - pushPinErrors("Warning: There %is %d output port%s missing set_output_delay.", + pushPinErrors("Warning: There {} {} output port{} missing set_output_delay.", no_departure); } @@ -179,12 +178,24 @@ CheckTiming::checkRegClks(bool reg_multiple_clks, if (reg_multiple_clks && clks && clks->size() > 1) multiple_clk_pins.insert(pin); } - pushPinErrors("Warning: There %is %d unclocked register/latch pin%s.", + pushPinErrors("Warning: There {} {} unclocked register/latch pin{}.", no_clk_pins); - pushPinErrors("Warning: There %is %d register/latch pin%s with multiple clocks.", + pushPinErrors("Warning: There {} {} register/latch pin{} with multiple clocks.", multiple_clk_pins); } +static const char * +plurality(int n) +{ + return n == 1 ? "is" : "are"; +} + +static const char * +pluralSuffix(int n) +{ + return n == 1 ? "" : "s"; +} + void CheckTiming::checkLoops() { @@ -198,11 +209,11 @@ CheckTiming::checkLoops() loop_count++; } if (loop_count > 0) { - std::string error_msg; - errorMsgSubst("Warning: There %is %d combinational loop%s in the design.", - loop_count, error_msg); CheckError *error = new CheckError; - error->push_back(error_msg); + error->push_back(sta::format("Warning: There {} {} combinational loop{} in the design.", + plurality(loop_count), + loop_count, + pluralSuffix(loop_count))); for (GraphLoop *loop : loops) { if (loop->isCombinational()) { @@ -232,7 +243,7 @@ CheckTiming::checkUnconstrainedEndpoints() PinSet unconstrained_ends(network_); checkUnconstrainedOutputs(unconstrained_ends); checkUnconstrainedSetups(unconstrained_ends); - pushPinErrors("Warning: There %is %d unconstrained endpoint%s.", + pushPinErrors("Warning: There {} {} unconstrained endpoint{}.", unconstrained_ends); } @@ -338,27 +349,21 @@ CheckTiming::checkGeneratedClocks() gen_clk_errors.insert(clk); } } - pushClkErrors("Warning: There %is %d generated clock%s that %is not connected to a clock source.", + pushClkErrors("Warning: There {} {} generated clock{} not connected to a clock source.", gen_clk_errors); } // Report the "msg" error for each pin in "pins". -// -// Substitutions in msg are done as follows if the pin count is one -// or greater than one. -// %is - is/are -// %d - pin count -// %s - s/"" -// %a - a/"" void -CheckTiming::pushPinErrors(const char *msg, +CheckTiming::pushPinErrors(std::string_view msg, PinSet &pins) { if (!pins.empty()) { CheckError *error = new CheckError; - std::string error_msg; - errorMsgSubst(msg, pins.size(), error_msg); - error->push_back(error_msg); + error->push_back(sta::formatRuntime(msg, + plurality(pins.size()), + pins.size(), + pluralSuffix(pins.size()))); // Sort the error pins so the output is independent of the order // the the errors are discovered. PinSeq pins1 = sortByPathName(&pins, network_); @@ -375,9 +380,10 @@ CheckTiming::pushClkErrors(const char *msg, { if (!clks.empty()) { CheckError *error = new CheckError; - std::string error_msg; - errorMsgSubst(msg, clks.size(), error_msg); - error->push_back(error_msg); + error->push_back(sta::formatRuntime(msg, + plurality(clks.size()), + clks.size(), + pluralSuffix(clks.size()))); // Sort the error clks so the output is independent of the order // the the errors are discovered. ClockSeq clks1 = sortByName(&clks); @@ -388,47 +394,4 @@ CheckTiming::pushClkErrors(const char *msg, } } -// Copy msg making substitutions for singular/plurals. -void -CheckTiming::errorMsgSubst(const char *msg, - int obj_count, - std::string &error_msg) -{ - for (const char *s = msg; *s; s++) { - char ch = *s; - if (ch == '%') { - char flag = s[1]; - if (flag == 'i') { - if (obj_count > 1) - error_msg += "are"; - else - error_msg += "is"; - s += 2; - } - else if (flag == 'a') { - if (obj_count == 1) { - error_msg += 'a'; - s++; - } - else - // Skip space after %a. - s += 2; - } - else if (flag == 's') { - if (obj_count > 1) - error_msg += 's'; - s++; - } - else if (flag == 'd') { - error_msg += std::to_string(obj_count); - s++; - } - else - criticalError(245, "unknown print flag"); - } - else - error_msg += ch; - } -} - } // namespace diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index 9cdd4227..94f845b5 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -70,13 +70,10 @@ protected: bool hasClkedCheck(Vertex *vertex); bool hasMaxDelay(Pin *pin); void checkGeneratedClocks(); - void pushPinErrors(const char *msg, + void pushPinErrors(std::string_view msg, PinSet &pins); void pushClkErrors(const char *msg, ClockSet &clks); - void errorMsgSubst(const char *msg, - int count, - std::string &error_msg); CheckErrorSeq errors_; const Mode *mode_; diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index b7b4bac5..887c009e 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "ClkLatency.hh" @@ -55,8 +55,7 @@ ClkLatency::findClkDelays(const Clock *clk, clks.push_back(clk); SceneSet scenes; scenes.insert(scene); - ClkDelayMap clk_delay_map = findClkDelays(clks, scenes, - include_internal_latency); + ClkDelayMap clk_delay_map = findClkDelays(clks, scenes, include_internal_latency); return clk_delay_map[clk]; } @@ -88,7 +87,7 @@ ClkLatency::reportClkLatency(const Clock *clk, int digits) { Unit *time_unit = units_->timeUnit(); - report_->reportLine("Clock %s", clk->name()); + report_->report("Clock {}", clk->name()); for (const RiseFall *src_rf : RiseFall::range()) { for (const RiseFall *end_rf : RiseFall::range()) { Path path_min; @@ -97,47 +96,41 @@ ClkLatency::reportClkLatency(const Clock *clk, float internal_latency_min; Delay latency_min; bool exists_min; - clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min, - delay_min, internal_latency_min, latency_min, - path_min, exists_min); + clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min, delay_min, + internal_latency_min, latency_min, path_min, exists_min); Path path_max; Delay insertion_max; Delay delay_max; float internal_latency_max; Delay latency_max; bool exists_max; - clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max, - delay_max, internal_latency_max, latency_max, - path_max, exists_max); + clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max, delay_max, + internal_latency_max, latency_max, path_max, exists_max); if (exists_min & exists_max) { - report_->reportLine("%s -> %s", - src_rf->name(), - end_rf->name()); - report_->reportLine(" min max"); - - report_->reportLine("%7s %7s source latency", - delayAsString(insertion_min, MinMax::min(), digits, this), - delayAsString(insertion_max, MinMax::max(), digits, this)); - report_->reportLine("%7s %7s network latency %s", - delayAsString(delay_min, MinMax::min(), digits, this), - "", - sdc_network_->pathName(path_min.pin(this))); - report_->reportLine("%7s %7s network latency %s", - "", - delayAsString(delay_max, MinMax::max(), digits, this), - sdc_network_->pathName(path_max.pin(this))); - if (internal_latency_min != 0.0 - || internal_latency_max != 0.0) - report_->reportLine("%7s %7s internal clock latency", - time_unit->asString(internal_latency_min, digits), - time_unit->asString(internal_latency_max, digits)); - report_->reportLine("---------------"); - report_->reportLine("%7s %7s latency", - delayAsString(latency_min, MinMax::min(), digits, this), - delayAsString(latency_max, MinMax::max(), digits, this)); + report_->report("{} -> {}", src_rf->name(), end_rf->name()); + report_->report(" min max"); + report_->report("{:>7} {:>7} source latency", + delayAsString(insertion_min, MinMax::min(), digits, this), + delayAsString(insertion_max, MinMax::max(), digits, this)); + report_->report("{:>7} {:>7} network latency {}", + delayAsString(delay_min, MinMax::min(), digits, this), + "", + sdc_network_->pathName(path_min.pin(this))); + report_->report("{:>7} {:>7} network latency {}", + "", + delayAsString(delay_max, MinMax::max(), digits, this), + sdc_network_->pathName(path_max.pin(this))); + if (internal_latency_min != 0.0 || internal_latency_max != 0.0) + report_->report("{:>7} {:>7} internal clock latency", + time_unit->asString(internal_latency_min, digits), + time_unit->asString(internal_latency_max, digits)); + report_->report("---------------"); + report_->report("{:>7} {:>7} latency", + delayAsString(latency_min, MinMax::min(), digits, this), + delayAsString(latency_max, MinMax::max(), digits, this)); Delay skew = delayDiff(latency_max, latency_min, this); - report_->reportLine(" %7s skew", - delayAsString(skew, MinMax::max(), digits, this)); + report_->report(" {:>7} skew", + delayAsString(skew, MinMax::max(), digits, this)); report_->reportBlankLine(); } } @@ -164,9 +157,7 @@ ClkLatency::findClkDelays(ConstClockSeq &clks, Path *path = path_iter.next(); const Scene *path_scene = path->scene(this); const Clock *path_clk = path->clock(this); - if (path_clk - && scenes.contains(path_scene) - && clk_set.contains(path_clk)) { + if (path_clk && scenes.contains(path_scene) && clk_set.contains(path_clk)) { auto delays_itr = clk_delay_map.find(path_clk); if (delays_itr != clk_delay_map.end()) { const ClockEdge *path_clk_edge = path->clkEdge(this); @@ -278,7 +269,6 @@ Delay ClkDelays::latency(Path *clk_path, StaState *sta) { - Delay insertion = insertionDelay(clk_path, sta); Delay delay1 = delay(clk_path, sta); float lib_clk_delay = clkTreeDelay(clk_path, sta); @@ -321,4 +311,4 @@ ClkDelays::clkTreeDelay(Path *clk_path, return port->clkTreeDelay(slew, rf, min_max); } -} // namespace +} // namespace sta diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 56590fc4..8167d486 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -1,30 +1,30 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "ClkSkew.hh" -#include // abs +#include // abs #include #include #include @@ -78,12 +78,12 @@ ClkSkews::reportClkSkew(ConstClockSeq &clks, sort(sorted_clks, ClkNameLess()); for (const Clock *clk : sorted_clks) { - report_->reportLine("Clock %s", clk->name()); + report_->report("Clock {}", clk->name()); auto skew_itr = skews_.find(clk); if (skew_itr != skews_.end()) reportClkSkew(skew_itr->second[setup_hold->index()], digits); else - report_->reportLine("No launch/capture paths found."); + report_->report("No launch/capture paths found."); report_->reportBlankLine(); } } @@ -104,33 +104,33 @@ ClkSkews::reportClkSkew(ClkSkew &clk_skew, if (src_internal_clk_latency != 0.0) delayDecr(src_latency, src_internal_clk_latency, this); - report_->reportLine("%7s source latency %s %s", - delayAsString(src_latency, src_min_max, digits, this), - sdc_network_->pathName(src_path->pin(this)), - src_path->transition(this)->shortName()); + report_->report("{:>7} source latency {} {}", + delayAsString(src_latency, src_min_max, digits, this), + sdc_network_->pathName(src_path->pin(this)), + src_path->transition(this)->shortName()); if (src_internal_clk_latency != 0.0) - report_->reportLine("%7s source internal clock delay", - time_unit->asString(src_internal_clk_latency, digits)); + report_->report("{:>7} source internal clock delay", + time_unit->asString(src_internal_clk_latency, digits)); if (tgt_internal_clk_latency != 0.0) tgt_latency -= tgt_internal_clk_latency; - report_->reportLine("%7s target latency %s %s", - time_unit->asString(-tgt_latency, digits), - sdc_network_->pathName(tgt_path->pin(this)), - tgt_path->transition(this)->shortName()); + report_->report("{:>7} target latency {} {}", + time_unit->asString(-tgt_latency, digits), + sdc_network_->pathName(tgt_path->pin(this)), + tgt_path->transition(this)->shortName()); if (tgt_internal_clk_latency != 0.0) - report_->reportLine("%7s target internal clock delay", - time_unit->asString(-tgt_internal_clk_latency, digits)); + report_->report("{:>7} target internal clock delay", + time_unit->asString(-tgt_internal_clk_latency, digits)); if (uncertainty != 0.0) - report_->reportLine("%7s clock uncertainty", - time_unit->asString(uncertainty, digits)); - report_->reportLine("%7s CRPR", - delayAsString(delayDiff(0.0, clk_skew.crpr(this), this), - MinMax::max(), digits, this)); - report_->reportLine("--------------"); - report_->reportLine("%7s %s skew", - delayAsString(clk_skew.skew(), MinMax::max(), digits, this), - src_path->minMax(this) == MinMax::max() ? "setup" : "hold"); + report_->report("{:>7} clock uncertainty", + time_unit->asString(uncertainty, digits)); + report_->report("{:>7} CRPR", + delayAsString(delayDiff(0.0, clk_skew.crpr(this), this), + MinMax::max(), digits, this)); + report_->report("--------------"); + report_->report("{:>7} {} skew", + delayAsString(clk_skew.skew(), MinMax::max(), digits, this), + src_path->minMax(this) == MinMax::max() ? "setup" : "hold"); } static float @@ -174,11 +174,9 @@ void ClkSkews::findClkSkew(ConstClockSeq &clks, const SceneSeq &scenes, bool include_internal_latency) -{ - if (scenes == scenes_ - && include_internal_latency == include_internal_latency_ - && clks == clks_ - && !skews_.empty()) +{ + if (scenes == scenes_ && include_internal_latency == include_internal_latency_ + && clks == clks_ && !skews_.empty()) return; skews_.clear(); @@ -206,14 +204,14 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, // Reduce skews from each register source. for (size_t i = 0; i < partial_skews.size(); i++) { - for (auto& [clk, partial_skew] : partial_skews[i]) { + for (auto &[clk, partial_skew] : partial_skews[i]) { auto itr = skews_.find(clk); if (itr == skews_.end()) { // Insert new entry using emplace with piecewise_construct // This will default-construct the array, then we copy the elements - auto result = skews_.emplace(std::piecewise_construct, - std::forward_as_tuple(clk), - std::make_tuple()); + auto result = + skews_.emplace(std::piecewise_construct, std::forward_as_tuple(clk), + std::make_tuple()); itr = result.first; // Copy array elements for (int setup_hold_idx : SetupHold::rangeIndex()) @@ -231,7 +229,8 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, if (partial_skew_max > final_skew_max || (fuzzyEqual(partial_skew_max, final_skew_max) // Break ties based on source/target path names. - && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this))) + && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, + this))) final_skew = partial_skew_val; } } @@ -269,9 +268,8 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, if (edge->role()->genericRole() == TimingRole::regClkToQ()) { Vertex *q_vertex = edge->to(graph_); const RiseFall *rf = edge->timingArcSet()->isRisingFallingEdge(); - const RiseFallBoth *src_rf = rf - ? rf->asRiseFallBoth() - : RiseFallBoth::riseFall(); + const RiseFallBoth *src_rf = + rf ? rf->asRiseFallBoth() : RiseFallBoth::riseFall(); findClkSkewFrom(src_vertex, q_vertex, src_rf, skews); } } @@ -291,12 +289,11 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, const TimingRole *role = edge->role(); if (role->genericRole() == TimingRole::setup() || role->genericRole() == TimingRole::hold()) { - Vertex *tgt_vertex = edge->from(graph_); - const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge(); - const RiseFallBoth *tgt_rf = tgt_rf1 - ? tgt_rf1->asRiseFallBoth() - : RiseFallBoth::riseFall(); - findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf, skews); + Vertex *tgt_vertex = edge->from(graph_); + const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge(); + const RiseFallBoth *tgt_rf = + tgt_rf1 ? tgt_rf1->asRiseFallBoth() : RiseFallBoth::riseFall(); + findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf, skews); } } } @@ -325,8 +322,7 @@ ClkSkews::findClkSkew(Vertex *src_vertex, while (tgt_iter.hasNext()) { Path *tgt_path = tgt_iter.next(); const Clock *tgt_clk = tgt_path->clock(this); - if (tgt_clk == src_clk - && tgt_path->isClock(this) + if (tgt_clk == src_clk && tgt_path->isClock(this) && tgt_rf->matches(tgt_path->transition(this)) && tgt_path->minMax(this) == tgt_min_max && tgt_path->scene(this) == src_scene) { @@ -334,7 +330,7 @@ ClkSkews::findClkSkew(Vertex *src_vertex, const SetupHold *setup_hold = src_path->minMax(this); ClkSkew &clk_skew = skews[src_clk][setup_hold->index()]; debugPrint(debug_, "clk_skew", 2, - "%s %s %s -> %s %s %s crpr = %s skew = %s", + "{} {} {} -> {} {} {} crpr = {} skew = {}", network_->pathName(src_path->pin(this)), src_path->transition(this)->shortName(), delayAsString(probe.srcLatency(this), src_min_max, this), @@ -356,14 +352,14 @@ VertexSet ClkSkews::findFanout(Vertex *from) { VertexSet endpoints = makeVertexSet(this); - std::unordered_set visited; + std::unordered_set visited; findFanout1(from, visited, endpoints); return endpoints; } void ClkSkews::findFanout1(Vertex *from, - std::unordered_set &visited, + std::unordered_set &visited, VertexSet &endpoints) { visited.insert(from); @@ -439,7 +435,8 @@ float ClkSkew::tgtLatency(const StaState *sta) { Arrival tgt_arrival = tgt_path_->arrival(); - return delayAsFloat(delaySum(delayDiff(tgt_arrival, tgt_path_->clkEdge(sta)->time(),sta), + return delayAsFloat(delaySum(delayDiff(tgt_arrival, + tgt_path_->clkEdge(sta)->time(),sta), clkTreeDelay(tgt_path_, sta), sta)); } @@ -477,8 +474,8 @@ float ClkSkew::uncertainty(const StaState *sta) { const TimingRole *check_role = (src_path_->minMax(sta) == SetupHold::max()) - ? TimingRole::setup() - : TimingRole::hold(); + ? TimingRole::setup() + : TimingRole::hold(); // Uncertainty decreases slack, but increases skew. return -PathEnd::checkTgtClkUncertainty(tgt_path_, tgt_path_->clkEdge(sta), check_role, sta); @@ -495,8 +492,7 @@ ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1, const char *tgt_path1 = network->pathName(clk_skew1.tgtPath()->pin(sta)); const char *tgt_path2 = network->pathName(clk_skew2.tgtPath()->pin(sta)); return stringLess(src_path1, src_path2) - || (stringEqual(src_path1, src_path2) - && stringEqual(tgt_path1, tgt_path2)); + || (stringEqual(src_path1, src_path2) && stringEqual(tgt_path1, tgt_path2)); } //////////////////////////////////////////////////////////////// @@ -512,10 +508,9 @@ FanOutSrchPred::searchThru(Edge *edge, { const TimingRole *role = edge->role(); return SearchPred1::searchThru(edge, mode) - && (role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable()); + && (role == TimingRole::wire() || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable()); } -} // namespace +} // namespace sta diff --git a/search/Crpr.cc b/search/Crpr.cc index 21361ed2..3d1295bb 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -236,7 +236,7 @@ CheckCrpr::findCrpr(const Path *src_clk_path, && tgt_clk_path2 && !tgt_clk_path2->isNull() && (src_clk_path2->transition(this) == tgt_clk_path2->transition(this) || same_pin)) { - debugPrint(debug_, "crpr", 2, "crpr pin %s", + debugPrint(debug_, "crpr", 2, "crpr pin {}", network_->pathName(src_clk_path2->pin(this))); crpr = findCrpr1(src_clk_path2, tgt_clk_path2); crpr_pin = src_clk_path2->pin(this); @@ -289,12 +289,12 @@ CheckCrpr::findCrpr1(const Path *src_clk_path, // is the min of the source and target max-min delay. float src_delta = crprArrivalDiff(src_clk_path); float tgt_delta = crprArrivalDiff(tgt_clk_path); - debugPrint(debug_, "crpr", 2, " src delta %s", + debugPrint(debug_, "crpr", 2, " src delta {}", delayAsString(src_delta, this)); - debugPrint(debug_, "crpr", 2, " tgt delta %s", + debugPrint(debug_, "crpr", 2, " tgt delta {}", delayAsString(tgt_delta, this)); float common_delay = std::min(src_delta, tgt_delta); - debugPrint(debug_, "crpr", 2, " %s delta %s", + debugPrint(debug_, "crpr", 2, " {} delta {}", network_->pathName(src_clk_path->pin(this)), delayAsString(common_delay, this)); return common_delay; diff --git a/search/Genclks.cc b/search/Genclks.cc index ca81bb0a..3dee07b9 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Genclks.hh" @@ -85,10 +85,7 @@ GenclkInfo::GenclkInfo(Clock *gclk, { } -GenclkInfo::~GenclkInfo() -{ - delete src_filter_; -} +GenclkInfo::~GenclkInfo() { delete src_filter_; } void GenclkInfo::setFoundLatchFdbkEdges(bool found) @@ -202,7 +199,7 @@ Genclks::ensureInsertionDelays() // Generated clocks derived from a generated clock inherit its // insertion delay, so sort the clocks by source pin level. - sort(gclks , ClockPinMaxLevelLess(this)); + sort(gclks, ClockPinMaxLevelLess(this)); for (Clock *gclk : gclks) { if (gclk->masterClk()) { @@ -232,8 +229,7 @@ GenClkMasterSearchPred::GenClkMasterSearchPred(const StaState *sta) : bool GenClkMasterSearchPred::searchThruAllow(const TimingRole *role) const { - return (role->isWire() - || role == TimingRole::combinational() + return (role->isWire() || role == TimingRole::combinational() || role->regClkToQ()); } @@ -245,7 +241,7 @@ Genclks::checkMaster(Clock *gclk, { ensureMaster(gclk, sdc); if (gclk->masterClk() == nullptr) - report_->warn(1060, "no master clock found for generated clock %s.", + report_->warn(1060, "no master clock found for generated clock {}.", gclk->name()); } @@ -266,8 +262,7 @@ Genclks::ensureMaster(Clock *gclk, // Master source pin can actually be a clock source pin. if (master_clk != gclk) { gclk->setInferedMasterClk(master_clk); - debugPrint(debug_, "genclk", 2, " %s master clk %s", - gclk->name(), + debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(), master_clk->name()); found_master = true; master_clk_count++; @@ -291,8 +286,7 @@ Genclks::ensureMaster(Clock *gclk, // Master source pin can actually be a clock source pin. if (master_clk != gclk) { gclk->setInferedMasterClk(master_clk); - debugPrint(debug_, "genclk", 2, " %s master clk %s", - gclk->name(), + debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(), master_clk->name()); master_clk_count++; break; @@ -305,9 +299,8 @@ Genclks::ensureMaster(Clock *gclk, } if (master_clk_count > 1) report_->warn(1061, - "generated clock %s pin %s is in the fanout of multiple clocks.", - gclk->name(), - network_->pathName(src_pin)); + "generated clock {} pin {} is in the fanout of multiple clocks.", + gclk->name(), network_->pathName(src_pin)); } } @@ -346,8 +339,7 @@ GenClkFaninSrchPred::GenClkFaninSrchPred(Clock *gclk, bool GenClkFaninSrchPred::searchThruAllow(const TimingRole *role) const { - return (role == TimingRole::combinational() - || role == TimingRole::wire() + return (role == TimingRole::combinational() || role == TimingRole::wire() || !combinational_); } @@ -363,8 +355,8 @@ Genclks::findFanin(Clock *gclk, Vertex *vertex = iter.next(); if (!fanins.contains(vertex)) { fanins.insert(vertex); - debugPrint(debug_, "genclk", 2, "gen clk %s fanin %s", - gclk->name(), vertex->to_string(this).c_str()); + debugPrint(debug_, "genclk", 2, "gen clk {} fanin {}", gclk->name(), + vertex->to_string(this)); iter.enqueueAdjacentVertices(vertex, mode_); } } @@ -398,7 +390,7 @@ public: bool searchThru(Edge *edge, const Mode *mode) const override; bool searchTo(const Vertex *to_vertex, - const Mode *mode) const override; + const Mode *mode) const override; private: bool isNonGeneratedClkPin(const Pin *pin, @@ -423,12 +415,11 @@ GenClkInsertionSearchPred::searchThru(Edge *edge, { const TimingRole *role = edge->role(); EdgeSet &fdbk_edges = genclk_info_->fdbkEdges(); - return SearchPred0::searchThru(edge, mode) - && !role->isTimingCheck() - && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())) - && !fdbk_edges.contains(edge); + return SearchPred0::searchThru(edge, mode) && !role->isTimingCheck() + && (sta_->variables()->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) + && !fdbk_edges.contains(edge); } bool @@ -437,11 +428,11 @@ GenClkInsertionSearchPred::searchTo(const Vertex *to_vertex, { Pin *to_pin = to_vertex->pin(); return SearchPred0::searchTo(to_vertex, mode) - // Propagate through other generated clock roots but not regular - // clock roots. - && !(!gclk_->leafPins().contains(to_pin) - && isNonGeneratedClkPin(to_pin, mode->sdc())) - && genclk_info_->fanins().contains(const_cast(to_vertex)); + // Propagate through other generated clock roots but not regular + // clock roots. + && !(!gclk_->leafPins().contains(to_pin) + && isNonGeneratedClkPin(to_pin, mode->sdc())) + && genclk_info_->fanins().contains(const_cast(to_vertex)); } bool @@ -463,8 +454,7 @@ GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin, void Genclks::findInsertionDelays(Clock *gclk) { - debugPrint(debug_, "genclk", 2, "find gen clk %s insertion", - gclk->name()); + debugPrint(debug_, "genclk", 2, "find gen clk {} insertion", gclk->name()); GenclkInfo *genclk_info = makeGenclkInfo(gclk); FilterPath *src_filter = genclk_info->srcFilter(); GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this); @@ -492,7 +482,7 @@ Genclks::makeGenclkInfo(Clock *gclk) GenclkInfo * Genclks::genclkInfo(const Clock *gclk) const { - return findKey(genclk_info_map_, const_cast(gclk)); + return findKey(genclk_info_map_, const_cast(gclk)); } FilterPath * @@ -516,8 +506,7 @@ void Genclks::findLatchFdbkEdges(const Clock *clk) { GenclkInfo *genclk_info = genclkInfo(clk); - if (genclk_info - && !genclk_info->foundLatchFdbkEdges()) + if (genclk_info && !genclk_info->foundLatchFdbkEdges()) findLatchFdbkEdges(clk, genclk_info); } @@ -563,15 +552,15 @@ Genclks::findLatchFdbkEdges(Vertex *from_vertex, Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); if (path_vertices.contains(to_vertex)) { - debugPrint(debug_, "genclk", 2, " found feedback edge %s", - edge->to_string(this).c_str()); + debugPrint(debug_, "genclk", 2, " found feedback edge {}", + edge->to_string(this)); fdbk_edges.insert(edge); } else if (srch_pred.searchThru(edge, mode_) && srch_pred.searchTo(to_vertex, mode_) && to_vertex->level() <= gclk_level) - findLatchFdbkEdges(to_vertex, gclk_level, srch_pred, - path_vertices, visited_vertices, fdbk_edges); + findLatchFdbkEdges(to_vertex, gclk_level, srch_pred, path_vertices, + visited_vertices, fdbk_edges); } path_vertices.erase(from_vertex); } @@ -584,11 +573,11 @@ Genclks::makeSrcFilter(Clock *gclk, ClockSet *from_clks = new ClockSet; from_clks->insert(gclk->masterClk()); const RiseFallBoth *rf = RiseFallBoth::riseFall(); - ExceptionFrom *from = sdc->makeExceptionFrom(nullptr,from_clks,nullptr,rf); + ExceptionFrom *from = sdc->makeExceptionFrom(nullptr, from_clks, nullptr, rf); PinSet *thru_pins = new PinSet(network_); thru_pins->insert(gclk->srcPin()); - ExceptionThru *thru = sdc->makeExceptionThru(thru_pins,nullptr,nullptr,rf); + ExceptionThru *thru = sdc->makeExceptionThru(thru_pins, nullptr, nullptr, rf); ExceptionThruSeq *thrus = new ExceptionThruSeq; thrus->push_back(thru); @@ -608,7 +597,7 @@ Genclks::seedSrcPins(Clock *gclk, for (const Pin *master_pin : master_clk->leafPins()) { Vertex *vertex = graph_->pinDrvrVertex(master_pin); if (vertex) { - debugPrint(debug_, "genclk", 2, " seed src pin %s", + debugPrint(debug_, "genclk", 2, " seed src pin {}", network_->pathName(master_pin)); TagGroupBldr tag_bldr(true, this); tag_bldr.init(vertex); @@ -619,8 +608,8 @@ Genclks::seedSrcPins(Clock *gclk, for (const RiseFall *rf : RiseFall::range()) { Arrival insert = search_->clockInsertion(master_clk, master_pin, rf, min_max, early_late, mode_); - Tag *tag = makeTag(gclk, master_clk, master_pin, rf, - src_filter, insert, scene, min_max); + Tag *tag = makeTag(gclk, master_clk, master_pin, rf, src_filter, insert, + scene, min_max); tag_bldr.setArrival(tag, insert); } } @@ -648,13 +637,11 @@ Genclks::makeTag(const Clock *gclk, state = state->nextState(); ExceptionStateSet *states = new ExceptionStateSet(); states->insert(state); - const ClkInfo *clk_info = search_->findClkInfo(scene, - master_clk->edge(master_rf), - master_pin, true, nullptr, true, - nullptr, insert, 0.0, nullptr, - min_max, nullptr); - return search_->findTag(scene, master_rf, min_max, clk_info, - false, nullptr, false, states, true, nullptr); + const ClkInfo *clk_info = search_->findClkInfo( + scene, master_clk->edge(master_rf), master_pin, true, nullptr, true, nullptr, + insert, 0.0, nullptr, min_max, nullptr); + return search_->findTag(scene, master_rf, min_max, clk_info, false, nullptr, false, + states, true, nullptr); } class GenClkArrivalSearchPred : public EvalPred @@ -684,12 +671,10 @@ GenClkArrivalSearchPred::searchThru(Edge *edge, { const TimingRole *role = edge->role(); return EvalPred::searchThru(edge, mode) - && (role == TimingRole::combinational() - || role->isWire() - || !combinational_) - && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())); + && (role == TimingRole::combinational() || role->isWire() || !combinational_) + && (sta_->variables()->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())); } // Override EvalPred::searchTo to search to generated clock pin. @@ -730,12 +715,14 @@ protected: GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, BfsFwdIterator *insert_iter, GenclkInfo *genclk_info, - const Mode *mode): + const Mode *mode) : ArrivalVisitor(mode), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk_, genclk_info, mode), + srch_pred_(gclk_, + genclk_info, + mode), mode_(mode), sdc_(mode->sdc()), genclks_(mode->genclks()) @@ -749,11 +736,15 @@ GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, bool always_to_endpoints, SearchPred *pred, const Mode *mode) : - ArrivalVisitor(always_to_endpoints, pred, mode), + ArrivalVisitor(always_to_endpoints, + pred, + mode), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk, genclk_info, mode), + srch_pred_(gclk, + genclk_info, + mode), mode_(mode), sdc_(mode->sdc()), genclks_(mode->genclks()) @@ -770,8 +761,8 @@ GenclkSrcArrivalVisitor::copy() const void GenclkSrcArrivalVisitor::visit(Vertex *vertex) { - debugPrint(debug_, "genclk", 2, "find gen clk insert arrival %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "genclk", 2, "find gen clk insert arrival {}", + vertex->to_string(this)); tag_bldr_->init(vertex); has_fanin_one_ = graph_->hasFaninOne(vertex); genclks_->copyGenClkSrcPaths(vertex, tag_bldr_); @@ -787,8 +778,7 @@ Genclks::findSrcArrivals(Clock *gclk, GenclkInfo *genclk_info) { GenClkArrivalSearchPred eval_pred(gclk, this); - GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter, - genclk_info, mode_); + GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter, genclk_info, mode_); arrival_visitor.init(true, false, &eval_pred); // This cannot restrict the search level because loops in the clock tree // can circle back to the generated clock src pin. @@ -803,7 +793,7 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex, { auto itr = vertex_src_paths_map_.find(vertex); if (itr != vertex_src_paths_map_.end()) { - const std::vector &src_paths = itr->second; + const std::vector &src_paths = itr->second; for (const Path *path : src_paths) { Path src_path = *path; Path *prev_path = src_path.prevPath(); @@ -811,11 +801,11 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex, Path *prev_vpath = Path::vertexPath(prev_path, this); src_path.setPrevPath(prev_vpath); } - debugPrint(debug_, "genclk", 3, "vertex %s insert genclk %s src path %s %ss", - src_path.vertex(this)->to_string(this).c_str(), + debugPrint(debug_, "genclk", 3, "vertex {} insert genclk {} src path {} {}s", + src_path.vertex(this)->to_string(this), src_path.tag(this)->genClkSrcPathClk()->name(), - src_path.tag(this)->minMax()->to_string().c_str(), - src_path.tag(this)->to_string(true, false, this).c_str()); + src_path.tag(this)->minMax()->to_string(), + src_path.tag(this)->to_string(true, false, this)); tag_bldr->insertPath(src_path); } } @@ -858,28 +848,21 @@ Genclks::recordSrcPaths(Clock *gclk) while (path_iter.hasNext()) { Path *path = path_iter.next(); const ClockEdge *src_clk_edge = path->clkEdge(this); - if (src_clk_edge - && matchesSrcFilter(path, gclk)) { + if (src_clk_edge && matchesSrcFilter(path, gclk)) { const EarlyLate *early_late = path->minMax(this); const RiseFall *src_clk_rf = src_clk_edge->transition(); const RiseFall *rf = path->transition(this); bool inverting_path = (rf != src_clk_rf); size_t path_index = srcPathIndex(rf, path->minMax(this)); Path &src_path = src_paths[path_index]; - if ((!divide_by_1 - || (inverting_path == invert)) - && (!has_edges - || src_clk_rf == gclk->masterClkEdgeTr(rf)) + if ((!divide_by_1 || (inverting_path == invert)) + && (!has_edges || src_clk_rf == gclk->masterClkEdgeTr(rf)) && (src_path.isNull() - || delayGreater(path->arrival(), - src_path.arrival(), - early_late, + || delayGreater(path->arrival(), src_path.arrival(), early_late, this))) { - debugPrint(debug_, "genclk", 2, " %s insertion %s %s %s", - network_->pathName(gclk_pin), - early_late->to_string().c_str(), - rf->shortName(), - delayAsString(path->arrival(), this)); + debugPrint(debug_, "genclk", 2, " {} insertion {} {} {}", + network_->pathName(gclk_pin), early_late->to_string(), + rf->shortName(), delayAsString(path->arrival(), this)); src_path = *path; } } @@ -905,19 +888,17 @@ Genclks::recordSrcPaths(Clock *gclk) } } // Don't warn if the master clock is ideal. - if (!found_src_paths - && gclk->masterClk() - && gclk->masterClk()->isPropagated()) - report_->warn(1062, "generated clock %s source pin %s missing paths from master clock %s.", - gclk->name(), - network_->pathName(gclk_pin), - gclk->masterClk()->name()); + if (!found_src_paths && gclk->masterClk() && gclk->masterClk()->isPropagated()) + report_->warn( + 1062, + "generated clock {} source pin {} missing paths from master clock {}.", + gclk->name(), network_->pathName(gclk_pin), gclk->masterClk()->name()); } deleteGenclkSrcPaths(gclk); } void -Genclks:: deleteGenclkSrcPaths(Clock *gclk) +Genclks::deleteGenclkSrcPaths(Clock *gclk) { GenclkInfo *genclk_info = genclkInfo(gclk); GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_); @@ -938,13 +919,10 @@ Genclks::matchesSrcFilter(Path *path, { Tag *tag = path->tag(this); const ExceptionStateSet *states = tag->states(); - if (tag->isGenClkSrcPath() - && states) { + if (tag->isGenClkSrcPath() && states) { for (ExceptionState *state : *states) { ExceptionPath *except = state->exception(); - if (except->isFilter() - && state->nextThru() == nullptr - && except->to() + if (except->isFilter() && state->nextThru() == nullptr && except->to() && except->to()->matches(gclk)) return true; } @@ -958,8 +936,7 @@ Genclks::srcPath(const Path *clk_path) const const Pin *src_pin = clk_path->pin(this); const ClockEdge *clk_edge = clk_path->clkEdge(this); const EarlyLate *early_late = clk_path->minMax(this); - return srcPath(clk_edge->clock(), src_pin, - clk_edge->transition(), early_late); + return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), early_late); } const Path * @@ -967,8 +944,7 @@ Genclks::srcPath(const ClockEdge *clk_edge, const Pin *src_pin, const MinMax *min_max) const { - return srcPath(clk_edge->clock(), src_pin, - clk_edge->transition(), min_max); + return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), min_max); } const Path * @@ -1016,9 +992,7 @@ ClockPinPairLess::operator()(const ClockPinPair &pair1, int clk_index2 = clk2->index(); const Pin *pin1 = pair1.second; const Pin *pin2 = pair2.second; - return (clk_index1 < clk_index2 - || (clk_index1 == clk_index2 - && pin1 < pin2)); + return (clk_index1 < clk_index2 || (clk_index1 == clk_index2 && pin1 < pin2)); } class ClockPinPairHash @@ -1054,8 +1028,7 @@ ClockPinPairEqual::operator()(const ClockPinPair &pair1, const ClockPinPair &pair2) const { - return pair1.first == pair2.first - && pair1.second == pair2.second; + return pair1.first == pair2.first && pair1.second == pair2.second; } -} // namespace +} // namespace sta diff --git a/search/Latches.cc b/search/Latches.cc index 16bde088..c701e7fd 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -76,7 +76,7 @@ Latches::latchRequired(const Path *data_path, time_given_to_startpoint = 0.0; } else if (enable_path && disable_path) { - debugPrint(debug_, "latch", 1, "latch %s", + debugPrint(debug_, "latch", 1, "latch {}", sdc_network_->pathName(data_path->pin(this))); Delay open_latency, latency_diff, max_borrow; float nom_pulse_width, open_uncertainty; @@ -107,7 +107,7 @@ Latches::latchRequired(const Path *data_path, open_latency, this); enable_arrival = delaySum(enable_arrival, open_crpr, this); - debugPrint(debug_, "latch", 1, "data %s enable %s", + debugPrint(debug_, "latch", 1, "data {} enable {}", delayAsString(data_arrival, this), delayAsString(enable_arrival, this)); if (delayLessEqual(data_arrival, enable_arrival, this)) { @@ -155,7 +155,7 @@ Latches::latchRequired(const Path *data_path, adjusted_data_arrival = data_arrival; time_given_to_startpoint = 0.0; } - debugPrint(debug_, "latch", 2, "req %s borrow %s time_given %s adj_arrival %s", + debugPrint(debug_, "latch", 2, "req {} borrow {} time_given {} adj_arrival {}", delayAsString(required, this), delayAsString(borrow, this), delayAsString(time_given_to_startpoint, this), @@ -226,12 +226,12 @@ Latches::latchBorrowInfo(const Path *data_path, open_crpr = 0.0; crpr_diff = 0.0; } - debugPrint(debug_, "latch", 2, "nom_width %s open_lat %s lat_diff %s open_uncert %s", + debugPrint(debug_, "latch", 2, "nom_width {} open_lat {} lat_diff {} open_uncert {}", delayAsString(nom_pulse_width, this), delayAsString(open_latency, this), delayAsString(latency_diff, this), delayAsString(open_uncertainty, this)); - debugPrint(debug_, "latch", 2, "open_crpr %s crpr_diff %s open_uncert %s max_borrow %s", + debugPrint(debug_, "latch", 2, "open_crpr {} crpr_diff {} open_uncert {} max_borrow {}", delayAsString(open_crpr, this), delayAsString(crpr_diff, this), delayAsString(open_uncertainty, this), diff --git a/search/Levelize.cc b/search/Levelize.cc index dff1f199..5302be0a 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Levelize.hh" @@ -162,8 +162,7 @@ Levelize::findRoots() while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); if (isRoot(vertex)) { - debugPrint(debug_, "levelize", 2, "root %s%s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "levelize", 2, "root {}{}", vertex->to_string(this), hasFanout(vertex) ? " fanout" : ""); roots_.insert(vertex); } @@ -174,9 +173,8 @@ Levelize::findRoots() if (hasFanout(root)) fanout_roots++; } - debugPrint(debug_, "levelize", 1, "Found %zu roots %zu with fanout", - roots_.size(), - fanout_roots); + debugPrint(debug_, "levelize", 1, "Found {} roots {} with fanout", + roots_.size(), fanout_roots); } } @@ -200,14 +198,11 @@ bool Levelize::searchThru(Edge *edge) { const TimingRole *role = edge->role(); - return !role->isTimingCheck() - && role != TimingRole::latchDtoQ() - && !edge->isDisabledLoop() - // Register/latch preset/clr edges are disabled by default. - && !(role == TimingRole::regSetClr() - && !variables_->presetClrArcsEnabled()) - && !(edge->isBidirectInstPath() - && !variables_->bidirectInstPathsEnabled()); + return !role->isTimingCheck() && role != TimingRole::latchDtoQ() + && !edge->isDisabledLoop() + // Register/latch preset/clr edges are disabled by default. + && !(role == TimingRole::regSetClr() && !variables_->presetClrArcsEnabled()) + && !(edge->isBidirectInstPath() && !variables_->bidirectInstPathsEnabled()); } bool @@ -271,7 +266,7 @@ Levelize::findBackEdges(EdgeSeq &path, EdgeSet back_edges; while (!stack.empty()) { VertexEdgeIterPair vertex_iter = stack.top(); - const auto& [vertex, edge_iter] = vertex_iter; + const auto &[vertex, edge_iter] = vertex_iter; if (edge_iter->hasNext()) { Edge *edge = edge_iter->next(); if (searchThru(edge)) { @@ -282,7 +277,7 @@ Levelize::findBackEdges(EdgeSeq &path, path.push_back(edge); stack.emplace(to_vertex, new VertexOutEdgeIterator(to_vertex, graph_)); } - else if (to_vertex->visited2()) { // on path + else if (to_vertex->visited2()) { // on path // Found a back edge (loop). recordLoop(edge, path); back_edges.insert(edge); @@ -327,7 +322,7 @@ Levelize::findCycleBackEdges() back_edge_count += back_edges.size(); } } - debugPrint(debug_, "levelize", 1, "Found %zu cycle back edges", back_edge_count); + debugPrint(debug_, "levelize", 1, "Found {} cycle back edges", back_edge_count); } // Find vertices in cycles that are were not accessible from roots. @@ -350,7 +345,7 @@ VertexSeq Levelize::findTopologicalOrder() { Stats stats(debug_, report_); - std::map in_degree; + std::map in_degree; VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { @@ -368,12 +363,13 @@ Levelize::findTopologicalOrder() const Pin *pin = vertex->pin(); if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) && !vertex->isBidirectDriver()) { - Vertex *to_vertex = graph_->pinDrvrVertex(pin);; + Vertex *to_vertex = graph_->pinDrvrVertex(pin); + ; in_degree[to_vertex] += 1; } } - std::deque queue; + std::deque queue; for (Vertex *root : roots_) queue.push_back(root); @@ -412,14 +408,14 @@ Levelize::findTopologicalOrder() while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); if (in_degree[vertex] != 0) - debugPrint(debug_, "levelize", 2, "topological sort missing %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "levelize", 2, "topological sort missing {}", + vertex->to_string(this)); } } if (debug_->check("levelize", 3)) { - report_->reportLine("Topological sort"); + report_->report("Topological sort"); for (Vertex *vertex : topo_order) - report_->reportLine("%s", vertex->to_string(this).c_str()); + report_->report("{}", vertex->to_string(this)); } stats.report("Levelize topological sort"); return topo_order; @@ -429,9 +425,8 @@ void Levelize::recordLoop(Edge *edge, EdgeSeq &path) { - debugPrint(debug_, "levelize", 2, "Loop edge %s (%s)", - edge->to_string(this).c_str(), - edge->role()->to_string().c_str()); + debugPrint(debug_, "levelize", 2, "Loop edge {} ({})", + edge->to_string(this), edge->role()->to_string()); EdgeSeq *loop_edges = loopEdges(path, edge); GraphLoop *loop = new GraphLoop(loop_edges); loops_.push_back(loop); @@ -460,14 +455,12 @@ Levelize::loopEdges(EdgeSeq &path, if (from_pin == loop_pin) copy = true; if (copy) { - debugPrint(debug_, "loop", 2, " %s", - edge->to_string(this).c_str()); + debugPrint(debug_, "loop", 2, " {}", edge->to_string(this)); loop_edges->push_back(edge); loop_edges_.insert(edge); } } - debugPrint(debug_, "loop", 2, " %s", - closing_edge->to_string(this).c_str()); + debugPrint(debug_, "loop", 2, " {}", closing_edge->to_string(this)); loop_edges->push_back(closing_edge); loop_edges_.insert(closing_edge); return loop_edges; @@ -479,8 +472,8 @@ Levelize::reportPath(EdgeSeq &path) const bool first_edge = true; for (Edge *edge : path) { if (first_edge) - report_->reportLine(" %s", edge->from(graph_)->to_string(this).c_str()); - report_->reportLine(" %s", edge->to(graph_)->to_string(this).c_str()); + report_->report(" {}", edge->from(graph_)->to_string(this)); + report_->report(" {}", edge->to(graph_)->to_string(this)); first_edge = false; } } @@ -499,16 +492,16 @@ Levelize::assignLevels(VertexSeq &topo_sorted) Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); if (searchThru(edge)) - setLevel(to_vertex, std::max(to_vertex->level(), - vertex->level() + level_space_)); + setLevel(to_vertex, + std::max(to_vertex->level(), vertex->level() + level_space_)); } // Levelize bidirect driver as if it was a fanout of the bidirect load. const Pin *pin = vertex->pin(); if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) && !vertex->isBidirectDriver()) { Vertex *to_vertex = graph_->pinDrvrVertex(pin); - setLevel(to_vertex, std::max(to_vertex->level(), - vertex->level() + level_space_)); + setLevel(to_vertex, + std::max(to_vertex->level(), vertex->level() + level_space_)); } } } @@ -528,12 +521,9 @@ Levelize::ensureLatchLevels() Vertex *to = edge->to(graph_); if (from->level() == to->level()) { Level adjusted_level = from->level() + level_space_; - debugPrint(debug_, "levelize", 2, "latch %s %d (adjusted %d) -> %s %d", - from->to_string(this).c_str(), - from->level(), - adjusted_level, - to->to_string(this).c_str(), - to->level()); + debugPrint(debug_, "levelize", 2, "latch {} {} (adjusted {}) -> {} {}", + from->to_string(this), from->level(), adjusted_level, + to->to_string(this), to->level()); setLevel(from, adjusted_level); } } @@ -541,12 +531,11 @@ Levelize::ensureLatchLevels() } void -Levelize::setLevel(Vertex *vertex, +Levelize::setLevel(Vertex *vertex, Level level) { - debugPrint(debug_, "levelize", 3, "set level %s %d", - vertex->to_string(this).c_str(), - level); + debugPrint(debug_, "levelize", 3, "set level {} {}", + vertex->to_string(this), level); vertex->setLevel(level); max_level_ = std::max(level, max_level_); if (level >= Graph::vertex_level_max) @@ -576,8 +565,8 @@ void Levelize::relevelizeFrom(Vertex *vertex) { if (levelized_) { - debugPrint(debug_, "levelize", 1, "level invalid from %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "levelize", 1, "level invalid from {}", + vertex->to_string(this)); relevelize_from_.insert(vertex); levels_valid_ = false; } @@ -586,10 +575,9 @@ Levelize::relevelizeFrom(Vertex *vertex) void Levelize::deleteEdgeBefore(Edge *edge) { - if (levelized_ - && loop_edges_.contains(edge)) { - debugPrint(debug_, "levelize", 2, "delete loop edge %s", - edge->to_string(this).c_str()); + if (levelized_ && loop_edges_.contains(edge)) { + debugPrint(debug_, "levelize", 2, "delete loop edge {}", + edge->to_string(this)); disabled_loop_edges_.erase(edge); // Relevelize if a loop edge is removed. Incremental levelization // fails because the DFS path will be missing. @@ -610,9 +598,9 @@ void Levelize::relevelize() { for (Vertex *vertex : relevelize_from_) { - debugPrint(debug_, "levelize", 2, "relevelize from %s", - vertex->to_string(this).c_str()); - if (isRoot(vertex)) + debugPrint(debug_, "levelize", 2, "relevelize from {}", + vertex->to_string(this)); + if (isRoot(vertex)) roots_.insert(vertex); VertexSet path_vertices = makeVertexSet(this); EdgeSeq path; @@ -646,8 +634,8 @@ Levelize::visit(Vertex *vertex, // Back edges form feedback loops. recordLoop(edge, path); else if (to_vertex->level() <= level) - visit(to_vertex, edge, level+level_space, level_space, - path_vertices, path); + visit(to_vertex, edge, level + level_space, level_space, path_vertices, + path); } const TimingRole *role = edge->role(); @@ -668,8 +656,8 @@ Levelize::visit(Vertex *vertex, && !vertex->isBidirectDriver()) { Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); if (to_vertex->level() <= level) - visit(to_vertex, nullptr, level+level_space, level_space, - path_vertices, path); + visit(to_vertex, nullptr, level + level_space, level_space, path_vertices, + path); } path_vertices.erase(vertex); if (from) @@ -683,12 +671,11 @@ Levelize::isDisabledLoop(Edge *edge) const } void -Levelize::setLevelIncr(Vertex *vertex, +Levelize::setLevelIncr(Vertex *vertex, Level level) { - debugPrint(debug_, "levelize", 2, "set level %s %d", - vertex->to_string(this).c_str(), - level); + debugPrint(debug_, "levelize", 2, "set level {} {}", + vertex->to_string(this), level); if (vertex->level() != level) { if (observer_) observer_->levelChangedBefore(vertex); @@ -715,11 +702,9 @@ Levelize::checkLevels() && from_level >= level // Loops with no entry edges are all level zero. && !(from_level == 0 && level == 0)) - report_->warn(617, "level check failed %s %d -> %s %d", - from_vertex->name(network_), - from_vertex->level(), - vertex->name(network_), - level); + report_->warn(617, "level check failed {} {} -> {} {}", + from_vertex->name(network_), from_vertex->level(), + vertex->name(network_), level); } } } @@ -731,18 +716,14 @@ GraphLoop::GraphLoop(EdgeSeq *edges) : { } -GraphLoop::~GraphLoop() -{ - delete edges_; -} +GraphLoop::~GraphLoop() { delete edges_; } bool GraphLoop::isCombinational() const { for (Edge *edge : *edges_) { const TimingRole *role = edge->role(); - if (!(role == TimingRole::wire() - || role == TimingRole::combinational() + if (!(role == TimingRole::wire() || role == TimingRole::combinational() || role == TimingRole::tristateEnable() || role == TimingRole::tristateDisable())) return false; @@ -758,10 +739,10 @@ GraphLoop::report(const StaState *sta) const bool first_edge = true; for (Edge *edge : *edges_) { if (first_edge) - report->reportLine(" %s", edge->from(graph)->to_string(sta).c_str()); - report->reportLine(" %s", edge->to(graph)->to_string(graph).c_str()); + report->report(" {}", edge->from(graph)->to_string(sta)); + report->report(" {}", edge->to(graph)->to_string(sta)); first_edge = false; } } -} // namespace +} // namespace sta diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 39f712fe..20ec6110 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "MakeTimingModel.hh" @@ -75,7 +75,8 @@ MakeTimingModel::MakeTimingModel(const char *lib_name, scene_(scene), cell_(nullptr), min_max_(MinMax::max()), - lib_builder_(new LibertyBuilder(debug_, report_)), + lib_builder_(new LibertyBuilder(debug_, + report_)), tbl_template_index_(1), sdc_(scene->sdc()), sdc_backup_(nullptr), @@ -84,10 +85,7 @@ MakeTimingModel::MakeTimingModel(const char *lib_name, scenes_.insert(scene_); } -MakeTimingModel::~MakeTimingModel() -{ - delete lib_builder_; -} +MakeTimingModel::~MakeTimingModel() { delete lib_builder_; } LibertyLibrary * MakeTimingModel::makeTimingModel() @@ -107,7 +105,7 @@ MakeTimingModel::makeTimingModel() cell_->finish(false, report_, debug_); restoreSdc(); - + return library_; } @@ -193,9 +191,8 @@ MakeTimingModel::makePorts() int from_index = network_->fromIndex(port); int to_index = network_->toIndex(port); BusDcl *bus_dcl = library_->makeBusDcl(port_name, from_index, to_index); - LibertyPort *lib_port = lib_builder_->makeBusPort(cell_, port_name, - from_index, to_index, - bus_dcl); + LibertyPort *lib_port = + lib_builder_->makeBusPort(cell_, port_name, from_index, to_index, bus_dcl); lib_port->setDirection(network_->direction(port)); PortMemberIterator *member_iter = network_->memberIterator(port); while (member_iter->hasNext()) { @@ -223,8 +220,7 @@ MakeTimingModel::checkClock(Clock *clk) { for (const Pin *pin : clk->leafPins()) { if (!network_->isTopLevelPort(pin)) - report_->warn(1355, "clock %s pin %s is inside model block.", - clk->name(), + report_->warn(1355, "clock {} pin {} is inside model block.", clk->name(), network_->pathName(pin)); } } @@ -235,7 +231,7 @@ class MakeEndTimingArcs : public PathEndVisitor { public: MakeEndTimingArcs(Sta *sta); - MakeEndTimingArcs(const MakeEndTimingArcs&) = default; + MakeEndTimingArcs(const MakeEndTimingArcs &) = default; ~MakeEndTimingArcs() override {} PathEndVisitor *copy() const override; void visit(PathEnd *path_end) override; @@ -273,8 +269,7 @@ MakeEndTimingArcs::visit(PathEnd *path_end) const Sdc *sdc = src_path->sdc(sta_); const Clock *src_clk = src_path->clock(sta_); const ClockEdge *tgt_clk_edge = path_end->targetClkEdge(sta_); - if (src_clk == sdc->defaultArrivalClock() - && tgt_clk_edge) { + if (src_clk == sdc->defaultArrivalClock() && tgt_clk_edge) { Network *network = sta_->network(); Debug *debug = sta_->debug(); const MinMax *min_max = path_end->minMax(sta_); @@ -285,13 +280,10 @@ MakeEndTimingArcs::visit(PathEnd *path_end) ? delaySum(delayDiff(data_delay, clk_latency, sta_), check_margin, sta_) : delaySum(delayDiff(clk_latency, data_delay, sta_), check_margin, sta_); float delay1 = delayAsFloat(margin, MinMax::max(), sta_); - debugPrint(debug, "make_timing_model", 2, "%s -> %s clock %s %s %s %s", - input_rf_->shortName(), - network->pathName(src_path->pin(sta_)), - tgt_clk_edge->name(), - path_end->typeName(), - min_max->to_string().c_str(), - delayAsString(margin, sta_)); + debugPrint(debug, "make_timing_model", 2, "{} -> {} clock {} {} {} {}", + input_rf_->shortName(), network->pathName(src_path->pin(sta_)), + tgt_clk_edge->name(), path_end->typeName(), + min_max->to_string(), delayAsString(margin, sta_)); if (debug->check("make_timing_model", 3)) sta_->reportPathEnd(path_end); @@ -335,15 +327,14 @@ MakeTimingModel::findTimingFromInput(Port *input_port) OutputPinDelays output_delays; for (const RiseFall *input_rf : RiseFall::range()) { const RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth(); - sta_->setInputDelay(input_pin, input_rf1, - sdc_->defaultArrivalClock(), - sdc_->defaultArrivalClockEdge()->transition(), - nullptr, false, false, MinMaxAll::all(), true, 0.0, sdc_); + sta_->setInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(), + sdc_->defaultArrivalClockEdge()->transition(), nullptr, + false, false, MinMaxAll::all(), true, 0.0, sdc_); PinSet *from_pins = new PinSet(network_); from_pins->insert(input_pin); - ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr, - input_rf1, sdc_); + ExceptionFrom *from = + sta_->makeExceptionFrom(from_pins, nullptr, nullptr, input_rf1, sdc_); search_->findFilteredArrivals(from, nullptr, nullptr, false, false); end_visitor.setInputRf(input_rf); @@ -354,8 +345,7 @@ MakeTimingModel::findTimingFromInput(Port *input_port) findOutputDelays(input_rf, output_delays); search_->deleteFilteredArrivals(); - sta_->removeInputDelay(input_pin, input_rf1, - sdc_->defaultArrivalClock(), + sta_->removeInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(), sdc_->defaultArrivalClockEdge()->transition(), MinMaxAll::all(), sdc_); } @@ -395,7 +385,7 @@ void MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, const ClockEdgeDelays &clk_margins) { - for (const auto& [clk_edge, margins] : clk_margins) { + for (const auto &[clk_edge, margins] : clk_margins) { for (const MinMax *min_max : MinMax::range()) { bool setup = (min_max == MinMax::max()); TimingArcAttrsPtr attrs = nullptr; @@ -404,16 +394,14 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, bool exists; margins.value(input_rf, min_max, margin, exists); if (exists) { - debugPrint(debug_, "make_timing_model", 2, "%s %s %s -> clock %s %s", - sta_->network()->pathName(input_pin), - input_rf->shortName(), - min_max == MinMax::max() ? "setup" : "hold", - clk_edge->name(), + debugPrint(debug_, "make_timing_model", 2, "{} {} {} -> clock {} {}", + sta_->network()->pathName(input_pin), input_rf->shortName(), + min_max == MinMax::max() ? "setup" : "hold", clk_edge->name(), delayAsString(margin, sta_)); - ScaleFactorType scale_type = setup - ? ScaleFactorType::setup - : ScaleFactorType::hold; - TimingModel *check_model = makeScalarCheckModel(margin, scale_type, input_rf); + ScaleFactorType scale_type = + setup ? ScaleFactorType::setup : ScaleFactorType::hold; + TimingModel *check_model = + makeScalarCheckModel(margin, scale_type, input_rf); if (attrs == nullptr) attrs = std::make_shared(); attrs->setModel(input_rf, check_model); @@ -425,12 +413,10 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, LibertyPort *clk_port = modelPort(clk_pin); if (clk_port) { const RiseFall *clk_rf = clk_edge->transition(); - const TimingRole *role = setup - ? TimingRole::setup() - : TimingRole::hold(); - lib_builder_->makeFromTransitionArcs(cell_, clk_port, - input_port, nullptr, - clk_rf, role, attrs); + const TimingRole *role = + setup ? TimingRole::setup() : TimingRole::hold(); + lib_builder_->makeFromTransitionArcs(cell_, clk_port, input_port, + nullptr, clk_rf, role, attrs); } } } @@ -442,7 +428,7 @@ void MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, OutputPinDelays &output_pin_delays) { - for (const auto& [output_pin, output_delays] : output_pin_delays) { + for (const auto &[output_pin, output_delays] : output_pin_delays) { TimingArcAttrsPtr attrs = nullptr; for (const RiseFall *output_rf : RiseFall::range()) { const MinMax *min_max = MinMax::max(); @@ -450,11 +436,9 @@ MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, bool exists; output_delays.delays.value(output_rf, min_max, delay, exists); if (exists) { - debugPrint(debug_, "make_timing_model", 2, "%s -> %s %s delay %s", - network_->pathName(input_pin), - network_->pathName(output_pin), - output_rf->shortName(), - delayAsString(delay, sta_)); + debugPrint(debug_, "make_timing_model", 2, "{} -> {} {} delay {}", + network_->pathName(input_pin), network_->pathName(output_pin), + output_rf->shortName(), delayAsString(delay, sta_)); TimingModel *gate_model = makeGateModelTable(output_pin, delay, output_rf); if (attrs == nullptr) attrs = std::make_shared(); @@ -465,8 +449,8 @@ MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, LibertyPort *output_port = modelPort(output_pin); LibertyPort *input_port = modelPort(input_pin); attrs->setTimingSense(output_delays.timingSense()); - lib_builder_->makeCombinationalArcs(cell_, input_port, output_port, - true, true, attrs); + lib_builder_->makeCombinationalArcs(cell_, input_port, output_port, true, true, + attrs); } } } @@ -479,7 +463,7 @@ MakeTimingModel::findClkedOutputPaths() { InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance()); while (output_iter->hasNext()) { - Pin *output_pin = output_iter->next(); + Pin *output_pin = output_iter->next(); if (network_->direction(output_pin)->isOutput()) { ClockEdgeDelays clk_delays; LibertyPort *output_port = modelPort(output_pin); @@ -493,11 +477,10 @@ MakeTimingModel::findClkedOutputPaths() const MinMax *min_max = path->minMax(sta_); Arrival delay = path->arrival(); RiseFallMinMax &delays = clk_delays[clk_edge]; - delays.mergeValue(output_rf, min_max, - delayAsFloat(delay, min_max, sta_)); + delays.mergeValue(output_rf, min_max, delayAsFloat(delay, min_max, sta_)); } } - for (const auto& [clk_edge, delays] : clk_delays) { + for (const auto &[clk_edge, delays] : clk_delays) { for (const Pin *clk_pin : clk_edge->clock()->pins()) { LibertyPort *clk_port = modelPort(clk_pin); if (clk_port) { @@ -505,16 +488,16 @@ MakeTimingModel::findClkedOutputPaths() TimingArcAttrsPtr attrs = nullptr; for (const RiseFall *output_rf : RiseFall::range()) { float delay = delays.value(output_rf, min_max_) - clk_edge->time(); - TimingModel *gate_model = makeGateModelTable(output_pin, delay, output_rf); + TimingModel *gate_model = + makeGateModelTable(output_pin, delay, output_rf); if (attrs == nullptr) attrs = std::make_shared(); attrs->setModel(output_rf, gate_model); } if (attrs) { - lib_builder_->makeFromTransitionArcs(cell_, clk_port, - output_port, nullptr, - clk_rf, TimingRole::regClkToQ(), - attrs); + lib_builder_->makeFromTransitionArcs(cell_, clk_port, output_port, + nullptr, clk_rf, + TimingRole::regClkToQ(), attrs); } } } @@ -545,8 +528,10 @@ MakeTimingModel::findClkTreeDelays() for (const Clock *clk : *clks) { ClkDelays delays = sta_->findClkDelays(clk, scene_, true); for (const MinMax *min_max : MinMax::range()) { - makeClkTreePaths(lib_port, min_max, TimingSense::positive_unate, delays); - makeClkTreePaths(lib_port, min_max, TimingSense::negative_unate, delays); + makeClkTreePaths(lib_port, min_max, TimingSense::positive_unate, + delays); + makeClkTreePaths(lib_port, min_max, TimingSense::negative_unate, + delays); } } } @@ -564,15 +549,14 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port, { TimingArcAttrsPtr attrs = nullptr; for (const RiseFall *clk_rf : RiseFall::range()) { - const RiseFall *end_rf = (sense == TimingSense::positive_unate) - ? clk_rf - : clk_rf->opposite(); + const RiseFall *end_rf = + (sense == TimingSense::positive_unate) ? clk_rf : clk_rf->opposite(); Path clk_path; Delay insertion, delay, latency; float lib_clk_delay; bool exists; - delays.delay(clk_rf, end_rf, min_max, insertion, delay, - lib_clk_delay, latency, clk_path, exists); + delays.delay(clk_rf, end_rf, min_max, insertion, delay, lib_clk_delay, latency, + clk_path, exists); if (exists) { TimingModel *model = makeGateModelScalar(delay, end_rf); if (attrs == nullptr) @@ -583,8 +567,8 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port, if (attrs) { attrs->setTimingSense(sense); const TimingRole *role = (min_max == MinMax::min()) - ? TimingRole::clockTreePathMin() - : TimingRole::clockTreePathMax(); + ? TimingRole::clockTreePathMin() + : TimingRole::clockTreePathMax(); lib_builder_->makeClockTreePathArcs(cell_, lib_port, role, attrs); } } @@ -669,12 +653,11 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, const Pin *gate_in_pin = network_->findPin(drvr_inst, gate_in_port); if (gate_in_pin) { Vertex *gate_in_vertex = graph_->pinLoadVertex(gate_in_pin); - Slew in_slew = graph_->slew(gate_in_vertex, - drvr_arc->fromEdge()->asRiseFall(), - ap_index); + Slew in_slew = graph_->slew( + gate_in_vertex, drvr_arc->fromEdge()->asRiseFall(), ap_index); float in_slew1 = delayAsFloat(in_slew); - GateTableModel *drvr_gate_model = drvr_arc->gateTableModel(scene_, - min_max_); + GateTableModel *drvr_gate_model = + drvr_arc->gateTableModel(scene_, min_max_); if (drvr_gate_model) { float output_load_cap = graph_delay_calc_->loadCap(output_pin, scene_, @@ -704,15 +687,17 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, } FloatSeq axis_values = drvr_axis_values; - TableAxisPtr load_axis = - std::make_shared(TableAxisVariable::total_output_net_capacitance, - std::move(axis_values)); + TableAxisPtr load_axis = std::make_shared( + TableAxisVariable::total_output_net_capacitance, + std::move(axis_values)); - TablePtr delay_table = std::make_shared
(load_values, load_axis); - TablePtr slew_table = std::make_shared
(slew_values, load_axis); + TablePtr delay_table = + std::make_shared
(load_values, load_axis); + TablePtr slew_table = + std::make_shared
(slew_values, load_axis); - TableTemplate *model_template = ensureTableTemplate(drvr_template, - load_axis); + TableTemplate *model_template = + ensureTableTemplate(drvr_template, load_axis); TableModel *delay_model = new TableModel(delay_table, model_template, ScaleFactorType::cell, rf); TableModels *delay_models = new TableModels(delay_model); @@ -745,8 +730,8 @@ MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template, std::string template_name = "template_"; template_name += std::to_string(tbl_template_index_++); - model_template = library_->makeTableTemplate(template_name, - TableTemplateType::delay); + model_template = + library_->makeTableTemplate(template_name, TableTemplateType::delay); model_template->setAxis1(load_axis); template_map_[drvr_template] = model_template; } @@ -757,13 +742,16 @@ const TableAxis * MakeTimingModel::loadCapacitanceAxis(const TableModel *table) { if (table->axis1() - && table->axis1()->variable() == TableAxisVariable::total_output_net_capacitance) + && table->axis1()->variable() + == TableAxisVariable::total_output_net_capacitance) return table->axis1(); else if (table->axis2() - && table->axis2()->variable() == TableAxisVariable::total_output_net_capacitance) + && table->axis2()->variable() + == TableAxisVariable::total_output_net_capacitance) return table->axis2(); else if (table->axis3() - && table->axis3()->variable() == TableAxisVariable::total_output_net_capacitance) + && table->axis3()->variable() + == TableAxisVariable::total_output_net_capacitance) return table->axis3(); else return nullptr; @@ -781,9 +769,9 @@ TimingSense OutputDelays::timingSense() const { if (rf_path_exists[RiseFall::riseIndex()][RiseFall::riseIndex()] - && rf_path_exists[RiseFall::fallIndex()][RiseFall::fallIndex()] - && !rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()] - && !rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()]) + && rf_path_exists[RiseFall::fallIndex()][RiseFall::fallIndex()] + && !rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()] + && !rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()]) return TimingSense::positive_unate; else if (rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()] && rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()] @@ -799,4 +787,4 @@ OutputDelays::timingSense() const return TimingSense::none; } -} // namespace +} // namespace sta diff --git a/search/Path.cc b/search/Path.cc index 38470f8e..c0bb35d1 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Path.hh" @@ -129,8 +129,7 @@ Path::init(Vertex *vertex, { const Graph *graph = sta->graph(); vertex_id_ = graph->id(vertex); - tag_index_ = tag_index_null, - prev_path_ = nullptr; + tag_index_ = tag_index_null, prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = arrival; required_ = 0.0; @@ -144,8 +143,7 @@ Path::init(Vertex *vertex, { const Graph *graph = sta->graph(); vertex_id_ = graph->id(vertex); - tag_index_ = tag->index(), - prev_path_ = nullptr; + tag_index_ = tag->index(), prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = 0.0; required_ = 0.0; @@ -160,8 +158,7 @@ Path::init(Vertex *vertex, { const Graph *graph = sta->graph(); vertex_id_ = graph->id(vertex); - tag_index_ = tag->index(), - prev_path_ = nullptr; + tag_index_ = tag->index(), prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = arrival; required_ = 0.0; @@ -178,8 +175,7 @@ Path::init(Vertex *vertex, const StaState *sta) { const Graph *graph = sta->graph(); - tag_index_ = tag->index(), - prev_path_ = prev_path; + tag_index_ = tag->index(), prev_path_ = prev_path; if (prev_path) { prev_edge_id_ = graph->id(prev_edge); prev_arc_idx_ = prev_arc->index(); @@ -199,12 +195,12 @@ Path::to_string(const StaState *sta) const if (isNull()) return "null path"; else - return stringPrintTmp("%s %s %s/%s %d", - vertex(sta)->to_string(sta).c_str(), - transition(sta)->shortName(), - scene(sta)->name().c_str(), - minMax(sta)->to_string().c_str(), - tagIndex(sta)); + return sta::format("{} {} {}/{} {}", + vertex(sta)->to_string(sta), + transition(sta)->shortName(), + scene(sta)->name(), + minMax(sta)->to_string(), + tagIndex(sta)); } bool @@ -221,7 +217,7 @@ Path::vertex(const StaState *sta) const const Edge *edge = graph->edge(prev_edge_id_); return edge->to(graph); } - else + else return graph->vertex(vertex_id_); } @@ -404,7 +400,7 @@ Path::prevArc(const StaState *sta) const TimingArcSet *arc_set = edge->timingArcSet(); return arc_set->findTimingArc(prev_arc_idx_); } - else + else return nullptr; } @@ -415,7 +411,7 @@ Path::prevEdge(const StaState *sta) const const Graph *graph = sta->graph(); return graph->edge(prev_edge_id_); } - else + else return nullptr; } @@ -448,8 +444,7 @@ void Path::checkPrevPath(const StaState *sta) const { if (prev_path_ && prev_path_->isNull()) - sta->report()->reportLine("path %s prev path is null.", - to_string(sta).c_str()); + sta->report()->report("path {} prev path is null.", to_string(sta)); if (prev_path_ && !prev_path_->isNull()) { Graph *graph = sta->graph(); Edge *edge = prevEdge(sta); @@ -457,10 +452,9 @@ Path::checkPrevPath(const StaState *sta) const Vertex *prev_edge_vertex = edge->from(graph); if (prev_vertex != prev_edge_vertex) { Network *network = sta->network(); - sta->report()->reportLine("path %s prev path corrupted %s vs %s.", - to_string(sta).c_str(), - prev_vertex->name(network), - prev_edge_vertex->name(network)); + sta->report()->report("path {} prev path corrupted {} vs {}.", to_string(sta), + prev_vertex->name(network), + prev_edge_vertex->name(network)); } } } @@ -478,14 +472,14 @@ Path::tgtClkMinMax(const StaState *sta) const { const MinMax *min_max = minMax(sta); switch (mode(sta)->sdc()->analysisType()) { - case AnalysisType::single: - case AnalysisType::bc_wc: - return min_max; - case AnalysisType::ocv: - return min_max->opposite(); - default: - // suppress gcc warning - return min_max; + case AnalysisType::single: + case AnalysisType::bc_wc: + return min_max; + case AnalysisType::ocv: + return min_max->opposite(); + default: + // suppress gcc warning + return min_max; } } //////////////////////////////////////////////////////////////// @@ -579,8 +573,7 @@ Path::cmpClk(const Path *path1, else return 1; } - else if (clk_edge1 == nullptr - && clk_edge2 == nullptr) + else if (clk_edge1 == nullptr && clk_edge2 == nullptr) return 0; else if (clk_edge2) return -1; @@ -594,11 +587,10 @@ Path::equal(const Path *path1, const StaState *sta) { return (path1 == nullptr && path2 == nullptr) - || (path1 - && path2 - && path1->vertexId(sta) == path2->vertexId(sta) - // Tag equal implies transition and path ap equal. - && path1->tagIndex(sta) == path2->tagIndex(sta)); + || (path1 && path2 + && path1->vertexId(sta) == path2->vertexId(sta) + // Tag equal implies transition and path ap equal. + && path1->tagIndex(sta) == path2->tagIndex(sta)); } //////////////////////////////////////////////////////////////// @@ -779,12 +771,9 @@ VertexPathIterator::findNext() Path *path = &paths_[path_index_++]; if (filtered_) { const Tag *tag = path->tag(search_); - if ((scene_ == nullptr - || path->scene(search_) == scene_) - && (rf_ == nullptr - || tag->rfIndex() == rf_->index()) - && (min_max_ == nullptr - || path->minMax(search_) == min_max_)) { + if ((scene_ == nullptr || path->scene(search_) == scene_) + && (rf_ == nullptr || tag->rfIndex() == rf_->index()) + && (min_max_ == nullptr || path->minMax(search_) == min_max_)) { next_ = path; return; } @@ -797,9 +786,7 @@ VertexPathIterator::findNext() next_ = nullptr; } -VertexPathIterator::~VertexPathIterator() -{ -} +VertexPathIterator::~VertexPathIterator() {} bool VertexPathIterator::hasNext() @@ -815,4 +802,4 @@ VertexPathIterator::next() return path; } -} // namespace +} // namespace sta diff --git a/search/PathEnum.cc b/search/PathEnum.cc index c5e11778..437966a8 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "PathEnum.hh" @@ -41,11 +41,11 @@ namespace sta { -// A diversion is an alternate path formed by changing the previous +// A diversion is an alternate path formed by changing the previous // path/arc of before_div to after_div/div_arc in path. // // div_arc -// after_div<--------+ +// after_div<--------+ // | // <--...--before_div<--...--path<---path_end // @@ -128,13 +128,14 @@ PathEnum::PathEnum(size_t group_path_count, void PathEnum::insert(PathEnd *path_end) { - debugPrint(debug_, "path_enum", 1, "insert %s", - path_end->path()->to_string(this).c_str()); - debugPrint(debug_, "path_enum", 2, "diversion %s %s %s", - path_end->path()->to_string(this).c_str(), + debugPrint(debug_, "path_enum", 1, "insert {}", + path_end->path()->to_string(this)); + debugPrint(debug_, "path_enum", 2, "diversion {} {} {}", + path_end->path()->to_string(this), cmp_slack_ ? "slack" : "delay", - delayAsString(cmp_slack_ ? path_end->slack(this) : - path_end->dataArrivalTime(this), this)); + delayAsString(cmp_slack_ ? path_end->slack(this) + : path_end->dataArrivalTime(this), + this)); Diversion *div = new Diversion(path_end, path_end->path()); div_queue_.push(div); div_count_++; @@ -154,13 +155,11 @@ PathEnum::~PathEnum() bool PathEnum::hasNext() { - if (unique_pins_ - && !inserts_pruned_) { + if (unique_pins_ && !inserts_pruned_) { pruneDiversionQueue(); inserts_pruned_ = true; } - if (next_ == nullptr - && !div_queue_.empty()) + if (next_ == nullptr && !div_queue_.empty()) findNext(); return next_ != nullptr; } @@ -186,11 +185,10 @@ PathEnum::findNext() Vertex *vertex = path_end->vertex(this); path_counts_[vertex]++; if (debug_->check("path_enum", 2)) { - report_->reportLine("path_enum: next path %zu %s delay %s slack %s", - path_counts_[vertex], - path->to_string(this).c_str(), - delayAsString(path_end->dataArrivalTime(this), this), - delayAsString(path_end->slack(this), this)); + report_->report("path_enum: next path {} {} delay {} slack {}", + path_counts_[vertex], path->to_string(this), + delayAsString(path_end->dataArrivalTime(this), this), + delayAsString(path_end->slack(this), this)); reportDiversionPath(div); } @@ -206,9 +204,8 @@ PathEnum::findNext() else { // We have endpoint_path_count paths for this endpoint, // so we are done with it. - debugPrint(debug_, "path_enum", 1, - "endpoint_path_count reached for %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "path_enum", 1, "endpoint_path_count reached for {}", + vertex->to_string(this)); deleteDiversionPathEnd(div); } } @@ -222,12 +219,10 @@ PathEnum::reportDiversionPath(Diversion *div) Path *p = path_end->path(); Path *after_div = div->divPath(); while (p) { - report_->reportLine("path_enum: %s %s%s", - p->to_string(this).c_str(), - delayAsString(p->arrival(), this), - Path::equal(p, after_div, this) ? " <-after diversion" : ""); - if (p != path - && network_->isLatchData(p->pin(this))) + report_->report("path_enum: {} {}{}", p->to_string(this), + delayAsString(p->arrival(), this), + Path::equal(p, after_div, this) ? " <-after diversion" : ""); + if (p != path && network_->isLatchData(p->pin(this))) break; p = p->prevPath(); } @@ -235,8 +230,8 @@ PathEnum::reportDiversionPath(Diversion *div) //////////////////////////////////////////////////////////////// -using VisitedFanins = std::set>; -using VertexEdge = std::pair; +using VisitedFanins = std::set>; +using VertexEdge = std::pair; class PathEnumFaninVisitor : public PathVisitor { @@ -251,19 +246,19 @@ public: Vertex *prev_vertex, TimingArc *prev_arc); bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, - const Arrival &from_arrival, - Edge *edge, - TimingArc *arc, - ArcDelay arc_delay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &to_arrival, - const MinMax *min_max) override; + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, + const Arrival &from_arrival, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) override; private: void makeDivertedPathEnd(Path *after_div, @@ -299,7 +294,7 @@ private: Vertex *prev_vertex_; bool crpr_active_; VisitedFanins visited_fanins_; - std::map unique_edge_divs_; + std::map unique_edge_divs_; }; PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end, @@ -372,8 +367,7 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin, Path *from_path = from_iter.next(); const Mode *mode = from_path->mode(this); const Sdc *sdc = mode->sdc(); - if (pred_->searchFrom(from_vertex, mode) - && pred_->searchThru(edge, mode) + if (pred_->searchFrom(from_vertex, mode) && pred_->searchThru(edge, mode) && pred_->searchTo(to_vertex, mode) // Fanin paths are broken by path delay internal pin startpoints. && !sdc->isPathDelayInternalFromBreak(to_pin)) { @@ -382,13 +376,13 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin, arc_set->arcsFrom(from_rf, arc1, arc2); // Filter arcs by to edge. if (arc1 && arc1->toEdge()->asRiseFall()->index() == before_div_rf_index_) { - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc1, to_pin, to_vertex, min_max_, mode)) + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc1, + to_pin, to_vertex, min_max_, mode)) return false; } if (arc2 && arc2->toEdge()->asRiseFall()->index() == before_div_rf_index_) { - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc2, to_pin, to_vertex, min_max_, mode)) + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc2, + to_pin, to_vertex, min_max_, mode)) return false; } } @@ -415,23 +409,20 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, { // These paths fanin to before_div_ so we know to_vertex matches. if ((!unique_pins_ || from_vertex != prev_vertex_) - && (!unique_edges_ - || from_vertex != prev_vertex_ + && (!unique_edges_ || from_vertex != prev_vertex_ || from_rf != prev_arc_->fromEdge()->asRiseFall()) && arc != prev_arc_ && Tag::matchNoCrpr(to_tag, before_div_tag_) // Ignore paths that only differ by crpr from same vertex/edge. - && (!crpr_active_ - || !visited_fanins_.contains({from_vertex, arc}))) { - debugPrint(debug_, "path_enum", 3, "visit fanin %s -> %s %s %s", - from_path->to_string(this).c_str(), - to_vertex->to_string(this).c_str(), - to_rf->shortName(), - delayAsString(search_->deratedDelay(from_vertex, arc, edge, - false, from_path->minMax(this), - from_path->dcalcAnalysisPtIndex(this), - from_path->sdc(this)), - this)); + && (!crpr_active_ || !visited_fanins_.contains({from_vertex, arc}))) { + debugPrint(debug_, "path_enum", 3, "visit fanin {} -> {} {} {}", + from_path->to_string(this), + to_vertex->to_string(this), to_rf->shortName(), + delayAsString( + search_->deratedDelay( + from_vertex, arc, edge, false, from_path->minMax(this), + from_path->dcalcAnalysisPtIndex(this), from_path->sdc(this)), + this)); PathEnd *div_end; Path *after_div_copy; // Make the diverted path end to check slack with from_path crpr. @@ -448,21 +439,18 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, else { if (debug_->check("path_enum", 3)) { bool unique_pins = !(!unique_pins_ || from_vertex != prev_vertex_); - bool unique_edges = !(!unique_edges_ - || from_rf != prev_arc_->fromEdge()->asRiseFall()); + bool unique_edges = + !(!unique_edges_ || from_rf != prev_arc_->fromEdge()->asRiseFall()); bool same_arc = !(arc != prev_arc_); bool tag_march = !Tag::matchNoCrpr(to_tag, before_div_tag_); - bool crpr = !(!crpr_active_ - || visited_fanins_.find({from_vertex, arc}) - == visited_fanins_.end()); - debugPrint(debug_, "path_enum", 3, " pruned %s%s%s%s%s %s %s", + bool crpr = + !(!crpr_active_ + || visited_fanins_.find({from_vertex, arc}) == visited_fanins_.end()); + debugPrint(debug_, "path_enum", 3, " pruned {}{}{}{}{} {} {}", unique_pins ? "unique_pins " : "", - unique_edges ? "unique_edges " : "", - same_arc ? "same_arc " : "", - tag_march ? "tag_march " : "", - crpr ? "crpr " : "", - edge->to_string(this).c_str(), - arc->to_string().c_str()); + unique_edges ? "unique_edges " : "", same_arc ? "same_arc " : "", + tag_march ? "tag_march " : "", crpr ? "crpr " : "", + edge->to_string(this), arc->to_string()); } } return true; @@ -495,8 +483,8 @@ PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div, Path *&after_div_copy) { Path *div_path; - path_enum_->makeDivertedPath(path_end_->path(), before_div_, after_div, - div_edge, div_arc, div_path, after_div_copy); + path_enum_->makeDivertedPath(path_end_->path(), before_div_, after_div, div_edge, + div_arc, div_path, after_div_copy); div_end = path_end_->copy(); div_end->setPath(div_path); } @@ -505,7 +493,7 @@ void PathEnumFaninVisitor::reportDiversion(const Edge *div_edge, const TimingArc *div_arc, Path *after_div) -{ +{ if (debug_->check("path_enum", 3)) { Path *path = path_end_->path(); Arrival path_delay = path_enum_->cmp_slack_ @@ -517,16 +505,12 @@ PathEnumFaninVisitor::reportDiversion(const Edge *div_edge, div_arc), this); Path *div_prev = before_div_->prevPath(); - report_->reportLine("path_enum: diversion %s %s %s -> %s", - path->to_string(this).c_str(), - path_enum_->cmp_slack_ ? "slack" : "delay", - delayAsString(path_delay, this), - delayAsString(div_delay, this)); - report_->reportLine("path_enum: from %s -> %s", - div_prev->to_string(this).c_str(), - before_div_->to_string(this).c_str()); - report_->reportLine("path_enum: to %s", - after_div->to_string(this).c_str()); + report_->report("path_enum: diversion {} {} {} -> {}", path->to_string(this), + path_enum_->cmp_slack_ ? "slack" : "delay", + delayAsString(path_delay, this), delayAsString(div_delay, this)); + report_->report("path_enum: from {} -> {}", div_prev->to_string(this), + before_div_->to_string(this)); + report_->report("path_enum: to {}", after_div->to_string(this)); } } @@ -610,8 +594,8 @@ PathEnum::makeDiversions(PathEnd *path_end, Path *path = before; Path *prev_path = path->prevPath(); TimingArc *prev_arc = path->prevArc(this); - PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, - unique_edges_, this); + PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, unique_edges_, + this); while (prev_path) { // Fanin visitor does all the work. // While visiting the fanins the fanin_visitor finds the @@ -620,8 +604,7 @@ PathEnum::makeDiversions(PathEnd *path_end, // Do not enumerate beyond latch D to Q edges. // This breaks latch loop paths. const TimingRole *prev_role = prev_arc->role(); - if (prev_role == TimingRole::latchDtoQ() - || prev_role == TimingRole::regClkToQ()) + if (prev_role == TimingRole::latchDtoQ() || prev_role == TimingRole::regClkToQ()) break; path = prev_path; prev_path = path->prevPath(); @@ -649,14 +632,10 @@ PathEnum::makeDivertedPath(Path *path, Path *prev_copy = nullptr; while (p) { // prev_path made in next pass. - Path *copy = new Path(p->vertex(this), - p->tag(this), - p->arrival(), - // Replaced on next pass. - p->prevPath(), - p->prevEdge(this), - p->prevArc(this), - true, this); + Path *copy = + new Path(p->vertex(this), p->tag(this), p->arrival(), + // Replaced on next pass. + p->prevPath(), p->prevEdge(this), p->prevArc(this), true, this); search_->saveEnumPath(copy); if (prev_copy) prev_copy->setPrevPath(copy); @@ -666,8 +645,7 @@ PathEnum::makeDivertedPath(Path *path, after_div_copy = copy; if (first) div_path = copy; - else if (found_div - && network_->isLatchData(p->pin(this))) + else if (found_div && network_->isLatchData(p->pin(this))) break; if (p == before_div) { // Replaced on next pass. @@ -704,13 +682,11 @@ PathEnum::updatePathHeadDelays(PathSeq &paths, if (edge) { Arrival arrival; const MinMax *min_max = path->minMax(this); - if (i == path_idx_max - && edge->role()->isLatchDtoQ() + if (i == path_idx_max && edge->role()->isLatchDtoQ() && min_max == MinMax::max()) { ArcDelay arc_delay; Tag *q_tag; - latches_->latchOutArrival(after_div, arc, edge, - q_tag, arc_delay, arrival); + latches_->latchOutArrival(after_div, arc, edge, q_tag, arc_delay, arrival); path->setArrival(arrival); path->setTag(q_tag); prev_clk_info = q_tag->clkInfo(); @@ -731,19 +707,15 @@ PathEnum::updatePathHeadDelays(PathSeq &paths, && arc->role() != TimingRole::latchDtoQ()) { // When crpr is enabled the diverion may be from another crpr clk pin, // so update the tags to use the corresponding ClkInfo. - Tag *updated_tag = search_->findTag(path->scene(this), - path->transition(this), - path->minMax(this), - prev_clk_info, - tag->isClock(), - tag->inputDelay(), - tag->isSegmentStart(), - tag->states(), false, nullptr); + Tag *updated_tag = search_->findTag( + path->scene(this), path->transition(this), path->minMax(this), + prev_clk_info, tag->isClock(), tag->inputDelay(), + tag->isSegmentStart(), tag->states(), false, nullptr); path->setTag(updated_tag); } - debugPrint(debug_, "path_enum", 5, "update arrival %s %s %s -> %s", - path->vertex(this)->to_string(this).c_str(), - path->tag(this)->to_string(this).c_str(), + debugPrint(debug_, "path_enum", 5, "update arrival {} {} {} -> {}", + path->vertex(this)->to_string(this), + path->tag(this)->to_string(this), delayAsString(path->arrival(), this), delayAsString(arrival, this)); } @@ -752,4 +724,4 @@ PathEnum::updatePathHeadDelays(PathSeq &paths, } } -} // namespace +} // namespace sta diff --git a/search/PathGroup.cc b/search/PathGroup.cc index a60b6c42..e2ca7711 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -309,7 +309,7 @@ PathGroups::makeGroups(int group_path_count, const Sdc *sdc = mode_->sdc(); for (const auto& [name, group] : sdc->groupPaths()) { if (reportGroup(name, group_names)) { - PathGroup *group = PathGroup::makePathGroupSlack(name, + PathGroup *group = PathGroup::makePathGroupSlack(name.c_str(), group_path_count, endpoint_path_count, unique_pins, @@ -394,7 +394,7 @@ PathGroups::~PathGroups() } PathGroup * -PathGroups::findPathGroup(const char *name, +PathGroups::findPathGroup(const std::string &name, const MinMax *min_max) const { auto itr = named_map_[min_max->index()].find(name); @@ -416,7 +416,7 @@ PathGroups::findPathGroup(const Clock *clock, } bool -PathGroups::reportGroup(const char *group_name, +PathGroups::reportGroup(const std::string &group_name, StringSet &group_names) const { return group_names.empty() @@ -441,7 +441,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const path_groups.push_back(path_delay_[mm_index]); } else { - const char *group_name = group_path->name(); + std::string group_name = group_path->name(); PathGroup *group = findPathGroup(group_name, min_max); if (group) path_groups.push_back(group); @@ -552,7 +552,7 @@ PathGroups::pushEnds(PathEndSeq &path_ends) for (const MinMax *min_max : MinMax::range()) { int mm_index = min_max->index(); for (std::string &group_name : pathGroupNames()) { - PathGroup *path_group = findPathGroup(group_name.c_str(), min_max); + PathGroup *path_group = findPathGroup(group_name, min_max); if (path_group) path_group->pushEnds(path_ends); } @@ -801,8 +801,8 @@ MakePathEndsAll::vertexEnd(Vertex *) // Only save the worst path end for each crpr tag. // PathEnum will peel the others. if (!unique_ends.contains(path_end)) { - debugPrint(debug, "path_group", 2, "insert %s %s %s %d", - path_end->vertex(sta_)->to_string(sta_).c_str(), + debugPrint(debug, "path_group", 2, "insert {} {} {} {}", + path_end->vertex(sta_)->to_string(sta_), path_end->typeName(), path_end->transition(sta_)->shortName(), path_end->path()->tag(sta_)->index()); @@ -816,8 +816,8 @@ MakePathEndsAll::vertexEnd(Vertex *) } } else - debugPrint(debug, "path_group", 3, "prune %s %s %s %d", - path_end->vertex(sta_)->to_string(sta_).c_str(), + debugPrint(debug, "path_group", 3, "prune {} {} {} {}", + path_end->vertex(sta_)->to_string(sta_), path_end->typeName(), path_end->transition(sta_)->shortName(), path_end->path()->tag(sta_)->index()); diff --git a/search/Property.cc b/search/Property.cc index dc37d4b3..35e6a096 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -27,6 +27,7 @@ #include #include +#include "Format.hh" #include "StringUtil.hh" #include "MinMax.hh" #include "Transition.hh" @@ -49,39 +50,26 @@ namespace sta { class PropertyUnknown : public Exception { public: - PropertyUnknown(const char *type, - const char *property); - PropertyUnknown(const char *type, - const std::string property); + PropertyUnknown(const std::string &type, + const std::string &property); virtual ~PropertyUnknown() {} virtual const char *what() const noexcept; private: - const char *type_; - const std::string property_; + std::string msg_; }; -PropertyUnknown::PropertyUnknown(const char *type, - const char *property) : +PropertyUnknown::PropertyUnknown(const std::string &type, + const std::string &property) : Exception(), - type_(type), - property_(property) -{ -} - -PropertyUnknown::PropertyUnknown(const char *type, - const std::string property) : - Exception(), - type_(type), - property_(property) + msg_(sta::format("{} objects do not have a {} property.", type, property)) { } const char * PropertyUnknown::what() const noexcept { - return stringPrint("%s objects do not have a %s property.", - type_, property_.c_str()); + return msg_.c_str(); } //////////////////////////////////////////////////////////////// @@ -89,29 +77,27 @@ PropertyUnknown::what() const noexcept class PropertyTypeWrong : public Exception { public: - PropertyTypeWrong(const char *accessor, - const char *type); + PropertyTypeWrong(const std::string &accessor, + const std::string &type); virtual ~PropertyTypeWrong() {} virtual const char *what() const noexcept; private: - const char *accessor_; - const char *type_; + std::string msg_; }; -PropertyTypeWrong::PropertyTypeWrong(const char *accessor, - const char *type) : +PropertyTypeWrong::PropertyTypeWrong(const std::string &accessor, + const std::string &type) : Exception(), - accessor_(accessor), - type_(type) + msg_(sta::format("property accessor {} is only valid for {} properties.", + accessor, type)) { } const char * PropertyTypeWrong::what() const noexcept { - return stringPrint("property accessor %s is only valid for %s properties.", - accessor_, type_); + return msg_.c_str(); } //////////////////////////////////////////////////////////////// @@ -1159,8 +1145,7 @@ Properties::getProperty(TimingArcSet *arc_set, const char *from = arc_set->from()->name(); const char *to = arc_set->to()->name(); const char *cell_name = arc_set->libertyCell()->name(); - std::string name; - stringPrint(name, "%s %s -> %s", cell_name, from, to); + std::string name = sta::format("{} {} -> {}", cell_name, from, to); return PropertyValue(name); } } diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 664af765..158b7919 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -28,6 +28,7 @@ #include "ReportPath.hh" #include "ContainerHelpers.hh" +#include "Format.hh" #include "Report.hh" #include "Error.hh" #include "StringUtil.hh" @@ -139,9 +140,7 @@ ReportPath::ReportPath(StaState *sta) : format_(ReportPathFormat::full), no_split_(false), start_end_pt_width_(80), - field_width_extra_(5), - plus_zero_(nullptr), - minus_zero_(nullptr) + field_width_extra_(5) { makeFields(); setDigits(2); @@ -160,9 +159,6 @@ ReportPath::~ReportPath() delete field_src_attr_; delete field_edge_; delete field_case_; - - stringDelete(plus_zero_); - stringDelete(minus_zero_); } void @@ -277,11 +273,8 @@ void ReportPath::setDigits(int digits) { digits_ = digits; - - stringDelete(plus_zero_); - stringDelete(minus_zero_); - minus_zero_ = stringPrint("-%.*f", digits_, 0.0); - plus_zero_ = stringPrint("%.*f", digits_, 0.0); + minus_zero_ = sta::formatRuntime("-{:.{}f}", 0.0, digits_); + plus_zero_ = sta::formatRuntime("{:.{}f}", 0.0, digits_); // Numeric field width expands with digits. int field_width = digits + field_width_extra_; @@ -348,7 +341,7 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const } else { if (format_ != ReportPathFormat::json) - report_->reportLine("No paths found."); + report_->report("No paths found."); } reportPathEndFooter(); } @@ -410,9 +403,7 @@ ReportPath::reportEndpointHeader(const PathEnd *end, const char *setup_hold = (end->minMax(this) == MinMax::min()) ? "min_delay/hold" : "max_delay/setup"; - report_->reportLine("%s group %s", - setup_hold, - group->name().c_str()); + report_->report("{} group {}", setup_hold, group->name()); reportBlankLine(); reportEndHeader(); } @@ -447,7 +438,7 @@ ReportPath::reportFull(const PathEndUnconstrained *end) const reportLine("data arrival time", end->dataArrivalTimeOffset(this), end->pathEarlyLate(this)); reportDashLine(); - report_->reportLine("(Path is unconstrained)"); + report_->report("(Path is unconstrained)"); } //////////////////////////////////////////////////////////////// @@ -482,8 +473,8 @@ ReportPath::reportFull(const PathEndCheck *end) const std::string ReportPath::checkRoleString(const PathEnd *end) const { - return stdstrPrint("library %s time", - end->checkRole(this)->to_string().c_str()); + return sta::format("library {} time", + end->checkRole(this)->to_string()); } void @@ -497,23 +488,23 @@ ReportPath::reportEndpoint(const PathEndCheck *end) const const TimingRole *check_generic_role = check_role->genericRole(); if (check_role == TimingRole::recovery() || check_role == TimingRole::removal()) { - auto reason = stdstrPrint("%s check against %s-edge clock %s", - check_role->to_string().c_str(), - rise_fall, - clk_name.c_str()); + std::string reason = sta::format("{} check against {}-edge clock {}", + check_role->to_string(), + rise_fall, + clk_name); reportEndpoint(inst_name, reason); } else if (check_generic_role == TimingRole::setup() || check_generic_role == TimingRole::hold()) { LibertyCell *cell = network_->libertyCell(inst); if (cell->isClockGate()) { - auto reason = stdstrPrint("%s clock gating-check end-point clocked by %s", - rise_fall, clk_name.c_str()); + std::string reason = sta::format("{} clock gating-check end-point clocked by {}", + rise_fall, clk_name); reportEndpoint(inst_name, reason); } else { const char *reg_desc = clkRegLatchDesc(end); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); reportEndpoint(inst_name, reason); } } @@ -600,7 +591,7 @@ ReportPath::reportEndpoint(const PathEndLatchCheck *end) const const char *inst_name = cmd_network_->pathName(inst); std::string clk_name = tgtClkName(end); const char *reg_desc = latchDesc(end); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); reportEndpoint(inst_name, reason); } @@ -625,7 +616,7 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, end->latchBorrowInfo(this, nom_pulse_width, open_latency, latency_diff, open_uncertainty, open_crpr, crpr_diff, max_borrow, borrow_limit_exists); - report_->reportLine("Time Borrowing Information"); + report_->report("Time Borrowing Information"); reportDashLineTotal(); if (borrow_limit_exists) reportLineTotal("user max time borrow", max_borrow, early_late); @@ -634,13 +625,13 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, Arrival tgt_clk_width = end->targetClkWidth(this); const Path *tgt_clk_path = end->targetClkPath(); if (tgt_clk_path->clkInfo(search_)->isPropagated()) { - auto width_msg = stdstrPrint("%s nominal pulse width", tgt_clk_name.c_str()); + std::string width_msg = sta::format("{} nominal pulse width", tgt_clk_name); reportLineTotal(width_msg.c_str(), nom_pulse_width, early_late); if (!delayZero(latency_diff, this)) reportLineTotalMinus("clock latency difference", latency_diff, early_late); } else { - auto width_msg = stdstrPrint("%s pulse width", tgt_clk_name.c_str()); + std::string width_msg = sta::format("{} pulse width", tgt_clk_name.c_str()); reportLineTotal(width_msg.c_str(), tgt_clk_width, early_late); } ArcDelay margin = end->margin(this); @@ -696,9 +687,9 @@ ReportPath::reportEndpoint(const PathEndPathDelay *end) const else { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *inst_name = cmd_network_->pathName(inst); - std::string clk_name = tgtClkName(end); - const char *reg_desc = clkRegLatchDesc(end); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + std::string reason = sta::format("{} clocked by {}", + clkRegLatchDesc(end), + tgtClkName(end)); reportEndpoint(inst_name, reason); } } @@ -826,8 +817,8 @@ ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end) const if (network_->isTopLevelPort(pin)) { // Pin direction is "output" even for bidirects. if (tgt_clk) { - std::string clk_name = tgtClkName(end); - auto reason = stdstrPrint("output port clocked by %s", clk_name.c_str()); + std::string reason = sta::format("output port clocked by {}", + tgtClkName(end)); reportEndpoint(pin_name, reason); } else @@ -835,9 +826,8 @@ ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end) const } else { if (tgt_clk) { - std::string clk_name = tgtClkName(end); - auto reason = stdstrPrint("internal path endpoint clocked by %s", - clk_name.c_str()); + std::string reason = sta::format("internal path endpoint clocked by {}", + tgtClkName(end)); reportEndpoint(pin_name, reason); } @@ -880,15 +870,14 @@ ReportPath::reportEndpoint(const PathEndGatedClock *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *inst_name = cmd_network_->pathName(inst); - std::string clk_name = tgtClkName(end); const RiseFall *clk_end_rf = end->targetClkEndTrans(this); - const RiseFall *clk_rf = - (end->minMax(this) == MinMax::max()) ? clk_end_rf : clk_end_rf->opposite(); - const char *rise_fall = asRisingFalling(clk_rf); + const RiseFall *clk_rf = (end->minMax(this) == MinMax::max()) + ? clk_end_rf + : clk_end_rf->opposite(); // Note that target clock transition is ignored. - auto reason = stdstrPrint("%s clock gating-check end-point clocked by %s", - rise_fall, - clk_name.c_str()); + std::string reason = sta::format("{} clock gating-check end-point clocked by {}", + asRisingFalling(clk_rf), + tgtClkName(end)); reportEndpoint(inst_name, reason); } @@ -948,11 +937,9 @@ ReportPath::reportEndpoint(const PathEndDataCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *inst_name = cmd_network_->pathName(inst); - const char *tgt_clk_rf = asRisingFalling(end->dataClkPath()->transition(this)); - const char *tgt_clk_name = end->targetClk(this)->name(); - auto reason = stdstrPrint("%s edge-triggered data to data check clocked by %s", - tgt_clk_rf, - tgt_clk_name); + std::string reason = sta::format("{} edge-triggered data to data check clocked by {}", + asRisingFalling(end->dataClkPath()->transition(this)), + end->targetClk(this)->name()); reportEndpoint(inst_name, reason); } @@ -968,7 +955,7 @@ ReportPath::reportEndHeader() const reportField("Required", field_total_, line); line += ' '; reportField("Actual", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); // Line two. line.clear(); @@ -979,7 +966,7 @@ ReportPath::reportEndHeader() const reportField("Delay", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } @@ -994,7 +981,7 @@ ReportPath::reportEndLine(const PathEnd *end) const reportSpaceFieldDelay(end->requiredTimeOffset(this), early_late, line); reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); reportSpaceSlack(end, line); - report_->reportLineString(line); + report_->reportLine(line); } //////////////////////////////////////////////////////////////// @@ -1008,7 +995,7 @@ ReportPath::reportSummaryHeader() const reportDescription("Endpoint", line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() * 2 + field_total_->width() + 1); } @@ -1019,16 +1006,16 @@ ReportPath::reportSummaryLine(const PathEnd *end) const std::string line; PathExpanded expanded(end->path(), this); const EarlyLate *early_late = end->pathEarlyLate(this); - auto startpoint = pathStartpoint(end, expanded); + std::string startpoint = pathStartpoint(end, expanded); reportDescription(startpoint.c_str(), line); line += ' '; - auto endpoint = pathEndpoint(end); + std::string endpoint = pathEndpoint(end); reportDescription(endpoint.c_str(), line); if (end->isUnconstrained()) reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); else reportSpaceFieldDelay(end->slack(this), EarlyLate::early(), line); - report_->reportLineString(line); + report_->reportLine(line); } std::string @@ -1040,12 +1027,12 @@ ReportPath::pathStartpoint(const PathEnd *end, const char *pin_name = cmd_network_->pathName(pin); if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); - return stdstrPrint("%s (%s)", pin_name, dir->name()); + return sta::format("{} ({})", pin_name, dir->name()); } else { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *cell_name = cmd_network_->name(network_->cell(inst)); - return stdstrPrint("%s (%s)", pin_name, cell_name); + return sta::format("{} ({})", pin_name, cell_name); } } @@ -1056,12 +1043,12 @@ ReportPath::pathEndpoint(const PathEnd *end) const const char *pin_name = cmd_network_->pathName(pin); if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); - return stdstrPrint("%s (%s)", pin_name, dir->name()); + return sta::format("{} ({})", pin_name, dir->name()); } else { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *cell_name = cmd_network_->name(network_->cell(inst)); - return stdstrPrint("%s (%s)", pin_name, cell_name); + return sta::format("{} ({})", pin_name, cell_name); } } @@ -1070,14 +1057,14 @@ ReportPath::pathEndpoint(const PathEnd *end) const void ReportPath::reportJsonHeader() const { - report_->reportLine("{\"checks\": ["); + report_->report("{{\"checks\": ["); } void ReportPath::reportJsonFooter() const { - report_->reportLine("]"); - report_->reportLine("}"); + report_->report("]"); + report_->report("}}"); } void @@ -1101,18 +1088,18 @@ ReportPath::reportJson(const PathEnd *end, PathExpanded expanded(end->path(), this); const Pin *startpoint = expanded.startPath()->vertex(this)->pin(); const Pin *endpoint = expanded.endPath()->vertex(this)->pin(); - stringAppend(result, " \"startpoint\": \"%s\",\n", + result += sta::format(" \"startpoint\": \"{}\",\n", sdc_network_->pathName(startpoint)); - stringAppend(result, " \"endpoint\": \"%s\",\n", + result += sta::format(" \"endpoint\": \"{}\",\n", sdc_network_->pathName(endpoint)); const ClockEdge *src_clk_edge = end->sourceClkEdge(this); const Path *src_clk_path = expanded.clkPath(); const Path *tgt_clk_path = end->targetClkPath(); if (src_clk_edge) { - stringAppend(result, " \"source_clock\": \"%s\",\n", + result += sta::format(" \"source_clock\": \"{}\",\n", src_clk_edge->clock()->name()); - stringAppend(result, " \"source_clock_edge\": \"%s\",\n", + result += sta::format(" \"source_clock_edge\": \"{}\",\n", src_clk_edge->transition()->name()); } if (src_clk_path) @@ -1121,41 +1108,41 @@ ReportPath::reportJson(const PathEnd *end, const ClockEdge *tgt_clk_edge = end->targetClkEdge(this); if (tgt_clk_edge) { - stringAppend(result, " \"target_clock\": \"%s\",\n", + result += sta::format(" \"target_clock\": \"{}\",\n", tgt_clk_edge->clock()->name()); - stringAppend(result, " \"target_clock_edge\": \"%s\",\n", + result += sta::format(" \"target_clock_edge\": \"{}\",\n", tgt_clk_edge->transition()->name()); } if (tgt_clk_path) reportJson(end->targetClkPath(), "target_clock_path", 2, true, result); if (end->checkRole(this)) { - stringAppend(result, " \"data_arrival_time\": %.3e,\n", + result += sta::format(" \"data_arrival_time\": {:.3e},\n", delayAsFloat(end->dataArrivalTimeOffset(this))); const MultiCyclePath *mcp = end->multiCyclePath(); if (mcp) - stringAppend(result, " \"multi_cycle_path\": %d,\n", + result += sta::format(" \"multi_cycle_path\": {},\n", mcp->pathMultiplier()); PathDelay *path_delay = end->pathDelay(); if (path_delay) - stringAppend(result, " \"path_delay\": %.3e,\n", + result += sta::format(" \"path_delay\": {:.3e},\n", path_delay->delay()); - stringAppend(result, " \"crpr\": %.3e,\n", + result += sta::format(" \"crpr\": {:.3e},\n", delayAsFloat(end->checkCrpr(this))); - stringAppend(result, " \"margin\": %.3e,\n", + result += sta::format(" \"margin\": {:.3e},\n", delayAsFloat(end->margin(this))); - stringAppend(result, " \"required_time\": %.3e,\n", + result += sta::format(" \"required_time\": {:.3e},\n", delayAsFloat(end->requiredTimeOffset(this))); - stringAppend(result, " \"slack\": %.3e\n", + result += sta::format(" \"slack\": {:.3e}\n", delayAsFloat(end->slack(this))); } result += "}"; if (!last) result += ","; - report_->reportLineString(result); + report_->reportLine(result); } void @@ -1165,7 +1152,7 @@ ReportPath::reportJson(const Path *path) const result += "{\n"; reportJson(path, "path", 0, false, result); result += "}\n"; - report_->reportLineString(result); + report_->reportLine(result); } void @@ -1186,7 +1173,7 @@ ReportPath::reportJson(const PathExpanded &expanded, bool trailing_comma, std::string &result) const { - stringAppend(result, "%*s\"%s\": [\n", indent, "", path_name); + result += sta::format("{:>{}}\"{}\": [\n", "", indent, path_name); for (size_t i = expanded.startIndex(); i < expanded.size(); i++) { const Path *path = expanded.path(i); const Pin *pin = path->vertex(this)->pin(); @@ -1197,69 +1184,69 @@ ReportPath::reportJson(const PathExpanded &expanded, const MinMax *min_max = path->minMax(this); bool is_driver = network_->isDriver(pin); - stringAppend(result, "%*s {\n", indent, ""); + result += sta::format("{:>{}} {{\n", "", indent); if (inst) { - stringAppend(result, "%*s \"instance\": \"%s\",\n", - indent, "", + result += sta::format("{:>{}} \"instance\": \"{}\",\n", + "", indent, sdc_network_->pathName(inst)); Cell *cell = network_->cell(inst); if (cell) - stringAppend(result, "%*s \"cell\": \"%s\",\n", - indent, "", + result += sta::format("{:>{}} \"cell\": \"{}\",\n", + "", indent, sdc_network_->name(cell)); - stringAppend(result, "%*s \"verilog_src\": \"%s\",\n", - indent, "", + result += sta::format("{:>{}} \"verilog_src\": \"{}\",\n", + "", indent, sdc_network_->getAttribute(inst, "src").c_str()); } - stringAppend(result, "%*s \"pin\": \"%s\",\n", - indent, "", + result += sta::format("{:>{}} \"pin\": \"{}\",\n", + "", indent, sdc_network_->pathName(pin)); if (net) { - stringAppend(result, "%*s \"net\": \"%s\",\n", - indent, "", + result += sta::format("{:>{}} \"net\": \"{}\",\n", + "", indent, sdc_network_->pathName(net)); } PinSeq pins_above; hierPinsAbove(pin, network_, pins_above); if (!pins_above.empty()) { - stringAppend(result, "%*s \"hier_pins\": [\n", indent, ""); + result += sta::format("{:>{}} \"hier_pins\": [\n", "", indent); for (const Pin *hpin : pins_above) { - stringAppend(result, "%*s \"%s\"%s\n", - indent, "", + result += sta::format("{:>{}} \"{}\"{}\n", + "", indent, sdc_network_->pathName(hpin), (hpin != pins_above.back()) ? "," : ""); } - stringAppend(result, "%*s ],\n", indent, ""); + result += sta::format("{:>{}} ],\n", "", indent); } double x, y; bool exists; network_->location(pin, x, y, exists); if (exists) { - stringAppend(result, "%*s \"x\": %.9f,\n", indent, "", x); - stringAppend(result, "%*s \"y\": %.9f,\n", indent, "", y); + result += sta::format("{:>{}} \"x\": {:.9f},\n", "", indent, x); + result += sta::format("{:>{}} \"y\": {:.9f},\n", "", indent, y); } - stringAppend(result, "%*s \"arrival\": %.3e,\n", - indent, "", + result += sta::format("{:>{}} \"arrival\": {:.3e},\n", + "", indent, delayAsFloat(path->arrival())); if (is_driver) - stringAppend(result, "%*s \"capacitance\": %.3e,\n", - indent, "", + result += sta::format("{:>{}} \"capacitance\": {:.3e},\n", + "", indent, graph_delay_calc_->loadCap(pin, rf, scene, min_max)); - stringAppend(result, "%*s \"slew\": %.3e\n", - indent, "", + result += sta::format("{:>{}} \"slew\": {:.3e}\n", + "", indent, delayAsFloat(path->slew(this))); - stringAppend(result, "%*s }%s\n", - indent, "", + result += sta::format("{:>{}} }}{}\n", + "", indent, (i < expanded.size() - 1) ? "," : ""); } - stringAppend(result, "%*s]%s\n", - indent, "", + result += sta::format("{:>{}}]{}\n", + "", indent, trailing_comma ? "," : ""); } @@ -1272,7 +1259,7 @@ ReportPath::reportSlackOnlyHeader() const reportDescription("Group", line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() + 1); } @@ -1287,7 +1274,7 @@ ReportPath::reportSlackOnly(const PathEnd *end) const reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); else reportSpaceFieldDelay(end->slack(this), early_late, line); - report_->reportLineString(line); + report_->reportLine(line); } //////////////////////////////////////////////////////////////// @@ -1336,7 +1323,7 @@ ReportPath::reportMpwHeaderShort() const reportField("Required", field_total_, line); line += ' '; reportField("Actual", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); line.clear(); reportDescription("Pin", line); @@ -1346,7 +1333,7 @@ ReportPath::reportMpwHeaderShort() const reportField("Width", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } @@ -1355,14 +1342,14 @@ void ReportPath::reportShort(const MinPulseWidthCheck &check) const { std::string line; - const char *pin_name = cmd_network_->pathName(check.pin(this)); - const char *hi_low = mpwCheckHiLow(check); - auto what = stdstrPrint("%s (%s)", pin_name, hi_low); + std::string what = sta::format("{} ({})", + cmd_network_->pathName(check.pin(this)), + mpwCheckHiLow(check)); reportDescription(what.c_str(), line); reportSpaceFieldTime(check.minWidth(this), line); reportSpaceFieldDelay(check.width(this), EarlyLate::late(), line); reportSpaceSlack(check.slack(this), line); - report_->reportLineString(line); + report_->reportLine(line); } void @@ -1372,19 +1359,19 @@ ReportPath::reportVerbose(const MinPulseWidthCheck &check) const const char *pin_name = cmd_network_->pathName(check.pin(this)); line += "Pin: "; line += pin_name; - report_->reportLineString(line); + report_->reportLine(line); - report_->reportLine("Check: sequential_clock_pulse_width"); + report_->report("Check: sequential_clock_pulse_width"); reportBlankLine(); reportPathHeader(); const EarlyLate *open_el = EarlyLate::late(); const ClockEdge *open_clk_edge = check.openClkEdge(this); const Clock *open_clk = open_clk_edge->clock(); - const char *open_clk_name = open_clk->name(); - const char *open_rise_fall = asRiseFall(open_clk_edge->transition()); float open_clk_time = open_clk_edge->time(); - auto open_clk_msg = stdstrPrint("clock %s (%s edge)", open_clk_name, open_rise_fall); + std::string open_clk_msg = sta::format("clock {} ({} edge)", + open_clk->name(), + asRiseFall(open_clk_edge->transition())); reportLine(open_clk_msg.c_str(), open_clk_time, open_clk_time, open_el); Arrival open_arrival = check.openArrival(this); @@ -1398,11 +1385,11 @@ ReportPath::reportVerbose(const MinPulseWidthCheck &check) const const EarlyLate *close_el = EarlyLate::late(); const ClockEdge *close_clk_edge = check.closeClkEdge(this); const Clock *close_clk = close_clk_edge->clock(); - const char *close_clk_name = close_clk->name(); - const char *close_rise_fall = asRiseFall(close_clk_edge->transition()); float close_offset = check.closeOffset(this); float close_clk_time = close_clk_edge->time() + close_offset; - auto close_clk_msg = stdstrPrint("clock %s (%s edge)", close_clk_name, close_rise_fall); + std::string close_clk_msg = sta::format("clock {} ({} edge)", + close_clk->name(), + asRiseFall(close_clk_edge->transition())); reportLine(close_clk_msg.c_str(), close_clk_time, close_clk_time, close_el); Arrival close_arrival = delaySum(check.closeArrival(this), close_offset, this); @@ -1419,8 +1406,8 @@ ReportPath::reportVerbose(const MinPulseWidthCheck &check) const reportDashLine(); float min_width = check.minWidth(this); - const char *hi_low = mpwCheckHiLow(check); - auto rpw_msg = stdstrPrint("required pulse width (%s)", hi_low); + std::string rpw_msg = sta::format("required pulse width ({})", + mpwCheckHiLow(check)); reportLine(rpw_msg.c_str(), min_width, EarlyLate::early()); reportLine("actual pulse width", check.width(this), EarlyLate::early()); reportDashLine(); @@ -1484,7 +1471,7 @@ ReportPath::reportPeriodHeaderShort() const reportField("Min", field_total_, line); line += ' '; reportField("", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); line.clear(); reportDescription("Pin", line); @@ -1494,7 +1481,7 @@ ReportPath::reportPeriodHeaderShort() const reportField("Period", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } @@ -1508,7 +1495,7 @@ ReportPath::reportShort(const MinPeriodCheck &check) const reportSpaceFieldDelay(check.period(), EarlyLate::early(), line); reportSpaceFieldDelay(check.minPeriod(this), EarlyLate::early(), line); reportSpaceSlack(check.slack(this), line); - report_->reportLineString(line); + report_->reportLine(line); } void @@ -1518,7 +1505,7 @@ ReportPath::reportVerbose(const MinPeriodCheck &check) const const char *pin_name = cmd_network_->pathName(check.pin()); line += "Pin: "; line += pin_name; - report_->reportLineString(line); + report_->reportLine(line); reportLine("period", check.period(), EarlyLate::early()); reportLine("min period", -check.minPeriod(this), EarlyLate::early()); @@ -1558,7 +1545,7 @@ ReportPath::reportMaxSkewHeaderShort() const reportField("Actual", field_total_, line); line += ' '; reportField("", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); line.clear(); reportDescription("Pin", line); @@ -1568,7 +1555,7 @@ ReportPath::reportMaxSkewHeaderShort() const reportField("Skew", field_total_, line); line += ' '; reportField("Slack", field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field_total_->width() * 3 + 3); } @@ -1578,36 +1565,33 @@ ReportPath::reportShort(const MaxSkewCheck &check) const { std::string line; Pin *clk_pin = check.clkPin(this); - const char *clk_pin_name = network_->pathName(clk_pin); TimingArc *check_arc = check.checkArc(); - auto what = stdstrPrint("%s (%s->%s)", - clk_pin_name, - check_arc->fromEdge()->to_string().c_str(), - check_arc->toEdge()->to_string().c_str()); + std::string what = sta::format("{} ({}->{})", + network_->pathName(clk_pin), + check_arc->fromEdge()->to_string(), + check_arc->toEdge()->to_string()); reportDescription(what.c_str(), line); const EarlyLate *early_late = EarlyLate::early(); reportSpaceFieldDelay(check.maxSkew(this), early_late, line); reportSpaceFieldDelay(check.skew(this), early_late, line); reportSpaceSlack(check.slack(this), line); - report_->reportLineString(line); + report_->reportLine(line); } void ReportPath::reportVerbose(const MaxSkewCheck &check) const { std::string line; - const char *clk_pin_name = cmd_network_->pathName(check.clkPin(this)); line += "Constrained Pin: "; - line += clk_pin_name; - report_->reportLineString(line); + line += cmd_network_->pathName(check.clkPin(this)); + report_->reportLine(line); - const char *ref_pin_name = cmd_network_->pathName(check.refPin(this)); line = "Reference Pin: "; - line += ref_pin_name; - report_->reportLineString(line); + line += cmd_network_->pathName(check.refPin(this)); + report_->reportLine(line); line = "Check: max_skew"; - report_->reportLineString(line); + report_->reportLine(line); reportBlankLine(); reportPathHeader(); @@ -1680,7 +1664,7 @@ ReportPath::reportLimitShortHeader(const ReportField *field) const reportField(field->title(), field, line); line += ' '; reportField("Slack", field, line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(field_description_->width() + field->width() * 3 + 3); } @@ -1704,7 +1688,7 @@ ReportPath::reportLimitShort(const ReportField *field, line += (slack >= 0.0) ? " (MET)" : " (VIOLATED)"; - report_->reportLineString(line); + report_->reportLine(line); } void @@ -1731,19 +1715,19 @@ ReportPath::reportLimitVerbose(const ReportField *field, line += scene->name(); line += ")"; } - report_->reportLineString(line); + report_->reportLine(line); line = min_max->to_string(); line += ' '; line += field->name(); line += ' '; reportField(limit, field, line); - report_->reportLineString(line); + report_->reportLine(line); line = field->name(); line += " "; reportField(value, field, line); - report_->reportLineString(line); + report_->reportLine(line); int name_width = strlen(field->name()) + 5; reportDashLine(name_width + field->width()); @@ -1755,7 +1739,7 @@ ReportPath::reportLimitVerbose(const ReportField *field, line += (slack >= 0.0) ? " (MET)" : " (VIOLATED)"; - report_->reportLineString(line); + report_->reportLine(line); } //////////////////////////////////////////////////////////////// @@ -1775,7 +1759,7 @@ ReportPath::reportStartpoint(const PathEnd *end, const char *pin_name = cmd_network_->pathName(pin); if (pathFromClkPin(path, pin)) { const char *clk_name = clk->name(); - auto reason = stdstrPrint("clock source '%s'", clk_name); + std::string reason = sta::format("clock source '{}'", clk_name); reportStartpoint(pin_name, reason); } else if (network_->isTopLevelPort(pin)) { @@ -1783,7 +1767,7 @@ ReportPath::reportStartpoint(const PathEnd *end, && clk != sdc->defaultArrivalClock()) { const char *clk_name = clk->name(); // Pin direction is "input" even for bidirects. - auto reason = stdstrPrint("input port clocked by %s", clk_name); + std::string reason = sta::format("input port clocked by {}", clk_name); reportStartpoint(pin_name, reason); } else @@ -1799,7 +1783,7 @@ ReportPath::reportStartpoint(const PathEnd *end, && clk_rf != clk_path->transition(this); std::string clk_name = clkName(clk, clk_inverted); const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); - auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); reportStartpoint(inst_name, reason); } else { @@ -1811,8 +1795,8 @@ ReportPath::reportStartpoint(const PathEnd *end, if (clk_edge) { Clock *clk = clk_edge->clock(); if (clk != sdc->defaultArrivalClock()) { - const char *clk_name = clk->name(); - auto reason = stdstrPrint("internal path startpoint clocked by %s", clk_name); + std::string reason = sta::format("internal path startpoint clocked by {}", + clk->name()); reportStartpoint(pin_name, reason); } else @@ -1912,7 +1896,7 @@ ReportPath::reportStartEndPoint(const char *pt, line = key; line += ": "; line += pt; - report_->reportLineString(line); + report_->reportLine(line); line.clear(); for (unsigned i = 0; i < strlen(key); i++) @@ -1921,7 +1905,7 @@ ReportPath::reportStartEndPoint(const char *pt, line += " ("; line += reason; line += ")"; - report_->reportLineString(line); + report_->reportLine(line); } else { line = key; @@ -1930,7 +1914,7 @@ ReportPath::reportStartEndPoint(const char *pt, line += " ("; line += reason; line += ")"; - report_->reportLineString(line); + report_->reportLine(line); } } @@ -1941,22 +1925,22 @@ ReportPath::reportGroup(const PathEnd *end) const line = "Path Group: "; PathGroup *group = end->pathGroup(); line += group ? group->name() : "(none)"; - report_->reportLineString(line); + report_->reportLine(line); line = "Path Type: "; line += end->minMax(this)->to_string(); - report_->reportLineString(line); + report_->reportLine(line); if (modes_.size() > 1) { line = "Mode: "; line += end->path()->mode(this)->name(); - report_->reportLineString(line); + report_->reportLine(line); } if (multiScene()) { line = "Corner: "; line += end->path()->scene(this)->name(); - report_->reportLineString(line); + report_->reportLine(line); } } @@ -1965,7 +1949,7 @@ ReportPath::reportGroup(const PathEnd *end) const std::string ReportPath::checkRoleReason(const PathEnd *end) const { - return stdstrPrint("%s time", end->checkRole(this)->to_string().c_str()); + return sta::format("{} time", end->checkRole(this)->to_string()); } std::string @@ -2348,7 +2332,7 @@ ReportPath::reportClkLine(const Clock *clk, const MinMax *min_max) const { const char *rise_fall = asRiseFall(clk_rf); - auto clk_msg = stdstrPrint("clock %s (%s edge)", clk_name, rise_fall); + std::string clk_msg = sta::format("clock {} ({} edge)", clk_name, rise_fall); if (clk->isPropagated()) reportLine(clk_msg.c_str(), delayDiff(clk_time, prev_time, this), @@ -2621,7 +2605,7 @@ ReportPath::reportPath(const Path *path) const case ReportPathFormat::endpoint: case ReportPathFormat::summary: case ReportPathFormat::slack_only: - report_->reportLine("Format not supported."); + report_->report("Format not supported."); break; } } @@ -3003,7 +2987,7 @@ ReportPath::descriptionField(const Pin *pin) const Instance *inst = network_->instance(pin); name2 = network_->cellName(inst); } - return stdstrPrint("%s (%s)", pin_name, name2); + return sta::format("{} ({})", pin_name, name2); } std::string @@ -3011,14 +2995,14 @@ ReportPath::descriptionNet(const Pin *pin) const { if (network_->isTopLevelPort(pin)) { const char *pin_name = cmd_network_->pathName(pin); - return stdstrPrint("%s (net)", pin_name); + return sta::format("{} (net)", pin_name); } else { Net *net = network_->net(pin); if (net) { Net *highest_net = network_->highestNetAbove(net); const char *net_name = cmd_network_->pathName(highest_net); - return stdstrPrint("%s (net)", net_name); + return sta::format("{} (net)", net_name); } else return "(unconnected)"; @@ -3141,7 +3125,7 @@ ReportPath::reportPathHeader() const } } trimRight(line); - report_->reportLineString(line); + report_->reportLine(line); reportDashLine(); } @@ -3247,9 +3231,9 @@ ReportPath::reportLine(const char *what, if (fanout == field_blank_) reportFieldBlank(field, line); else - line += stdstrPrint("%*d", - field_fanout_->width(), - static_cast(fanout)); + line += sta::format("{:{}}", + static_cast(fanout), + field_fanout_->width()); } else if (field == field_capacitance_) reportField(cap, field, line); @@ -3289,7 +3273,7 @@ ReportPath::reportLine(const char *what, // Trim trailing spaces and report the line. std::string line_stdstr = line; trimRight(line_stdstr); - report_->reportLineString(line_stdstr.c_str()); + report_->reportLine(line_stdstr); } //////////////////////////////////////////////////////////////// @@ -3325,7 +3309,7 @@ ReportPath::reportLineTotal1(const char *what, reportFieldDelayMinus(incr, early_late, field_total_, line); else reportFieldDelay(incr, early_late, field_total_, line); - report_->reportLineString(line); + report_->reportLine(line); } void @@ -3372,11 +3356,11 @@ ReportPath::reportFieldTime(float value, if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); else { - const char *str = units_->timeUnit()->asString(value, digits_); - if (stringEq(str, minus_zero_)) + std::string str = units_->timeUnit()->asString(value, digits_); + if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; - reportField(str, field, line); + reportField(str.c_str(), field, line); } } @@ -3402,11 +3386,11 @@ ReportPath::reportTotalDelay(const Delay &value, const EarlyLate *early_late, std::string &line) const { - const char *str = delayAsString(value, early_late, digits_, this); - if (stringEq(str, minus_zero_)) + std::string str = delayAsString(value, early_late, digits_, this); + if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; - reportField(str, field_total_, line); + reportField(str.c_str(), field_total_, line); } // Total time always with leading minus sign. @@ -3420,12 +3404,12 @@ ReportPath::reportFieldDelayMinus(const Delay &value, reportFieldBlank(field, line); else { // Opposite min/max for negative value. - const char *str = delayAsString(delayDiff(delay_zero, value, this), + std::string str = delayAsString(delayDiff(delay_zero, value, this), early_late->opposite(), digits_, this); - if (stringEq(str, plus_zero_)) + if (str == plus_zero_) // Force leading minus sign. str = minus_zero_; - reportField(str, field, line); + reportField(str.c_str(), field, line); } } @@ -3438,11 +3422,11 @@ ReportPath::reportFieldDelay(const Delay &value, if (value.mean() == field_blank_) reportFieldBlank(field, line); else { - const char *str = delayAsString(value, early_late, digits_, this); - if (stringEq(str, minus_zero_)) + std::string str = delayAsString(value, early_late, digits_, this); + if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; - reportField(str, field, line); + reportField(str.c_str(), field, line); } } @@ -3456,13 +3440,12 @@ ReportPath::reportField(float value, else { Unit *unit = field->unit(); if (unit) { - const char *value_str = unit->asString(value, digits_); - reportField(value_str, field, line); + std::string value_str = unit->asString(value, digits_); + reportField(value_str.c_str(), field, line); } else { // fanout - std::string value_str; - stringPrint(value_str, "%.0f", value); + std::string value_str = sta::format("{:.0f}", value); reportField(value_str.c_str(), field, line); } } @@ -3499,7 +3482,7 @@ ReportPath::reportDashLine() const } } line += "------"; - report_->reportLineString(line); + report_->reportLine(line); } void @@ -3508,7 +3491,7 @@ ReportPath::reportDashLine(int line_width) const std::string line; for (int i = 0; i < line_width; i++) line += '-'; - report_->reportLineString(line); + report_->reportLine(line); } void diff --git a/search/ReportPath.hh b/search/ReportPath.hh index cad81c74..cec91ba7 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -491,11 +491,13 @@ protected: ReportField *field_src_attr_; ReportField *field_edge_; ReportField *field_case_; - static constexpr float field_blank_ = -1; - int field_width_extra_; - const char *plus_zero_; - const char *minus_zero_; + std::string plus_zero_; + std::string minus_zero_; + + int field_width_extra_; + static constexpr float field_blank_ = -1; + static const float field_skip_; }; class ReportField diff --git a/search/Search.cc b/search/Search.cc index eeafaace..ae6bbfbf 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Search.hh" @@ -28,7 +28,7 @@ #include "ContainerHelpers.hh" #include "Mutex.hh" -#include "Report.hh" +#include "Report.hh" #include "Debug.hh" #include "Stats.hh" #include "Fuzzy.hh" @@ -89,11 +89,9 @@ EvalPred::searchThru(Edge *edge, { const TimingRole *role = edge->role(); return SearchPred0::searchThru(edge, mode) - && (sta_->variables()->dynamicLoopBreaking() - || !edge->isDisabledLoop()) - && (search_thru_latches_ - || role->isLatchDtoQ() - || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); + && (sta_->variables()->dynamicLoopBreaking() || !edge->isDisabledLoop()) + && (search_thru_latches_ || role->isLatchDtoQ() + || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); } bool @@ -103,10 +101,9 @@ EvalPred::searchTo(const Vertex *to_vertex, const Pin *to_pin = to_vertex->pin(); const Sdc *sdc = mode->sdc(); return SearchPred0::searchTo(to_vertex, mode) - && !(sdc->isLeafPinClock(to_pin) - && !sdc->isPathDelayInternalTo(to_pin)) - // Fanin paths are broken by path delay internal pin startpoints. - && !sdc->isPathDelayInternalFromBreak(to_pin); + && !(sdc->isLeafPinClock(to_pin) && !sdc->isPathDelayInternalTo(to_pin)) + // Fanin paths are broken by path delay internal pin startpoints. + && !sdc->isPathDelayInternalFromBreak(to_pin); } //////////////////////////////////////////////////////////////// @@ -130,8 +127,7 @@ bool SearchThru::searchThru(Edge *edge, const Mode *mode) const { - return EvalPred::searchThru(edge, mode) - && !edge->role()->isLatchDtoQ(); + return EvalPred::searchThru(edge, mode) && !edge->role()->isLatchDtoQ(); } //////////////////////////////////////////////////////////////// @@ -159,7 +155,6 @@ protected: TagGroupBldr *tag_bldr_; const StaState *sta_; - }; SearchAdj::SearchAdj(TagGroupBldr *tag_bldr, @@ -184,30 +179,25 @@ SearchAdj::searchThru(Edge *edge, const TimingRole *role = edge->role(); const Variables *variables = sta_->variables(); return !role->isTimingCheck() - && !role->isLatchDtoQ() - // Register/latch preset/clr edges are disabled by default. - && !(role == TimingRole::regSetClr() - && !variables->presetClrArcsEnabled()) - && !(edge->isBidirectInstPath() - && !variables->bidirectInstPathsEnabled()) - && (!edge->isDisabledLoop() - || (variables->dynamicLoopBreaking() - && hasPendingLoopPaths(edge))); + && !role->isLatchDtoQ() + // Register/latch preset/clr edges are disabled by default. + && !(role == TimingRole::regSetClr() && !variables->presetClrArcsEnabled()) + && !(edge->isBidirectInstPath() && !variables->bidirectInstPathsEnabled()) + && (!edge->isDisabledLoop() + || (variables->dynamicLoopBreaking() && hasPendingLoopPaths(edge))); } bool SearchAdj::loopEnabled(Edge *edge) const { return !edge->isDisabledLoop() - || (sta_->variables()->dynamicLoopBreaking() - && hasPendingLoopPaths(edge)); + || (sta_->variables()->dynamicLoopBreaking() && hasPendingLoopPaths(edge)); } bool SearchAdj::hasPendingLoopPaths(Edge *edge) const { - if (tag_bldr_ - && tag_bldr_->hasLoopTag()) { + if (tag_bldr_ && tag_bldr_->hasLoopTag()) { const Graph *graph = sta_->graph(); Search *search = sta_->search(); Vertex *from_vertex = edge->from(graph); @@ -218,8 +208,7 @@ SearchAdj::hasPendingLoopPaths(Edge *edge) const // does not matter. Tag *to_tag = search->thruTag(from_tag, edge, RiseFall::rise(), nullptr); if (to_tag - && (prev_tag_group == nullptr - || !prev_tag_group->hasTag(from_tag))) + && (prev_tag_group == nullptr || !prev_tag_group->hasTag(from_tag))) return true; } } @@ -243,19 +232,24 @@ Search::Search(StaState *sta) : crpr_approx_missing_requireds_(true), search_thru_(new SearchThru(this)), - search_adj_(new SearchAdj(nullptr, this)), + search_adj_(new SearchAdj(nullptr, + this)), eval_pred_(new EvalPred(this)), - + arrivals_exist_(false), arrivals_seeded_(false), invalid_arrivals_(makeVertexSet(this)), - arrival_iter_(new BfsFwdIterator(BfsIndex::arrival, nullptr, this)), + arrival_iter_(new BfsFwdIterator(BfsIndex::arrival, + nullptr, + this)), arrival_visitor_(new ArrivalVisitor(this)), requireds_exist_(false), requireds_seeded_(false), invalid_requireds_(makeVertexSet(this)), - required_iter_(new BfsBkwdIterator(BfsIndex::required, search_adj_, this)), + required_iter_(new BfsBkwdIterator(BfsIndex::required, + search_adj_, + this)), tns_exists_(false), invalid_tns_(makeVertexSet(this)), @@ -263,12 +257,14 @@ Search::Search(StaState *sta) : clk_info_set_(new ClkInfoSet(ClkInfoLess(this))), tag_capacity_(128), - tags_(new Tag*[tag_capacity_]), - tag_set_(new TagSet(tag_capacity_, TagHash(this), TagEqual(this))), + tags_(new Tag *[tag_capacity_]), + tag_set_(new TagSet(tag_capacity_, + TagHash(this), + TagEqual(this))), tag_next_(0), tag_group_capacity_(tag_capacity_), - tag_groups_(new TagGroup*[tag_group_capacity_]), + tag_groups_(new TagGroup *[tag_group_capacity_]), tag_group_set_(new TagGroupSet(tag_group_capacity_)), tag_group_next_(0), @@ -310,8 +306,8 @@ Search::~Search() deleteTags(); delete tag_set_; delete clk_info_set_; - delete [] tags_; - delete [] tag_groups_; + delete[] tags_; + delete[] tag_groups_; delete tag_group_set_; delete search_thru_; delete search_adj_; @@ -476,8 +472,8 @@ Search::deletePathsIncr(Vertex *vertex) void Search::deletePaths(Vertex *vertex) { - debugPrint(debug_, "search", 4, "delete paths %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 4, "delete paths {}", + vertex->to_string(this)); TagGroup *tag_group = tagGroup(vertex); if (tag_group) { vertex->deletePaths(); @@ -521,15 +517,10 @@ Search::findPathEnds(ExceptionFrom *from, const ModeSeq modes = Scene::modesSorted(scenes); PathEndSeq path_ends; for (Mode *mode : modes) { - PathGroups *path_groups = mode->makePathGroups(group_path_count, - endpoint_path_count, - unique_pins, unique_edges, - slack_min, slack_max, - group_names, - setup, hold, - recovery, removal, - clk_gating_setup, clk_gating_hold, - unconstrained_paths_); + PathGroups *path_groups = mode->makePathGroups( + group_path_count, endpoint_path_count, unique_pins, unique_edges, slack_min, + slack_max, group_names, setup, hold, recovery, removal, clk_gating_setup, + clk_gating_hold, unconstrained_paths_); SceneSeq mode_scenes; for (Scene *scene : scenes) { if (scene->mode() == mode) @@ -555,10 +546,7 @@ Search::findFilteredArrivals(ExceptionFrom *from, filter_from_ = from; filter_thrus_ = thrus; filter_to_ = to; - if ((from - && (from->pins() - || from->instances())) - || thrus) { + if ((from && (from->pins() || from->instances())) || thrus) { for (const Mode *mode : modes_) { Sdc *sdc = mode->sdc(); sdc->makeFilter(from ? from->clone(network_) : nullptr, @@ -583,9 +571,7 @@ Search::deleteFilteredArrivals() { if (have_filter_) { ExceptionThruSeq *thrus = filter_thrus_; - if ((filter_from_ - && (filter_from_->pins() - || filter_from_->instances())) + if ((filter_from_ && (filter_from_->pins() || filter_from_->instances())) || thrus) { for (Vertex *vertex : filtered_arrivals_) { deletePathsIncr(vertex); @@ -598,14 +584,13 @@ Search::deleteFilteredArrivals() while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); TagGroup *tag_group = tagGroup(vertex); - if (tag_group - && tag_group->hasFilterTag()) + if (tag_group && tag_group->hasFilterTag()) filtered_arrivals_.erase(vertex); } if (!filtered_arrivals_.empty()) { - report_->reportLine("Filtered verticies mismatch"); + report_->report("Filtered verticies mismatch"); for (Vertex *vertex : filtered_arrivals_) - report_->reportLine(" %s", vertex->to_string(this).c_str()); + report_->report(" {}", vertex->to_string(this)); } } filtered_arrivals_.clear(); @@ -623,8 +608,7 @@ Search::deleteFilterTagGroups() { for (TagGroupIndex i = 0; i < tag_group_next_; i++) { TagGroup *group = tag_groups_[i]; - if (group - && group->hasFilterTag()) + if (group && group->hasFilterTag()) deleteTagGroup(group); } } @@ -643,9 +627,7 @@ Search::deleteFilterTags() { for (TagIndex i = 0; i < tag_next_; i++) { Tag *tag = tags_[i]; - if (tag - && (tag->isFilter() - || tag->clkInfo()->crprPathRefsFilter())) { + if (tag && (tag->isFilter() || tag->clkInfo()->crprPathRefsFilter())) { tags_[i] = nullptr; tag_set_->erase(tag); delete tag; @@ -656,7 +638,7 @@ Search::deleteFilterTags() void Search::deleteFilterClkInfos() { - for (auto itr = clk_info_set_->cbegin(); itr != clk_info_set_->cend(); ) { + for (auto itr = clk_info_set_->cbegin(); itr != clk_info_set_->cend();) { const ClkInfo *clk_info = *itr; if (clk_info->crprPathRefsFilter()) { itr = clk_info_set_->erase(itr); @@ -680,13 +662,14 @@ Search::findFilteredArrivals(bool thru_latches) // Iterate until data arrivals at all latches stop changing. postpone_latch_outputs_ = true; enqueuePendingClkFanouts(); - for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()) ; pass++) { + for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); + pass++) { if (thru_latches) enqueuePendingLatchOutputs(); - debugPrint(debug_, "search", 1, "find arrivals pass %d", pass); + debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_); deleteTagsPrev(); - debugPrint(debug_, "search", 1, "found %d arrivals", arrival_count); + debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count); postpone_latch_outputs_ = false; } arrivals_exist_ = true; @@ -696,12 +679,12 @@ Search::findFilteredArrivals(bool thru_latches) void Search::deleteTagsPrev() { - for (Tag** tags: tags_prev_) - delete [] tags; + for (Tag **tags : tags_prev_) + delete[] tags; tags_prev_.clear(); - for (TagGroup** tag_groups: tag_groups_prev_) - delete [] tag_groups; + for (TagGroup **tag_groups : tag_groups_prev_) + delete[] tag_groups; tag_groups_prev_.clear(); } @@ -824,8 +807,7 @@ Search::deleteEdgeBefore(Edge *edge) bool Search::arrivalsValid() { - return arrivals_exist_ - && invalid_arrivals_.empty(); + return arrivals_exist_ && invalid_arrivals_.empty(); } void @@ -872,8 +854,8 @@ void Search::arrivalInvalid(Vertex *vertex) { if (arrivals_exist_) { - debugPrint(debug_, "search", 2, "arrival invalid %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "arrival invalid {}", + vertex->to_string(this)); if (!arrival_iter_->inQueue(vertex)) { // Lock for StaDelayCalcObserver called by delay calc threads. LockGuard lock(invalid_arrivals_lock_); @@ -951,8 +933,8 @@ void Search::requiredInvalid(Vertex *vertex) { if (requireds_exist_) { - debugPrint(debug_, "search", 2, "required invalid %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "required invalid {}", + vertex->to_string(this)); if (!required_iter_->inQueue(vertex)) { // Lock for StaDelayCalcObserver called by delay calc threads. LockGuard lock(invalid_arrivals_lock_); @@ -1020,9 +1002,10 @@ Search::findAllArrivals(bool thru_latches, arrival_visitor_->init(false, clks_only, eval_pred_); // Iterate until data arrivals at all latches stop changing. postpone_latch_outputs_ = true; - for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); pass++) { + for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); + pass++) { enqueuePendingLatchOutputs(); - debugPrint(debug_, "search", 1, "find arrivals pass %d", pass); + debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); findArrivals1(levelize_->maxLevel()); if (pass > 2) postpone_latch_outputs_ = false; @@ -1045,8 +1028,8 @@ void Search::enqueuePendingLatchOutputs() { for (Vertex *latch_vertex : pending_latch_outputs_) { - debugPrint(debug_, "search", 2, "enqueue latch output %s", - latch_vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "enqueue latch output {}", + latch_vertex->to_string(this)); arrival_iter_->enqueue(latch_vertex); } clearPendingLatchOutputs(); @@ -1056,8 +1039,8 @@ void Search::enqueuePendingClkFanouts() { for (Vertex *vertex : pending_clk_endpoints_) { - debugPrint(debug_, "search", 2, "enqueue clk fanout %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "enqueue clk fanout {}", + vertex->to_string(this)); arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); } pending_clk_endpoints_.clear(); @@ -1086,7 +1069,7 @@ Search::findArrivals(Level level) void Search::findArrivals1(Level level) { - debugPrint(debug_, "search", 1, "find arrivals to level %d", level); + debugPrint(debug_, "search", 1, "find arrivals to level {}", level); findArrivalsSeed(); Stats stats(debug_, report_); int arrival_count = arrival_iter_->visitParallel(level, arrival_visitor_); @@ -1095,7 +1078,7 @@ Search::findArrivals1(Level level) deleteUnusedTagGroups(); stats.report("Find arrivals"); arrivals_exist_ = true; - debugPrint(debug_, "search", 1, "found %d arrivals", arrival_count); + debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count); } void @@ -1119,7 +1102,9 @@ Search::findArrivalsSeed() //////////////////////////////////////////////////////////////// ArrivalVisitor::ArrivalVisitor(const StaState *sta) : - PathVisitor(nullptr, false, sta) + PathVisitor(nullptr, + false, + sta) { init0(); init(true, false, nullptr); @@ -1129,7 +1114,9 @@ ArrivalVisitor::ArrivalVisitor(const StaState *sta) : ArrivalVisitor::ArrivalVisitor(bool always_to_endpoints, SearchPred *pred, const StaState *sta) : - PathVisitor(pred, true, sta) + PathVisitor(pred, + true, + sta) { init0(); init(always_to_endpoints, false, pred); @@ -1154,7 +1141,6 @@ ArrivalVisitor::init(bool always_to_endpoints, crpr_active_ = variables_->crprEnabled(); } - VertexVisitor * ArrivalVisitor::copy() const { @@ -1184,21 +1170,19 @@ ArrivalVisitor::setAlwaysToEndpoints(bool to_endpoints) void ArrivalVisitor::visit(Vertex *vertex) { - debugPrint(debug_, "search", 2, "find arrivals %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "find arrivals {}", + vertex->to_string(this)); Pin *pin = vertex->pin(); tag_bldr_->init(vertex); has_fanin_one_ = graph_->hasFaninOne(vertex); - if (crpr_active_ - && !has_fanin_one_) + if (crpr_active_ && !has_fanin_one_) tag_bldr_no_crpr_->init(vertex); visitFaninPaths(vertex); if (crpr_active_ && search_->crprPathPruningEnabled() // No crpr for ideal clocks. - && tag_bldr_->hasPropagatedClk() - && !has_fanin_one_) + && tag_bldr_->hasPropagatedClk() && !has_fanin_one_) pruneCrprArrivals(); // Insert paths that originate here. @@ -1209,14 +1193,11 @@ ArrivalVisitor::visit(Vertex *vertex) // If vertex is a latch data input arrival that changed from the // previous eval pass enqueue the latch outputs to be re-evaled on the // next pass. - if (arrivals_changed - && network_->isLatchData(pin)) + if (arrivals_changed && network_->isLatchData(pin)) search_->enqueueLatchDataOutputs(vertex); - if ((always_to_endpoints_ - || arrivals_changed)) { - if (clks_only_ - && vertex->isRegClk()) { + if ((always_to_endpoints_ || arrivals_changed)) { + if (clks_only_ && vertex->isRegClk()) { debugPrint(debug_, "search", 3, "postponing clk fanout"); search_->postponeClkFanouts(vertex); } @@ -1244,22 +1225,18 @@ ArrivalVisitor::seedArrivals(Vertex *vertex) if (search_->isInputArrivalSrchStart(vertex)) search_->seedInputArrival(pin, vertex, mode, tag_bldr_); // Do not apply input delay to bidir load vertices. - if (!(network_->direction(pin)->isBidirect() - && !vertex->isBidirectDriver()) - && !network_->isTopLevelPort(pin) - && sdc->hasInputDelay(pin)) + if (!(network_->direction(pin)->isBidirect() && !vertex->isBidirectDriver()) + && !network_->isTopLevelPort(pin) && sdc->hasInputDelay(pin)) search_->seedInputSegmentArrival(pin, vertex, mode, tag_bldr_); - if (sdc->isPathDelayInternalFrom(pin) - && !sdc->isLeafPinClock(pin)) + if (sdc->isPathDelayInternalFrom(pin) && !sdc->isLeafPinClock(pin)) // set_min/max_delay -from internal pin. search_->makeUnclkedPaths(vertex, false, true, tag_bldr_, mode); if (search_->isSrchRoot(vertex, mode)) { bool is_reg_clk = vertex->isRegClk(); if (is_reg_clk // Internal roots isolated by disabled pins are seeded with no clock. - || (search_->unconstrainedPaths() - && !network_->isTopLevelPort(pin))) { - debugPrint(debug_, "search", 2, "arrival seed unclked root %s", + || (search_->unconstrainedPaths() && !network_->isTopLevelPort(pin))) { + debugPrint(debug_, "search", 2, "arrival seed unclked root {}", network_->pathName(pin)); search_->makeUnclkedPaths(vertex, is_reg_clk, false, tag_bldr_, mode); } @@ -1270,7 +1247,7 @@ ArrivalVisitor::seedArrivals(Vertex *vertex) // These paths are required to report path delays from unclocked registers // For example, "set_max_delay -to" from an unclocked source register. if (vertex->isRegClk() && !is_clk) { - debugPrint(debug_, "search", 2, "arrival seed unclked reg clk %s", + debugPrint(debug_, "search", 2, "arrival seed unclked reg clk {}", network_->pathName(pin)); search_->makeUnclkedPaths(vertex, true, false, tag_bldr_, mode); } @@ -1286,8 +1263,7 @@ ArrivalVisitor::constrainedRequiredsInvalid(Vertex *vertex, bool is_clk) { Pin *pin = vertex->pin(); - if (network_->isLoad(pin) - && search_->requiredsExist()) { + if (network_->isLoad(pin) && search_->requiredsExist()) { if (is_clk && network_->isCheckClk(pin)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { @@ -1326,8 +1302,7 @@ Search::arrivalsChanged(Vertex *vertex, Path *paths1 = vertex->paths(); if (paths1) { TagGroup *tag_group = tagGroup(vertex); - if (tag_group == nullptr - || tag_group->pathCount() != tag_bldr->pathCount()) + if (tag_group == nullptr || tag_group->pathCount() != tag_bldr->pathCount()) return true; for (auto const [tag1, path_index1] : *tag_group->pathIndexMap()) { Path *path1 = &paths1[path_index1]; @@ -1362,16 +1337,12 @@ ArrivalVisitor::visitFromToPath(const Pin * /* from_pin */, Arrival &to_arrival, const MinMax *min_max) { - debugPrint(debug_, "search", 3, " %s", - from_vertex->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " %s -> %s %s", - from_rf->shortName(), - to_rf->shortName(), - min_max->to_string().c_str()); - debugPrint(debug_, "search", 3, " from tag: %s", - from_tag->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " to tag : %s", - to_tag->to_string(this).c_str()); + debugPrint(debug_, "search", 3, " {}", from_vertex->to_string(this)); + debugPrint(debug_, "search", 3, " {} -> {} {}", from_rf->shortName(), + to_rf->shortName(), min_max->to_string()); + debugPrint(debug_, "search", 3, " from tag: {}", + from_tag->to_string(this)); + debugPrint(debug_, "search", 3, " to tag : {}", to_tag->to_string(this)); const ClkInfo *to_clk_info = to_tag->clkInfo(); bool to_is_clk = to_tag->isClock(); Path *match; @@ -1379,16 +1350,13 @@ ArrivalVisitor::visitFromToPath(const Pin * /* from_pin */, tag_bldr_->tagMatchPath(to_tag, match, path_index); if (match == nullptr || delayGreater(to_arrival, match->arrival(), min_max, this)) { - debugPrint(debug_, "search", 3, " %s + %s = %s %s %s", - delayAsString(from_arrival, this), - delayAsString(arc_delay, this), - delayAsString(to_arrival, this), - min_max == MinMax::max() ? ">" : "<", + debugPrint(debug_, "search", 3, " {} + {} = {} {} {}", + delayAsString(from_arrival, this), delayAsString(arc_delay, this), + delayAsString(to_arrival, this), min_max == MinMax::max() ? ">" : "<", match ? delayAsString(match->arrival(), this) : "MIA"); - tag_bldr_->setMatchPath(match, path_index, to_tag, to_arrival, from_path, edge, arc); - if (crpr_active_ - && !has_fanin_one_ - && to_clk_info->hasCrprClkPin() + tag_bldr_->setMatchPath(match, path_index, to_tag, to_arrival, from_path, edge, + arc); + if (crpr_active_ && !has_fanin_one_ && to_clk_info->hasCrprClkPin() && !to_is_clk) { tag_bldr_no_crpr_->tagMatchPath(to_tag, match, path_index); if (match == nullptr @@ -1406,13 +1374,12 @@ ArrivalVisitor::pruneCrprArrivals() { CheckCrpr *crpr = search_->checkCrpr(); PathIndexMap &path_index_map = tag_bldr_->pathIndexMap(); - for (auto path_itr = path_index_map.cbegin(); path_itr != path_index_map.cend(); ) { + for (auto path_itr = path_index_map.cbegin(); path_itr != path_index_map.cend();) { Tag *tag = path_itr->first; size_t path_index = path_itr->second; const ClkInfo *clk_info = tag->clkInfo(); bool deleted_tag = false; - if (!tag->isClock() - && clk_info->hasCrprClkPin()) { + if (!tag->isClock() && clk_info->hasCrprClkPin()) { const MinMax *min_max = tag->minMax(); Path *path_no_crpr = tag_bldr_no_crpr_->tagMatchPath(tag); if (path_no_crpr) { @@ -1422,7 +1389,7 @@ ArrivalVisitor::pruneCrprArrivals() Arrival max_arrival_max_crpr = (min_max == MinMax::max()) ? delayDiff(max_arrival, max_crpr, this) : delaySum(max_arrival, max_crpr, this); - debugPrint(debug_, "search", 4, " cmp %s %s - %s = %s", + debugPrint(debug_, "search", 4, " cmp {} {} - {} = {}", tag->to_string(this).c_str(), delayAsString(max_arrival, this), delayAsString(max_crpr, this), @@ -1432,9 +1399,9 @@ ArrivalVisitor::pruneCrprArrivals() // does not match the path min/max. if (delayGreater(max_arrival_max_crpr, arrival, min_max, this) && clk_info_no_crpr->crprClkPath(this)->minMax(this) - == clk_info->crprClkPath(this)->minMax(this)) { - debugPrint(debug_, "search", 3, " pruned %s", - tag->to_string(this).c_str()); + == clk_info->crprClkPath(this)->minMax(this)) { + debugPrint(debug_, "search", 3, " pruned {}", + tag->to_string(this)); path_itr = path_index_map.erase(path_itr); deleted_tag = true; } @@ -1547,24 +1514,21 @@ Search::seedClkArrivals(const Pin *pin, ClockSet *clks = sdc->findLeafPinClocks(pin); if (clks) { for (const Clock *clk : *clks) { - debugPrint(debug_, "search", 2, "arrival seed clk %s/%s pin %s", - mode->name().c_str(), - clk->name(), - network_->pathName(pin)); + debugPrint(debug_, "search", 2, "arrival seed clk {}/{} pin {}", + mode->name(), clk->name(), network_->pathName(pin)); for (Scene *scene : mode->scenes()) { for (const MinMax *min_max : MinMax::range()) { for (const RiseFall *rf : RiseFall::range()) { const ClockEdge *clk_edge = clk->edge(rf); const EarlyLate *early_late = min_max; - if (clk->isGenerated() - && clk->masterClk() == nullptr) - seedClkDataArrival(pin, rf, clk, clk_edge, min_max, - 0.0, scene, tag_bldr); + if (clk->isGenerated() && clk->masterClk() == nullptr) + seedClkDataArrival(pin, rf, clk, clk_edge, min_max, 0.0, scene, + tag_bldr); else { - Arrival insertion = clockInsertion(clk, pin, rf, min_max, - early_late, mode); - seedClkArrival(pin, rf, clk, clk_edge, min_max, - insertion, scene, tag_bldr); + Arrival insertion = + clockInsertion(clk, pin, rf, min_max, early_late, mode); + seedClkArrival(pin, rf, clk, clk_edge, min_max, insertion, scene, + tag_bldr); } } } @@ -1588,12 +1552,10 @@ Search::seedClkArrival(const Pin *pin, float latency = 0.0; bool latency_exists; // Check for clk pin latency. - sdc->clockLatency(clk, pin, rf, min_max, - latency, latency_exists); + sdc->clockLatency(clk, pin, rf, min_max, latency, latency_exists); if (!latency_exists) { // Check for clk latency (lower priority). - sdc->clockLatency(clk, rf, min_max, - latency, latency_exists); + sdc->clockLatency(clk, rf, min_max, latency, latency_exists); if (latency_exists) { // Propagated pin overrides latency on clk. if (sdc->isPropagatedClock(pin)) { @@ -1603,8 +1565,7 @@ Search::seedClkArrival(const Pin *pin, } } else - is_propagated = sdc->isPropagatedClock(pin) - || clk->isPropagated(); + is_propagated = sdc->isPropagatedClock(pin) || clk->isPropagated(); } const ClockUncertainties *uncertainties = sdc->clockUncertainties(pin); @@ -1613,9 +1574,8 @@ Search::seedClkArrival(const Pin *pin, // Propagate liberty "pulse_clock" transition to transitive fanout. LibertyPort *port = network_->libertyPort(pin); const RiseFall *pulse_clk_sense = (port ? port->pulseClkSense() : nullptr); - const ClkInfo *clk_info = findClkInfo(scene, clk_edge, pin, is_propagated, - nullptr, false, - pulse_clk_sense, insertion, latency, + const ClkInfo *clk_info = findClkInfo(scene, clk_edge, pin, is_propagated, nullptr, + false, pulse_clk_sense, insertion, latency, uncertainties, min_max, nullptr); // Only false_paths -from apply to clock tree pins. ExceptionStateSet *states = nullptr; @@ -1635,7 +1595,7 @@ Search::seedClkDataArrival(const Pin *pin, Arrival insertion, Scene *scene, TagGroupBldr *tag_bldr) -{ +{ Tag *tag = clkDataTag(pin, clk, rf, clk_edge, insertion, min_max, scene); if (tag) { // Data arrivals include insertion delay. @@ -1656,12 +1616,11 @@ Search::clkDataTag(const Pin *pin, Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; if (sdc->exceptionFromStates(pin, rf, clk, rf, min_max, states)) { - bool is_propagated = (clk->isPropagated() - || sdc->isPropagatedClock(pin)); - const ClkInfo *clk_info = findClkInfo(scene, clk_edge, pin, is_propagated, - insertion, min_max); - return findTag(scene, rf, min_max, clk_info, false, nullptr, false, - states, true, nullptr); + bool is_propagated = (clk->isPropagated() || sdc->isPropagatedClock(pin)); + const ClkInfo *clk_info = + findClkInfo(scene, clk_edge, pin, is_propagated, insertion, min_max); + return findTag(scene, rf, min_max, clk_info, false, nullptr, false, states, true, + nullptr); } else return nullptr; @@ -1748,8 +1707,7 @@ Search::isInputArrivalSrchStart(Vertex *vertex) PortDirection *dir = network_->direction(pin); bool is_top_level_port = network_->isTopLevelPort(pin); return (is_top_level_port - && (dir->isInput() - || (dir->isBidirect() && vertex->isBidirectDriver()))) ; + && (dir->isInput() || (dir->isBidirect() && vertex->isBidirectDriver()))); } void @@ -1792,10 +1750,9 @@ Search::seedInputArrival1(const Pin *pin, // Input arrival wrt a clock source pin is the clock insertion // delay (source latency), but arrivals wrt other clocks // propagate. - if (pin_clks == nullptr - || !pin_clks->contains(input_clk)) - seedInputDelayArrival(pin, vertex, input_delay, is_segment_start, - mode, tag_bldr); + if (pin_clks == nullptr || !pin_clks->contains(input_clk)) + seedInputDelayArrival(pin, vertex, input_delay, is_segment_start, mode, + tag_bldr); } } } @@ -1809,17 +1766,14 @@ Search::seedInputDelayArrival(const Pin *pin, TagGroupBldr *tag_bldr) { debugPrint(debug_, "search", 2, - input_delay - ? "arrival seed input arrival %s" - : "arrival seed input %s", + input_delay ? "arrival seed input arrival {}" : "arrival seed input {}", vertex->to_string(this).c_str()); const ClockEdge *clk_edge = nullptr; const Pin *ref_pin = nullptr; const Sdc *sdc = mode->sdc(); if (input_delay) { clk_edge = input_delay->clkEdge(); - if (clk_edge == nullptr - && variables_->useDefaultArrivalClock()) + if (clk_edge == nullptr && variables_->useDefaultArrivalClock()) clk_edge = sdc->defaultArrivalClockEdge(); ref_pin = input_delay->refPin(); } @@ -1835,12 +1789,10 @@ Search::seedInputDelayArrival(const Pin *pin, while (ref_path_iter.hasNext()) { Path *ref_path = ref_path_iter.next(); if (ref_path->isClock(this) - && (clk == nullptr - || ref_path->clock(this) == clk)) { + && (clk == nullptr || ref_path->clock(this) == clk)) { float ref_arrival, ref_insertion, ref_latency; - inputDelayRefPinArrival(ref_path, ref_path->clkEdge(this), min_max, - sdc, ref_arrival, ref_insertion, - ref_latency); + inputDelayRefPinArrival(ref_path, ref_path->clkEdge(this), min_max, sdc, + ref_arrival, ref_insertion, ref_latency); seedInputDelayArrival(pin, input_delay, ref_path->clkEdge(this), ref_arrival, ref_insertion, ref_latency, is_segment_start, min_max, scene, tag_bldr); @@ -1852,12 +1804,12 @@ Search::seedInputDelayArrival(const Pin *pin, else { for (const MinMax *min_max : MinMax::range()) { float clk_arrival, clk_insertion, clk_latency; - inputDelayClkArrival(input_delay, clk_edge, min_max, mode, - clk_arrival, clk_insertion, clk_latency); + inputDelayClkArrival(input_delay, clk_edge, min_max, mode, clk_arrival, + clk_insertion, clk_latency); for (Scene *scene : mode->scenes()) { - seedInputDelayArrival(pin, input_delay, clk_edge, - clk_arrival, clk_insertion, clk_latency, - is_segment_start, min_max, scene, tag_bldr); + seedInputDelayArrival(pin, input_delay, clk_edge, clk_arrival, clk_insertion, + clk_latency, is_segment_start, min_max, scene, + tag_bldr); } } } @@ -1911,15 +1863,13 @@ Search::seedInputDelayArrival(const Pin *pin, bool exists; input_delay->delays()->value(rf, min_max, delay, exists); if (exists) - seedInputDelayArrival(pin, rf, clk_arrival + delay, - input_delay, clk_edge, - clk_insertion, clk_latency, is_segment_start, - min_max, scene, tag_bldr); + seedInputDelayArrival(pin, rf, clk_arrival + delay, input_delay, clk_edge, + clk_insertion, clk_latency, is_segment_start, min_max, + scene, tag_bldr); } else - seedInputDelayArrival(pin, rf, 0.0, nullptr, clk_edge, - clk_insertion, clk_latency, is_segment_start, - min_max, scene, tag_bldr); + seedInputDelayArrival(pin, rf, 0.0, nullptr, clk_edge, clk_insertion, + clk_latency, is_segment_start, min_max, scene, tag_bldr); } } @@ -1961,13 +1911,11 @@ Search::inputDelayClkArrival(InputDelay *input_delay, const RiseFall *clk_rf = clk_edge->transition(); if (!input_delay->sourceLatencyIncluded()) { const EarlyLate *early_late = min_max; - clk_insertion = delayAsFloat(clockInsertion(clk, clk->defaultPin(), - clk_rf, min_max, - early_late, mode)); + clk_insertion = delayAsFloat( + clockInsertion(clk, clk->defaultPin(), clk_rf, min_max, early_late, mode)); clk_arrival += clk_insertion; } - if (!clk->isPropagated() - && !input_delay->networkLatencyIncluded()) { + if (!clk->isPropagated() && !input_delay->networkLatencyIncluded()) { clk_latency = mode->sdc()->clockLatency(clk, clk_rf, min_max); clk_arrival += clk_latency; } @@ -2001,21 +1949,19 @@ Search::inputDelayTag(const Pin *pin, Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; Tag *tag = nullptr; - if (sdc->exceptionFromStates(pin,rf,clk,clk_rf,min_max,states)) { - const ClkInfo *clk_info = findClkInfo(scene, clk_edge, clk_pin, - is_propagated, nullptr, - false, nullptr, clk_insertion, clk_latency, - clk_uncertainties, min_max, nullptr); - tag = findTag(scene, rf, min_max, clk_info, false, - input_delay, is_segment_start, states, true, nullptr); + if (sdc->exceptionFromStates(pin, rf, clk, clk_rf, min_max, states)) { + const ClkInfo *clk_info = + findClkInfo(scene, clk_edge, clk_pin, is_propagated, nullptr, false, nullptr, + clk_insertion, clk_latency, clk_uncertainties, min_max, nullptr); + tag = findTag(scene, rf, min_max, clk_info, false, input_delay, is_segment_start, + states, true, nullptr); } if (tag) { const ClkInfo *clk_info = tag->clkInfo(); // Check for state changes on existing tag exceptions (pending -thru pins). - tag = mutateTag(tag, pin, rf, false, clk_info, - pin, rf, false, false, is_segment_start, clk_info, - input_delay, nullptr); + tag = mutateTag(tag, pin, rf, false, clk_info, pin, rf, false, false, + is_segment_start, clk_info, input_delay, nullptr); } return tag; } @@ -2026,7 +1972,7 @@ PathVisitor::PathVisitor(const StaState *sta) : StaState(sta), pred_(sta->search()->evalPred()), - tag_cache_( nullptr) + tag_cache_(nullptr) { } @@ -2036,16 +1982,14 @@ PathVisitor::PathVisitor(SearchPred *pred, StaState(sta), pred_(pred), - tag_cache_(make_tag_cache - ? new TagSet(128, TagSet::hasher(sta), TagSet::key_equal(sta)) - : nullptr) + tag_cache_(make_tag_cache ? new TagSet(128, + TagSet::hasher(sta), + TagSet::key_equal(sta)) + : nullptr) { } -PathVisitor::~PathVisitor() -{ - delete tag_cache_; -} +PathVisitor::~PathVisitor() { delete tag_cache_; } void PathVisitor::visitFaninPaths(Vertex *to_vertex) @@ -2070,8 +2014,7 @@ PathVisitor::visitFanoutPaths(Vertex *from_vertex) Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); const Pin *to_pin = to_vertex->pin(); - debugPrint(debug_, "search", 3, " %s", - to_vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 3, " {}", to_vertex->to_string(this)); if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) break; } @@ -2093,19 +2036,18 @@ PathVisitor::visitEdge(const Pin *from_pin, Path *from_path = from_iter.next(); const Mode *mode = from_path->mode(this); if (mode == prev_mode - || (pred_->searchFrom(from_vertex, mode) - && pred_->searchThru(edge, mode) + || (pred_->searchFrom(from_vertex, mode) && pred_->searchThru(edge, mode) && pred_->searchTo(to_vertex, mode))) { - prev_mode = mode; + prev_mode = mode; const MinMax *min_max = from_path->minMax(this); const RiseFall *from_rf = from_path->transition(this); TimingArc *arc1, *arc2; arc_set->arcsFrom(from_rf, arc1, arc2); - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc1, to_pin, to_vertex, min_max, mode)) + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc1, to_pin, + to_vertex, min_max, mode)) return false; - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc2, to_pin, to_vertex, min_max, mode)) + if (!visitArc(from_pin, from_vertex, from_rf, from_path, edge, arc2, to_pin, + to_vertex, min_max, mode)) return false; } } @@ -2128,8 +2070,8 @@ PathVisitor::visitArc(const Pin *from_pin, if (arc) { const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (searchThru(from_vertex, from_rf, edge, to_vertex, to_rf, mode)) - return visitFromPath(from_pin, from_vertex, from_rf, from_path, - edge, arc, to_pin, to_vertex, to_rf, min_max); + return visitFromPath(from_pin, from_vertex, from_rf, from_path, edge, arc, + to_pin, to_vertex, to_rf, min_max); } return true; } @@ -2160,7 +2102,7 @@ PathVisitor::visitFromPath(const Pin *from_pin, DcalcAPIndex dcalc_ap = from_path->dcalcAnalysisPtIndex(this); Arrival to_arrival; if (from_clk_info->isGenClkSrcPath()) { - if (!sdc->clkStopPropagation(clk,from_pin,from_rf,to_pin,to_rf) + if (!sdc->clkStopPropagation(clk, from_pin, from_rf, to_pin, to_rf) && (variables_->clkThruTristateEnabled() || !(role == TimingRole::tristateEnable() || role == TimingRole::tristateDisable()))) { @@ -2169,18 +2111,16 @@ PathVisitor::visitFromPath(const Pin *from_pin, Genclks *genclks = mode->genclks(); VertexSet *fanins = genclks->fanins(gclk); // Note: encountering a latch d->q edge means find the - // latch feedback edges, but they are referenced for + // latch feedback edges, but they are referenced for // other edges in the gen clk fanout. if (role == TimingRole::latchDtoQ()) genclks->findLatchFdbkEdges(gclk); EdgeSet &fdbk_edges = genclks->latchFdbkEdges(gclk); - if ((role == TimingRole::combinational() - || role == TimingRole::wire() + if ((role == TimingRole::combinational() || role == TimingRole::wire() || !gclk->combinational()) - && fanins->contains(to_vertex) - && !fdbk_edges.contains(edge)) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, - true, min_max, dcalc_ap, sdc); + && fanins->contains(to_vertex) && !fdbk_edges.contains(edge)) { + arc_delay = search_->deratedDelay(from_vertex, arc, edge, true, min_max, + dcalc_ap, sdc); DcalcAPIndex dcalc_ap = scene->dcalcAnalysisPtIndex(min_max->opposite()); Delay arc_delay_opp = search_->deratedDelay(from_vertex, arc, edge, true, min_max, @@ -2197,10 +2137,9 @@ PathVisitor::visitFromPath(const Pin *from_pin, } } else if (role->genericRole() == TimingRole::regClkToQ()) { - if (clk == nullptr - || !sdc->clkStopPropagation(from_pin, clk)) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, - min_max, dcalc_ap, sdc); + if (clk == nullptr || !sdc->clkStopPropagation(from_pin, clk)) { + arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max, + dcalc_ap, sdc); // Remove clock network delay for macros created with propagated // clocks when used in a context with ideal clocks. @@ -2217,8 +2156,7 @@ PathVisitor::visitFromPath(const Pin *from_pin, // Propagate from unclocked reg/latch clk pins, which have no // clk but are distinguished with a segment_start flag. - if ((clk_edge == nullptr - && from_tag->isSegmentStart()) + if ((clk_edge == nullptr && from_tag->isSegmentStart()) // Do not propagate paths from input ports with default // input arrival clk thru CLK->Q edges. || (clk != sdc->defaultArrivalClock() @@ -2229,11 +2167,9 @@ PathVisitor::visitFromPath(const Pin *from_pin, const ClkInfo *to_clk_info = from_clk_info; if (from_clk_info->crprClkPath(this) == nullptr || network_->direction(to_pin)->isInternal()) - to_clk_info = search_->clkInfoWithCrprClkPath(from_clk_info, - from_path); - to_tag = search_->fromRegClkTag(from_pin, from_rf, clk, clk_rf, - to_clk_info, to_pin, to_rf, min_max, - from_tag->scene()); + to_clk_info = search_->clkInfoWithCrprClkPath(from_clk_info, from_path); + to_tag = search_->fromRegClkTag(from_pin, from_rf, clk, clk_rf, to_clk_info, + to_pin, to_rf, min_max, from_tag->scene()); if (to_tag) to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_); from_arrival = search_->clkPathArrival(from_path, from_clk_info, @@ -2245,8 +2181,7 @@ PathVisitor::visitFromPath(const Pin *from_pin, } } else if (edge->role() == TimingRole::latchDtoQ()) { - if (min_max == MinMax::max() - && clk) { + if (min_max == MinMax::max() && clk) { bool postponed = false; if (search_->postponeLatchOutputs()) { const Path *from_clk_path = from_clk_info->crprClkPath(this); @@ -2258,21 +2193,19 @@ PathVisitor::visitFromPath(const Pin *from_pin, // Crpr clk path on latch data input is required to find Q // arrival. If the data clk path level is >= Q level the // crpr clk path prev_path pointers are not complete. - debugPrint(debug_, "search", 3, "postponed latch eval %d %s -> %s %d", - d_clk_level, - d_clk_vertex->to_string(this).c_str(), - edge->to_string(this).c_str(), - q_level); + debugPrint(debug_, "search", 3, "postponed latch eval {} {} -> {} {}", + d_clk_level, d_clk_vertex->to_string(this), + edge->to_string(this), q_level); postponed = true; search_->enqueueLatchOutput(to_vertex); } } } if (!postponed) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, - min_max, dcalc_ap, sdc); - latches_->latchOutArrival(from_path, arc, edge, to_tag, - arc_delay, to_arrival); + arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max, + dcalc_ap, sdc); + latches_->latchOutArrival(from_path, arc, edge, to_tag, arc_delay, + to_arrival); if (to_tag) to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_); } @@ -2288,22 +2221,18 @@ PathVisitor::visitFromPath(const Pin *from_pin, && sdc->clkDisabledByHpinThru(clk, from_pin, to_pin)) // Generated clock source pins have arrivals for the source clock. // Do not propagate them past the generated clock source pin. - && !(clks - && !clks->contains(const_cast(from_tag->clock())))) { + && !(clks && !clks->contains(const_cast(from_tag->clock())))) { // Propagate arrival as non-clock at the end of the clock tree. bool to_propagates_clk = - !sdc->clkStopPropagation(clk,from_pin,from_rf,to_pin,to_rf) - && (variables_->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())); - arc_delay = search_->deratedDelay(from_vertex, arc, edge, - to_propagates_clk, min_max, - dcalc_ap, sdc); - DcalcAPIndex dcalc_ap_opp = - scene->dcalcAnalysisPtIndex(min_max->opposite()); - Delay arc_delay_opp = search_->deratedDelay(from_vertex, arc, edge, - to_propagates_clk, - min_max, dcalc_ap_opp, sdc); + !sdc->clkStopPropagation(clk, from_pin, from_rf, to_pin, to_rf) + && (variables_->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())); + arc_delay = search_->deratedDelay(from_vertex, arc, edge, to_propagates_clk, + min_max, dcalc_ap, sdc); + DcalcAPIndex dcalc_ap_opp = scene->dcalcAnalysisPtIndex(min_max->opposite()); + Delay arc_delay_opp = search_->deratedDelay( + from_vertex, arc, edge, to_propagates_clk, min_max, dcalc_ap_opp, sdc); bool arc_delay_min_max_eq = fuzzyEqual(delayAsFloat(arc_delay), delayAsFloat(arc_delay_opp)); to_tag = search_->thruClkTag(from_path, from_vertex, from_tag, @@ -2325,11 +2254,9 @@ PathVisitor::visitFromPath(const Pin *from_pin, } } if (to_tag) - return visitFromToPath(from_pin, from_vertex, from_rf, - from_tag, from_path, from_arrival, - edge, arc, arc_delay, - to_vertex, to_rf, to_tag, to_arrival, - min_max); + return visitFromToPath(from_pin, from_vertex, from_rf, from_tag, from_path, + from_arrival, edge, arc, arc_delay, to_vertex, to_rf, + to_tag, to_arrival, min_max); else return true; } @@ -2350,9 +2277,7 @@ Search::clkPathArrival(const Path *clk_path, const MinMax *min_max) const { const Scene *scene = clk_path->scene(this); - if (clk_path->vertex(this)->isRegClk() - && clk_path->isClock(this) - && clk_edge + if (clk_path->vertex(this)->isRegClk() && clk_path->isClock(this) && clk_edge && !clk_info->isPropagated()) { // Ideal clock, apply ideal insertion delay and latency. const EarlyLate *early_late = min_max; @@ -2425,10 +2350,10 @@ Search::fromUnclkedInputTag(const Pin *pin, ExceptionStateSet *states = nullptr; if (sdc->exceptionFromStates(pin, rf, nullptr, nullptr, min_max, states) && (!require_exception || states)) { - const ClkInfo *clk_info = findClkInfo(scene, nullptr, nullptr, false, - 0.0, min_max); - return findTag(scene, rf, min_max, clk_info, false, nullptr, - is_segment_start, states, true, nullptr); + const ClkInfo *clk_info = + findClkInfo(scene, nullptr, nullptr, false, 0.0, min_max); + return findTag(scene, rf, min_max, clk_info, false, nullptr, is_segment_start, + states, true, nullptr); } return nullptr; } @@ -2446,12 +2371,11 @@ Search::fromRegClkTag(const Pin *from_pin, { Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; - if (sdc->exceptionFromStates(from_pin, from_rf, clk, clk_rf, - min_max, states)) { + if (sdc->exceptionFromStates(from_pin, from_rf, clk, clk_rf, min_max, states)) { // Hack for filter -from reg/Q. sdc->filterRegQStates(to_pin, to_rf, min_max, states); - return findTag(scene, to_rf, min_max, clk_info, false, nullptr, - false, states, true, nullptr); + return findTag(scene, to_rf, min_max, clk_info, false, nullptr, false, states, + true, nullptr); } else return nullptr; @@ -2464,18 +2388,12 @@ Search::clkInfoWithCrprClkPath(const ClkInfo *from_clk_info, { Scene *scene = from_clk_info->scene(); if (crprActive(scene->mode())) - return findClkInfo(scene, - from_clk_info->clkEdge(), - from_clk_info->clkSrc(), - from_clk_info->isPropagated(), - from_clk_info->genClkSrc(), + return findClkInfo(scene, from_clk_info->clkEdge(), from_clk_info->clkSrc(), + from_clk_info->isPropagated(), from_clk_info->genClkSrc(), from_clk_info->isGenClkSrcPath(), - from_clk_info->pulseClkSense(), - from_clk_info->insertion(), - from_clk_info->latency(), - from_clk_info->uncertainties(), - from_clk_info->minMax(), - from_path); + from_clk_info->pulseClkSense(), from_clk_info->insertion(), + from_clk_info->latency(), from_clk_info->uncertainties(), + from_clk_info->minMax(), from_path); else return from_clk_info; } @@ -2494,8 +2412,8 @@ Search::thruTag(Tag *from_tag, const RiseFall *from_rf = from_tag->transition(); const ClkInfo *from_clk_info = from_tag->clkInfo(); bool to_is_reg_clk = to_vertex->isRegClk(); - Tag *to_tag = mutateTag(from_tag, from_pin, from_rf, false, from_clk_info, - to_pin, to_rf, false, to_is_reg_clk, false, + Tag *to_tag = mutateTag(from_tag, from_pin, from_rf, false, from_clk_info, to_pin, + to_rf, false, to_is_reg_clk, false, // input delay is not propagated. from_clk_info, nullptr, tag_cache); return to_tag; @@ -2521,15 +2439,12 @@ Search::thruClkTag(Path *from_path, bool from_is_clk = from_tag->isClock(); bool to_is_reg_clk = to_vertex->isRegClk(); const TimingRole *role = edge->role(); - bool to_is_clk = (from_is_clk - && to_propagates_clk - && (role->isWire() - || role == TimingRole::combinational())); - const ClkInfo *to_clk_info = thruClkInfo(from_path, from_vertex, - from_clk_info, from_is_clk, - edge, to_vertex, to_pin, to_is_clk, - arc_delay_min_max_eq, min_max, scene); - Tag *to_tag = mutateTag(from_tag,from_pin,from_rf,from_is_clk,from_clk_info, + bool to_is_clk = (from_is_clk && to_propagates_clk + && (role->isWire() || role == TimingRole::combinational())); + const ClkInfo *to_clk_info = thruClkInfo( + from_path, from_vertex, from_clk_info, from_is_clk, edge, to_vertex, to_pin, + to_is_clk, arc_delay_min_max_eq, min_max, scene); + Tag *to_tag = mutateTag(from_tag, from_pin, from_rf, from_is_clk, from_clk_info, to_pin, to_rf, to_is_clk, to_is_reg_clk, false, to_clk_info, nullptr, nullptr); return to_tag; @@ -2557,8 +2472,7 @@ Search::thruClkInfo(Path *from_path, bool from_clk_prop = from_clk_info->isPropagated(); bool to_clk_prop = from_clk_prop; - if (!from_clk_prop - && sdc->isPropagatedClock(to_pin)) { + if (!from_clk_prop && sdc->isPropagatedClock(to_pin)) { to_clk_prop = true; changed = true; } @@ -2567,9 +2481,7 @@ Search::thruClkInfo(Path *from_path, // so that generated clock crpr info can be (later) safely set on // the clkinfo. const Pin *gen_clk_src = nullptr; - if (from_clk_info->isGenClkSrcPath() - && crprActive(mode) - && sdc->isClock(to_pin)) { + if (from_clk_info->isGenClkSrcPath() && crprActive(mode) && sdc->isClock(to_pin)) { // Don't care that it could be a regular clock root. gen_clk_src = to_pin; changed = true; @@ -2579,9 +2491,7 @@ Search::thruClkInfo(Path *from_path, if (crprActive(mode) // Update crpr clk path for combinational paths leaving the clock // network (ie, tristate en->out) and buffer driving reg clk. - && ((from_is_clk - && !to_is_clk - && !from_vertex->isRegClk()) + && ((from_is_clk && !to_is_clk && !from_vertex->isRegClk()) || (to_vertex->isRegClk() // If the wire delay to the reg clk pin is zero, // leave the crpr_clk_path null to indicate that @@ -2599,8 +2509,8 @@ Search::thruClkInfo(Path *from_path, to_pulse_sense = port->pulseClkSense(); changed = true; } - else if (from_pulse_sense && - edge->timingArcSet()->sense() == TimingSense::negative_unate) { + else if (from_pulse_sense + && edge->timingArcSet()->sense() == TimingSense::negative_unate) { to_pulse_sense = from_pulse_sense->opposite(); changed = true; } @@ -2610,8 +2520,7 @@ Search::thruClkInfo(Path *from_path, float to_latency = from_clk_info->latency(); float latency; bool exists; - sdc->clockLatency(from_clk, to_pin, clk_rf, min_max, - latency, exists); + sdc->clockLatency(from_clk, to_pin, clk_rf, min_max, latency, exists); if (exists) { // Latency on pin has precedence over fanin or hierarchical // pin latency. @@ -2621,8 +2530,7 @@ Search::thruClkInfo(Path *from_path, } else { // Check for hierarchical pin latency thru edge. - sdc->clockLatency(edge, clk_rf, min_max, - latency, exists); + sdc->clockLatency(edge, clk_rf, min_max, latency, exists); if (exists) { to_latency = latency; to_clk_prop = false; @@ -2638,11 +2546,10 @@ Search::thruClkInfo(Path *from_path, } if (changed) - to_clk_info = findClkInfo(scene, from_clk_edge, from_clk_info->clkSrc(), - to_clk_prop, gen_clk_src, - from_clk_info->isGenClkSrcPath(), - to_pulse_sense, to_insertion, to_latency, - to_uncertainties, min_max, to_crpr_clk_path); + to_clk_info = findClkInfo( + scene, from_clk_edge, from_clk_info->clkSrc(), to_clk_prop, gen_clk_src, + from_clk_info->isGenClkSrcPath(), to_pulse_sense, to_insertion, to_latency, + to_uncertainties, min_max, to_crpr_clk_path); return to_clk_info; } @@ -2680,7 +2587,7 @@ Search::mutateTag(Tag *from_tag, for (ExceptionState *state : *from_states) { ExceptionPath *exception = state->exception(); // One edge may traverse multiple hierarchical thru pins. - while (state->matchesNextThru(from_pin,to_pin,to_rf,min_max,network_)) { + while (state->matchesNextThru(from_pin, to_pin, to_rf, min_max, network_)) { // Found a -thru that we've been waiting for. state = state->nextState(); state_change = true; @@ -2692,20 +2599,16 @@ Search::mutateTag(Tag *from_tag, // Don't propagate a completed false path -thru unless it is a // clock. Clocks carry the completed false path to disable // downstream paths that use the clock as data. - if ((state->isComplete() - && exception->isFalse() - && !from_is_clk) + if ((state->isComplete() && exception->isFalse() && !from_is_clk) // to_pin/edge completes a loop path. - || (exception->isLoop() - && state->isComplete())) + || (exception->isLoop() && state->isComplete())) return nullptr; // Kill path delay tags past the -to pin. if ((exception->isPathDelay() && sdc->isCompleteTo(state, to_pin, to_rf, min_max)) // Kill loop tags at register clock pins. - || (exception->isLoop() - && to_is_reg_clk)) { + || (exception->isLoop() && to_is_reg_clk)) { state_change = true; break; } @@ -2721,19 +2624,16 @@ Search::mutateTag(Tag *from_tag, for (auto state : *from_states) { ExceptionPath *exception = state->exception(); // One edge may traverse multiple hierarchical thru pins. - while (state->matchesNextThru(from_pin,to_pin,to_rf,min_max,network_)) + while (state->matchesNextThru(from_pin, to_pin, to_rf, min_max, network_)) // Found a -thru that we've been waiting for. state = state->nextState(); // Don't propagate a completed false path -thru unless it is a // clock. Clocks carry the completed false path to disable // downstream paths that use the clock as data. - if ((state->isComplete() - && exception->isFalse() - && !from_is_clk) + if ((state->isComplete() && exception->isFalse() && !from_is_clk) // to_pin/edge completes a loop path. - || (exception->isLoop() - && state->isComplete())) { + || (exception->isLoop() && state->isComplete())) { delete new_states; return nullptr; } @@ -2742,8 +2642,7 @@ Search::mutateTag(Tag *from_tag, if (!((exception->isPathDelay() && sdc->isCompleteTo(state, from_pin, from_rf, min_max)) // Kill loop tags at register clock pins. - || (to_is_reg_clk - && exception->isLoop()))) + || (to_is_reg_clk && exception->isLoop()))) new_states->insert(state); } } @@ -2754,20 +2653,18 @@ Search::mutateTag(Tag *from_tag, if (new_states) return findTag(scene, to_rf, min_max, to_clk_info, to_is_clk, - from_tag->inputDelay(), to_is_segment_start, - new_states, true, tag_cache); + from_tag->inputDelay(), to_is_segment_start, new_states, true, + tag_cache); else { // No state change. - if (to_clk_info == from_clk_info - && to_is_clk == from_is_clk + if (to_clk_info == from_clk_info && to_is_clk == from_is_clk && from_tag->isSegmentStart() == to_is_segment_start && from_tag->inputDelay() == to_input_delay) { return tags_[tagsTableRfIndex(from_tag->index(), to_rf)]; } else - return findTag(scene, to_rf, min_max, to_clk_info, to_is_clk, - to_input_delay, to_is_segment_start, - from_states, false, tag_cache); + return findTag(scene, to_rf, min_max, to_clk_info, to_is_clk, to_input_delay, + to_is_segment_start, from_states, false, tag_cache); } } @@ -2793,9 +2690,8 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) // can use Search::tagGroup(TagGroupIndex) without returning gubbish. if (tag_group_next_ == tag_group_capacity_) { TagGroupIndex tag_capacity = tag_group_capacity_ * 2; - TagGroup **tag_groups = new TagGroup*[tag_capacity]; - memcpy(tag_groups, tag_groups_, - tag_group_capacity_ * sizeof(TagGroup*)); + TagGroup **tag_groups = new TagGroup *[tag_capacity]; + memcpy(tag_groups, tag_groups_, tag_group_capacity_ * sizeof(TagGroup *)); tag_groups_prev_.push_back(tag_groups_); tag_groups_ = tag_groups; tag_group_capacity_ = tag_capacity; @@ -2865,7 +2761,6 @@ private: const StaState *sta_; }; - ReportPathLess::ReportPathLess(const StaState *sta) : sta_(sta) { @@ -2882,12 +2777,12 @@ void Search::reportArrivals(Vertex *vertex, bool report_tag_index) const { - report_->reportLine("Vertex %s", vertex->to_string(this).c_str()); + report_->report("Vertex {}", vertex->to_string(this)); TagGroup *tag_group = tagGroup(vertex); if (tag_group) { if (report_tag_index) - report_->reportLine("Group %u", tag_group->index()); - std::vector paths; + report_->report("Group {}", tag_group->index()); + std::vector paths; VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { const Path *path = path_iter.next(); @@ -2897,7 +2792,7 @@ Search::reportArrivals(Vertex *vertex, for (const Path *path : paths) { const Tag *tag = path->tag(this); const RiseFall *rf = tag->transition(); - const char *req = delayAsString(path->required(), this); + std::string req = delayAsString(path->required(), this); bool report_prev = false; std::string prev_str; if (report_prev) { @@ -2919,17 +2814,14 @@ Search::reportArrivals(Vertex *vertex, else prev_str += "NULL"; } - report_->reportLine(" %s %s %s / %s %s%s", - rf->shortName(), - path->minMax(this)->to_string().c_str(), - delayAsString(path->arrival(), this), - req, - tag->to_string(report_tag_index, false, this).c_str(), - prev_str.c_str()); + report_->report(" {} {} {} / {} {}{}", rf->shortName(), + path->minMax(this)->to_string(), + delayAsString(path->arrival(), this), req, + tag->to_string(report_tag_index, false, this), prev_str); } } else - report_->reportLine(" no arrivals"); + report_->report(" no arrivals"); } TagGroup * @@ -2960,10 +2852,8 @@ Search::reportTagGroups() const for (TagGroupIndex i = 0; i < tag_group_next_; i++) { TagGroup *tag_group = tag_groups_[i]; if (tag_group) { - report_->reportLine("Group %4u hash = %4lu (%4lu)", - i, - tag_group->hash(), - tag_group->hash() % tag_group_set_->bucket_count()); + report_->report("Group {:4} hash = {:4} ({:4})", i, tag_group->hash(), + tag_group->hash() % tag_group_set_->bucket_count()); tag_group->reportArrivalMap(this); } } @@ -2972,9 +2862,8 @@ Search::reportTagGroups() const if (tag_group_set_->bucket_size(i) > long_hash) long_hash = i; } - report_->reportLine("Longest hash bucket length %zu hash=%zu", - tag_group_set_->bucket_size(long_hash), - long_hash); + report_->report("Longest hash bucket length {} hash={}", + tag_group_set_->bucket_size(long_hash), long_hash); } void @@ -2996,7 +2885,7 @@ Search::reportPathCountHistogram() const for (size_t path_count = 0; path_count < vertex_counts.size(); path_count++) { int vertex_count = vertex_counts[path_count]; if (vertex_count > 0) - report_->reportLine("%6lu %6d",path_count, vertex_count); + report_->report("{:6} {:6}", path_count, vertex_count); } } @@ -3026,8 +2915,8 @@ Search::findTag(Scene *scene, bool own_states, TagSet *tag_cache) { - Tag probe(scene, 0, rf, min_max, clk_info, is_clk, - input_delay, is_segment_start, states, false); + Tag probe(scene, 0, rf, min_max, clk_info, is_clk, input_delay, is_segment_start, + states, false); if (tag_cache) { Tag *tag = findKey(tag_cache, &probe); if (tag) @@ -3040,8 +2929,8 @@ Search::findTag(Scene *scene, // Make rise/fall versions of the tag to avoid tag_set lookups when the // only change is the rise/fall edge. for (const RiseFall *rf1 : RiseFall::range()) { - ExceptionStateSet *new_states = !own_states && states - ? new ExceptionStateSet(*states) : states; + ExceptionStateSet *new_states = + !own_states && states ? new ExceptionStateSet(*states) : states; TagIndex tag_index = tag_next_++; Tag *tag1 = new Tag(scene, tag_index, rf1, min_max, clk_info, is_clk, input_delay, is_segment_start, new_states, true); @@ -3064,8 +2953,8 @@ Search::findTag(Scene *scene, // can use Search::tag(TagIndex) without returning gubbish. if (tag_next_ == tag_capacity_) { TagIndex tag_capacity = tag_capacity_ * 2; - Tag **tags = new Tag*[tag_capacity]; - memcpy(tags, tags_, tag_capacity_ * sizeof(Tag*)); + Tag **tags = new Tag *[tag_capacity]; + memcpy(tags, tags_, tag_capacity_ * sizeof(Tag *)); tags_prev_.push_back(tags_); tags_ = tags; tag_capacity_ = tag_capacity; @@ -3083,29 +2972,28 @@ Search::reportTags() const for (TagIndex i = 0; i < tag_next_; i++) { Tag *tag = tags_[i]; if (tag) - report_->reportLine("%s", tag->to_string(this).c_str()) ; + report_->report("{}", tag->to_string(this)); } size_t long_hash = 0; for (size_t i = 0; i < tag_set_->bucket_count(); i++) { if (tag_set_->bucket_size(i) > long_hash) long_hash = i; } - report_->reportLine("Longest hash bucket length %zu hash=%zu", - tag_set_->bucket_size(long_hash), - long_hash); + report_->report("Longest hash bucket length {} hash={}", + tag_set_->bucket_size(long_hash), long_hash); } void Search::reportClkInfos() const { - std::vector clk_infos; + std::vector clk_infos; // set -> vector for sorting. for (const ClkInfo *clk_info : *clk_info_set_) clk_infos.push_back(clk_info); sort(clk_infos, ClkInfoLess(this)); for (const ClkInfo *clk_info : clk_infos) - report_->reportLine("%s", clk_info->to_string(this).c_str()); - report_->reportLine("%zu clk infos", clk_info_set_->size()); + report_->report("{}", clk_info->to_string(this)); + report_->report("{} clk infos", clk_info_set_->size()); } const ClkInfo * @@ -3123,16 +3011,14 @@ Search::findClkInfo(Scene *scene, Path *crpr_clk_path) { const ClkInfo probe(scene, clk_edge, clk_src, is_propagated, gen_clk_src, - gen_clk_src_path, pulse_clk_sense, - insertion, latency, uncertainties, min_max, - crpr_clk_path, this); + gen_clk_src_path, pulse_clk_sense, insertion, latency, + uncertainties, min_max, crpr_clk_path, this); LockGuard lock(clk_info_lock_); const ClkInfo *clk_info = findKey(clk_info_set_, &probe); if (clk_info == nullptr) { - clk_info = new ClkInfo(scene, clk_edge, clk_src, - is_propagated, gen_clk_src, gen_clk_src_path, - pulse_clk_sense, insertion, latency, uncertainties, - min_max, crpr_clk_path, this); + clk_info = new ClkInfo(scene, clk_edge, clk_src, is_propagated, gen_clk_src, + gen_clk_src_path, pulse_clk_sense, insertion, latency, + uncertainties, min_max, crpr_clk_path, this); clk_info_set_->insert(clk_info); } return clk_info; @@ -3146,9 +3032,8 @@ Search::findClkInfo(Scene *scene, Arrival insertion, const MinMax *min_max) { - return findClkInfo(scene, clk_edge, clk_src, is_propagated, - nullptr, false, nullptr, - insertion, 0.0, nullptr, min_max, nullptr); + return findClkInfo(scene, clk_edge, clk_src, is_propagated, nullptr, false, + nullptr, insertion, 0.0, nullptr, min_max, nullptr); } int @@ -3179,8 +3064,7 @@ Search::timingDerate(const Vertex *from_vertex, const Sdc *sdc, const MinMax *min_max) { - PathClkOrData derate_clk_data = - is_clk ? PathClkOrData::clk : PathClkOrData::data; + PathClkOrData derate_clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; const TimingRole *role = edge->role(); const Pin *pin = from_vertex->pin(); if (role->isWire()) { @@ -3195,11 +3079,10 @@ Search::timingDerate(const Vertex *from_vertex, rf = arc->toEdge()->asRiseFall(); } else { - derate_type = TimingDerateCellType::cell_delay; - rf = arc->fromEdge()->asRiseFall(); + derate_type = TimingDerateCellType::cell_delay; + rf = arc->fromEdge()->asRiseFall(); } - return sdc->timingDerateInstance(pin, derate_type, derate_clk_data, - rf, min_max); + return sdc->timingDerateInstance(pin, derate_type, derate_clk_data, rf, min_max); } } @@ -3219,7 +3102,7 @@ Search::clockDomains(const Vertex *vertex, // Return value. ClockSet &clks) const { - VertexPathIterator path_iter(const_cast(vertex), this); + VertexPathIterator path_iter(const_cast(vertex), this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const Clock *clk = path->clock(this); @@ -3273,11 +3156,10 @@ Search::clocks(const Vertex *vertex, // Return value. ClockSet &clks) const { - VertexPathIterator path_iter(const_cast(vertex), this); + VertexPathIterator path_iter(const_cast(vertex), this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - if (path->isClock(this) - && path->mode(this) == mode) + if (path->isClock(this) && path->mode(this) == mode) clks.insert(const_cast(path->clock(this))); } } @@ -3294,7 +3176,7 @@ void Search::findRequireds(Level level) { Stats stats(debug_, report_); - debugPrint(debug_, "search", 1, "find requireds to level %d", level); + debugPrint(debug_, "search", 1, "find requireds to level {}", level); RequiredVisitor req_visitor(this); if (!requireds_seeded_) seedRequireds(); @@ -3302,7 +3184,7 @@ Search::findRequireds(Level level) int required_count = required_iter_->visitParallel(level, &req_visitor); deleteTagsPrev(); requireds_exist_ = true; - debugPrint(debug_, "search", 1, "found %d requireds", required_count); + debugPrint(debug_, "search", 1, "found {} requireds", required_count); stats.report("Find requireds"); } @@ -3324,8 +3206,8 @@ Search::endpoints() while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); if (isEndpoint(vertex)) { - debugPrint(debug_, "endpoint", 2, "insert %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "endpoint", 2, "insert {}", + vertex->to_string(this)); endpoints_.insert(vertex); } } @@ -3334,15 +3216,13 @@ Search::endpoints() if (!invalid_endpoints_.empty()) { for (Vertex *vertex : invalid_endpoints_) { if (isEndpoint(vertex)) { - debugPrint(debug_, "endpoint", 2, "insert %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "endpoint", 2, "insert {}", + vertex->to_string(this)); endpoints_.insert(vertex); } else { - if (debug_->check("endpoint", 2) - && endpoints_.contains(vertex)) - report_->reportLine("endpoint: remove %s", - vertex->to_string(this).c_str()); + if (debug_->check("endpoint", 2) && endpoints_.contains(vertex)) + report_->report("endpoint: remove {}", vertex->to_string(this)); endpoints_.erase(vertex); } } @@ -3354,8 +3234,7 @@ Search::endpoints() void Search::endpointInvalid(Vertex *vertex) { - debugPrint(debug_, "endpoint", 2, "invalid %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "endpoint", 2, "invalid {}", vertex->to_string(this)); invalid_endpoints_.insert(vertex); } @@ -3395,16 +3274,13 @@ Search::isEndpoint(Vertex *vertex, const Pin *pin = vertex->pin(); const Sdc *sdc = mode->sdc(); return hasFanin(vertex, pred, graph_, mode) - && ((vertex->hasChecks() - && hasEnabledChecks(vertex, mode)) - || sdc->isConstrainedEnd(pin) - || !hasFanout(vertex, pred, graph_, mode) - || sdc->isPathDelayInternalTo(pin) - // Unconstrained paths at register clk pins. - || (unconstrained_paths_ - && vertex->isRegClk()) - || (variables_->gatedClkChecksEnabled() - && gated_clk_->isGatedClkEnable(vertex, mode))); + && ((vertex->hasChecks() && hasEnabledChecks(vertex, mode)) + || sdc->isConstrainedEnd(pin) || !hasFanout(vertex, pred, graph_, mode) + || sdc->isPathDelayInternalTo(pin) + // Unconstrained paths at register clk pins. + || (unconstrained_paths_ && vertex->isRegClk()) + || (variables_->gatedClkChecksEnabled() + && gated_clk_->isGatedClkEnable(vertex, mode))); } bool @@ -3497,8 +3373,8 @@ FindEndRequiredVisitor::visit(PathEnd *path_end) void Search::seedRequired(Vertex *vertex) { - debugPrint(debug_, "search", 2, "required seed %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "required seed {}", + vertex->to_string(this)); RequiredCmp required_cmp; FindEndRequiredVisitor seeder(&required_cmp, this); required_cmp.requiredsInit(vertex, this); @@ -3572,7 +3448,7 @@ RequiredCmp::requiredsSave(Vertex *vertex, const Required req = requireds_[path_index]; const Required &prev_req = path->required(); bool changed = !delayEqual(prev_req, req, sta); - debugPrint(debug, "search", 3, "required %s save %s -> %s%s", + debugPrint(debug, "search", 3, "required {} save {} -> {}{}", path->to_string(sta).c_str(), delayAsString(prev_req, sta), delayAsString(req, sta), @@ -3600,7 +3476,9 @@ RequiredVisitor::RequiredVisitor(const StaState *sta) : RequiredVisitor::RequiredVisitor(bool make_tag_cache, const StaState *sta) : - PathVisitor(sta->search()->evalPred(), make_tag_cache, sta), + PathVisitor(sta->search()->evalPred(), + make_tag_cache, + sta), required_cmp_(new RequiredCmp), visit_path_ends_(new VisitPathEnds(sta)) { @@ -3621,8 +3499,8 @@ RequiredVisitor::copy() const void RequiredVisitor::visit(Vertex *vertex) { - debugPrint(debug_, "search", 2, "find required %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "find required {}", + vertex->to_string(this)); required_cmp_->requiredsInit(vertex, this); // Back propagate requireds from fanout. visitFanoutPaths(vertex); @@ -3656,13 +3534,10 @@ RequiredVisitor::visitFromToPath(const Pin *, { // Don't propagate required times through latch D->Q edges. if (!edge->role()->isLatchDtoQ()) { - debugPrint(debug_, "search", 3, " %s -> %s %s", - from_rf->shortName(), - to_rf->shortName(), - min_max->to_string().c_str()); - debugPrint(debug_, "search", 3, " from tag %2u: %s", - from_tag->index(), - from_tag->to_string(this).c_str()); + debugPrint(debug_, "search", 3, " {} -> {} {}", from_rf->shortName(), + to_rf->shortName(), min_max->to_string()); + debugPrint(debug_, "search", 3, " from tag {:2}: {}", from_tag->index(), + from_tag->to_string(this)); size_t path_index = from_path->pathIndex(this); const MinMax *req_min = min_max->opposite(); TagGroup *to_tag_group = search_->tagGroup(to_vertex); @@ -3672,10 +3547,10 @@ RequiredVisitor::visitFromToPath(const Pin *, Path &to_path = to_vertex->paths()[to_path_index]; const Required &to_required = to_path.required(); Required from_required = delayDiff(to_required, arc_delay, this); - debugPrint(debug_, "search", 3, " to tag %2u: %s", + debugPrint(debug_, "search", 3, " to tag {:2}: {}", to_tag->index(), to_tag->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " %s - %s = %s %s %s", + debugPrint(debug_, "search", 3, " {} - {} = {} {} {}", delayAsString(to_required, this), delayAsString(arc_delay, this), delayAsString(from_required, this), @@ -3696,16 +3571,15 @@ RequiredVisitor::visitFromToPath(const Pin *, if (Tag::matchNoCrpr(to_path_tag, to_tag)) { Required to_required = to_path->required(); Required from_required = delayDiff(to_required, arc_delay, this); - debugPrint(debug_, "search", 3, " to tag %2u: %s", + debugPrint(debug_, "search", 3, " to tag {:2}: {}", to_path_tag->index(), to_path_tag->to_string(this).c_str()); - debugPrint(debug_, "search", 3, " %s - %s = %s %s %s", + debugPrint(debug_, "search", 3, " {} - {} = {} {} {}", delayAsString(to_required, this), delayAsString(arc_delay, this), delayAsString(from_required, this), min_max == MinMax::max() ? "<" : ">", - delayAsString(required_cmp_->required(path_index), - this)); + delayAsString(required_cmp_->required(path_index), this)); required_cmp_->requiredSet(path_index, from_required, req_min, this); break; } @@ -3744,9 +3618,7 @@ bool Search::matchesFilter(Path *path, const ClockEdge *to_clk_edge) { - if (!have_filter_ - && filter_from_ == nullptr - && filter_to_ == nullptr) + if (!have_filter_ && filter_from_ == nullptr && filter_to_ == nullptr) return true; else if (have_filter_) { // -from pins|inst @@ -3755,29 +3627,25 @@ Search::matchesFilter(Path *path, ExceptionStateSet *states = path->tag(this)->states(); if (states) { for (auto state : *states) { - if (state->exception()->isFilter() - && state->nextThru() == nullptr + if (state->exception()->isFilter() && state->nextThru() == nullptr && matchesFilterTo(path, to_clk_edge)) return true; } } return false; } - else if (filter_from_ - && filter_from_->pins() == nullptr - && filter_from_->instances() == nullptr - && filter_from_->clks()) { + else if (filter_from_ && filter_from_->pins() == nullptr + && filter_from_->instances() == nullptr && filter_from_->clks()) { // -from clks const ClockEdge *path_clk_edge = path->clkEdge(this); const Clock *path_clk = path_clk_edge ? path_clk_edge->clock() : nullptr; const RiseFall *path_clk_rf = - path_clk_edge ? path_clk_edge->transition() : nullptr; - return filter_from_->clks()->contains(const_cast(path_clk)) - && filter_from_->transition()->matches(path_clk_rf) - && matchesFilterTo(path, to_clk_edge); + path_clk_edge ? path_clk_edge->transition() : nullptr; + return filter_from_->clks()->contains(const_cast(path_clk)) + && filter_from_->transition()->matches(path_clk_rf) + && matchesFilterTo(path, to_clk_edge); } - else if (filter_from_ == nullptr - && filter_to_) + else if (filter_from_ == nullptr && filter_to_) // -to return matchesFilterTo(path, to_clk_edge); else { @@ -3819,12 +3687,10 @@ Search::exceptionTo(ExceptionPathType type, for (auto state : *states) { ExceptionPath *exception = state->exception(); int priority = exception->priority(min_max); - if ((type == ExceptionPathType::any - || exception->type() == type) + if ((type == ExceptionPathType::any || exception->type() == type) && sdc->isCompleteTo(state, pin, rf, clk_edge, min_max, match_min_max_exactly, require_to_pin) - && (hi_priority_exception == nullptr - || priority > hi_priority + && (hi_priority_exception == nullptr || priority > hi_priority || (priority == hi_priority && exception->tighterThan(hi_priority_exception)))) { hi_priority = priority; @@ -3833,8 +3699,7 @@ Search::exceptionTo(ExceptionPathType type, } } // Check for -to exceptions originating at the end pin or target clock. - sdc->exceptionTo(type, pin, rf, clk_edge, min_max, - match_min_max_exactly, + sdc->exceptionTo(type, pin, rf, clk_edge, min_max, match_min_max_exactly, hi_priority_exception, hi_priority); return hi_priority_exception; } @@ -3858,8 +3723,8 @@ Search::groupPathsTo(const PathEnd *path_end) const for (auto state : *states) { ExceptionPath *exception = state->exception(); if (exception->isGroupPath() - && sdc->exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, - false, false)) + && sdc->exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, false, + false)) group_paths.push_back(exception); } } @@ -3909,10 +3774,8 @@ Search::tnsPreamble() void Search::tnsInvalid(Vertex *vertex) { - if ((tns_exists_ || worst_slacks_) - && isEndpoint(vertex)) { - debugPrint(debug_, "tns", 2, "tns invalid %s", - vertex->to_string(this).c_str()); + if ((tns_exists_ || worst_slacks_) && isEndpoint(vertex)) { + debugPrint(debug_, "tns", 2, "tns invalid {}", vertex->to_string(this)); LockGuard lock(tns_lock_); invalid_tns_.insert(vertex); } @@ -3925,8 +3788,7 @@ Search::updateInvalidTns() for (Vertex *vertex : invalid_tns_) { // Network edits can change endpointedness since tnsInvalid was called. if (isEndpoint(vertex)) { - debugPrint(debug_, "tns", 2, "update tns %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "tns", 2, "update tns {}", vertex->to_string(this)); SlackSeq slacks(path_count); wnsSlacks(vertex, slacks); @@ -3974,7 +3836,7 @@ Search::tnsIncr(Vertex *vertex, PathAPIndex path_ap_index) { if (delayLess(slack, 0.0, this)) { - debugPrint(debug_, "tns", 3, "tns+ %s %s", + debugPrint(debug_, "tns", 3, "tns+ {} {}", delayAsString(slack, this), vertex->to_string(this).c_str()); delayIncr(tns_[path_ap_index], slack, this); @@ -3993,7 +3855,7 @@ Search::tnsDecr(Vertex *vertex, findKeyValue(tns_slacks_[path_ap_index], vertex, slack, found); if (found && delayLess(slack, 0.0, this)) { - debugPrint(debug_, "tns", 3, "tns- %s %s", + debugPrint(debug_, "tns", 3, "tns- {} {}", delayAsString(slack, this), vertex->to_string(this).c_str()); delayDecr(tns_[path_ap_index], slack, this); @@ -4005,8 +3867,7 @@ Search::tnsDecr(Vertex *vertex, void Search::tnsNotifyBefore(Vertex *vertex) { - if (tns_exists_ - && isEndpoint(vertex)) { + if (tns_exists_ && isEndpoint(vertex)) { size_t path_count = scenePathCount(); for (size_t i = 0; i < path_count; i++) { tnsDecr(vertex, i); @@ -4053,10 +3914,10 @@ Search::wnsTnsPreamble() findAllArrivals(); // Required times are only needed at endpoints. if (requireds_seeded_) { - for (auto itr = invalid_requireds_.begin(); itr != invalid_requireds_.end(); ) { + for (auto itr = invalid_requireds_.begin(); itr != invalid_requireds_.end();) { Vertex *vertex = *itr; - debugPrint(debug_, "search", 2, "tns update required %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "tns update required {}", + vertex->to_string(this)); if (isEndpoint(vertex)) { seedRequired(vertex); // If the endpoint has fanout it's required time @@ -4164,4 +4025,4 @@ Search::wnsSlack(Vertex *vertex, return slacks[path_ap_index]; } -} // namespace +} // namespace sta diff --git a/search/Search.i b/search/Search.i index f83399b5..e6716ae1 100644 --- a/search/Search.i +++ b/search/Search.i @@ -241,7 +241,7 @@ endpoint_slack(const Pin *pin, return sta->units()->timeUnit()->staToUser(delayAsFloat(slack, min_max, sta)); } else { - sta->report()->error(1577, "%s is not a known path group name.", + sta->report()->error(1577, "{} is not a known path group name.", path_group_name); return INF; } @@ -322,7 +322,7 @@ report_loops() Report *report = sta->report(); for (GraphLoop *loop : sta->graphLoops()) { loop->report(sta); - report->reportLineString(""); + report->reportLine(""); } } @@ -436,7 +436,7 @@ set_report_path_field_properties(const char *field_name, if (field) field->setProperties(title, width, left_justify); else - sta->report()->warn(1575, "unknown report path field %s", field_name); + sta->report()->warn(1575, "unknown report path field {}", field_name); } void diff --git a/search/Sim.cc b/search/Sim.cc index 31e7ca8e..bf459097 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Sim.hh" @@ -68,10 +68,7 @@ Sim::Sim(StaState *sta) : { } -Sim::~Sim() -{ - delete observer_; -} +Sim::~Sim() { delete observer_; } void Sim::copyState(const StaState *sta) @@ -92,9 +89,8 @@ Sim::functionSense(const FuncExpr *expr, const Pin *input_pin, const Instance *inst) { - debugPrint(debug_, "sim", 4, "find sense pin %s %s", - network_->pathName(input_pin), - expr->to_string().c_str()); + debugPrint(debug_, "sim", 4, "find sense pin {} {}", network_->pathName(input_pin), + expr->to_string()); bool increasing, decreasing; { LockGuard lock(bdd_lock_); @@ -103,10 +99,10 @@ Sim::functionSense(const FuncExpr *expr, LibertyPort *input_port = network_->libertyPort(input_pin); DdNode *input_node = bdd_.ensureNode(input_port); unsigned int input_index = Cudd_NodeReadIndex(input_node); - increasing = (Cudd_Increasing(cudd_mgr, bdd, input_index) - == Cudd_ReadOne(cudd_mgr)); - decreasing = (Cudd_Decreasing(cudd_mgr, bdd, input_index) - == Cudd_ReadOne(cudd_mgr)); + increasing = + (Cudd_Increasing(cudd_mgr, bdd, input_index) == Cudd_ReadOne(cudd_mgr)); + decreasing = + (Cudd_Decreasing(cudd_mgr, bdd, input_index) == Cudd_ReadOne(cudd_mgr)); Cudd_RecursiveDeref(cudd_mgr, bdd); bdd_.clearVarMap(); } @@ -119,7 +115,7 @@ Sim::functionSense(const FuncExpr *expr, sense = TimingSense::negative_unate; else sense = TimingSense::non_unate; - debugPrint(debug_, "sim", 4, " %s", to_string(sense)); + debugPrint(debug_, "sim", 4, " {}", to_string(sense)); return sense; } @@ -159,16 +155,17 @@ Sim::funcBddSim(const FuncExpr *expr, LogicValue value = simValue(pin); int var_index = Cudd_NodeReadIndex(port_node); switch (value) { - case LogicValue::zero: - bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadLogicZero(cudd_mgr), var_index); - Cudd_Ref(bdd); - break; - case LogicValue::one: - bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadOne(cudd_mgr), var_index); - Cudd_Ref(bdd); - break; - default: - break; + case LogicValue::zero: + bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadLogicZero(cudd_mgr), + var_index); + Cudd_Ref(bdd); + break; + case LogicValue::one: + bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadOne(cudd_mgr), var_index); + Cudd_Ref(bdd); + break; + default: + break; } } } @@ -243,16 +240,14 @@ bool Sim::isConstant(const Vertex *vertex) const { LogicValue value = simValue(vertex); - return value == LogicValue::zero - || value == LogicValue::one; + return value == LogicValue::zero || value == LogicValue::one; } bool Sim::isConstant(const Pin *pin) const { LogicValue value = simValue(pin); - return value == LogicValue::zero - || value == LogicValue::one; + return value == LogicValue::zero || value == LogicValue::one; } TimingSense @@ -283,8 +278,7 @@ Sim::setSimTimingSense(Edge *edge, bool Sim::isDisabledCond(const Edge *edge) const { - return edge->hasDisabledCond() - && edge_disabled_cond_set_.contains(edge); + return edge->hasDisabledCond() && edge_disabled_cond_set_.contains(edge); } //////////////////////////////////////////////////////////////// @@ -356,13 +350,12 @@ Sim::propagateFromInvalidDrvrsToLoads() { for (const Pin *drvr_pin : invalid_drvr_pins_) { LogicValue value = const_func_pins_.contains(drvr_pin) - ? pinConstFuncValue(drvr_pin) - : simValue(drvr_pin); - PinConnectedPinIterator *load_iter=network_->connectedPinIterator(drvr_pin); + ? pinConstFuncValue(drvr_pin) + : simValue(drvr_pin); + PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin)) + if (load_pin != drvr_pin && network_->isLoad(load_pin)) setPinValue(load_pin, value); } delete load_iter; @@ -413,8 +406,7 @@ Sim::recordConstPinFunc(const Pin *pin) if (expr // Tristate outputs do not force the output to be constant. && port->tristateEnable() == nullptr - && (expr->op() == FuncExpr::Op::zero - || expr->op() == FuncExpr::Op::one)) + && (expr->op() == FuncExpr::Op::zero || expr->op() == FuncExpr::Op::one)) const_func_pins_.insert(pin); } } @@ -507,17 +499,15 @@ void Sim::setConstraintConstPins(const LogicValueMap &value_map) { for (const auto [pin, value] : value_map) { - debugPrint(debug_, "sim", 2, "case pin %s = %c", - network_->pathName(pin), + debugPrint(debug_, "sim", 2, "case pin {} = {}", network_->pathName(pin), logicValueString(value)); if (network_->isHierarchical(pin)) { // Set the logic value on pins inside the instance of a hierarchical pin. bool pin_is_output = network_->direction(pin)->isAnyOutput(); - PinConnectedPinIterator *pin_iter=network_->connectedPinIterator(pin); + PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(pin); while (pin_iter->hasNext()) { const Pin *pin1 = pin_iter->next(); - if (network_->isLeaf(pin1) - && network_->direction(pin1)->isAnyInput() + if (network_->isLeaf(pin1) && network_->direction(pin1)->isAnyInput() && ((pin_is_output && !network_->isInside(pin1, pin)) || (!pin_is_output && network_->isInside(pin1, pin)))) setPinValue(pin1, value); @@ -537,8 +527,7 @@ Sim::setConstFuncPins() for (const Pin *pin : const_func_pins_) { LogicValue value = pinConstFuncValue(pin); setPinValue(pin, value); - debugPrint(debug_, "sim", 2, "func pin %s = %c", - network_->pathName(pin), + debugPrint(debug_, "sim", 2, "func pin {} = {}", network_->pathName(pin), logicValueString(value)); } } @@ -565,9 +554,8 @@ Sim::enqueueConstantPinInputs() LogicValue value; const Pin *pin; const_iter->next(pin, value); - debugPrint(debug_, "sim", 2, "network constant pin %s = %c", - network_->pathName(pin), - logicValueString(value)); + debugPrint(debug_, "sim", 2, "network constant pin {} = {}", + network_->pathName(pin), logicValueString(value)); setPinValue(pin, value); } delete const_iter; @@ -587,7 +575,7 @@ Sim::removePropagatedValue(const Pin *pin) if (!exists) { sdc->logicValue(pin, constraint_value, exists); if (!exists) { - debugPrint(debug_, "sim", 2, "pin %s remove prop constant", + debugPrint(debug_, "sim", 2, "pin {} remove prop constant", network_->pathName(pin)); setSimValue(pin, LogicValue::unknown); } @@ -604,17 +592,16 @@ Sim::setPinValue(const Pin *pin, sdc->caseLogicValue(pin, constraint_value, exists); if (!exists) sdc->logicValue(pin, constraint_value, exists); - if (exists - && value != constraint_value) { + if (exists && value != constraint_value) { if (value != LogicValue::unknown) - report_->warn(1521, "propagated logic value %c differs from constraint value of %c on pin %s.", - logicValueString(value), - logicValueString(constraint_value), - sdc_network_->pathName(pin)); + report_->warn( + 1521, + "propagated logic value {} differs from constraint value of {} on pin {}.", + logicValueString(value), logicValueString(constraint_value), + sdc_network_->pathName(pin)); } else { - debugPrint(debug_, "sim", 3, "pin %s = %c", - network_->pathName(pin), + debugPrint(debug_, "sim", 3, "pin {} = {}", network_->pathName(pin), logicValueString(value)); bool value_changed = false; value_changed |= value != simValue(pin); @@ -623,19 +610,16 @@ Sim::setPinValue(const Pin *pin, Instance *inst = network_->instance(pin); instances_to_annotate_.insert(inst); - if (network_->isLeaf(inst) - && network_->direction(pin)->isAnyInput()) { - if (eval_queue_.empty() - || (eval_queue_.back() != inst)) + if (network_->isLeaf(inst) && network_->direction(pin)->isAnyInput()) { + if (eval_queue_.empty() || (eval_queue_.back() != inst)) eval_queue_.push(inst); } else if (network_->isDriver(pin)) { // Enqueue instances with input pins connected to net. - PinConnectedPinIterator *pin_iter=network_->connectedPinIterator(pin); + PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(pin); while (pin_iter->hasNext()) { const Pin *pin1 = pin_iter->next(); - if (pin1 != pin - && network_->isLoad(pin1)) + if (pin1 != pin && network_->isLoad(pin1)) setPinValue(pin1, value); } delete pin_iter; @@ -648,7 +632,7 @@ void Sim::evalInstance(const Instance *inst, bool thru_sequentials) { - debugPrint(debug_, "sim", 2, "eval %s", network_->pathName(inst)); + debugPrint(debug_, "sim", 2, "eval {}", network_->pathName(inst)); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); @@ -664,39 +648,32 @@ Sim::evalInstance(const Instance *inst, if (tri_en_expr) { if (evalExpr(tri_en_expr, inst) == LogicValue::one) { value = evalExpr(expr, inst); - debugPrint(debug_, "sim", 2, " %s tri_en=1 %s = %c", - port->name(), - expr->to_string().c_str(), - logicValueString(value)); + debugPrint(debug_, "sim", 2, " {} tri_en=1 {} = {}", port->name(), + expr->to_string(), logicValueString(value)); } } else { LibertyPort *expr_port = expr->port(); - Sequential *sequential = (thru_sequentials && expr_port) - ? cell->outputPortSequential(expr_port) - : nullptr; + Sequential *sequential = (thru_sequentials && expr_port) + ? cell->outputPortSequential(expr_port) + : nullptr; if (sequential) { value = evalExpr(sequential->data(), inst); if (expr_port == sequential->outputInv()) value = logicNot(value); - debugPrint(debug_, "sim", 2, " %s seq %s = %c", - port->name(), - expr->to_string().c_str(), - logicValueString(value)); + debugPrint(debug_, "sim", 2, " {} seq {} = {}", port->name(), + expr->to_string(), logicValueString(value)); } else { value = evalExpr(expr, inst); - debugPrint(debug_, "sim", 2, " %s %s = %c", - port->name(), - expr->to_string().c_str(), - logicValueString(value)); + debugPrint(debug_, "sim", 2, " {} {} = {}", port->name(), + expr->to_string(), logicValueString(value)); } } } else if (port->isClockGateOut()) { value = clockGateOutValue(inst); - debugPrint(debug_, "sim", 2, " %s gated_clk = %c", - port->name(), + debugPrint(debug_, "sim", 2, " {} gated_clk = {}", port->name(), logicValueString(value)); } if (value != simValue(pin)) @@ -714,11 +691,9 @@ Sim::clockGateOutValue(const Instance *inst) LibertyCellPortIterator port_iter(cell); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); - if (port->isClockGateClock() - || port->isClockGateEnable()) { + if (port->isClockGateClock() || port->isClockGateEnable()) { Pin *gclk_pin = network_->findPin(inst, port); - if (gclk_pin - && simValue(gclk_pin) == LogicValue::zero) + if (gclk_pin && simValue(gclk_pin) == LogicValue::zero) return LogicValue::zero; } } @@ -932,15 +907,15 @@ Sim::isDisabledMode(Edge *edge, void Sim::findDisabledEdges() { - for (const Instance *inst : instances_to_annotate_) - findDisabledEdges(inst); - instances_to_annotate_.clear(); + for (const Instance *inst : instances_to_annotate_) + findDisabledEdges(inst); + instances_to_annotate_.clear(); } void Sim::findDisabledEdges(const Instance *inst) { - debugPrint(debug_, "sim", 4, "annotate %s", network_->pathName(inst)); + debugPrint(debug_, "sim", 4, "annotate {}", network_->pathName(inst)); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); @@ -974,9 +949,9 @@ Sim::findDisabledEdges(const Instance *inst, if (sense != TimingSense::none) // Disable conditional timing edges based on constant pins. is_disabled_cond = isDisabledCond(edge, inst, from_pin, pin) - // Disable mode conditional timing - // edges based on constant pins. - || isDisabledMode(edge,inst); + // Disable mode conditional timing + // edges based on constant pins. + || isDisabledMode(edge, inst); bool disables_changed = false; if (sense != simTimingSense(edge)) { @@ -997,4 +972,4 @@ Sim::findDisabledEdges(const Instance *inst, observer_->faninEdgesChangeAfter(vertex->pin()); } -} // namespace +} // namespace sta diff --git a/search/Sta.cc b/search/Sta.cc index d5d58bbb..355ef3f0 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Sta.hh" @@ -29,6 +29,7 @@ #include #include "Machine.hh" +#include "Format.hh" #include "ContainerHelpers.hh" #include "DispatchQueue.hh" #include "ReportTcl.hh" @@ -95,13 +96,13 @@ namespace sta { static bool libertyPortCapsEqual(const LibertyPort *port1, - const LibertyPort *port2); + const LibertyPort *port2); static bool hasDisabledArcs(Edge *edge, const Mode *mode); static InstanceSet pinInstances(PinSet &pins, - const Network *network); + const Network *network); //////////////////////////////////////////////////////////////// // @@ -201,7 +202,8 @@ StaSimObserver::fanoutEdgesChangeAfter(const Pin *pin) class StaLevelizeObserver : public LevelizeObserver { public: - StaLevelizeObserver(Search *search, GraphDelayCalc *graph_delay_calc); + StaLevelizeObserver(Search *search, + GraphDelayCalc *graph_delay_calc); void levelsChangedBefore() override; void levelChangedBefore(Vertex *vertex) override; @@ -583,8 +585,7 @@ Sta::setCmdMode(const std::string &mode_name) { if (!mode_name.empty()) { if (!mode_name_map_.contains(mode_name)) { - if (modes_.size() == 1 - && modes_[0]->name() == "default") { + if (modes_.size() == 1 && modes_[0]->name() == "default") { // No need for default mode if one is defined. delete modes_[0]; mode_name_map_.clear(); @@ -596,8 +597,7 @@ Sta::setCmdMode(const std::string &mode_name) mode->sim()->setMode(mode); mode->sim()->setObserver(new StaSimObserver(this)); - if (scenes_.size() == 1 - && scenes_[0]->name() == "default") + if (scenes_.size() == 1 && scenes_[0]->name() == "default") scenes_[0]->setMode(mode); updateComponentsState(); } @@ -665,12 +665,12 @@ Sta::setCmdNamespace1(CmdNamespace namespc) { cmd_namespace_ = namespc; switch (cmd_namespace_) { - case CmdNamespace::sta: - cmd_network_ = network_; - break; - case CmdNamespace::sdc: - cmd_network_ = sdc_network_; - break; + case CmdNamespace::sta: + cmd_network_ = network_; + break; + case CmdNamespace::sdc: + cmd_network_ = sdc_network_; + break; } } @@ -694,12 +694,11 @@ Sta::setCurrentInstance(Instance *inst) LibertyLibrary * Sta::readLiberty(const char *filename, Scene *scene, - const MinMaxAll *min_max, - bool infer_latches) + const MinMaxAll *min_max, + bool infer_latches) { Stats stats(debug_, report_); - LibertyLibrary *library = readLibertyFile(filename, scene, min_max, - infer_latches); + LibertyLibrary *library = readLibertyFile(filename, scene, min_max, infer_latches); if (library // The default library is the first library read. // This corresponds to a link_path of '*'. @@ -715,11 +714,10 @@ Sta::readLiberty(const char *filename, LibertyLibrary * Sta::readLibertyFile(const char *filename, Scene *scene, - const MinMaxAll *min_max, - bool infer_latches) + const MinMaxAll *min_max, + bool infer_latches) { - LibertyLibrary *liberty = sta::readLibertyFile(filename, infer_latches, - network_); + LibertyLibrary *liberty = sta::readLibertyFile(filename, infer_latches, network_); if (liberty) { // Don't map liberty cells if they are redefined by reading another // library with the same cell names. @@ -736,7 +734,7 @@ Sta::readLibertyFile(const char *filename, LibertyLibrary * Sta::readLibertyFile(const char *filename, - bool infer_latches) + bool infer_latches) { return sta::readLibertyFile(filename, infer_latches, network_); } @@ -744,11 +742,11 @@ Sta::readLibertyFile(const char *filename, void Sta::readLibertyAfter(LibertyLibrary *liberty, Scene *scene, - const MinMax *min_max) + const MinMax *min_max) { scene->addLiberty(liberty, min_max); - LibertyLibrary::makeSceneMap(liberty, scene->libertyIndex(min_max), - network_, report_); + LibertyLibrary::makeSceneMap(liberty, scene->libertyIndex(min_max), network_, + report_); } bool @@ -780,9 +778,7 @@ Sta::linkDesign(const char *top_cell_name, { clear(); Stats stats(debug_, report_); - bool status = network_->linkNetwork(top_cell_name, - make_black_boxes, - report_); + bool status = network_->linkNetwork(top_cell_name, make_black_boxes, report_); stats.report("Link"); return status; } @@ -791,7 +787,7 @@ Sta::linkDesign(const char *top_cell_name, void Sta::setDebugLevel(const char *what, - int level) + int level) { debug_->setLevel(what, level); } @@ -837,9 +833,9 @@ Sta::pvt(Instance *inst, void Sta::setPvt(Instance *inst, - const MinMaxAll *min_max, - float process, - float voltage, + const MinMaxAll *min_max, + float process, + float voltage, float temperature, Sdc *sdc) { @@ -849,7 +845,7 @@ Sta::setPvt(Instance *inst, void Sta::setPvt(const Instance *inst, - const MinMaxAll *min_max, + const MinMaxAll *min_max, const Pvt &pvt, Sdc *sdc) { @@ -876,9 +872,9 @@ Sta::setVoltage(const Net *net, void Sta::setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, float derate, Sdc *sdc) { @@ -890,9 +886,9 @@ Sta::setTimingDerate(TimingDerateType type, void Sta::setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, float derate, Sdc *sdc) { @@ -904,10 +900,10 @@ Sta::setTimingDerate(const Net *net, void Sta::setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, float derate, Sdc *sdc) { @@ -919,10 +915,10 @@ Sta::setTimingDerate(const Instance *inst, void Sta::setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, float derate, Sdc *sdc) { @@ -943,8 +939,8 @@ Sta::unsetTimingDerate(Sdc *sdc) void Sta::setInputSlew(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, + const RiseFallBoth *rf, + const MinMaxAll *min_max, float slew, Sdc *sdc) { @@ -954,24 +950,24 @@ Sta::setInputSlew(const Port *port, void Sta::setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const Port *port, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, + const LibertyCell *cell, + const Port *port, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, const MinMaxAll *min_max, Sdc *sdc) { - sdc->setDriveCell(library, cell, port, from_port, from_slews, to_port, - rf, min_max); + sdc->setDriveCell(library, cell, port, from_port, from_slews, to_port, rf, + min_max); delaysInvalidFrom(port); } void Sta::setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, + const RiseFallBoth *rf, + const MinMaxAll *min_max, float res, Sdc *sdc) { @@ -1016,7 +1012,7 @@ Sta::setMinPulseWidth(const RiseFallBoth *rf, void Sta::setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, + const RiseFallBoth *rf, float min_width, Sdc *sdc) { @@ -1025,7 +1021,7 @@ Sta::setMinPulseWidth(const Pin *pin, void Sta::setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, + const RiseFallBoth *rf, float min_width, Sdc *sdc) { @@ -1034,7 +1030,7 @@ Sta::setMinPulseWidth(const Instance *inst, void Sta::setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, + const RiseFallBoth *rf, float min_width, Sdc *sdc) { @@ -1069,9 +1065,9 @@ Sta::setWireloadSelection(WireloadSelection *selection, void Sta::setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, + const RiseFallBoth *rf, + const PathClkOrData clk_data, + const MinMax *min_max, float slew, Sdc *sdc) { @@ -1080,7 +1076,7 @@ Sta::setSlewLimit(Clock *clk, void Sta::setSlewLimit(Port *port, - const MinMax *min_max, + const MinMax *min_max, float slew, Sdc *sdc) { @@ -1089,7 +1085,7 @@ Sta::setSlewLimit(Port *port, void Sta::setSlewLimit(Cell *cell, - const MinMax *min_max, + const MinMax *min_max, float slew, Sdc *sdc) { @@ -1098,7 +1094,7 @@ Sta::setSlewLimit(Cell *cell, void Sta::setCapacitanceLimit(Cell *cell, - const MinMax *min_max, + const MinMax *min_max, float cap, Sdc *sdc) { @@ -1107,7 +1103,7 @@ Sta::setCapacitanceLimit(Cell *cell, void Sta::setCapacitanceLimit(Port *port, - const MinMax *min_max, + const MinMax *min_max, float cap, Sdc *sdc) { @@ -1116,7 +1112,7 @@ Sta::setCapacitanceLimit(Port *port, void Sta::setCapacitanceLimit(Pin *pin, - const MinMax *min_max, + const MinMax *min_max, float cap, Sdc *sdc) { @@ -1125,7 +1121,7 @@ Sta::setCapacitanceLimit(Pin *pin, void Sta::setFanoutLimit(Cell *cell, - const MinMax *min_max, + const MinMax *min_max, float fanout, Sdc *sdc) { @@ -1134,7 +1130,7 @@ Sta::setFanoutLimit(Cell *cell, void Sta::setFanoutLimit(Port *port, - const MinMax *min_max, + const MinMax *min_max, float fanout, Sdc *sdc) { @@ -1150,10 +1146,10 @@ Sta::setMaxArea(float area, void Sta::makeClock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, char *comment, const Mode *mode) { @@ -1166,25 +1162,23 @@ Sta::makeClock(const char *name, void Sta::makeGeneratedClock(const char *name, - PinSet *pins, - bool add_to_pins, - Pin *src_pin, - Clock *master_clk, - int divide_by, - int multiply_by, - float duty_cycle, - bool invert, - bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + PinSet *pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + IntSeq *edges, + FloatSeq *edge_shifts, char *comment, const Mode *mode) { - mode->sdc()->makeGeneratedClock(name, pins, add_to_pins, - src_pin, master_clk, - divide_by, multiply_by, duty_cycle, - invert, combinational, - edges, edge_shifts, comment); + mode->sdc()->makeGeneratedClock(name, pins, add_to_pins, src_pin, master_clk, + divide_by, multiply_by, duty_cycle, invert, + combinational, edges, edge_shifts, comment); update_genclks_ = true; search_->arrivalsInvalid(); power_->activitiesInvalid(); @@ -1245,8 +1239,8 @@ Sta::removePropagatedClock(Pin *pin, void Sta::setClockSlew(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, + const RiseFallBoth *rf, + const MinMaxAll *min_max, float slew, Sdc *sdc) { @@ -1272,9 +1266,9 @@ Sta::clockSlewChanged(Clock *clk) void Sta::setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, float delay, Sdc *sdc) { @@ -1293,10 +1287,10 @@ Sta::removeClockLatency(const Clock *clk, void Sta::setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, float delay, Sdc *sdc) { @@ -1315,8 +1309,8 @@ Sta::removeClockInsertion(const Clock *clk, void Sta::setClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold, - float uncertainty) + const SetupHoldAll *setup_hold, + float uncertainty) { clk->setUncertainty(setup_hold, uncertainty); search_->arrivalsInvalid(); @@ -1324,7 +1318,7 @@ Sta::setClockUncertainty(Clock *clk, void Sta::removeClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold) { clk->removeUncertainty(setup_hold); search_->arrivalsInvalid(); @@ -1332,7 +1326,7 @@ Sta::removeClockUncertainty(Clock *clk, void Sta::setClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold, + const SetupHoldAll *setup_hold, float uncertainty, Sdc *sdc) { @@ -1351,23 +1345,23 @@ Sta::removeClockUncertainty(Pin *pin, void Sta::setClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, float uncertainty, Sdc *sdc) { - sdc->setClockUncertainty(from_clk, from_rf, to_clk, to_rf, - setup_hold, uncertainty); + sdc->setClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold, + uncertainty); search_->arrivalsInvalid(); } void Sta::removeClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold, Sdc *sdc) { @@ -1376,26 +1370,31 @@ Sta::removeClockUncertainty(Clock *from_clk, } ClockGroups * -Sta::makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, +Sta::makeClockGroups(const std::string &name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, const char *comment, Sdc *sdc) { - ClockGroups *groups = sdc->makeClockGroups(name, - logically_exclusive, - physically_exclusive, - asynchronous, - allow_paths, - comment); + ClockGroups *groups = sdc->makeClockGroups(name, logically_exclusive, + physically_exclusive, + asynchronous, allow_paths, + comment); search_->requiredsInvalid(); return groups; } void -Sta::removeClockGroupsLogicallyExclusive(const char *name, +Sta::removeClockGroupsLogicallyExclusive(Sdc *sdc) +{ + sdc->removeClockGroupsLogicallyExclusive(); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsLogicallyExclusive(const std::string &name, Sdc *sdc) { sdc->removeClockGroupsLogicallyExclusive(name); @@ -1403,7 +1402,14 @@ Sta::removeClockGroupsLogicallyExclusive(const char *name, } void -Sta::removeClockGroupsPhysicallyExclusive(const char *name, +Sta::removeClockGroupsPhysicallyExclusive(Sdc *sdc) +{ + sdc->removeClockGroupsPhysicallyExclusive(); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsPhysicallyExclusive(const std::string &name, Sdc *sdc) { sdc->removeClockGroupsPhysicallyExclusive(name); @@ -1411,7 +1417,14 @@ Sta::removeClockGroupsPhysicallyExclusive(const char *name, } void -Sta::removeClockGroupsAsynchronous(const char *name, +Sta::removeClockGroupsAsynchronous(Sdc *sdc) +{ + sdc->removeClockGroupsAsynchronous(); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsAsynchronous(const std::string &name, Sdc *sdc) { sdc->removeClockGroupsAsynchronous(name); @@ -1428,7 +1441,7 @@ Sta::makeClockGroup(ClockGroups *clk_groups, void Sta::setClockSense(PinSet *pins, - ClockSet *clks, + ClockSet *clks, ClockSense sense, Sdc *sdc) { @@ -1440,7 +1453,7 @@ Sta::setClockSense(PinSet *pins, void Sta::setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, + const SetupHold *setup_hold, float margin, Sdc *sdc) { @@ -1450,8 +1463,8 @@ Sta::setClockGatingCheck(const RiseFallBoth *rf, void Sta::setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, + const RiseFallBoth *rf, + const SetupHold *setup_hold, float margin, Sdc *sdc) { @@ -1461,48 +1474,48 @@ Sta::setClockGatingCheck(Clock *clk, void Sta::setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, LogicValue active_value, Sdc *sdc) { - sdc->setClockGatingCheck(inst, rf, setup_hold, margin,active_value); + sdc->setClockGatingCheck(inst, rf, setup_hold, margin, active_value); search_->arrivalsInvalid(); } void Sta::setClockGatingCheck(Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, LogicValue active_value, Sdc *sdc) { - sdc->setClockGatingCheck(pin, rf, setup_hold, margin,active_value); + sdc->setClockGatingCheck(pin, rf, setup_hold, margin, active_value); search_->arrivalsInvalid(); } void Sta::setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, float margin, Sdc *sdc) { - sdc->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold,margin); + sdc->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin); search_->requiredInvalid(to); } void Sta::removeDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, const SetupHoldAll *setup_hold, Sdc *sdc) { @@ -1533,7 +1546,7 @@ Sta::removeDisable(Pin *pin, void Sta::disable(Instance *inst, - LibertyPort *from, + LibertyPort *from, LibertyPort *to, Sdc *sdc) { @@ -1559,7 +1572,7 @@ Sta::disable(Instance *inst, void Sta::removeDisable(Instance *inst, - LibertyPort *from, + LibertyPort *from, LibertyPort *to, Sdc *sdc) { @@ -1585,7 +1598,7 @@ Sta::removeDisable(Instance *inst, void Sta::disable(LibertyCell *cell, - LibertyPort *from, + LibertyPort *from, LibertyPort *to, Sdc *sdc) { @@ -1595,7 +1608,7 @@ Sta::disable(LibertyCell *cell, void Sta::removeDisable(LibertyCell *cell, - LibertyPort *from, + LibertyPort *from, LibertyPort *to, Sdc *sdc) { @@ -1687,18 +1700,15 @@ Sta::disabledEdges(const Mode *mode) VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - if (isDisabledConstant(edge, mode) - || isDisabledCondDefault(edge) - || isDisabledConstraint(edge, sdc) - || edge->isDisabledLoop() - || isDisabledPresetClr(edge)) - disabled_edges.push_back(edge); + if (isDisabledConstant(edge, mode) || isDisabledCondDefault(edge) + || isDisabledConstraint(edge, sdc) || edge->isDisabledLoop() + || isDisabledPresetClr(edge)) + disabled_edges.push_back(edge); } } return disabled_edges; } - EdgeSeq Sta::disabledEdgesSorted(const Mode *mode) { @@ -1713,9 +1723,8 @@ Sta::isDisabledConstraint(Edge *edge, { Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); - return sdc->isDisabledConstraint(from_pin) - || sdc->isDisabledConstraint(to_pin) - || sdc->isDisabledConstraint(edge); + return sdc->isDisabledConstraint(from_pin) || sdc->isDisabledConstraint(to_pin) + || sdc->isDisabledConstraint(edge); } bool @@ -1739,12 +1748,10 @@ Sta::isDisabledConstant(Edge *edge, Vertex *to_vertex = edge->to(graph_); Pin *to_pin = to_vertex->pin(); const Instance *inst = network_->instance(from_pin); - return sim->isConstant(from_vertex) - || sim->isConstant(to_vertex) - || (!role->isWire() - && (sim->isDisabledCond(edge, inst, from_pin, to_pin) - || sim->isDisabledMode(edge, inst) - || hasDisabledArcs(edge, mode))); + return sim->isConstant(from_vertex) || sim->isConstant(to_vertex) + || (!role->isWire() + && (sim->isDisabledCond(edge, inst, from_pin, to_pin) + || sim->isDisabledMode(edge, inst) || hasDisabledArcs(edge, mode))); } static bool @@ -1786,8 +1793,7 @@ Sta::disabledConstantPins(Edge *edge, const Instance *inst = network_->instance(to_pin); bool is_disabled; FuncExpr *disable_cond; - sim->isDisabledCond(edge, inst, from_pin, to_pin, - is_disabled, disable_cond); + sim->isDisabledCond(edge, inst, from_pin, to_pin, is_disabled, disable_cond); if (is_disabled) exprConstantPins(disable_cond, inst, mode, pins); sim->isDisabledMode(edge, inst, is_disabled, disable_cond); @@ -1796,9 +1802,8 @@ Sta::disabledConstantPins(Edge *edge, if (hasDisabledArcs(edge, mode)) { LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { - FuncExpr *func = to_port->function(); - if (func - && sim->functionSense(inst, from_pin, to_pin) != edge->sense()) + FuncExpr *func = to_port->function(); + if (func && sim->functionSense(inst, from_pin, to_pin) != edge->sense()) exprConstantPins(func, inst, mode, pins); } } @@ -1829,7 +1834,7 @@ Sta::exprConstantPins(FuncExpr *expr, if (pin) { LogicValue value = mode->sim()->simValue(pin); if (value != LogicValue::unknown) - pins.insert(pin); + pins.insert(pin); } } } @@ -1837,15 +1842,14 @@ Sta::exprConstantPins(FuncExpr *expr, bool Sta::isDisabledBidirectInstPath(Edge *edge) const { - return !variables_->bidirectInstPathsEnabled() - && edge->isBidirectInstPath(); + return !variables_->bidirectInstPathsEnabled() && edge->isBidirectInstPath(); } bool Sta::isDisabledPresetClr(Edge *edge) const { return !variables_->presetClrArcsEnabled() - && edge->role() == TimingRole::regSetClr(); + && edge->role() == TimingRole::regSetClr(); } void @@ -1930,29 +1934,28 @@ Sta::removeCaseAnalysis(Pin *pin, void Sta::setInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, float delay, Sdc *sdc) { - sdc->setInputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); + sdc->setInputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included, + network_latency_included, min_max, add, delay); search_->arrivalInvalid(pin); } -void +void Sta::removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, const MinMaxAll *min_max, Sdc *sdc) { @@ -1962,28 +1965,27 @@ Sta::removeInputDelay(const Pin *pin, void Sta::setOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, float delay, Sdc *sdc) { - sdc->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included,network_latency_included, - min_max, add, delay); + sdc->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included, + network_latency_included, min_max, add, delay); search_->requiredInvalid(pin); } -void +void Sta::removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, const MinMaxAll *min_max, Sdc *sdc) { @@ -1993,9 +1995,9 @@ Sta::removeOutputDelay(const Pin *pin, void Sta::makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, const char *comment, Sdc *sdc) { @@ -2005,42 +2007,40 @@ Sta::makeFalsePath(ExceptionFrom *from, void Sta::makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, const char *comment, Sdc *sdc) { - sdc->makeMulticyclePath(from, thrus, to, min_max, - use_end_clk, path_multiplier, - comment); + sdc->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, path_multiplier, + comment); search_->arrivalsInvalid(); } void Sta::makePathDelay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, bool break_path, - float delay, + float delay, const char *comment, Sdc *sdc) { - sdc->makePathDelay(from, thrus, to, min_max, - ignore_clk_latency, break_path, - delay, comment); + sdc->makePathDelay(from, thrus, to, min_max, ignore_clk_latency, break_path, delay, + comment); search_->endpointsInvalid(); search_->arrivalsInvalid(); } void Sta::resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, + ExceptionThruSeq *thrus, + ExceptionTo *to, const MinMaxAll *min_max, Sdc *sdc) { @@ -2049,11 +2049,11 @@ Sta::resetPath(ExceptionFrom *from, } void -Sta::makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, +Sta::makeGroupPath(const std::string &name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, const char *comment, Sdc *sdc) { @@ -2072,12 +2072,11 @@ bool Sta::isPathGroupName(const char *group_name, const Sdc *sdc) const { - return sdc->findClock(group_name) - || sdc->isGroupPathName(group_name) - || stringEq(group_name, PathGroups::asyncPathGroupName()) - || stringEq(group_name, PathGroups::pathDelayGroupName()) - || stringEq(group_name, PathGroups::gatedClkGroupName()) - || stringEq(group_name, PathGroups::unconstrainedGroupName()); + return sdc->findClock(group_name) || sdc->isGroupPathName(group_name) + || stringEq(group_name, PathGroups::asyncPathGroupName()) + || stringEq(group_name, PathGroups::pathDelayGroupName()) + || stringEq(group_name, PathGroups::gatedClkGroupName()) + || stringEq(group_name, PathGroups::unconstrainedGroupName()); } StringSeq @@ -2099,8 +2098,8 @@ Sta::pathGroupNames(const Sdc *sdc) const ExceptionFrom * Sta::makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, + ClockSet *from_clks, + InstanceSet *from_insts, const RiseFallBoth *from_rf, const Sdc *sdc) { @@ -2109,7 +2108,7 @@ Sta::makeExceptionFrom(PinSet *from_pins, void Sta::checkExceptionFromPins(ExceptionFrom *from, - const char *file, + const char *file, int line, const Sdc *sdc) const { @@ -2118,16 +2117,16 @@ Sta::checkExceptionFromPins(ExceptionFrom *from, if (pins) { for (const Pin *pin : *pins) { if (!sdc->isExceptionStartpoint(pin)) { - if (line) - report_->fileWarn(1554, file, line, "'%s' is not a valid start point.", - cmd_network_->pathName(pin)); - else - report_->warn(1550, "'%s' is not a valid start point.", - cmd_network_->pathName(pin)); + if (line) + report_->fileWarn(1554, file, line, "'{}' is not a valid start point.", + cmd_network_->pathName(pin)); + else + report_->warn(1550, "'{}' is not a valid start point.", + cmd_network_->pathName(pin)); + } } } } - } } void @@ -2138,8 +2137,8 @@ Sta::deleteExceptionFrom(ExceptionFrom *from) ExceptionThru * Sta::makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, + NetSet *nets, + InstanceSet *insts, const RiseFallBoth *rf, const Sdc *sdc) { @@ -2154,9 +2153,9 @@ Sta::deleteExceptionThru(ExceptionThru *thru) ExceptionTo * Sta::makeExceptionTo(PinSet *to_pins, - ClockSet *to_clks, - InstanceSet *to_insts, - const RiseFallBoth *rf, + ClockSet *to_clks, + InstanceSet *to_insts, + const RiseFallBoth *rf, const RiseFallBoth *end_rf, const Sdc *sdc) { @@ -2171,7 +2170,7 @@ Sta::deleteExceptionTo(ExceptionTo *to) void Sta::checkExceptionToPins(ExceptionTo *to, - const char *file, + const char *file, int line, const Sdc *sdc) const { @@ -2180,30 +2179,30 @@ Sta::checkExceptionToPins(ExceptionTo *to, if (pins) { for (const Pin *pin : *pins) { if (!sdc->isExceptionEndpoint(pin)) { - if (line) - report_->fileWarn(1551, file, line, "'%s' is not a valid endpoint.", - cmd_network_->pathName(pin)); - else - report_->warn(1552, "'%s' is not a valid endpoint.", - cmd_network_->pathName(pin)); + if (line) + report_->fileWarn(1551, file, line, "'{}' is not a valid endpoint.", + cmd_network_->pathName(pin)); + else + report_->warn(1552, "'{}' is not a valid endpoint.", + cmd_network_->pathName(pin)); + } } } } - } } void Sta::writeSdc(const Sdc *sdc, const char *filename, - bool leaf, - bool native, - int digits, + bool leaf, + bool native, + int digits, bool gzip, - bool no_timestamp) + bool no_timestamp) { ensureLibLinked(); - sta::writeSdc(sdc, network_->topInstance(), filename, "write_sdc", - leaf, native, digits, gzip, no_timestamp); + sta::writeSdc(sdc, network_->topInstance(), filename, "write_sdc", leaf, native, + digits, gzip, no_timestamp); } //////////////////////////////////////////////////////////////// @@ -2211,12 +2210,12 @@ Sta::writeSdc(const Sdc *sdc, CheckErrorSeq & Sta::checkTiming(const Mode *mode, bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks) + bool no_output_delay, + bool reg_multiple_clks, + bool reg_no_clks, + bool unconstrained_endpoints, + bool loops, + bool generated_clks) { if (unconstrained_endpoints) { // Only need non-clock arrivals to find unconstrained_endpoints. @@ -2230,9 +2229,8 @@ Sta::checkTiming(const Mode *mode, mode->clkNetwork()->ensureClkNetwork(); } return check_timing_->check(mode, no_input_delay, no_output_delay, - reg_multiple_clks, reg_no_clks, - unconstrained_endpoints, - loops, generated_clks); + reg_multiple_clks, reg_no_clks, + unconstrained_endpoints, loops, generated_clks); } //////////////////////////////////////////////////////////////// @@ -2261,8 +2259,7 @@ void Sta::setCrprMode(CrprMode mode) { // Pessimism is only relevant for on_chip_variation analysis. - if (variables_->crprEnabled() - && variables_->crprMode() != mode) + if (variables_->crprEnabled() && variables_->crprMode() != mode) search_->arrivalsInvalid(); variables_->setCrprMode(mode); } @@ -2378,7 +2375,7 @@ bool Sta::recoveryRemovalChecksEnabled() const { return variables_->recoveryRemovalChecksEnabled(); -} +} void Sta::setRecoveryRemovalChecksEnabled(bool enabled) @@ -2529,10 +2526,9 @@ Sta::makeScene(const std::string &name, parasitics_min = findParasitics(spef_min_file); parasitics_max = findParasitics(spef_max_file); if (parasitics_min == nullptr) - report_->error(1558, "Spef file %s not found.", spef_min_file.c_str()); - if (parasitics_max == nullptr - && spef_max_file != spef_min_file) - report_->error(1559, "Spef file %s not found.", spef_max_file.c_str()); + report_->error(1558, "Spef file {} not found.", spef_min_file); + if (parasitics_max == nullptr && spef_max_file != spef_min_file) + report_->error(1559, "Spef file {} not found.", spef_max_file); } mode->sdc()->makeSceneBefore(); @@ -2544,7 +2540,7 @@ Sta::makeScene(const std::string &name, cmd_scene_ = scene; } else - report_->error(1572, "mode %s not found.", mode_name.c_str()); + report_->error(1572, "mode {} not found.", mode_name); } Scene * @@ -2576,12 +2572,11 @@ Sta::makeScene(const std::string &name, Parasitics *parasitics_min, Parasitics *parasitics_max) { - if (scenes_.size() == 1 - && findScene("default")) + if (scenes_.size() == 1 && findScene("default")) deleteScenes(); - Scene *scene = new Scene(name, scenes_.size(), mode, - parasitics_min, parasitics_max); + Scene *scene = + new Scene(name, scenes_.size(), mode, parasitics_min, parasitics_max); scene_name_map_[name] = scene; scenes_.push_back(scene); mode->addScene(scene); @@ -2628,18 +2623,17 @@ Sta::updateSceneLiberty(Scene *scene, { StringSet warned_files; for (const MinMax *min_max : MinMax::range()) { - const StringSeq &liberty_files = min_max == MinMax::min() - ? liberty_min_files - : liberty_max_files; + const StringSeq &liberty_files = + min_max == MinMax::min() ? liberty_min_files : liberty_max_files; for (const std::string &lib_file : liberty_files) { LibertyLibrary *lib = network_->findLiberty(lib_file.c_str()); - if (lib == nullptr) + if (lib == nullptr) lib = network_->findLibertyFilename(lib_file.c_str()); if (lib) - LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), - network_, report_); + LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), network_, + report_); else if (!warned_files.contains(lib_file)) { - report_->warn(1555, "liberty name/filename %s not found.", lib_file.c_str()); + report_->warn(1555, "liberty name/filename {} not found.", lib_file); warned_files.insert(lib_file); } } @@ -2654,8 +2648,8 @@ Sta::updateLibertyScenes() while (iter->hasNext()) { LibertyLibrary *lib = iter->next(); for (const MinMax *min_max : MinMax::range()) { - LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), - network_, report_); + LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), network_, + report_); } } } @@ -2691,36 +2685,33 @@ Sta::makeSceneSeq(Scene *scene) const // PathEnds are owned by Search PathGroups and deleted on next call. PathEndSeq Sta::findPathEnds(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, const SceneSeq &scenes, - const MinMaxAll *min_max, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - bool sort_by_slack, + const MinMaxAll *min_max, + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool sort_by_slack, StringSeq &group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold) { searchPreamble(); clk_skews_->clear(); - return search_->findPathEnds(from, thrus, to, unconstrained, - scenes, min_max, group_path_count, - endpoint_path_count, - unique_pins, unique_edges, slack_min, slack_max, - sort_by_slack, group_names, - setup, hold, - recovery, removal, - clk_gating_setup, clk_gating_hold); + return search_->findPathEnds(from, thrus, to, unconstrained, scenes, min_max, + group_path_count, endpoint_path_count, unique_pins, + unique_edges, slack_min, slack_max, sort_by_slack, + group_names, setup, hold, recovery, removal, + clk_gating_setup, clk_gating_hold); } //////////////////////////////////////////////////////////////// @@ -2824,23 +2815,21 @@ Sta::updateTiming(bool full) void Sta::reportClkSkew(ConstClockSeq &clks, const SceneSeq &scenes, - const SetupHold *setup_hold, + const SetupHold *setup_hold, bool include_internal_latency, - int digits) + int digits) { clkSkewPreamble(); - clk_skews_->reportClkSkew(clks, scenes, setup_hold, - include_internal_latency, digits); + clk_skews_->reportClkSkew(clks, scenes, setup_hold, include_internal_latency, + digits); } Delay Sta::findWorstClkSkew(const SetupHold *setup_hold, bool include_internal_latency) { - clkSkewPreamble(); - return clk_skews_->findWorstClkSkew(scenes_, setup_hold, - include_internal_latency); + return clk_skews_->findWorstClkSkew(scenes_, setup_hold, include_internal_latency); } void @@ -2944,15 +2933,15 @@ Sta::findRequireds() Path * Sta::vertexWorstArrivalPath(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { return vertexWorstArrivalPath(vertex, nullptr, min_max); } Path * Sta::vertexWorstArrivalPath(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) + const RiseFall *rf, + const MinMax *min_max) { Path *worst_path = nullptr; Arrival worst_arrival = min_max->initValue(); @@ -2961,7 +2950,7 @@ Sta::vertexWorstArrivalPath(Vertex *vertex, Path *path = path_iter.next(); Arrival arrival = path->arrival(); if (!path->tag(this)->isGenClkSrcPath() - && delayGreater(arrival, worst_arrival, min_max, this)) { + && delayGreater(arrival, worst_arrival, min_max, this)) { worst_arrival = arrival; worst_path = path; } @@ -2989,7 +2978,7 @@ Sta::vertexWorstRequiredPath(Vertex *vertex, Path *path = path_iter.next(); const Required path_req = path->required(); if (!path->tag(this)->isGenClkSrcPath() - && delayGreater(path_req, worst_req, req_min_max, this)) { + && delayGreater(path_req, worst_req, req_min_max, this)) { worst_req = path_req; worst_path = path; } @@ -2999,8 +2988,8 @@ Sta::vertexWorstRequiredPath(Vertex *vertex, Path * Sta::vertexWorstSlackPath(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) + const RiseFall *rf, + const MinMax *min_max) { Path *worst_path = nullptr; Slack min_slack = MinMax::min()->initValue(); @@ -3008,8 +2997,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack slack = path->slack(this); - if (!path->tag(this)->isGenClkSrcPath() - && delayLess(slack, min_slack, this)) { + if (!path->tag(this)->isGenClkSrcPath() && delayLess(slack, min_slack, this)) { min_slack = slack; worst_path = path; } @@ -3019,7 +3007,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, Path * Sta::vertexWorstSlackPath(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { return vertexWorstSlackPath(vertex, nullptr, min_max); @@ -3028,7 +3016,7 @@ Sta::vertexWorstSlackPath(Vertex *vertex, Arrival Sta::arrival(const Pin *pin, const RiseFallBoth *rf, - const MinMax *min_max) + const MinMax *min_max) { Vertex *vertex, *bidirect_vertex; graph_->pinVertices(pin, vertex, bidirect_vertex); @@ -3047,7 +3035,7 @@ Arrival Sta::arrival(Vertex *vertex, const RiseFallBoth *rf, const SceneSeq &scenes, - const MinMax *min_max) + const MinMax *min_max) { searchPreamble(); search_->findArrivals(vertex->level()); @@ -3061,9 +3049,8 @@ Sta::arrival(Vertex *vertex, if (!clk_info->isGenClkSrcPath() && (rf == RiseFallBoth::riseFall() || path->transition(this)->asRiseFallBoth() == rf) - && path->minMax(this) == min_max - && scenes_set.contains(path->scene(this)) - && delayGreater(path->arrival(), arrival, min_max, this)) + && path->minMax(this) == min_max && scenes_set.contains(path->scene(this)) + && delayGreater(path->arrival(), arrival, min_max, this)) arrival = path_arrival; } return arrival; @@ -3073,7 +3060,7 @@ Required Sta::required(Vertex *vertex, const RiseFallBoth *rf, const SceneSeq &scenes, - const MinMax *min_max) + const MinMax *min_max) { findRequired(vertex); const SceneSet scenes_set = Scene::sceneSet(scenes); @@ -3085,9 +3072,8 @@ Sta::required(Vertex *vertex, const Required path_required = path->required(); if ((rf == RiseFallBoth::riseFall() || path->transition(this)->asRiseFallBoth() == rf) - && path->minMax(this) == min_max - && scenes_set.contains(path->scene(this)) - && delayGreater(path_required, required, req_min_max, this)) + && path->minMax(this) == min_max && scenes_set.contains(path->scene(this)) + && delayGreater(path_required, required, req_min_max, this)) required = path_required; } return required; @@ -3144,8 +3130,8 @@ Sta::slack(Vertex *vertex, Slack Sta::slack(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) + const RiseFall *rf, + const MinMax *min_max) { return slack(vertex, rf->asRiseFallBoth(), scenes_, min_max); } @@ -3166,8 +3152,7 @@ Sta::slack(Vertex *vertex, Slack path_slack = path->slack(this); if ((rf == RiseFallBoth::riseFall() || path->transition(this)->asRiseFallBoth() == rf) - && path->minMax(this) == min_max - && scenes_set.contains(path->scene(this)) + && path->minMax(this) == min_max && scenes_set.contains(path->scene(this)) && delayLess(path_slack, slack, this)) slack = path_slack; } @@ -3237,9 +3222,9 @@ EndpointPathEndVisitor::visit(PathEnd *path_end) StringSeq group_names = PathGroups::pathGroupNames(path_end, sta_); for (std::string &group_name : group_names) { if (group_name == path_group_name_) { - Slack end_slack = path_end->slack(sta_); - if (delayLess(end_slack, slack_, sta_)) - slack_ = end_slack; + Slack end_slack = path_end->slack(sta_); + if (delayLess(end_slack, slack_, sta_)) + slack_ = end_slack; } } } @@ -3355,25 +3340,21 @@ Sta::reportDelaysWrtClks(Vertex *vertex, int digits, PathDelayFunc get_path_delay) { - RiseFallMinMaxDelay delays = findDelaysWrtClks(vertex, clk_edge, scene, - get_path_delay); + RiseFallMinMaxDelay delays = + findDelaysWrtClks(vertex, clk_edge, scene, get_path_delay); if (!delays.empty()) { std::string clk_name; - if (clk_edge) { - clk_name = " ("; - clk_name += clk_edge->name(); - clk_name += ')'; - } - report_->reportLine("%s r %s:%s f %s:%s", - clk_name.c_str(), - formatDelay(RiseFall::rise(), MinMax::min(), - delays, report_variance, digits).c_str(), - formatDelay(RiseFall::rise(), MinMax::max(), - delays, report_variance, digits).c_str(), - formatDelay(RiseFall::fall(), MinMax::min(), - delays, report_variance, digits).c_str(), - formatDelay(RiseFall::fall(), MinMax::max(), - delays, report_variance, digits).c_str()); + if (clk_edge) + clk_name = sta::format("({})", clk_edge->name()); + report_->report("{} r {}:{} f {}:{}", clk_name, + formatDelay(RiseFall::rise(), MinMax::min(), + delays, report_variance, digits).c_str(), + formatDelay(RiseFall::rise(), MinMax::max(), + delays, report_variance, digits).c_str(), + formatDelay(RiseFall::fall(), MinMax::min(), + delays, report_variance, digits).c_str(), + formatDelay(RiseFall::fall(), MinMax::max(), + delays, report_variance, digits).c_str()); } } @@ -3460,10 +3441,8 @@ MinPeriodEndVisitor::visit(PathEnd *path_end) const ClockEdge *src_edge = path_end->sourceClkEdge(sta_); const ClockEdge *tgt_edge = path_end->targetClkEdge(sta_); PathEnd::Type end_type = path_end->type(); - if ((end_type == PathEnd::Type::check - || end_type == PathEnd::Type::output_delay) - && path->minMax(sta_) == MinMax::max() - && src_edge->clock() == clk_ + if ((end_type == PathEnd::Type::check || end_type == PathEnd::Type::output_delay) + && path->minMax(sta_) == MinMax::max() && src_edge->clock() == clk_ && tgt_edge->clock() == clk_ // Only consider rise/rise and fall/fall paths. && src_edge->transition() == tgt_edge->transition() @@ -3527,7 +3506,7 @@ Sta::totalNegativeSlack(const MinMax *min_max) Slack Sta::totalNegativeSlack(const Scene *scene, - const MinMax *min_max) + const MinMax *min_max) { searchPreamble(); return search_->totalNegativeSlack(scene, min_max); @@ -3545,9 +3524,9 @@ Sta::worstSlack(const MinMax *min_max) void Sta::worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { searchPreamble(); search_->worstSlack(min_max, worst_slack, worst_vertex); @@ -3555,10 +3534,10 @@ Sta::worstSlack(const MinMax *min_max, void Sta::worstSlack(const Scene *scene, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { searchPreamble(); return search_->worstSlack(scene, min_max, worst_slack, worst_vertex); @@ -3624,7 +3603,7 @@ Sta::setIncrementalDelayTolerance(float tol) const ArcDelay Sta::arcDelay(Edge *edge, - TimingArc *arc, + TimingArc *arc, DcalcAPIndex ap_index) { findDelays(edge->to(graph_)); @@ -3633,7 +3612,7 @@ Sta::arcDelay(Edge *edge, bool Sta::arcDelayAnnotated(Edge *edge, - TimingArc *arc, + TimingArc *arc, const Scene *scene, const MinMax *min_max) { @@ -3643,10 +3622,10 @@ Sta::arcDelayAnnotated(Edge *edge, void Sta::setArcDelayAnnotated(Edge *edge, - TimingArc *arc, + TimingArc *arc, const Scene *scene, const MinMax *min_max, - bool annotated) + bool annotated) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); graph_->setArcDelayAnnotated(edge, arc, ap_index, annotated); @@ -3661,7 +3640,7 @@ Slew Sta::slew(Vertex *vertex, const RiseFallBoth *rf, const SceneSeq &scenes, - const MinMax *min_max) + const MinMax *min_max) { findDelays(vertex); Slew mm_slew = min_max->initValue(); @@ -3736,22 +3715,22 @@ Sta::updateGeneratedClks() for (Mode *mode : modes_) { Genclks *genclks = mode->genclks(); Sdc *sdc = mode->sdc(); - bool gen_clk_changed = true; - while (gen_clk_changed) { - gen_clk_changed = false; + bool gen_clk_changed = true; + while (gen_clk_changed) { + gen_clk_changed = false; for (Clock *clk : sdc->clocks()) { - if (clk->isGenerated() && !clk->waveformValid()) { + if (clk->isGenerated() && !clk->waveformValid()) { genclks->ensureMaster(clk, sdc); - Clock *master_clk = clk->masterClk(); - if (master_clk && master_clk->waveformValid()) { - clk->generate(master_clk); - gen_clk_changed = true; - } - } + Clock *master_clk = clk->masterClk(); + if (master_clk && master_clk->waveformValid()) { + clk->generate(master_clk); + gen_clk_changed = true; + } + } + } } } } - } update_genclks_ = false; } @@ -3787,7 +3766,7 @@ Sta::maxPathCountVertex() const } int -Sta::vertexPathCount(Vertex *vertex) const +Sta::vertexPathCount(Vertex *vertex) const { TagGroup *tag_group = search_->tagGroup(vertex); if (tag_group) @@ -3828,10 +3807,10 @@ Sta::clkInfoCount() const void Sta::setArcDelay(Edge *edge, - TimingArc *arc, + TimingArc *arc, const Scene *scene, - const MinMaxAll *min_max, - ArcDelay delay) + const MinMaxAll *min_max, + ArcDelay delay) { ensureGraph(); for (const MinMax *mm : min_max->range()) { @@ -3851,9 +3830,9 @@ Sta::setArcDelay(Edge *edge, void Sta::setAnnotatedSlew(Vertex *vertex, const Scene *scene, - const MinMaxAll *min_max, - const RiseFallBoth *rf, - float slew) + const MinMaxAll *min_max, + const RiseFallBoth *rf, + float slew) { ensureGraph(); for (const MinMax *mm : min_max->range()) { @@ -3870,16 +3849,16 @@ Sta::setAnnotatedSlew(Vertex *vertex, void Sta::writeSdf(const char *filename, const Scene *scene, - char divider, - bool include_typ, + char divider, + bool include_typ, int digits, - bool gzip, - bool no_timestamp, - bool no_version) + bool gzip, + bool no_timestamp, + bool no_version) { findDelays(); - sta::writeSdf(filename, scene, divider, include_typ, digits, gzip, - no_timestamp, no_version, this); + sta::writeSdf(filename, scene, divider, include_typ, digits, gzip, no_timestamp, + no_version, this); } void @@ -3925,8 +3904,8 @@ Sta::clearLogicConstants() void Sta::setPortExtPinCap(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, + const RiseFallBoth *rf, + const MinMaxAll *min_max, float cap, Sdc *sdc) { @@ -3955,10 +3934,8 @@ Sta::portExtCaps(const Port *port, float pin_cap1, wire_cap1; int fanout1; bool pin_exists1, wire_exists1, fanout_exists1; - sdc->portExtCap(port, rf, min_max, - pin_cap1, pin_exists1, - wire_cap1, wire_exists1, - fanout1, fanout_exists1); + sdc->portExtCap(port, rf, min_max, pin_cap1, pin_exists1, wire_cap1, + wire_exists1, fanout1, fanout_exists1); if (pin_exists1) { pin_cap = min_max->minMax(pin_cap, pin_cap1); pin_exists = true; @@ -3982,8 +3959,8 @@ Sta::portExtCaps(const Port *port, void Sta::setPortExtWireCap(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, + const RiseFallBoth *rf, + const MinMaxAll *min_max, float cap, Sdc *sdc) { @@ -4003,7 +3980,7 @@ Sta::removeNetLoadCaps(Sdc *sdc) const void Sta::setPortExtFanout(const Port *port, - int fanout, + int fanout, const MinMaxAll *min_max, Sdc *sdc) { @@ -4014,8 +3991,8 @@ Sta::setPortExtFanout(const Port *port, void Sta::setNetWireCap(const Net *net, - bool subtract_pin_cap, - const MinMaxAll *min_max, + bool subtract_pin_cap, + const MinMaxAll *min_max, float cap, Sdc *sdc) { @@ -4026,22 +4003,21 @@ Sta::setNetWireCap(const Net *net, void Sta::connectedCap(const Pin *drvr_pin, - const RiseFall *rf, + const RiseFall *rf, const Scene *scene, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const { - graph_delay_calc_->loadCap(drvr_pin, rf, scene, min_max, - pin_cap, wire_cap); + graph_delay_calc_->loadCap(drvr_pin, rf, scene, min_max, pin_cap, wire_cap); } void Sta::connectedCap(const Net *net, Scene *scene, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const { const Pin *drvr_pin = findNetParasiticDrvrPin(net); if (drvr_pin) { @@ -4073,7 +4049,8 @@ Sta::capacitance(const LibertyPort *port, OperatingConditions *op_cond = operatingConditions(min_max, sdc); const LibertyPort *scene_port = port->scenePort(scene, min_max); for (const RiseFall *rf : RiseFall::range()) - cap = min_max->minMax(cap, scene_port->capacitance(rf, min_max, op_cond, op_cond)); + cap = min_max->minMax(cap, + scene_port->capacitance(rf, min_max, op_cond, op_cond)); } return cap; } @@ -4100,7 +4077,7 @@ Sta::findNetParasiticDrvrPin(const Net *net) const void Sta::setResistance(const Net *net, - const MinMaxAll *min_max, + const MinMaxAll *min_max, float res, Sdc *sdc) { @@ -4113,7 +4090,7 @@ bool Sta::readSpef(const std::string &name, const std::string &filename, Instance *instance, - Scene *scene, // -scene deprecated 11/20/2025 + Scene *scene, // -scene deprecated 11/20/2025 const MinMaxAll *min_max, bool pin_cap_included, bool keep_coupling_caps, @@ -4125,8 +4102,7 @@ Sta::readSpef(const std::string &name, // Use -name to distinguish rel 2.7 args for compatibility. if (name.empty()) { std::string spef_name = "default"; - if (scene - || min_max != MinMaxAll::minMax()) { + if (scene || min_max != MinMaxAll::minMax()) { if (scene) spef_name = scene->name(); if (min_max != MinMaxAll::minMax()) { @@ -4151,10 +4127,9 @@ Sta::readSpef(const std::string &name, parasitics = makeConcreteParasitics(name, filename); } - bool success = readSpefFile(filename.c_str(), instance, - pin_cap_included, keep_coupling_caps, - coupling_cap_factor, reduce, - scene, min_max, parasitics, this); + bool success = + readSpefFile(filename.c_str(), instance, pin_cap_included, keep_coupling_caps, + coupling_cap_factor, reduce, scene, min_max, parasitics, this); delaysInvalid(); return success; } @@ -4175,22 +4150,21 @@ Sta::reportParasiticAnnotation(const std::string &spef_name, if (!spef_name.empty()) { parasitics = findParasitics(spef_name); if (parasitics == nullptr) - report_->error(1560, "spef %s not found.", spef_name.c_str()); + report_->error(1560, "spef {} not found.", spef_name); } else parasitics = cmd_scene_->parasitics(MinMax::max()); - sta::reportParasiticAnnotation(parasitics, report_unannotated, - cmd_scene_, this); + sta::reportParasiticAnnotation(parasitics, report_unannotated, cmd_scene_, this); } void Sta::findPiElmore(Pin *drvr_pin, - const RiseFall *rf, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - bool &exists) const + const RiseFall *rf, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + bool &exists) const { Scene *scene = cmd_scene_; const Parasitics *parasitics = scene->parasitics(min_max); @@ -4205,11 +4179,11 @@ Sta::findPiElmore(Pin *drvr_pin, void Sta::makePiElmore(Pin *drvr_pin, - const RiseFall *rf, - const MinMaxAll *min_max, - float c2, - float rpi, - float c1) + const RiseFall *rf, + const MinMaxAll *min_max, + float c2, + float rpi, + float c1) { const Scene *scene = cmd_scene_; for (const MinMax *mm : min_max->range()) { @@ -4221,11 +4195,11 @@ Sta::makePiElmore(Pin *drvr_pin, void Sta::findElmore(Pin *drvr_pin, - Pin *load_pin, - const RiseFall *rf, - const MinMax *min_max, - float &elmore, - bool &exists) const + Pin *load_pin, + const RiseFall *rf, + const MinMax *min_max, + float &elmore, + bool &exists) const { Scene *scene = cmd_scene_; const Parasitics *parasitics = scene->parasitics(min_max); @@ -4238,10 +4212,10 @@ Sta::findElmore(Pin *drvr_pin, void Sta::setElmore(Pin *drvr_pin, - Pin *load_pin, - const RiseFall *rf, - const MinMaxAll *min_max, - float elmore) + Pin *load_pin, + const RiseFall *rf, + const MinMaxAll *min_max, + float elmore) { const Scene *scene = cmd_scene_; for (const MinMax *mm : min_max->range()) { @@ -4308,13 +4282,13 @@ Sta::makeParasiticNetwork(const Net *net, NetworkEdit * Sta::networkCmdEdit() { - return dynamic_cast(cmd_network_); + return dynamic_cast(cmd_network_); } Instance * Sta::makeInstance(const char *name, - LibertyCell *cell, - Instance *parent) + LibertyCell *cell, + Instance *parent) { NetworkEdit *network = networkCmdEdit(); Instance *inst = network->makeInstance(cell, name, parent); @@ -4333,7 +4307,7 @@ Sta::deleteInstance(Instance *inst) void Sta::replaceCell(Instance *inst, - LibertyCell *to_lib_cell) + LibertyCell *to_lib_cell) { Cell *to_cell = network_->cell(to_lib_cell); replaceCell(inst, to_cell, to_lib_cell); @@ -4341,7 +4315,7 @@ Sta::replaceCell(Instance *inst, void Sta::replaceCell(Instance *inst, - Cell *to_cell) + Cell *to_cell) { LibertyCell *to_lib_cell = network_->libertyCell(to_cell); replaceCell(inst, to_cell, to_lib_cell); @@ -4349,8 +4323,8 @@ Sta::replaceCell(Instance *inst, void Sta::replaceCell(Instance *inst, - Cell *to_cell, - LibertyCell *to_lib_cell) + Cell *to_cell, + LibertyCell *to_lib_cell) { NetworkEdit *network = networkCmdEdit(); LibertyCell *from_lib_cell = network->libertyCell(inst); @@ -4370,7 +4344,7 @@ Sta::replaceCell(Instance *inst, Net * Sta::makeNet(const char *name, - Instance *parent) + Instance *parent) { NetworkEdit *network = networkCmdEdit(); Net *net = network->makeNet(name, parent); @@ -4388,8 +4362,8 @@ Sta::deleteNet(Net *net) void Sta::connectPin(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { NetworkEdit *network = networkCmdEdit(); Pin *pin = network->connect(inst, port, net); @@ -4398,8 +4372,8 @@ Sta::connectPin(Instance *inst, void Sta::connectPin(Instance *inst, - LibertyPort *port, - Net *net) + LibertyPort *port, + Net *net) { NetworkEdit *network = networkCmdEdit(); Pin *pin = network->connect(inst, port, net); @@ -4419,7 +4393,7 @@ Sta::makePortPin(const char *port_name, PortDirection *dir) { ensureLinked(); - NetworkReader *network = dynamic_cast(network_); + NetworkReader *network = dynamic_cast(network_); Instance *top_inst = network->topInstance(); Cell *top_cell = network->cell(top_inst); Port *port = network->makePort(top_cell, port_name); @@ -4427,7 +4401,7 @@ Sta::makePortPin(const char *port_name, Pin *pin = network->makePin(top_inst, port, nullptr); makePortPinAfter(pin); } - + //////////////////////////////////////////////////////////////// // // Network edit before/after methods. @@ -4437,7 +4411,7 @@ Sta::makePortPin(const char *port_name, void Sta::makeInstanceAfter(const Instance *inst) { - debugPrint(debug_, "network_edit", 1, "make instance %s", + debugPrint(debug_, "network_edit", 1, "make instance {}", sdc_network_->pathName(inst)); if (graph_) { LibertyCell *lib_cell = network_->libertyCell(inst); @@ -4450,7 +4424,6 @@ Sta::makeInstanceAfter(const Instance *inst) if (pin) { Vertex *vertex, *bidir_drvr_vertex; graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); - } } graph_->makeInstanceEdges(inst); @@ -4470,7 +4443,7 @@ Sta::makePortPinAfter(Pin *pin) void Sta::replaceEquivCellBefore(const Instance *inst, - const LibertyCell *to_cell) + const LibertyCell *to_cell) { if (graph_) { InstancePinIterator *pin_iter = network_->pinIterator(inst); @@ -4494,15 +4467,16 @@ Sta::replaceEquivCellBefore(const Instance *inst, if (to_set) edge->setTimingArcSet(to_set); else - report_->critical(1556, "corresponding timing arc set not found in equiv cells"); + report_->critical( + 1556, "corresponding timing arc set not found in equiv cells"); } } } else { // Force delay calculation on output pins. Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) - graph_delay_calc_->delayInvalid(vertex); + if (vertex) + graph_delay_calc_->delayInvalid(vertex); } } } @@ -4530,16 +4504,15 @@ Sta::replaceEquivCellAfter(const Instance *inst) void Sta::replaceCellPinInvalidate(const LibertyPort *from_port, - Vertex *vertex, - const LibertyCell *to_cell) + Vertex *vertex, + const LibertyCell *to_cell) { LibertyPort *to_port = to_cell->findLibertyPort(from_port->name()); if (to_port == nullptr || (!libertyPortCapsEqual(to_port, from_port) // If this is an ideal clock pin, do not invalidate // arrivals and delay calc on the clock pin driver. - && !(to_port->isClock() - && idealClockMode()))) + && !(to_port->isClock() && idealClockMode()))) // Input port capacitance changed, so invalidate delay // calculation from input driver. delaysInvalidFromFanin(vertex); @@ -4553,30 +4526,30 @@ Sta::idealClockMode() for (Mode *mode : modes_) { Sdc *sdc = mode->sdc(); for (Clock *clk : sdc->clocks()) { - if (clk->isPropagated()) - return false; - } + if (clk->isPropagated()) + return false; + } } return true; } static bool libertyPortCapsEqual(const LibertyPort *port1, - const LibertyPort *port2) + const LibertyPort *port2) { return port1->capacitance(RiseFall::rise(), MinMax::min()) - == port2->capacitance(RiseFall::rise(), MinMax::min()) - && port1->capacitance(RiseFall::rise(), MinMax::max()) - == port2->capacitance(RiseFall::rise(), MinMax::max()) - && port1->capacitance(RiseFall::fall(), MinMax::min()) - == port2->capacitance(RiseFall::fall(), MinMax::min()) - && port1->capacitance(RiseFall::fall(), MinMax::max()) - == port2->capacitance(RiseFall::fall(), MinMax::max()); + == port2->capacitance(RiseFall::rise(), MinMax::min()) + && port1->capacitance(RiseFall::rise(), MinMax::max()) + == port2->capacitance(RiseFall::rise(), MinMax::max()) + && port1->capacitance(RiseFall::fall(), MinMax::min()) + == port2->capacitance(RiseFall::fall(), MinMax::min()) + && port1->capacitance(RiseFall::fall(), MinMax::max()) + == port2->capacitance(RiseFall::fall(), MinMax::max()); } void Sta::replaceCellBefore(const Instance *inst, - const LibertyCell *to_cell) + const LibertyCell *to_cell) { if (graph_) { // Delete all graph edges between instance pins. @@ -4585,16 +4558,16 @@ Sta::replaceCellBefore(const Instance *inst, Pin *pin = pin_iter->next(); LibertyPort *port = network_->libertyPort(pin); if (port->direction()->isAnyInput()) { - Vertex *vertex = graph_->pinLoadVertex(pin); - replaceCellPinInvalidate(port, vertex, to_cell); + Vertex *vertex = graph_->pinLoadVertex(pin); + replaceCellPinInvalidate(port, vertex, to_cell); - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - if (network_->instance(to_vertex->pin()) == inst) - deleteEdge(edge); - } + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (network_->instance(to_vertex->pin()) == inst) + deleteEdge(edge); + } } } delete pin_iter; @@ -4623,7 +4596,7 @@ Sta::replaceCellAfter(const Instance *inst) void Sta::connectPinAfter(const Pin *pin) { - debugPrint(debug_, "network_edit", 1, "connect %s to %s", + debugPrint(debug_, "network_edit", 1, "connect {} to {}", sdc_network_->pathName(pin), sdc_network_->pathName(network_->net(pin))); if (graph_) { @@ -4631,11 +4604,11 @@ Sta::connectPinAfter(const Pin *pin) graph_->makeWireEdgesThruPin(pin); EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) { - connectDrvrPinAfter(edge->from(graph_)); - connectLoadPinAfter(edge->to(graph_)); - } + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) { + connectDrvrPinAfter(edge->from(graph_)); + connectLoadPinAfter(edge->to(graph_)); + } } } else { @@ -4724,7 +4697,7 @@ Sta::connectLoadPinAfter(Vertex *vertex) void Sta::disconnectPinBefore(const Pin *pin) { - debugPrint(debug_, "network_edit", 1, "disconnect %s from %s", + debugPrint(debug_, "network_edit", 1, "disconnect {} from {}", sdc_network_->pathName(pin), sdc_network_->pathName(network_->net(pin))); @@ -4742,37 +4715,37 @@ Sta::disconnectPinBefore(const Pin *pin) Vertex *vertex = graph_->pinDrvrVertex(pin); // Delete wire edges from pin. if (vertex) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) - deleteEdge(edge); - } + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) + deleteEdge(edge); + } } } if (network_->isLoad(pin)) { // Delete wire edges to pin. Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) - deleteEdge(edge); - } + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) + deleteEdge(edge); + } } } if (is_hierarchical) { // Delete wire edges thru pin. EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) { - deleteEdge(edge); + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) { + deleteEdge(edge); const Pin *from_pin = edge->from(graph_)->pin(); for (Mode *mode : modes_) mode->clkNetwork()->disconnectPinBefore(from_pin); - } + } } } clk_skews_->clear(); @@ -4783,7 +4756,7 @@ Sta::disconnectPinBefore(const Pin *pin) void Sta::deleteEdge(Edge *edge) { - debugPrint(debug_, "network_edit", 2, "delete edge %s -> %s", + debugPrint(debug_, "network_edit", 2, "delete edge {} -> {}", edge->from(graph_)->name(sdc_network_), edge->to(graph_)->name(sdc_network_)); Vertex *to = edge->to(graph_); @@ -4801,24 +4774,24 @@ Sta::deleteEdge(Edge *edge) void Sta::deleteNetBefore(const Net *net) { - debugPrint(debug_, "network_edit", 1, "delete net %s", + debugPrint(debug_, "network_edit", 1, "delete net {}", sdc_network_->pathName(net)); if (graph_) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (!network_->isHierarchical(pin)) { - disconnectPinBefore(pin); - // Delete wire edges on net pins. - Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isWire()) - deleteEdge(edge); - } - } + disconnectPinBefore(pin); + // Delete wire edges on net pins. + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (vertex) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) + deleteEdge(edge); + } + } } } delete pin_iter; @@ -4832,7 +4805,7 @@ Sta::deleteNetBefore(const Net *net) void Sta::deleteInstanceBefore(const Instance *inst) { - debugPrint(debug_, "network_edit", 1, "delete instance %s", + debugPrint(debug_, "network_edit", 1, "delete instance {}", sdc_network_->pathName(inst)); if (network_->isLeaf(inst)) { deleteInstancePinsBefore(inst); @@ -4875,8 +4848,8 @@ void Sta::deletePinBefore(const Pin *pin) { if (graph_) { - debugPrint(debug_, "network_edit", 1, "delete pin %s", - sdc_network_->pathName(pin)); + debugPrint(debug_, "network_edit", 1, "delete pin {}", + sdc_network_->pathName(pin)); if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { @@ -4894,7 +4867,7 @@ Sta::deletePinBefore(const Pin *pin) } levelize_->deleteEdgeBefore(edge); } - // Deletes edges to/from vertex also. + // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } @@ -4917,7 +4890,7 @@ Sta::deletePinBefore(const Pin *pin) } levelize_->deleteEdgeBefore(edge); } - // Deletes edges to/from vertex also. + // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } @@ -5018,12 +4991,12 @@ Sta::delaysInvalidFromFanin(const Net *net) while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (!network_->isHierarchical(pin)) { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - delaysInvalidFrom(vertex); - if (bidirect_drvr_vertex) - delaysInvalidFrom(bidirect_drvr_vertex); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + delaysInvalidFrom(vertex); + if (bidirect_drvr_vertex) + delaysInvalidFrom(bidirect_drvr_vertex); } } delete pin_iter; @@ -5065,62 +5038,57 @@ Sta::clockDomains(const Pin *pin, InstanceSet Sta::findRegisterInstances(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, + const RiseFallBoth *clk_rf, + bool edge_triggered, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegInstances(clks, clk_rf, edge_triggered, latches, - mode, this); + return findRegInstances(clks, clk_rf, edge_triggered, latches, mode, this); } PinSet Sta::findRegisterDataPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, + const RiseFallBoth *clk_rf, + bool edge_triggered, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegDataPins(clks, clk_rf, edge_triggered, latches, - mode, this); + return findRegDataPins(clks, clk_rf, edge_triggered, latches, mode, this); } PinSet Sta::findRegisterClkPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, + const RiseFallBoth *clk_rf, + bool edge_triggered, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegClkPins(clks, clk_rf, edge_triggered, latches, - mode, this); + return findRegClkPins(clks, clk_rf, edge_triggered, latches, mode, this); } PinSet Sta::findRegisterAsyncPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, + const RiseFallBoth *clk_rf, + bool edge_triggered, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, - mode, this); + return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, mode, this); } PinSet Sta::findRegisterOutputPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, + const RiseFallBoth *clk_rf, + bool edge_triggered, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegOutputPins(clks, clk_rf, edge_triggered, latches, - mode, this); + return findRegOutputPins(clks, clk_rf, edge_triggered, latches, mode, this); } void @@ -5137,8 +5105,8 @@ class FanInOutSrchPred : public SearchPred { public: FanInOutSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta); + bool thru_constants, + const StaState *sta); bool searchFrom(const Vertex *from_vertex, const Mode *mode) const override; bool searchThru(Edge *edge, @@ -5156,8 +5124,8 @@ protected: }; FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta) : + bool thru_constants, + const StaState *sta) : SearchPred(sta), thru_disabled_(thru_disabled), thru_constants_(thru_constants), @@ -5171,10 +5139,8 @@ FanInOutSrchPred::searchFrom(const Vertex *from_vertex, { const Pin *from_pin = from_vertex->pin(); const Sdc *sdc = mode->sdc(); - return (thru_disabled_ - || !sdc->isDisabledConstraint(from_pin)) - && (thru_constants_ - || !mode->sim()->isConstant(from_vertex)); + return (thru_disabled_ || !sdc->isDisabledConstraint(from_pin)) + && (thru_constants_ || !mode->sim()->isConstant(from_vertex)); } bool @@ -5184,22 +5150,19 @@ FanInOutSrchPred::searchThru(Edge *edge, const Sdc *sdc = mode->sdc(); const Sim *sim = mode->sim(); return searchThruRole(edge) - && (thru_disabled_ - || !(sdc->isDisabledConstraint(edge) - || sim->isDisabledCond(edge) - || sta_->isDisabledCondDefault(edge))) - && (thru_constants_ - || sim->simTimingSense(edge) != TimingSense::none); + && (thru_disabled_ + || !(sdc->isDisabledConstraint(edge) || sim->isDisabledCond(edge) + || sta_->isDisabledCondDefault(edge))) + && (thru_constants_ || sim->simTimingSense(edge) != TimingSense::none); } bool FanInOutSrchPred::searchThruRole(Edge *edge) const { const TimingRole *role = edge->role(); - return role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable(); + return role == TimingRole::wire() || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable(); } bool @@ -5220,27 +5183,27 @@ FanInOutSrchPred::searchTo(const Vertex *to_vertex, { const Pin *to_pin = to_vertex->pin(); const Sdc *sdc = mode->sdc(); - return (thru_disabled_ - || !sdc->isDisabledConstraint(to_pin)) - && (thru_constants_ - || !mode->sim()->isConstant(to_vertex)); + return (thru_disabled_ || !sdc->isDisabledConstraint(to_pin)) + && (thru_constants_ || !mode->sim()->isConstant(to_vertex)); } class FaninSrchPred : public FanInOutSrchPred { public: FaninSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta); + bool thru_constants, + const StaState *sta); protected: bool searchThruRole(Edge *edge) const override; }; FaninSrchPred::FaninSrchPred(bool thru_disabled, - bool thru_constants, - const StaState *sta) : - FanInOutSrchPred(thru_disabled, thru_constants, sta) + bool thru_constants, + const StaState *sta) : + FanInOutSrchPred(thru_disabled, + thru_constants, + sta) { } @@ -5248,21 +5211,19 @@ bool FaninSrchPred::searchThruRole(Edge *edge) const { const TimingRole *role = edge->role(); - return role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable() - || role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ(); + return role == TimingRole::wire() || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable() || role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ(); } PinSet Sta::findFaninPins(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, bool thru_constants, const Mode *mode) { @@ -5275,15 +5236,15 @@ Sta::findFaninPins(PinSeq *to, if (network_->isHierarchical(pin)) { EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - findFaninPins(edge->from(graph_), flat, startpoints_only, - inst_levels, pin_levels, fanin, pred, mode); + Edge *edge = edge_iter.next(); + findFaninPins(edge->from(graph_), flat, startpoints_only, inst_levels, + pin_levels, fanin, pred, mode); } } else { Vertex *vertex = graph_->pinLoadVertex(pin); - findFaninPins(vertex, flat, startpoints_only, - inst_levels, pin_levels, fanin, pred, mode); + findFaninPins(vertex, flat, startpoints_only, inst_levels, pin_levels, fanin, + pred, mode); } } return fanin; @@ -5291,21 +5252,19 @@ Sta::findFaninPins(PinSeq *to, void Sta::findFaninPins(Vertex *vertex, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanin, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanin, SearchPred &pred, const Mode *mode) { VertexSet visited = makeVertexSet(this); - findFaninPins(vertex, flat, inst_levels, - pin_levels, visited, &pred, 0, 0, mode); + findFaninPins(vertex, flat, inst_levels, pin_levels, visited, &pred, 0, 0, mode); for (Vertex *visited_vertex : visited) { Pin *visited_pin = visited_vertex->pin(); - if (!startpoints_only - || network_->isRegClkPin(visited_pin) + if (!startpoints_only || network_->isRegClkPin(visited_pin) || !hasFanin(visited_vertex, &pred, graph_, mode)) fanin.insert(visited_pin); } @@ -5313,40 +5272,32 @@ Sta::findFaninPins(Vertex *vertex, void Sta::findFaninPins(Vertex *to, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, int pin_level, const Mode *mode) { - debugPrint(debug_, "fanin", 1, "%s", - to->to_string(this).c_str()); + debugPrint(debug_, "fanin", 1, "{}", to->to_string(this)); if (!visited.contains(to)) { visited.insert(to); Pin *to_pin = to->pin(); bool is_reg_clk_pin = network_->isRegClkPin(to_pin); - if (!is_reg_clk_pin - && (inst_levels <= 0 - || inst_level < inst_levels) - && (pin_levels <= 0 - || pin_level < pin_levels) - && pred->searchTo(to, mode)) { + if (!is_reg_clk_pin && (inst_levels <= 0 || inst_level < inst_levels) + && (pin_levels <= 0 || pin_level < pin_levels) && pred->searchTo(to, mode)) { VertexInEdgeIterator edge_iter(to, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - if (pred->searchThru(edge, mode) - && (flat - || !crossesHierarchy(edge)) + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (pred->searchThru(edge, mode) && (flat || !crossesHierarchy(edge)) && pred->searchFrom(from_vertex, mode)) { - findFaninPins(from_vertex, flat, inst_levels, - pin_levels, visited, pred, - edge->role()->isWire() ? inst_level : inst_level+1, - pin_level+1, mode); - } + findFaninPins(from_vertex, flat, inst_levels, pin_levels, visited, pred, + edge->role()->isWire() ? inst_level : inst_level + 1, + pin_level + 1, mode); + } } } } @@ -5354,26 +5305,26 @@ Sta::findFaninPins(Vertex *to, InstanceSet Sta::findFaninInstances(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, bool thru_constants, const Mode *mode) { - PinSet pins = findFaninPins(to, flat, startpoints_only, inst_levels, - pin_levels, thru_disabled, thru_constants, mode); + PinSet pins = findFaninPins(to, flat, startpoints_only, inst_levels, pin_levels, + thru_disabled, thru_constants, mode); return pinInstances(pins, network_); } PinSet Sta::findFanoutPins(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, bool thru_constants, const Mode *mode) { @@ -5386,15 +5337,15 @@ Sta::findFanoutPins(PinSeq *from, if (network_->isHierarchical(pin)) { EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - findFanoutPins(edge->to(graph_), flat, endpoints_only, - inst_levels, pin_levels, fanout, pred, mode); + Edge *edge = edge_iter.next(); + findFanoutPins(edge->to(graph_), flat, endpoints_only, inst_levels, + pin_levels, fanout, pred, mode); } } else { Vertex *vertex = graph_->pinDrvrVertex(pin); - findFanoutPins(vertex, flat, endpoints_only, - inst_levels, pin_levels, fanout, pred, mode); + findFanoutPins(vertex, flat, endpoints_only, inst_levels, pin_levels, fanout, + pred, mode); } } return fanout; @@ -5402,21 +5353,19 @@ Sta::findFanoutPins(PinSeq *from, void Sta::findFanoutPins(Vertex *vertex, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanout, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanout, SearchPred &pred, const Mode *mode) { VertexSet visited = makeVertexSet(this); - findFanoutPins(vertex, flat, inst_levels, - pin_levels, visited, &pred, 0, 0, mode); + findFanoutPins(vertex, flat, inst_levels, pin_levels, visited, &pred, 0, 0, mode); for (Vertex *visited_vertex : visited) { Pin *visited_pin = visited_vertex->pin(); - if (!endpoints_only - || search_->isEndpoint(visited_vertex, &pred, mode)) + if (!endpoints_only || search_->isEndpoint(visited_vertex, &pred, mode)) fanout.insert(visited_pin); } } @@ -5424,38 +5373,32 @@ Sta::findFanoutPins(Vertex *vertex, // DFS to support level limits. void Sta::findFanoutPins(Vertex *from, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, int pin_level, const Mode *mode) { - debugPrint(debug_, "fanout", 1, "%s", - from->to_string(this).c_str()); + debugPrint(debug_, "fanout", 1, "{}", from->to_string(this)); if (!visited.contains(from)) { visited.insert(from); if (!search_->isEndpoint(from, pred, mode) - && (inst_levels <= 0 - || inst_level < inst_levels) - && (pin_levels <= 0 - || pin_level < pin_levels) + && (inst_levels <= 0 || inst_level < inst_levels) + && (pin_levels <= 0 || pin_level < pin_levels) && pred->searchFrom(from, mode)) { VertexOutEdgeIterator edge_iter(from, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - if (pred->searchThru(edge, mode) - && (flat - || !crossesHierarchy(edge)) + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (pred->searchThru(edge, mode) && (flat || !crossesHierarchy(edge)) && pred->searchTo(to_vertex, mode)) { - findFanoutPins(to_vertex, flat, inst_levels, - pin_levels, visited, pred, - edge->role()->isWire() ? inst_level : inst_level+1, - pin_level+1, mode); - } + findFanoutPins(to_vertex, flat, inst_levels, pin_levels, visited, pred, + edge->role()->isWire() ? inst_level : inst_level + 1, + pin_level + 1, mode); + } } } } @@ -5463,22 +5406,22 @@ Sta::findFanoutPins(Vertex *from, InstanceSet Sta::findFanoutInstances(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, bool thru_constants, const Mode *mode) { - PinSet pins = findFanoutPins(from, flat, endpoints_only, inst_levels, - pin_levels, thru_disabled, thru_constants, mode); + PinSet pins = findFanoutPins(from, flat, endpoints_only, inst_levels, pin_levels, + thru_disabled, thru_constants, mode); return pinInstances(pins, network_); } static InstanceSet pinInstances(PinSet &pins, - const Network *network) + const Network *network) { InstanceSet insts(network); for (const Pin *pin : pins) @@ -5498,11 +5441,11 @@ Sta::crossesHierarchy(Edge *edge) const // Treat input/output port pins as "inside". if (network_->isTopInstance(from_inst)) from_parent = from_inst; - else + else from_parent = network_->parent(from_inst); if (network_->isTopInstance(to_inst)) to_parent = to_inst; - else + else to_parent = network_->parent(to_inst); return from_parent != to_parent; } @@ -5521,8 +5464,8 @@ instMaxSlew(const Instance *inst, Pin *pin = pin_iter->next(); if (network->isDriver(pin)) { Vertex *vertex = graph->pinDrvrVertex(pin); - Slew slew = sta->slew(vertex, RiseFallBoth::riseFall(), - sta->scenes(), MinMax::max()); + Slew slew = + sta->slew(vertex, RiseFallBoth::riseFall(), sta->scenes(), MinMax::max()); if (delayGreater(slew, max_slew, sta)) max_slew = slew; } @@ -5536,11 +5479,8 @@ Sta::slowDrivers(int count) { findDelays(); InstanceSeq insts = network_->leafInstances(); - sort(insts, [this] (const Instance *inst1, - const Instance *inst2) { - return delayGreater(instMaxSlew(inst1, this), - instMaxSlew(inst2, this), - this); + sort(insts, [this](const Instance *inst1, const Instance *inst2) { + return delayGreater(instMaxSlew(inst1, this), instMaxSlew(inst2, this), this); }); insts.resize(count); return insts; @@ -5557,10 +5497,11 @@ Sta::reportSlewChecks(const Net *net, const MinMax *min_max) { checkSlewsPreamble(); - SlewCheckSeq &checks = check_slews_->check(net, max_count, violators, scenes, min_max); + SlewCheckSeq &checks = + check_slews_->check(net, max_count, violators, scenes, min_max); if (!checks.empty()) { - report_->reportLine("%s slew", min_max->to_string().c_str()); - report_->reportLine(""); + report_->report("{} slew", min_max->to_string()); + report_->report(""); if (!verbose) report_path_->reportLimitShortHeader(report_path_->fieldSlew()); @@ -5576,7 +5517,7 @@ Sta::reportSlewChecks(const Net *net, delayAsFloat(check.slew(), min_max, this), check.limit(), check.slack()); } - report_->reportLine(""); + report_->report(""); } } @@ -5589,7 +5530,7 @@ Sta::checkSlewsPreamble() if (mode->sdc()->haveClkSlewLimits()) have_clk_slew_limits = true; mode->clkNetwork()->ensureClkNetwork(); - } + } if (have_clk_slew_limits) // Arrivals are needed to know pin clock domains. updateTiming(false); @@ -5602,17 +5543,17 @@ Sta::checkSlewsPreamble() void Sta::checkSlew(const Pin *pin, const SceneSeq &scenes, - const MinMax *min_max, - bool check_clks, - // Return values. - Slew &slew, - float &limit, + const MinMax *min_max, + bool check_clks, + // Return values. + Slew &slew, + float &limit, float &slack, const RiseFall *&rf, const Scene *&scene) { - check_slews_->check(pin, scenes, min_max, check_clks, - slew, limit, slack, rf, scene); + check_slews_->check(pin, scenes, min_max, check_clks, slew, limit, slack, rf, + scene); } size_t @@ -5623,14 +5564,15 @@ Sta::maxSlewViolationCount() } void -Sta::maxSlewCheck(// Return values. - const Pin *&pin, - Slew &slew, - float &slack, - float &limit) +Sta::maxSlewCheck( // Return values. + const Pin *&pin, + Slew &slew, + float &slack, + float &limit) { checkSlewsPreamble(); - SlewCheckSeq &checks = check_slews_->check(nullptr, 1, false, scenes_, MinMax::max()); + SlewCheckSeq &checks = + check_slews_->check(nullptr, 1, false, scenes_, MinMax::max()); if (!checks.empty()) { SlewCheck &check = checks[0]; pin = check.pin(); @@ -5639,9 +5581,9 @@ Sta::maxSlewCheck(// Return values. limit = check.limit(); } else { - pin = nullptr; - slew = 0.0; - slack = INF; + pin = nullptr; + slew = 0.0; + slack = INF; } } @@ -5655,8 +5597,7 @@ Sta::findSlewLimit(const LibertyPort *port, { if (check_slews_ == nullptr) makeCheckSlews(); - check_slews_->findLimit(port, scene, min_max, - limit, exists); + check_slews_->findLimit(port, scene, min_max, limit, exists); } ////////////////////////////////////////////////////////////////' @@ -5664,34 +5605,32 @@ Sta::findSlewLimit(const LibertyPort *port, void Sta::reportFanoutChecks(const Net *net, size_t max_count, - bool violators, + bool violators, bool verbose, const SceneSeq &scenes, - const MinMax *min_max) + const MinMax *min_max) { checkFanoutPreamble(); - const ModeSeq modes = Scene::modesSorted(scenes); - FanoutCheckSeq &checks = check_fanouts_->check(net, max_count, violators, - modes, min_max); + const ModeSeq modes = Scene::modesSorted(scenes); + FanoutCheckSeq &checks = + check_fanouts_->check(net, max_count, violators, modes, min_max); if (!checks.empty()) { - report_->reportLine("%s fanout", min_max->to_string().c_str()); - report_->reportLine(""); + report_->report("{} fanout", min_max->to_string()); + report_->report(""); if (!verbose) - report_path_->reportLimitShortHeader(report_path_->fieldFanout()); + report_path_->reportLimitShortHeader(report_path_->fieldFanout()); for (const FanoutCheck &check : checks) { if (verbose) - report_path_->reportLimitVerbose(report_path_->fieldFanout(), - check.pin(), nullptr, check.fanout(), - check.limit(), check.slack(), nullptr, - min_max); + report_path_->reportLimitVerbose(report_path_->fieldFanout(), check.pin(), + nullptr, check.fanout(), check.limit(), + check.slack(), nullptr, min_max); else - report_path_->reportLimitShort(report_path_->fieldFanout(), - check.pin(), check.fanout(), - check.limit(), check.slack()); + report_path_->reportLimitShort(report_path_->fieldFanout(), check.pin(), + check.fanout(), check.limit(), check.slack()); } - report_->reportLine(""); + report_->report(""); } } @@ -5718,11 +5657,11 @@ Sta::fanoutViolationCount(const MinMax *min_max, void Sta::checkFanout(const Pin *pin, const Mode *mode, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack) + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack) { FanoutCheck check = check_fanouts_->check(pin, mode, min_max); pin = check.pin(); @@ -5734,15 +5673,15 @@ Sta::checkFanout(const Pin *pin, void Sta::maxFanoutMinSlackPin(const ModeSeq &modes, // Return values. - const Pin *&pin, - float &fanout, + const Pin *&pin, + float &fanout, float &limit, - float &slack, + float &slack, const Mode *&mode) { checkFanoutPreamble(); - FanoutCheckSeq &checks = check_fanouts_->check(nullptr, 1, false, - modes, MinMax::max()); + FanoutCheckSeq &checks = + check_fanouts_->check(nullptr, 1, false, modes, MinMax::max()); if (!checks.empty()) { FanoutCheck &check = checks[0]; pin = check.pin(); @@ -5752,9 +5691,9 @@ Sta::maxFanoutMinSlackPin(const ModeSeq &modes, mode = check.mode(); } else { - pin = nullptr; - fanout = 0; - limit = INF; + pin = nullptr; + fanout = 0; + limit = INF; slack = INF; mode = nullptr; } @@ -5765,32 +5704,31 @@ Sta::maxFanoutMinSlackPin(const ModeSeq &modes, void Sta::reportCapacitanceChecks(const Net *net, size_t max_count, - bool violators, + bool violators, bool verbose, const SceneSeq &scenes, - const MinMax *min_max) + const MinMax *min_max) { checkCapacitancesPreamble(scenes); - CapacitanceCheckSeq &checks = check_capacitances_->check(net, max_count, - violators, - scenes, min_max); + CapacitanceCheckSeq &checks = + check_capacitances_->check(net, max_count, violators, scenes, min_max); if (!checks.empty()) { - report_->reportLine("%s capacitance", min_max->to_string().c_str()); - report_->reportLine(""); + report_->report("{} capacitance", min_max->to_string()); + report_->report(""); if (!verbose) - report_path_->reportLimitShortHeader(report_path_->fieldCapacitance()); + report_path_->reportLimitShortHeader(report_path_->fieldCapacitance()); for (CapacitanceCheck &check : checks) { if (verbose) - report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), - check.pin(), check.rf(), check.capacitance(), - check.limit(), check.slack(), check.scene(), - min_max); + report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), + check.pin(), check.rf(), + check.capacitance(), check.limit(), + check.slack(), check.scene(), min_max); else - report_path_->reportLimitShort(report_path_->fieldCapacitance(), - check.pin(), check.capacitance(), - check.limit(), check.slack()); - report_->reportLine(""); + report_path_->reportLimitShort(report_path_->fieldCapacitance(), check.pin(), + check.capacitance(), check.limit(), + check.slack()); + report_->report(""); } } } @@ -5810,10 +5748,10 @@ Sta::checkCapacitancesPreamble(const SceneSeq &scenes) void Sta::checkCapacitance(const Pin *pin, const SceneSeq &scenes, - const MinMax *min_max, - // Return values. - float &capacitance, - float &limit, + const MinMax *min_max, + // Return values. + float &capacitance, + float &limit, float &slack, const RiseFall *&rf, const Scene *&scene) @@ -5831,21 +5769,19 @@ size_t Sta::maxCapacitanceViolationCount() { checkCapacitancesPreamble(scenes_); - return check_capacitances_->check(nullptr, 1, true, scenes_, - MinMax::max()).size(); + return check_capacitances_->check(nullptr, 1, true, scenes_, MinMax::max()).size(); } void -Sta::maxCapacitanceCheck(// Return values. - const Pin *&pin, - float &capacitance, - float &slack, - float &limit) +Sta::maxCapacitanceCheck( // Return values. + const Pin *&pin, + float &capacitance, + float &slack, + float &limit) { checkCapacitancesPreamble(scenes_); - CapacitanceCheckSeq &checks = check_capacitances_->check(nullptr, 1, false, - scenes_, - MinMax::max()); + CapacitanceCheckSeq &checks = + check_capacitances_->check(nullptr, 1, false, scenes_, MinMax::max()); pin = nullptr; capacitance = 0.0; slack = INF; @@ -5872,7 +5808,7 @@ Sta::reportMinPulseWidthChecks(const Net *net, if (check_min_pulse_widths_ == nullptr) makeCheckMinPulseWidths(); MinPulseWidthCheckSeq &checks = - check_min_pulse_widths_->check(net, max_count, violators, scenes); + check_min_pulse_widths_->check(net, max_count, violators, scenes); report_path_->reportMpwChecks(checks, verbose); } @@ -5889,8 +5825,8 @@ Sta::reportMinPeriodChecks(const Net *net, ensureClkArrivals(); if (check_min_periods_ == nullptr) makeCheckMinPeriods(); - MinPeriodCheckSeq &checks = check_min_periods_->check(net, max_count, - violators, scenes); + MinPeriodCheckSeq &checks = + check_min_periods_->check(net, max_count, violators, scenes); report_path_->reportChecks(checks, verbose); } @@ -5904,7 +5840,8 @@ Sta::reportMaxSkewChecks(const Net *net, const SceneSeq &scenes) { maxSkewPreamble(); - MaxSkewCheckSeq &checks = check_max_skews_->check(net, max_count, violators, scenes); + MaxSkewCheckSeq &checks = + check_max_skews_->check(net, max_count, violators, scenes); report_path_->reportChecks(checks, verbose); } @@ -5920,7 +5857,7 @@ Sta::maxSkewPreamble() void Sta::makeEquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs) + LibertyLibrarySeq *map_libs) { delete equiv_cells_; equiv_cells_ = new EquivCells(equiv_libs, map_libs); @@ -5945,8 +5882,8 @@ Sta::writeTimingModel(const char *lib_name, { ensureLibLinked(); ensureGraph(); - LibertyLibrary *library = makeTimingModel(lib_name, cell_name, filename, - scene, this); + LibertyLibrary *library = + makeTimingModel(lib_name, cell_name, filename, scene, this); writeLiberty(library, filename, this); } @@ -6012,13 +5949,13 @@ Sta::powerPreamble(const Scene *scene) void Sta::power(const Scene *scene, - // Return values. - PowerResult &total, - PowerResult &sequential, - PowerResult &combinational, - PowerResult &clock, - PowerResult ¯o, - PowerResult &pad) + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad) { powerPreamble(scene); power_->power(scene, total, sequential, combinational, clock, macro, pad); @@ -6053,9 +5990,8 @@ Sta::writePathSpice(const Path *path, CircuitSim ckt_sim) { ensureLibLinked(); - sta::writePathSpice(path, spice_filename, subckt_filename, - lib_subckt_filename, model_filename, - power_name, gnd_name, ckt_sim, this); + sta::writePathSpice(path, spice_filename, subckt_filename, lib_subckt_filename, + model_filename, power_name, gnd_name, ckt_sim, this); } //////////////////////////////////////////////////////////////// @@ -6113,4 +6049,4 @@ Sta::clkPinsInvalid(const Mode *mode) mode->clkNetwork()->clkPinsInvalid(); } -} // namespace +} // namespace sta diff --git a/search/Tag.cc b/search/Tag.cc index a242be9c..9e5113e4 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -160,10 +160,10 @@ Tag::to_string(bool report_index, for (ExceptionState *state : *states_) { ExceptionPath *exception = state->exception(); result += " "; - result += exception->asString(network); + result += exception->to_string(network); if (state->nextThru()) { result += " (next thru "; - result += state->nextThru()->asString(network); + result += state->nextThru()->to_string(network); result += ")"; } else { diff --git a/search/TagGroup.cc b/search/TagGroup.cc index 67d0d5eb..85fc29a5 100644 --- a/search/TagGroup.cc +++ b/search/TagGroup.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "TagGroup.hh" @@ -43,7 +43,8 @@ TagGroup::TagGroup(TagGroupIndex index, bool has_loop_tag, const StaState *sta) : path_index_map_(path_index_map), - hash_(hash(path_index_map, sta)), + hash_(hash(path_index_map, + sta)), ref_count_(0), index_(index), has_clk_tag_(has_clk_tag), @@ -57,7 +58,8 @@ TagGroup::TagGroup(TagGroupIndex index, TagGroup::TagGroup(TagGroupBldr *tag_bldr, const StaState *sta) : path_index_map_(&tag_bldr->pathIndexMap()), - hash_(hash(path_index_map_, sta)), + hash_(hash(path_index_map_, + sta)), ref_count_(0), own_path_map_(false) { @@ -121,7 +123,7 @@ void TagGroup::report(const StaState *sta) const { Report *report = sta->report(); - report->reportLine("Group %u hash = %zu", index_, hash_); + report->report("Group {} hash = {}", index_, hash_); pathIndexMapReport(path_index_map_, sta); } @@ -137,9 +139,7 @@ pathIndexMapReport(const PathIndexMap *path_index_map, { Report *report = sta->report(); for (auto const [tag, path_index] : *path_index_map) - report->reportLine(" %2zu %s", - path_index, - tag->to_string(sta).c_str()); + report->report(" {:2} {}", path_index, tag->to_string(sta)); report->reportBlankLine(); } @@ -147,10 +147,10 @@ pathIndexMapReport(const PathIndexMap *path_index_map, TagGroupBldr::TagGroupBldr(bool match_crpr_clk_pin, const StaState *sta) : - default_path_count_(sta->scenes().size() - * RiseFall::index_count + default_path_count_(sta->scenes().size() * RiseFall::index_count * MinMax::index_count), - path_index_map_(TagMatchLess(match_crpr_clk_pin, sta)), + path_index_map_(TagMatchLess(match_crpr_clk_pin, + sta)), paths_(default_path_count_), has_clk_tag_(false), has_genclk_src_tag_(false), @@ -213,7 +213,7 @@ TagGroupBldr::tagMatchPath(Tag *tag, } } -Arrival +Arrival TagGroupBldr::arrival(size_t path_index) const { return paths_[path_index].arrival(); @@ -247,8 +247,8 @@ TagGroupBldr::setMatchPath(Path *match, path_index_map_.erase(tag_match); path_index_map_[tag] = path_index; } - paths_[path_index].init(vertex_, tag, arrival, prev_path, - prev_edge, prev_arc, sta_); + paths_[path_index].init(vertex_, tag, arrival, prev_path, prev_edge, prev_arc, + sta_); } else insertPath(tag, arrival, prev_path, prev_edge, prev_arc); @@ -264,15 +264,13 @@ TagGroupBldr::insertPath(Tag *tag, { size_t path_index = paths_.size(); path_index_map_[tag] = path_index; - paths_.emplace_back(vertex_, tag, arrival, prev_path, - prev_edge, prev_arc, sta_); + paths_.emplace_back(vertex_, tag, arrival, prev_path, prev_edge, prev_arc, sta_); if (tag->isClock()) has_clk_tag_ = true; if (tag->isGenClkSrcPath()) has_genclk_src_tag_ = true; - if (tag->isFilter() - || tag->clkInfo()->crprPathRefsFilter()) + if (tag->isFilter() || tag->clkInfo()->crprPathRefsFilter()) has_filter_tag_ = true; if (tag->isLoop()) has_loop_tag_ = true; @@ -283,18 +281,16 @@ TagGroupBldr::insertPath(Tag *tag, void TagGroupBldr::insertPath(const Path &path) { - insertPath(path.tag(sta_), path.arrival(), path.prevPath(), - path.prevEdge(sta_), path.prevArc(sta_)); + insertPath(path.tag(sta_), path.arrival(), path.prevPath(), path.prevEdge(sta_), + path.prevArc(sta_)); } TagGroup * TagGroupBldr::makeTagGroup(TagGroupIndex index, const StaState *sta) { - return new TagGroup(index, makePathIndexMap(sta), - has_clk_tag_, has_genclk_src_tag_, has_filter_tag_, - has_loop_tag_, sta); - + return new TagGroup(index, makePathIndexMap(sta), has_clk_tag_, + has_genclk_src_tag_, has_filter_tag_, has_loop_tag_, sta); } PathIndexMap * @@ -352,9 +348,9 @@ TagGroupEqual::operator()(const TagGroup *tag_group1, const TagGroup *tag_group2) const { return tag_group1 == tag_group2 - || (tag_group1->hash() == tag_group2->hash() - && pathIndexMapEqual(tag_group1->pathIndexMap(), - tag_group2->pathIndexMap())); + || (tag_group1->hash() == tag_group2->hash() + && pathIndexMapEqual(tag_group1->pathIndexMap(), + tag_group2->pathIndexMap())); } -} // namespace +} // namespace sta diff --git a/search/VisitPathEnds.cc b/search/VisitPathEnds.cc index 0c414c86..4a668d9b 100644 --- a/search/VisitPathEnds.cc +++ b/search/VisitPathEnds.cc @@ -67,8 +67,8 @@ VisitPathEnds::visitPathEnds(Vertex *vertex, // Ignore slack on bidirect driver vertex. The load vertex gets the slack. if (!vertex->isBidirectDriver()) { const Pin *pin = vertex->pin(); - debugPrint(debug_, "search", 2, "find end slack %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "search", 2, "find end slack {}", + vertex->to_string(this)); visitor->vertexBegin(vertex); bool is_constrained = false; visitClkedPathEnds(pin, vertex, scenes, min_max, filtered, visitor, diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index 8a19ca57..a58efb64 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "WorstSlack.hh" @@ -37,7 +37,8 @@ namespace sta { WorstSlacks::WorstSlacks(StaState *sta) : - worst_slacks_(sta->scenePathCount(), sta), + worst_slacks_(sta->scenePathCount(), + sta), sta_(sta) { } @@ -54,8 +55,8 @@ WorstSlacks::worstSlack(const MinMax *min_max, PathAPIndex path_ap_index = scene->pathIndex(min_max); Slack worst_slack1; Vertex *worst_vertex1; - worst_slacks_[path_ap_index].worstSlack(path_ap_index, - worst_slack1, worst_vertex1); + worst_slacks_[path_ap_index].worstSlack(path_ap_index, worst_slack1, + worst_vertex1); if (delayLess(worst_slack1, worst_slack, sta_)) { worst_slack = worst_slack1; worst_vertex = worst_vertex1; @@ -71,8 +72,7 @@ WorstSlacks::worstSlack(const Scene *scene, Vertex *&worst_vertex) { PathAPIndex path_ap_index = scene->pathIndex(min_max); - worst_slacks_[path_ap_index].worstSlack(path_ap_index, - worst_slack, worst_vertex); + worst_slacks_[path_ap_index].worstSlack(path_ap_index, worst_slack, worst_vertex); } void @@ -105,10 +105,7 @@ WorstSlack::WorstSlack(StaState *sta) : { } -WorstSlack::~WorstSlack() -{ - delete queue_; -} +WorstSlack::~WorstSlack() { delete queue_; } WorstSlack::WorstSlack(const WorstSlack &worst_slack) : StaState(worst_slack), @@ -164,7 +161,7 @@ WorstSlack::initQueue(PathAPIndex path_ap_index) worst_vertex_ = nullptr; worst_slack_ = slack_init_; slack_threshold_ = slack_init_; - for(Vertex *vertex : search_->endpoints()) { + for (Vertex *vertex : search_->endpoints()) { Slack slack = search_->wnsSlack(vertex, path_ap_index); if (!delayEqual(slack, slack_init_, this)) { if (delayLess(slack, worst_slack_, this)) @@ -176,7 +173,7 @@ WorstSlack::initQueue(PathAPIndex path_ap_index) sortQueue(path_ap_index); } } - debugPrint(debug_, "wns", 3, "threshold %s", + debugPrint(debug_, "wns", 3, "threshold {}", delayAsString(slack_threshold_, MinMax::max(), this)); //checkQueue(); } @@ -198,7 +195,7 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index) int threshold_index = std::min(min_queue_size_, vertex_count - 1); Vertex *threshold_vertex = vertices[threshold_index]; slack_threshold_ = search_->wnsSlack(threshold_vertex, path_ap_index); - debugPrint(debug_, "wns", 3, "threshold %s", + debugPrint(debug_, "wns", 3, "threshold {}", delayAsString(slack_threshold_, MinMax::max(), this)); // Reinsert vertices with slack < threshold. @@ -234,9 +231,9 @@ void WorstSlack::checkQueue(PathAPIndex path_ap_index) { VertexSeq ends; - for(Vertex *end : search_->endpoints()) { - if (delayLessEqual(search_->wnsSlack(end, path_ap_index), - slack_threshold_, this)) + for (Vertex *end : search_->endpoints()) { + if (delayLessEqual(search_->wnsSlack(end, path_ap_index), slack_threshold_, + this)) ends.push_back(end); } WnsSlackLess slack_less(path_ap_index, this); @@ -246,20 +243,18 @@ WorstSlack::checkQueue(PathAPIndex path_ap_index) for (Vertex *end : ends) { end_set.insert(end); if (!queue_->contains(end) - && delayLessEqual(search_->wnsSlack(end, path_ap_index), - slack_threshold_, this)) - report_->reportLine("WorstSlack queue missing %s %s < %s", - end->to_string(this).c_str(), - delayAsString(search_->wnsSlack(end, path_ap_index), this), - delayAsString(slack_threshold_, this)); + && delayLessEqual(search_->wnsSlack(end, path_ap_index), slack_threshold_, + this)) + report_->report("WorstSlack queue missing {} {} < {}", end->to_string(this), + delayAsString(search_->wnsSlack(end, path_ap_index), this), + delayAsString(slack_threshold_, this)); } for (Vertex *end : *queue_) { if (!end_set.contains(end)) - report_->reportLine("WorstSlack queue extra %s %s > %s", - end->to_string(this).c_str(), - delayAsString(search_->wnsSlack(end, path_ap_index), this), - delayAsString(slack_threshold_, this)); + report_->report("WorstSlack queue extra {} {} > {}", end->to_string(this), + delayAsString(search_->wnsSlack(end, path_ap_index), this), + delayAsString(slack_threshold_, this)); } } @@ -274,8 +269,7 @@ WorstSlack::updateWorstSlack(Vertex *vertex, // Locking is required because ArrivalVisitor is called by multiple // threads. LockGuard lock(lock_); - if (worst_vertex_ - && delayLess(slack, worst_slack_, this)) + if (worst_vertex_ && delayLess(slack, worst_slack_, this)) setWorstSlack(vertex, slack); else if (vertex == worst_vertex_) // Mark worst slack as unknown (updated by findWorstSlack(). @@ -283,18 +277,16 @@ WorstSlack::updateWorstSlack(Vertex *vertex, if (!delayEqual(slack, slack_init_, this) && delayLessEqual(slack, slack_threshold_, this)) { - debugPrint(debug_, "wns", 3, "insert %s %s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "wns", 3, "insert {} {}", vertex->to_string(this), delayAsString(slack, this)); queue_->insert(vertex); } else { - debugPrint(debug_, "wns", 3, "delete %s %s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "wns", 3, "delete {} {}", vertex->to_string(this), delayAsString(slack, this)); queue_->erase(vertex); } - //checkQueue(path_ap_index); + // checkQueue(path_ap_index); } } @@ -302,8 +294,7 @@ void WorstSlack::setWorstSlack(Vertex *vertex, Slack slack) { - debugPrint(debug_, "wns", 3, "%s %s", - vertex->to_string(this).c_str(), + debugPrint(debug_, "wns", 3, "{} {}", vertex->to_string(this), delayAsString(slack, this)); worst_vertex_ = vertex; worst_slack_ = slack; @@ -323,8 +314,7 @@ WnsSlackLess::operator()(Vertex *vertex1, Vertex *vertex2) { return delayLess(search_->wnsSlack(vertex1, path_ap_index_), - search_->wnsSlack(vertex2, path_ap_index_), - search_); + search_->wnsSlack(vertex2, path_ap_index_), search_); } -} // namespace +} // namespace sta diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 95fabc42..e5c019bd 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -30,6 +30,7 @@ #include "Debug.hh" #include "Error.hh" #include "Report.hh" +#include "Format.hh" #include "StringUtil.hh" #include "FuncExpr.hh" #include "Units.hh" @@ -203,7 +204,7 @@ WritePathSpice::writeSpice() writeInputSource(); writeStageInstances(); writeStageSubckts(); - streamPrint(spice_stream_, ".end\n"); + sta::print(spice_stream_, ".end\n"); spice_stream_.close(); } else @@ -214,11 +215,11 @@ void WritePathSpice::writeHeader() { const Path *start_path = path_expanded_.startPath(); - std::string title = stdstrPrint("Path from %s %s to %s %s", - network_->pathName(start_path->pin(this)), - start_path->transition(this)->shortName(), - network_->pathName(path_->pin(this)), - path_->transition(this)->shortName()); + std::string title = sta::format("Path from {} {} to {} {}", + network_->pathName(start_path->pin(this)), + start_path->transition(this)->shortName(), + network_->pathName(path_->pin(this)), + path_->transition(this)->shortName()); float max_time = maxTime(); float time_step = 1e-13; writeHeader(title, max_time, time_step); @@ -281,37 +282,37 @@ WritePathSpice::pathMaxTime() void WritePathSpice::writeStageInstances() { - streamPrint(spice_stream_, "*****************\n"); - streamPrint(spice_stream_, "* Stage instances\n"); - streamPrint(spice_stream_, "*****************\n\n"); + sta::print(spice_stream_, "*****************\n"); + sta::print(spice_stream_, "* Stage instances\n"); + sta::print(spice_stream_, "*****************\n\n"); for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { std::string stage_name = stageName(stage); const char *stage_cname = stage_name.c_str(); if (stage == stageFirst()) - streamPrint(spice_stream_, "x%s %s %s %s\n", - stage_cname, - stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_cname); + sta::print(spice_stream_, "x{} {} {} {}\n", + stage_cname, + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_cname); else { - streamPrint(spice_stream_, "x%s %s %s %s %s\n", - stage_cname, - stageGateInputPinName(stage), - stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_cname); + sta::print(spice_stream_, "x{} {} {} {} {}\n", + stage_cname, + stageGateInputPinName(stage), + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_cname); } } - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); } void WritePathSpice::writeInputSource() { - streamPrint(spice_stream_, "**************\n"); - streamPrint(spice_stream_, "* Input source\n"); - streamPrint(spice_stream_, "**************\n\n"); + sta::print(spice_stream_, "**************\n"); + sta::print(spice_stream_, "* Input source\n"); + sta::print(spice_stream_, "**************\n\n"); Stage input_stage = stageFirst(); const Path *input_path = stageDrvrPath(input_stage); @@ -319,7 +320,7 @@ WritePathSpice::writeInputSource() writeClkWaveform(); else writeInputWaveform(); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); } void @@ -372,17 +373,17 @@ WritePathSpice::writeClkWaveform() } float slew0 = findSlew(input_path, rf0, next_arc); float slew1 = findSlew(input_path, rf1, next_arc); - streamPrint(spice_stream_, "v1 %s 0 pwl(\n", - stageDrvrPinName(input_stage)); - streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + sta::print(spice_stream_, "v1 {} 0 pwl(\n", + stageDrvrPinName(input_stage)); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", 0.0, volt0); for (int cycle = 0; cycle < clk_cycle_count_; cycle++) { float time0 = time_offset + cycle * period; float time1 = time0 + period / 2.0; writeWaveformEdge(rf0, time0, slew0); writeWaveformEdge(rf1, time1, slew1); } - streamPrint(spice_stream_, "+%.3e %.3e\n", max_time_, volt0); - streamPrint(spice_stream_, "+)\n"); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", max_time_, volt0); + sta::print(spice_stream_, "+)\n"); } float @@ -407,9 +408,9 @@ WritePathSpice::findSlew(const Path *path, void WritePathSpice::writeMeasureStmts() { - streamPrint(spice_stream_, "********************\n"); - streamPrint(spice_stream_, "* Measure statements\n"); - streamPrint(spice_stream_, "********************\n\n"); + sta::print(spice_stream_, "********************\n"); + sta::print(spice_stream_, "* Measure statements\n"); + sta::print(spice_stream_, "********************\n\n"); for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { const Path *gate_input_path = stageGateInputPath(stage); @@ -426,7 +427,7 @@ WritePathSpice::writeMeasureStmts() if (stage == stageLast()) writeMeasureSlewStmt(stage, load_path); } - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); } void @@ -452,9 +453,9 @@ WritePathSpice::writeMeasureSlewStmt(Stage stage, void WritePathSpice::writeStageSubckts() { - streamPrint(spice_stream_, "***************\n"); - streamPrint(spice_stream_, "* Stage subckts\n"); - streamPrint(spice_stream_, "***************\n\n"); + sta::print(spice_stream_, "***************\n"); + sta::print(spice_stream_, "* Stage subckts\n"); + sta::print(spice_stream_, "***************\n\n"); for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { cap_index_ = 1; @@ -476,12 +477,12 @@ WritePathSpice::writeInputStage(Stage stage) const char *drvr_pin_name = stageDrvrPinName(stage); const char *load_pin_name = stageLoadPinName(stage); std::string prefix = stageName(stage); - streamPrint(spice_stream_, ".subckt %s %s %s\n", - prefix.c_str(), - drvr_pin_name, - load_pin_name); + sta::print(spice_stream_, ".subckt {} {} {}\n", + prefix, + drvr_pin_name, + load_pin_name); writeStageParasitics(stage); - streamPrint(spice_stream_, ".ends\n\n"); + sta::print(spice_stream_, ".ends\n\n"); } // Gate and load parasitics. @@ -500,17 +501,17 @@ WritePathSpice::writeGateStage(Stage stage) const LibertyPort *input_port = stageGateInputPort(stage); const LibertyPort *drvr_port = stageDrvrPort(stage); - streamPrint(spice_stream_, ".subckt %s %s %s %s\n", - subckt_name.c_str(), - input_pin_name, - drvr_pin_name, - load_pin_name); + sta::print(spice_stream_, ".subckt {} {} {} {}\n", + subckt_name, + input_pin_name, + drvr_pin_name, + load_pin_name); // Driver subckt call. - streamPrint(spice_stream_, "* Gate %s %s -> %s\n", - network_->pathName(inst), - input_port->name(), - drvr_port->name()); + sta::print(spice_stream_, "* Gate {} {} -> {}\n", + network_->pathName(inst), + input_port->name(), + drvr_port->name()); writeSubcktInst(inst); const Path *drvr_path = stageDrvrPath(stage); @@ -525,7 +526,7 @@ WritePathSpice::writeGateStage(Stage stage) PinSet inputs(network_); inputs.insert(input_pin); writeSubcktInstVoltSrcs(inst, port_values, inputs); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); PinSet drvr_loads(network_); PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin); @@ -537,7 +538,7 @@ WritePathSpice::writeGateStage(Stage stage) writeSubcktInstLoads(drvr_pin, load_pin, drvr_loads, written_insts_); writeStageParasitics(stage); - streamPrint(spice_stream_, ".ends\n\n"); + sta::print(spice_stream_, ".ends\n\n"); } void @@ -575,7 +576,7 @@ WritePathSpice::findPathCellNames() if (arc) { LibertyCell *cell = arc->set()->libertyCell(); if (cell) { - debugPrint(debug_, "write_spice", 2, "cell %s", cell->name()); + debugPrint(debug_, "write_spice", 2, "cell {}", cell->name()); path_cell_names.insert(cell->name()); } // Include side receivers. @@ -612,9 +613,7 @@ WritePathSpice::stageLast() std::string WritePathSpice::stageName(Stage stage) { - std::string name; - stringPrint(name, "stage%d", stage); - return name; + return sta::format("stage{}", stage); } int diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 1e844ed5..6a62785c 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -1,30 +1,30 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "spice/WriteSpice.hh" -#include // swap +#include // swap #include #include #include @@ -60,7 +60,7 @@ class SubcktEndsMissing : public Exception { public: SubcktEndsMissing(const char *cell_name, - const char *subckt_filename); + const char *subckt_filename); const char *what() const noexcept; protected: @@ -68,7 +68,7 @@ protected: }; SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, - const char *subckt_filename) : + const char *subckt_filename) : Exception() { what_ = "spice subckt for cell "; @@ -122,7 +122,7 @@ WriteSpice::initPowerGnd() default_library_->supplyVoltage(power_name_, power_voltage_, exists); if (!exists) { const OperatingConditions *op_cond = - scene_->sdc()->operatingConditions(min_max_); + scene_->sdc()->operatingConditions(min_max_); if (op_cond == nullptr) op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions(); power_voltage_ = op_cond->voltage(); @@ -137,31 +137,31 @@ WriteSpice::writeHeader(std::string &title, float max_time, float time_step) { - streamPrint(spice_stream_, "* %s\n", title.c_str()); - streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_); - std::filesystem::path subckt_filename - = std::filesystem::path(subckt_filename_).filename(); - streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename.c_str()); - streamPrint(spice_stream_, ".tran %.3g %.3g\n", time_step, max_time); + sta::print(spice_stream_, "* {}\n", title); + sta::print(spice_stream_, ".include \"{}\"\n", model_filename_); + std::filesystem::path subckt_filename = + std::filesystem::path(subckt_filename_).filename(); + sta::print(spice_stream_, ".include \"{}\"\n", subckt_filename.string()); + sta::print(spice_stream_, ".tran {:.3g} {:.3g}\n", time_step, max_time); // Suppress printing model parameters. if (ckt_sim_ == CircuitSim::hspice) - streamPrint(spice_stream_, ".options nomod\n"); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, ".options nomod\n"); + sta::print(spice_stream_, "\n"); max_time_ = max_time; } void WriteSpice::writePrintStmt(StringSeq &node_names) { - streamPrint(spice_stream_, ".print tran"); + sta::print(spice_stream_, ".print tran"); if (ckt_sim_ == CircuitSim::xyce) { std::string csv_filename = replaceFileExt(spice_filename_, "csv"); - streamPrint(spice_stream_, " format=csv file=%s", csv_filename.c_str()); + sta::print(spice_stream_, " format=csv file={}", csv_filename); writeGnuplotFile(node_names); } for (std::string &name : node_names) - streamPrint(spice_stream_, " v(%s)", name.c_str()); - streamPrint(spice_stream_, "\n\n"); + sta::print(spice_stream_, " v({})", name); + sta::print(spice_stream_, "\n\n"); } std::string @@ -183,17 +183,16 @@ WriteSpice::writeGnuplotFile(StringSeq &node_nanes) std::ofstream gnuplot_stream; gnuplot_stream.open(gnuplot_filename); if (gnuplot_stream.is_open()) { - streamPrint(gnuplot_stream, "set datafile separator ','\n"); - streamPrint(gnuplot_stream, "set key autotitle columnhead\n"); - streamPrint(gnuplot_stream, "plot\\\n"); - streamPrint(gnuplot_stream, "\"%s\" using 1:2 with lines", - csv_filename.c_str()); + sta::print(gnuplot_stream, "set datafile separator ','\n"); + sta::print(gnuplot_stream, "set key autotitle columnhead\n"); + sta::print(gnuplot_stream, "plot\\\n"); + sta::print(gnuplot_stream, "\"{}\" using 1:2 with lines", csv_filename); for (size_t i = 3; i <= node_nanes.size() + 1; i++) { - streamPrint(gnuplot_stream, ",\\\n"); - streamPrint(gnuplot_stream, "'' using 1:%zu with lines", i); + sta::print(gnuplot_stream, ",\\\n"); + sta::print(gnuplot_stream, "'' using 1:{} with lines", i); } - streamPrint(gnuplot_stream, "\n"); - streamPrint(gnuplot_stream, "pause mouse close\n"); + sta::print(gnuplot_stream, "\n"); + sta::print(gnuplot_stream, "pause mouse close\n"); gnuplot_stream.close(); } } @@ -208,28 +207,27 @@ WriteSpice::writeSubckts(StringSet &cell_names) if (subckts_stream.is_open()) { std::string line; while (std::getline(lib_subckts_stream, line)) { - // .subckt [args..] - StringSeq tokens = parseTokens(line, ' '); - if (tokens.size() >= 2 - && stringEqual(tokens[0].c_str(), ".subckt")) { - const char *cell_name = tokens[1].c_str(); + // .subckt [args..] + StringSeq tokens = parseTokens(line, ' '); + if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { + const char *cell_name = tokens[1].c_str(); if (cell_names.contains(cell_name)) { - subckts_stream << line << "\n"; - bool found_ends = false; - while (std::getline(lib_subckts_stream, line)) { - subckts_stream << line << "\n"; - if (stringBeginEqual(line.c_str(), ".ends")) { - subckts_stream << "\n"; - found_ends = true; - break; - } - } - if (!found_ends) - throw SubcktEndsMissing(cell_name, lib_subckt_filename_); - cell_names.erase(cell_name); - } - recordSpicePortNames(cell_name, tokens); - } + subckts_stream << line << "\n"; + bool found_ends = false; + while (std::getline(lib_subckts_stream, line)) { + subckts_stream << line << "\n"; + if (stringBeginEqual(line.c_str(), ".ends")) { + subckts_stream << "\n"; + found_ends = true; + break; + } + } + if (!found_ends) + throw SubcktEndsMissing(cell_name, lib_subckt_filename_); + cell_names.erase(cell_name); + } + recordSpicePortNames(cell_name, tokens); + } } subckts_stream.close(); lib_subckts_stream.close(); @@ -240,9 +238,8 @@ WriteSpice::writeSubckts(StringSet &cell_names) missing_cells += "\n"; missing_cells += cell_name; } - report_->error(1605, "The subkct file %s is missing definitions for %s", - lib_subckt_filename_, - missing_cells.c_str()); + report_->error(1605, "The subkct file {} is missing definitions for {}", + lib_subckt_filename_, missing_cells); } } else { @@ -265,12 +262,13 @@ WriteSpice::recordSpicePortNames(const char *cell_name, const char *port_name = tokens[i].c_str(); LibertyPort *port = cell->findLibertyPort(port_name); LibertyPort *pg_port = cell->findLibertyPort(port_name); - if (port == nullptr - && pg_port == nullptr - && !stringEqual(port_name, power_name_) - && !stringEqual(port_name, gnd_name_)) - report_->error(1606, "subckt %s port %s has no corresponding liberty port, pg_port and is not power or ground.", - cell_name, port_name); + if (port == nullptr && pg_port == nullptr + && !stringEqual(port_name, power_name_) + && !stringEqual(port_name, gnd_name_)) + report_->error(1606, + "subckt {} port {} has no corresponding liberty port, " + "pg_port and is not power or ground.", + cell_name, port_name); spice_port_names.push_back(port_name); } } @@ -286,8 +284,7 @@ WriteSpice::findCellSubckts(StringSet &cell_names) while (std::getline(lib_subckts_stream, line)) { // .subckt [args..] StringSeq tokens = parseTokens(line, ' '); - if (tokens.size() >= 2 - && stringEqual(tokens[0].c_str(), ".subckt")) { + if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); if (cell_names.contains(cell_name)) { // Scan the subckt definition for subckt calls. @@ -324,7 +321,7 @@ WriteSpice::writeSubcktInst(const Instance *inst) LibertyCell *cell = network_->libertyCell(inst); const char *cell_name = cell->name(); StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; - streamPrint(spice_stream_, "x%s", inst_name); + sta::print(spice_stream_, "x{}", inst_name); for (std::string subckt_port_name : spice_port_names) { const char *subckt_port_cname = subckt_port_name.c_str(); Pin *pin = network_->findPin(inst, subckt_port_cname); @@ -332,15 +329,15 @@ WriteSpice::writeSubcktInst(const Instance *inst) const char *pin_name; if (pin) { pin_name = network_->pathName(pin); - streamPrint(spice_stream_, " %s", pin_name); + sta::print(spice_stream_, " {}", pin_name); } else if (pg_port) - streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_cname); + sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_cname); else if (stringEq(subckt_port_cname, power_name_) - || stringEq(subckt_port_cname, gnd_name_)) - streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_cname); + || stringEq(subckt_port_cname, gnd_name_)) + sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_cname); } - streamPrint(spice_stream_, " %s\n", cell_name); + sta::print(spice_stream_, " {}\n", cell_name); } // Power/ground and input voltage sources. @@ -354,24 +351,21 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; const char *inst_name = network_->pathName(inst); - debugPrint(debug_, "write_spice", 2, "subckt %s", cell->name()); + debugPrint(debug_, "write_spice", 2, "subckt {}", cell->name()); for (std::string subckt_port_sname : spice_port_names) { const char *subckt_port_name = subckt_port_sname.c_str(); LibertyPort *port = cell->findLibertyPort(subckt_port_name); const Pin *pin = port ? network_->findPin(inst, port) : nullptr; bool is_pg_port = port && port->isPwrGnd(); - debugPrint(debug_, "write_spice", 2, " port %s%s", - subckt_port_name, + debugPrint(debug_, "write_spice", 2, " port {}{}", subckt_port_name, is_pg_port ? " pwr/gnd" : ""); if (is_pg_port) - writeVoltageSource(inst_name, subckt_port_name, - pgPortVoltage(port)); + writeVoltageSource(inst_name, subckt_port_name, pgPortVoltage(port)); else if (stringEq(subckt_port_name, power_name_)) writeVoltageSource(inst_name, subckt_port_name, power_voltage_); else if (stringEq(subckt_port_name, gnd_name_)) writeVoltageSource(inst_name, subckt_port_name, gnd_voltage_); - else if (port - && !excluded_input_pins.contains(pin) + else if (port && !excluded_input_pins.contains(pin) && port->direction()->isAnyInput()) { // Input voltage to sensitize path from gate input to output. // Look for tie high/low or propagated constant values. @@ -384,20 +378,18 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, port_value = value; } switch (port_value) { - case LogicValue::zero: - case LogicValue::unknown: - writeVoltageSource(cell, inst_name, subckt_port_name, - port->relatedGroundPin(), - gnd_voltage_); - break; - case LogicValue::one: - writeVoltageSource(cell, inst_name, subckt_port_name, - port->relatedPowerPin(), - power_voltage_); - break; - case LogicValue::rise: - case LogicValue::fall: - break; + case LogicValue::zero: + case LogicValue::unknown: + writeVoltageSource(cell, inst_name, subckt_port_name, + port->relatedGroundPin(), gnd_voltage_); + break; + case LogicValue::one: + writeVoltageSource(cell, inst_name, subckt_port_name, + port->relatedPowerPin(), power_voltage_); + break; + case LogicValue::rise: + case LogicValue::fall: + break; } } } @@ -426,10 +418,7 @@ WriteSpice::writeVoltageSource(LibertyCell *cell, if (pg_port) voltage = pgPortVoltage(pg_port); else - report_->error(1603, "%s pg_port %s not found,", - cell->name(), - pg_port_name); - + report_->error(1603, "{} pg_port {} not found,", cell->name(), pg_port_name); } writeVoltageSource(inst_name, subckt_port_name, voltage); } @@ -445,20 +434,18 @@ WriteSpice::pgPortVoltage(LibertyPort *pg_port) liberty->supplyVoltage(voltage_name, voltage, exists); if (!exists) { if (stringEqual(voltage_name, power_name_)) - voltage = power_voltage_; + voltage = power_voltage_; else if (stringEqual(voltage_name, gnd_name_)) - voltage = gnd_voltage_; + voltage = gnd_voltage_; else - report_->error(1601 , "pg_pin %s/%s voltage %s not found,", - pg_port->libertyCell()->name(), - pg_port->name(), - voltage_name); + report_->error(1601, "pg_pin {}/{} voltage {} not found,", + pg_port->libertyCell()->name(), pg_port->name(), + voltage_name); } } else - report_->error(1602, "Liberty pg_port %s/%s missing voltage_name attribute,", - pg_port->libertyCell()->name(), - pg_port->name()); + report_->error(1602, "Liberty pg_port {}/{} missing voltage_name attribute,", + pg_port->libertyCell()->name(), pg_port->name()); return voltage; } @@ -488,19 +475,19 @@ WriteSpice::slewAxisMinValue(const TimingArc *arc) const TableAxis *axis1 = model->axis1(); TableAxisVariable var1 = axis1->variable(); if (var1 == TableAxisVariable::input_transition_time - || var1 == TableAxisVariable::input_net_transition) + || var1 == TableAxisVariable::input_net_transition) return axis1->axisValue(0); const TableAxis *axis2 = model->axis2(); TableAxisVariable var2 = axis2->variable(); if (var2 == TableAxisVariable::input_transition_time - || var2 == TableAxisVariable::input_net_transition) + || var2 == TableAxisVariable::input_net_transition) return axis2->axisValue(0); const TableAxis *axis3 = model->axis3(); TableAxisVariable var3 = axis3->variable(); if (var3 == TableAxisVariable::input_transition_time - || var3 == TableAxisVariable::input_net_transition) + || var3 == TableAxisVariable::input_net_transition) return axis3->axisValue(0); } return 0.0; @@ -514,15 +501,16 @@ WriteSpice::writeDrvrParasitics(const Pin *drvr_pin, const NetSet &coupling_nets) { Net *net = network_->net(drvr_pin); - const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); - streamPrint(spice_stream_, "* Net %s\n", net_name); + const char *net_name = + net ? network_->pathName(net) : network_->pathName(drvr_pin); + sta::print(spice_stream_, "* Net {}\n", net_name); if (parasitics_->isParasiticNetwork(parasitic)) writeParasiticNetwork(drvr_pin, parasitic, coupling_nets); else if (parasitics_->isPiElmore(parasitic)) writePiElmore(drvr_pin, parasitic); else { - streamPrint(spice_stream_, "* Net has no parasitics.\n"); + sta::print(spice_stream_, "* Net has no parasitics.\n"); writeNullParasitic(drvr_pin); } } @@ -532,22 +520,18 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, const Parasitic *parasitic, const NetSet &coupling_nets) { - std::set reachable_pins; + std::set reachable_pins; // Sort resistors for consistent regression results. ParasiticResistorSeq resistors = parasitics_->resistors(parasitic); - sort(resistors, [this] (const ParasiticResistor *r1, - const ParasiticResistor *r2) { - return parasitics_->id(r1) < parasitics_->id(r2); - }); + sort(resistors, [this](const ParasiticResistor *r1, const ParasiticResistor *r2) { + return parasitics_->id(r1) < parasitics_->id(r2); + }); for (ParasiticResistor *resistor : resistors) { float resistance = parasitics_->value(resistor); ParasiticNode *node1 = parasitics_->node1(resistor); ParasiticNode *node2 = parasitics_->node2(resistor); - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index_++, - parasitics_->name(node1), - parasitics_->name(node2), - resistance); + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", res_index_++, + parasitics_->name(node1), parasitics_->name(node2), resistance); // Necessary but not sufficient. Need a DFS. const Pin *pin1 = parasitics_->pin(node1); @@ -562,15 +546,11 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); - if (pin != drvr_pin - && network_->isLoad(pin) - && !network_->isHierarchical(pin) + if (pin != drvr_pin && network_->isLoad(pin) && !network_->isHierarchical(pin) && !reachable_pins.contains(pin)) { - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index_++, - network_->pathName(drvr_pin), - network_->pathName(pin), - short_ckt_resistance_); + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", res_index_++, + network_->pathName(drvr_pin), network_->pathName(pin), + short_ckt_resistance_); } } delete pin_iter; @@ -578,28 +558,25 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, // Grounded node capacitors. // Sort nodes for consistent regression results. ParasiticNodeSeq nodes = parasitics_->nodes(parasitic); - sort(nodes, [this] (const ParasiticNode *node1, - const ParasiticNode *node2) { - const char *name1 = parasitics_->name(node1); - const char *name2 = parasitics_->name(node2); - return stringLess(name1, name2); - }); + sort(nodes, [this](const ParasiticNode *node1, const ParasiticNode *node2) { + const char *name1 = parasitics_->name(node1); + const char *name2 = parasitics_->name(node2); + return stringLess(name1, name2); + }); for (ParasiticNode *node : nodes) { float cap = parasitics_->nodeGndCap(node); // Spice has a cow over zero value caps. if (cap > 0.0) { - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index_++, - parasitics_->name(node), - cap); + sta::print(spice_stream_, "C{} {} 0 {:.3e}\n", cap_index_++, + parasitics_->name(node), cap); } } // Sort coupling capacitors for consistent regression results. ParasiticCapacitorSeq capacitors = parasitics_->capacitors(parasitic); - sort(capacitors, [this] (const ParasiticCapacitor *c1, - const ParasiticCapacitor *c2) { + sort(capacitors, + [this](const ParasiticCapacitor *c1, const ParasiticCapacitor *c2) { return parasitics_->id(c1) < parasitics_->id(c2); }); const Net *net = pinNet(drvr_pin, network_); @@ -615,16 +592,11 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, } if (net2 && coupling_nets.contains(net2)) // Write half the capacitance because the coupled net will do the same. - streamPrint(spice_stream_, "C%d %s %s %.3e\n", - cap_index_++, - parasitics_->name(node1), - parasitics_->name(node2), - cap * .5); + sta::print(spice_stream_, "C{} {} {} {:.3e}\n", cap_index_++, + parasitics_->name(node1), parasitics_->name(node2), cap * .5); else - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index_++, - parasitics_->name(node1), - cap); + sta::print(spice_stream_, "C{} {} 0 {:.3e}\n", cap_index_++, + parasitics_->name(node1), cap); } } @@ -650,50 +622,35 @@ WriteSpice::writePiElmore(const Pin *drvr_pin, float c2, rpi, c1; parasitics_->piModel(parasitic, c2, rpi, c1); const char *c1_node = "n1"; - streamPrint(spice_stream_, "RPI %s %s %.3e\n", - network_->pathName(drvr_pin), - c1_node, - rpi); + sta::print(spice_stream_, "RPI {} {} {:.3e}\n", network_->pathName(drvr_pin), + c1_node, rpi); if (c2 > 0.0) - streamPrint(spice_stream_, "C2 %s 0 %.3e\n", - network_->pathName(drvr_pin), - c2); + sta::print(spice_stream_, "C2 {} 0 {:.3e}\n", network_->pathName(drvr_pin), c2); if (c1 > 0.0) - streamPrint(spice_stream_, "C1 %s 0 %.3e\n", - c1_node, - c1); - + sta::print(spice_stream_, "C1 {} 0 {:.3e}\n", c1_node, c1); + int load_index = 3; auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *load_pin = pin_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin) - && !network_->isHierarchical(load_pin)) { + if (load_pin != drvr_pin && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { float elmore; bool exists; parasitics_->findElmore(parasitic, load_pin, elmore, exists); if (exists) { - streamPrint(spice_stream_, "E%d el%d 0 %s 0 1.0\n", - load_index, - load_index, - network_->pathName(drvr_pin)); - streamPrint(spice_stream_, "R%d el%d %s 1.0\n", - load_index, - load_index, - network_->pathName(load_pin)); - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - load_index, - network_->pathName(load_pin), - elmore); + sta::print(spice_stream_, "E{} el{} 0 {} 0 1.0\n", load_index, load_index, + network_->pathName(drvr_pin)); + sta::print(spice_stream_, "R{} el{} {} 1.0\n", load_index, load_index, + network_->pathName(load_pin)); + sta::print(spice_stream_, "C{} {} 0 {:.3e}\n", load_index, + network_->pathName(load_pin), elmore); } else // Add resistor from drvr to load for missing elmore. - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - load_index, - network_->pathName(drvr_pin), - network_->pathName(load_pin), - short_ckt_resistance_); + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", load_index, + network_->pathName(drvr_pin), network_->pathName(load_pin), + short_ckt_resistance_); load_index++; } } @@ -707,14 +664,11 @@ WriteSpice::writeNullParasitic(const Pin *drvr_pin) auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *load_pin = pin_iter->next(); - if (load_pin != drvr_pin - && network_->isLoad(load_pin) - && !network_->isHierarchical(load_pin)) { - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index_++, - network_->pathName(drvr_pin), - network_->pathName(load_pin), - short_ckt_resistance_); + if (load_pin != drvr_pin && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { + sta::print(spice_stream_, "R{} {} {} {:.3e}\n", res_index_++, + network_->pathName(drvr_pin), network_->pathName(load_pin), + short_ckt_resistance_); } } delete pin_iter; @@ -726,10 +680,7 @@ void WriteSpice::writeVoltageSource(const char *node_name, float voltage) { - streamPrint(spice_stream_, "v%d %s 0 %.3f\n", - volt_index_++, - node_name, - voltage); + sta::print(spice_stream_, "v{} {} 0 {:.3f}\n", volt_index_++, node_name, voltage); } void @@ -750,20 +701,19 @@ WriteSpice::writeWaveformVoltSource(const Pin *pin, volt1 = gnd_voltage_; volt_factor = -power_voltage_; } - streamPrint(spice_stream_, "v%d %s 0 pwl(\n", - volt_index_++, - network_->pathName(pin)); - streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + sta::print(spice_stream_, "v{} {} 0 pwl(\n", volt_index_++, + network_->pathName(pin)); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", 0.0, volt0); Table waveform = drvr_waveform->waveform(slew); const TableAxis *time_axis = waveform.axis1(); - for (size_t time_index = 0; time_index < time_axis->size(); time_index++) { + for (size_t time_index = 0; time_index < time_axis->size(); time_index++) { float time = delay + time_axis->axisValue(time_index); float wave_volt = waveform.value(time_index); float volt = volt0 + wave_volt * volt_factor; - streamPrint(spice_stream_, "+%.3e %.3e\n", time, volt); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", time, volt); } - streamPrint(spice_stream_, "+%.3e %.3e\n", max_time_, volt1); - streamPrint(spice_stream_, "+)\n"); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", max_time_, volt1); + sta::print(spice_stream_, "+)\n"); } void @@ -781,13 +731,12 @@ WriteSpice::writeRampVoltSource(const Pin *pin, volt0 = power_voltage_; volt1 = gnd_voltage_; } - streamPrint(spice_stream_, "v%d %s 0 pwl(\n", - volt_index_++, - network_->pathName(pin)); - streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + sta::print(spice_stream_, "v{} {} 0 pwl(\n", volt_index_++, + network_->pathName(pin)); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", 0.0, volt0); writeWaveformEdge(rf, time, slew); - streamPrint(spice_stream_, "+%.3e %.3e\n", max_time_, volt1); - streamPrint(spice_stream_, "+)\n"); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", max_time_, volt1); + sta::print(spice_stream_, "+)\n"); } // Write PWL rise/fall edge that crosses threshold at time. @@ -810,8 +759,8 @@ WriteSpice::writeWaveformEdge(const RiseFall *rf, float time0 = time - dt * threshold; float time1 = time0 + dt; if (time0 > 0.0) - streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); - streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", time0, volt0); + sta::print(spice_stream_, "+{:.3e} {:.3e}\n", time1, volt1); } float @@ -841,10 +790,8 @@ WriteSpice::gatePortValues(const Pin *input_pin, const LibertyPort *drvr_port = network_->libertyPort(drvr_pin); const FuncExpr *drvr_func = drvr_port->function(); if (drvr_func) { - if (gate_edge - && gate_edge->role()->genericRole() == TimingRole::regClkToQ()) - regPortValues(input_pin, drvr_rf, drvr_port, drvr_func, - port_values, is_clked); + if (gate_edge && gate_edge->role()->genericRole() == TimingRole::regClkToQ()) + regPortValues(input_pin, drvr_rf, drvr_port, drvr_func, port_values, is_clked); else gatePortValues(inst, drvr_func, input_port, port_values); } @@ -873,16 +820,16 @@ WriteSpice::gatePortValues(const Instance *, int var_index = Cudd_NodeReadIndex(port_node); LogicValue value; switch (cube[var_index]) { - case 0: - value = LogicValue::zero; - break; - case 1: - value = LogicValue::one; - break; - case 2: - default: - value = LogicValue::unknown; - break; + case 0: + value = LogicValue::zero; + break; + case 1: + value = LogicValue::one; + break; + case 2: + default: + value = LogicValue::unknown; + break; } port_values[port] = value; } @@ -914,9 +861,8 @@ WriteSpice::regPortValues(const Pin *input_pin, } else { const LibertyPort *input_port = network_->libertyPort(input_pin); - report_->error(1604, "no register/latch found for path from %s to %s,", - input_port->name(), - drvr_port->name()); + report_->error(1604, "no register/latch found for path from {} to {},", + input_port->name(), drvr_port->name()); } } } @@ -934,23 +880,23 @@ WriteSpice::seqPortValues(Sequential *seq, if (port) { TimingSense sense = data->portTimingSense(port); switch (sense) { - case TimingSense::positive_unate: - if (rf == RiseFall::rise()) - port_values[port] = LogicValue::one; - else - port_values[port] = LogicValue::zero; - break; - case TimingSense::negative_unate: - if (rf == RiseFall::rise()) - port_values[port] = LogicValue::zero; - else - port_values[port] = LogicValue::one; - break; - case TimingSense::non_unate: - case TimingSense::none: - case TimingSense::unknown: - default: - break; + case TimingSense::positive_unate: + if (rf == RiseFall::rise()) + port_values[port] = LogicValue::one; + else + port_values[port] = LogicValue::zero; + break; + case TimingSense::negative_unate: + if (rf == RiseFall::rise()) + port_values[port] = LogicValue::zero; + else + port_values[port] = LogicValue::one; + break; + case TimingSense::non_unate: + case TimingSense::none: + case TimingSense::unknown: + default: + break; } } } @@ -963,21 +909,21 @@ WriteSpice::onePort(FuncExpr *expr) FuncExpr *right = expr->right(); LibertyPort *port; switch (expr->op()) { - case FuncExpr::Op::port: - return expr->port(); - case FuncExpr::Op::not_: - return onePort(left); - case FuncExpr::Op::or_: - case FuncExpr::Op::and_: - case FuncExpr::Op::xor_: - port = onePort(left); - if (port == nullptr) - port = onePort(right); - return port; - case FuncExpr::Op::one: - case FuncExpr::Op::zero: - default: - return nullptr; + case FuncExpr::Op::port: + return expr->port(); + case FuncExpr::Op::not_: + return onePort(left); + case FuncExpr::Op::or_: + case FuncExpr::Op::and_: + case FuncExpr::Op::xor_: + port = onePort(left); + if (port == nullptr) + port = onePort(right); + return port; + case FuncExpr::Op::one: + case FuncExpr::Op::zero: + default: + return nullptr; } } @@ -1006,20 +952,18 @@ WriteSpice::writeSubcktInstLoads(const Pin *drvr_pin, const PinSet &excluded_input_pins, InstanceSet &written_insts) { - streamPrint(spice_stream_, "* Load pins\n"); + sta::print(spice_stream_, "* Load pins\n"); PinSeq drvr_loads = drvrLoads(drvr_pin); // Do not sensitize side load gates. LibertyPortLogicValues port_values; for (const Pin *load_pin : drvr_loads) { const Instance *load_inst = network_->instance(load_pin); - if (load_pin != path_load - && network_->direction(load_pin)->isAnyInput() - && !network_->isHierarchical(load_pin) - && !network_->isTopLevelPort(load_pin) + if (load_pin != path_load && network_->direction(load_pin)->isAnyInput() + && !network_->isHierarchical(load_pin) && !network_->isTopLevelPort(load_pin) && !written_insts.contains(load_inst)) { writeSubcktInst(load_inst); writeSubcktInstVoltSrcs(load_inst, port_values, excluded_input_pins); - streamPrint(spice_stream_, "\n"); + sta::print(spice_stream_, "\n"); written_insts.insert(load_inst); } } @@ -1038,21 +982,12 @@ WriteSpice::writeMeasureDelayStmt(const Pin *from_pin, float from_threshold = power_voltage_ * default_library_->inputThreshold(from_rf); const char *to_pin_name = network_->pathName(to_pin); float to_threshold = power_voltage_ * default_library_->inputThreshold(to_rf); - streamPrint(spice_stream_, - ".measure tran %s_%s_delay_%s\n", - prefix.c_str(), - from_pin_name, - to_pin_name); - streamPrint(spice_stream_, - "+trig v(%s) val=%.3f %s=last\n", - from_pin_name, - from_threshold, - spiceTrans(from_rf)); - streamPrint(spice_stream_, - "+targ v(%s) val=%.3f %s=last\n", - to_pin_name, - to_threshold, - spiceTrans(to_rf)); + sta::print(spice_stream_, ".measure tran {}_{}_delay_{}\n", prefix, + from_pin_name, to_pin_name); + sta::print(spice_stream_, "+trig v({}) val={:.3f} {}=last\n", from_pin_name, + from_threshold, spiceTrans(from_rf)); + sta::print(spice_stream_, "+targ v({}) val={:.3f} {}=last\n", to_pin_name, + to_threshold, spiceTrans(to_rf)); } void @@ -1073,25 +1008,15 @@ WriteSpice::writeMeasureSlewStmt(const Pin *pin, threshold1 = upper; threshold2 = lower; } - streamPrint(spice_stream_, - ".measure tran %s_%s_slew\n", - prefix.c_str(), - pin_name); - streamPrint(spice_stream_, - "+trig v(%s) val=%.3f %s=last\n", - pin_name, - threshold1, - spice_rf); - streamPrint(spice_stream_, - "+targ v(%s) val=%.3f %s=last\n", - pin_name, - threshold2, - spice_rf); + sta::print(spice_stream_, ".measure tran {}_{}_slew\n", prefix, pin_name); + sta::print(spice_stream_, "+trig v({}) val={:.3f} {}=last\n", pin_name, threshold1, + spice_rf); + sta::print(spice_stream_, "+targ v({}) val={:.3f} {}=last\n", pin_name, threshold2, + spice_rf); } //////////////////////////////////////////////////////////////// - const char * WriteSpice::spiceTrans(const RiseFall *rf) { @@ -1101,23 +1026,6 @@ WriteSpice::spiceTrans(const RiseFall *rf) return "FALL"; } -// fprintf for c++ streams. -// Yes, I hate formatted output to ostream THAT much. -void -streamPrint(std::ofstream &stream, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *result = nullptr; - if (vasprintf(&result, fmt, args) == -1) - criticalError(267, "out of memory"); - stream << result; - free(result); - va_end(args); -} - //////////////////////////////////////////////////////////////// // Unused @@ -1139,4 +1047,4 @@ WriteSpice::clkWaveformTimeOffset(const Clock *clk) return clk->period() / 10; } -} // namespace +} // namespace sta diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index ace2b78b..74fecf00 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -29,6 +29,7 @@ #include #include +#include "Format.hh" #include "StaState.hh" #include "StringUtil.hh" #include "Liberty.hh" @@ -186,9 +187,4 @@ protected: Parasitics *parasitics_; }; -void -streamPrint(std::ofstream &stream, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); - -} // namespace +} // namespace sta diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 732a651a..b01986a2 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -444,7 +444,7 @@ using namespace sta; const char *arg = Tcl_GetStringFromObj($input, &length); Transition *tr = Transition::find(arg); if (tr == nullptr) { - tclArgError(interp, 2150, "Unknown transition '%s'.", arg); + tclArgError(interp, 2150, "Unknown transition '{}'.", arg); return TCL_ERROR; } else @@ -464,7 +464,7 @@ using namespace sta; const char *arg = Tcl_GetStringFromObj($input, &length); const RiseFall *rf = RiseFall::find(arg); if (rf == nullptr) { - tclArgError(interp, 2151, "Unknown rise/fall edge '%s'.", arg); + tclArgError(interp, 2151, "Unknown rise/fall edge '{}'.", arg); return TCL_ERROR; } // Swig is retarded and drops const on args. @@ -484,7 +484,7 @@ using namespace sta; const char *arg = Tcl_GetStringFromObj($input, &length); const RiseFallBoth *rf = RiseFallBoth::find(arg); if (rf == nullptr) { - tclArgError(interp, 2152, "Unknown transition name '%s'.", arg); + tclArgError(interp, 2152, "Unknown transition name '{}'.", arg); return TCL_ERROR; } // Swig is retarded and drops const on args. @@ -504,7 +504,7 @@ using namespace sta; const char *arg = Tcl_GetStringFromObj($input, &length); PortDirection *dir = PortDirection::find(arg); if (dir == nullptr) { - tclArgError(interp, 2153, "Unknown port direction '%s'.", arg); + tclArgError(interp, 2153, "Unknown port direction '{}'.", arg); return TCL_ERROR; } else @@ -519,7 +519,7 @@ using namespace sta; // Swig is retarded and drops const on args. $1 = const_cast(TimingRole::find(arg)); else { - tclArgError(interp, 2154, "Unknown timing role '%s'.", arg); + tclArgError(interp, 2154, "Unknown timing role '{}'.", arg); return TCL_ERROR; } } @@ -542,7 +542,7 @@ using namespace sta; else if (stringEq(arg, "fall") || stringEq(arg, "falling")) $1 = LogicValue::fall; else { - tclArgError(interp, 2155, "Unknown logic value '%s'.", arg); + tclArgError(interp, 2155, "Unknown logic value '{}'.", arg); return TCL_ERROR; } } @@ -557,7 +557,7 @@ using namespace sta; else if (stringEq(arg, "on_chip_variation")) $1 = AnalysisType::ocv; else { - tclArgError(interp, 2156, "Unknown analysis type '%s'.", arg); + tclArgError(interp, 2156, "Unknown analysis type '{}'.", arg); return TCL_ERROR; } } @@ -762,7 +762,7 @@ using namespace sta; floats->push_back(static_cast(value)); else { delete floats; - tclArgError(interp, 2157, "%s is not a floating point number.", arg); + tclArgError(interp, 2157, "{} is not a floating point number.", arg); return TCL_ERROR; } } @@ -807,7 +807,7 @@ using namespace sta; ints->push_back(value); else { delete ints; - tclArgError(interp, 2158, "%s is not an integer.", arg); + tclArgError(interp, 2158, "{} is not an integer.", arg); return TCL_ERROR; } } @@ -869,7 +869,7 @@ using namespace sta; if (min_max) $1 = min_max; else { - tclArgError(interp, 2159, "%s not min or max.", arg); + tclArgError(interp, 2159, "{} not min or max.", arg); return TCL_ERROR; } } @@ -890,7 +890,7 @@ using namespace sta; if (min_max) $1 = min_max; else { - tclArgError(interp, 2160, "%s not min, max or min_max.", arg); + tclArgError(interp, 2160, "{} not min, max or min_max.", arg); return TCL_ERROR; } } @@ -906,7 +906,7 @@ using namespace sta; if (min_max) $1 = min_max; else { - tclArgError(interp, 2161, "%s not min, max or min_max.", arg); + tclArgError(interp, 2161, "{} not min, max or min_max.", arg); return TCL_ERROR; } } @@ -928,7 +928,7 @@ using namespace sta; || stringEqual(arg, "max")) $1 = const_cast(MinMax::max()); else { - tclArgError(interp, 2162, "%s not setup, hold, min or max.", arg); + tclArgError(interp, 2162, "{} not setup, hold, min or max.", arg); return TCL_ERROR; } } @@ -948,7 +948,7 @@ using namespace sta; || stringEqual(arg, "min_max")) $1 = const_cast(SetupHoldAll::all()); else { - tclArgError(interp, 2163, "%s not setup, hold, setup_hold, min, max or min_max.", + tclArgError(interp, 2163, "{} not setup, hold, setup_hold, min, max or min_max.", arg); return TCL_ERROR; } @@ -963,7 +963,7 @@ using namespace sta; if (early_late) $1 = early_late; else { - tclArgError(interp, 2164, "%s not early/min, late/max or early_late/min_max.", arg); + tclArgError(interp, 2164, "{} not early/min, late/max or early_late/min_max.", arg); return TCL_ERROR; } } @@ -977,7 +977,7 @@ using namespace sta; if (early_late) $1 = early_late; else { - tclArgError(interp, 2165, "%s not early/min, late/max or early_late/min_max.", arg); + tclArgError(interp, 2165, "{} not early/min, late/max or early_late/min_max.", arg); return TCL_ERROR; } } @@ -992,7 +992,7 @@ using namespace sta; else if (stringEq(arg, "cell_check")) $1 = TimingDerateType::cell_check; else { - tclArgError(interp, 2166, "%s not net_delay, cell_delay or cell_check.", arg); + tclArgError(interp, 2166, "{} not net_delay, cell_delay or cell_check.", arg); return TCL_ERROR; } } @@ -1005,7 +1005,7 @@ using namespace sta; else if (stringEq(arg, "cell_check")) $1 = TimingDerateCellType::cell_check; else { - tclArgError(interp, 2167, "%s not cell_delay or cell_check.", arg); + tclArgError(interp, 2167, "{} not cell_delay or cell_check.", arg); return TCL_ERROR; } } @@ -1018,7 +1018,7 @@ using namespace sta; else if (stringEq(arg, "data")) $1 = PathClkOrData::data; else { - tclArgError(interp, 2168, "%s not clk or data.", arg); + tclArgError(interp, 2168, "{} not clk or data.", arg); return TCL_ERROR; } } @@ -1031,7 +1031,7 @@ using namespace sta; else if (stringEq(arg, "slack")) $1 = sort_by_slack; else { - tclArgError(interp, 2169, "%s not group or slack.", arg); + tclArgError(interp, 2169, "{} not group or slack.", arg); return TCL_ERROR; } } @@ -1056,7 +1056,7 @@ using namespace sta; else if (stringEq(arg, "json")) $1 = ReportPathFormat::json; else { - tclArgError(interp, 2170, "unknown path type %s.", arg); + tclArgError(interp, 2170, "unknown path type {}.", arg); return TCL_ERROR; } } @@ -1225,7 +1225,7 @@ using namespace sta; if (mode) seq.push_back(mode); else { - tclArgError(interp, 2174, "mode %s not found.", mode_name); + tclArgError(interp, 2174, "mode {} not found.", mode_name); return TCL_ERROR; } } @@ -1256,7 +1256,7 @@ using namespace sta; if (scene) $1 = scene; else { - tclArgError(interp, 2173, "scene %s not found.", scene_name); + tclArgError(interp, 2173, "scene {} not found.", scene_name); return TCL_ERROR; } } @@ -1285,7 +1285,7 @@ using namespace sta; if (scene) seq.push_back(scene); else { - tclArgError(interp, 2172, "scene %s not found.", scene_name); + tclArgError(interp, 2172, "scene {} not found.", scene_name); return TCL_ERROR; } } @@ -1321,8 +1321,8 @@ using namespace sta; break; case PropertyValue::Type::float_: { const Unit *unit = value.unit(); - const char *float_string = unit->asString(value.floatValue(), 6); - Tcl_SetResult(interp, const_cast(float_string), TCL_VOLATILE); + std::string float_string = unit->asString(value.floatValue(), 6); + Tcl_SetResult(interp, const_cast(float_string.c_str()), TCL_VOLATILE); } break; case PropertyValue::Type::bool_: { @@ -1422,18 +1422,17 @@ using namespace sta; PwrActivity activity = value.pwrActivity(); Tcl_Obj *list = Tcl_NewListObj(0, nullptr); Tcl_Obj *obj; - const char *str; - str = stringPrintTmp("%.5e", activity.density()); - obj = Tcl_NewStringObj(str, strlen(str)); + std::string density = sta::format("{:.5e}", activity.density()); + obj = Tcl_NewStringObj(density.c_str(), density.size()); Tcl_ListObjAppendElement(interp, list, obj); - str = stringPrintTmp("%.3f", activity.duty()); - obj = Tcl_NewStringObj(str, strlen(str)); + std::string duty = sta::format("{:.3f}", activity.duty()); + obj = Tcl_NewStringObj(duty.c_str(), duty.size()); Tcl_ListObjAppendElement(interp, list, obj); - str = activity.originName(); - obj = Tcl_NewStringObj(str, strlen(str)); + std::string name = activity.originName(); + obj = Tcl_NewStringObj(name.c_str(), name.size()); Tcl_ListObjAppendElement(interp, list, obj); Tcl_SetObjResult(interp, list); @@ -1452,7 +1451,7 @@ using namespace sta; else if (stringEq(arg, "xyce")) $1 = CircuitSim::xyce; else { - tclArgError(interp, 2171, "unknown circuit simulator %s.", arg); + tclArgError(interp, 2171, "unknown circuit simulator {}.", arg); return TCL_ERROR; } } diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 0de899c5..66dc529c 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -170,8 +170,8 @@ tclArcDcalcArg(ArcDcalcArg &gate, obj = Tcl_NewStringObj(to_edge, strlen(to_edge)); Tcl_ListObjAppendElement(interp, list, obj); - const char *input_delay = delayAsString(gate.inputDelay(), sta); - obj = Tcl_NewStringObj(input_delay, strlen(input_delay)); + std::string input_delay = delayAsString(gate.inputDelay(), sta); + obj = Tcl_NewStringObj(input_delay.c_str(), input_delay.size()); Tcl_ListObjAppendElement(interp, list, obj); return list; diff --git a/util/Debug.cc b/util/Debug.cc index c2429db4..917e40fa 100644 --- a/util/Debug.cc +++ b/util/Debug.cc @@ -81,19 +81,4 @@ Debug::setLevel(const char *what, } } -void -Debug::reportLine(const char *what, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - std::unique_lock lock(buffer_lock_); - report_->printToBuffer("%s", what); - report_->printToBufferAppend(": "); - report_->printToBufferAppend(fmt, args); - report_->printBufferLine(); - va_end(args); -} - } // namespace diff --git a/util/Error.cc b/util/Error.cc index aa4b85e5..88914f26 100644 --- a/util/Error.cc +++ b/util/Error.cc @@ -27,6 +27,7 @@ #include #include +#include "Format.hh" #include "StringUtil.hh" namespace sta { @@ -36,7 +37,7 @@ Exception::Exception() : { } -ExceptionMsg::ExceptionMsg(const char *msg, +ExceptionMsg::ExceptionMsg(const std::string &msg, const bool suppressed) : Exception(), msg_(msg), @@ -50,7 +51,7 @@ ExceptionMsg::what() const noexcept return msg_.c_str(); } -ExceptionLine::ExceptionLine(const char *filename, +ExceptionLine::ExceptionLine(const std::string &filename, int line) : Exception(), filename_(filename), @@ -58,26 +59,28 @@ ExceptionLine::ExceptionLine(const char *filename, { } -FileNotReadable::FileNotReadable(const char *filename) : - filename_(filename) +FileNotReadable::FileNotReadable(std::string filename) : + filename_(std::move(filename)), + msg_(sta::format("cannot read file {}.", filename_)) { } const char * FileNotReadable::what() const noexcept { - return stringPrintTmp("cannot read file %s.", filename_); + return msg_.c_str(); } -FileNotWritable::FileNotWritable(const char *filename) : - filename_(filename) +FileNotWritable::FileNotWritable(std::string filename) : + filename_(std::move(filename)), + msg_(sta::format("cannot write file {}.", filename_)) { } const char * FileNotWritable::what() const noexcept { - return stringPrintTmp("cannot write file %s.", filename_); + return msg_.c_str(); } } // namespace diff --git a/util/MachineLinux.cc b/util/MachineLinux.cc index 261d143f..7be581f6 100644 --- a/util/MachineLinux.cc +++ b/util/MachineLinux.cc @@ -33,6 +33,7 @@ #include "StaConfig.hh" #include "StringUtil.hh" +#include "Format.hh" namespace sta { @@ -81,8 +82,7 @@ systemRunTime() size_t memoryUsage() { - std::string proc_filename; - stringPrint(proc_filename, "/proc/%d/status", getpid()); + std::string proc_filename = sta::format("/proc/{}/status", getpid()); size_t memory = 0; FILE *status = fopen(proc_filename.c_str(), "r"); if (status) { diff --git a/util/Report.cc b/util/Report.cc index 4a6e6bf8..d539c574 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -1,35 +1,36 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Report.hh" -#include // min -#include // exit -#include // strlen +#include // min +#include // exit +#include // strlen -#include "Machine.hh" #include "Error.hh" +#include "Machine.hh" +#include "Format.hh" namespace sta { @@ -46,10 +47,7 @@ Report::Report() : default_ = this; } -Report::~Report() -{ - delete [] buffer_; -} +Report::~Report() { delete[] buffer_; } size_t Report::printConsole(const char *buffer, @@ -85,17 +83,6 @@ Report::printString(const char *buffer, return ret; } -void -Report::reportLine(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - std::unique_lock lock(buffer_lock_); - printToBuffer(fmt, args); - printBufferLine(); - va_end(args); -} - void Report::reportBlankLine() { @@ -103,13 +90,7 @@ Report::reportBlankLine() } void -Report::reportLineString(const char *line) -{ - printLine(line, strlen(line)); -} - -void -Report::reportLineString(const std::string &line) +Report::reportLine(const std::string &line) { printLine(line.c_str(), line.length()); } @@ -151,16 +132,16 @@ Report::printToBufferAppend(const char *fmt, // Copy args in case we need to grow the buffer. va_list args_copy; va_copy(args_copy, args); - size_t length = vsnprint(buffer_ + buffer_length_, buffer_size_- buffer_length_, - fmt, args); + size_t length = + vsnprint(buffer_ + buffer_length_, buffer_size_ - buffer_length_, fmt, args); if (length >= buffer_size_ - buffer_length_) { buffer_size_ = buffer_length_ + length * 2; char *new_buffer = new char[buffer_size_]; strncpy(new_buffer, buffer_, buffer_length_); - delete [] buffer_; + delete[] buffer_; buffer_ = new_buffer; - length = vsnprint(buffer_ + buffer_length_, buffer_size_ - buffer_length_, - fmt, args_copy); + length = vsnprint(buffer_ + buffer_length_, buffer_size_ - buffer_length_, fmt, + args_copy); } buffer_length_ += length; va_end(args_copy); @@ -175,152 +156,10 @@ Report::printBufferLine() //////////////////////////////////////////////////////////////// void -Report::warn(int id, - const char *fmt, - ...) +reportThrowExceptionMsg(const std::string &msg, + bool suppressed) { - // Skip suppressed messages. - if (!isSuppressed(id)) { - va_list args; - va_start(args, fmt); - printToBuffer("Warning %d: ", id); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - } -} - -void -Report::vwarn(int id, - const char *fmt, - va_list args) -{ - // Skip suppressed messages. - if (!isSuppressed(id)) { - printToBuffer("Warning %d: ", id); - printToBufferAppend(fmt, args); - printBufferLine(); - } -} - -void -Report::fileWarn(int id, - const char *filename, - int line, - const char *fmt, - ...) -{ - // Skip suppressed messages. - if (!isSuppressed(id)) { - va_list args; - va_start(args, fmt); - printToBuffer("Warning %d: %s line %d, ", id, filename, line); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - } -} - -void -Report::vfileWarn(int id, - const char *filename, - int line, - const char *fmt, - va_list args) -{ - // Skip suppressed messages. - if (!isSuppressed(id)) { - printToBuffer("Warning %d: %s line %d, ", id, filename, line); - printToBufferAppend(fmt, args); - printBufferLine(); - } -} - -//////////////////////////////////////////////////////////////// - -void -Report::error(int id, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - // No prefix msg, no \n. - printToBuffer("%d ", id); - printToBufferAppend(fmt, args); - va_end(args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -void -Report::verror(int id, - const char *fmt, - va_list args) -{ - // No prefix msg, no \n. - printToBuffer("%d ", id); - printToBufferAppend(fmt, args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -void -Report::fileError(int id, - const char *filename, - int line, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - // No prefix msg, no \n. - printToBuffer("%d %s line %d, ", id, filename, line); - printToBufferAppend(fmt, args); - va_end(args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -void -Report::vfileError(int id, - const char *filename, - int line, - const char *fmt, - va_list args) -{ - // No prefix msg, no \n. - printToBuffer("%d %s line %d, ", id, filename, line); - printToBufferAppend(fmt, args); - throw ExceptionMsg(buffer_, isSuppressed(id)); -} - -//////////////////////////////////////////////////////////////// - -void -Report::critical(int id, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - printToBuffer("Critical %d: ", id); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - exit(1); -} - -void -Report::fileCritical(int id, - const char *filename, - int line, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - printToBuffer("Critical %d: %s line %d, ", id, filename, line); - printToBufferAppend(fmt, args); - printBufferLine(); - va_end(args); - exit(1); + throw ExceptionMsg(msg, suppressed); } //////////////////////////////////////////////////////////////// @@ -346,11 +185,12 @@ Report::isSuppressed(int id) //////////////////////////////////////////////////////////////// void -Report::logBegin(const char *filename) +Report::logBegin(std::string_view filename) { - log_stream_ = fopen(filename, "w"); + std::string filename_str(filename); + log_stream_ = fopen(filename_str.c_str(), "w"); if (log_stream_ == nullptr) - throw FileNotWritable(filename); + throw FileNotWritable(std::move(filename_str)); } void @@ -362,19 +202,21 @@ Report::logEnd() } void -Report::redirectFileBegin(const char *filename) +Report::redirectFileBegin(std::string_view filename) { - redirect_stream_ = fopen(filename, "w"); + std::string filename_str(filename); + redirect_stream_ = fopen(filename_str.c_str(), "w"); if (redirect_stream_ == nullptr) - throw FileNotWritable(filename); + throw FileNotWritable(std::move(filename_str)); } void -Report::redirectFileAppendBegin(const char *filename) +Report::redirectFileAppendBegin(std::string_view filename) { - redirect_stream_ = fopen(filename, "a"); + std::string filename_str(filename); + redirect_stream_ = fopen(filename_str.c_str(), "a"); if (redirect_stream_ == nullptr) - throw FileNotWritable(filename); + throw FileNotWritable(std::move(filename_str)); } void @@ -406,4 +248,4 @@ Report::redirectStringPrint(const char *buffer, redirect_string_.append(buffer, length); } -} // namespace +} // namespace sta diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc index d3e07048..a418a53a 100644 --- a/util/ReportTcl.cc +++ b/util/ReportTcl.cc @@ -183,7 +183,7 @@ ReportTcl::flush() // Tcl_Main can eval multiple commands before the flushing the command // output, so the log/redirect commands must force a flush. void -ReportTcl::logBegin(const char *filename) +ReportTcl::logBegin(std::string_view filename) { flush(); Report::logBegin(filename); @@ -197,14 +197,14 @@ ReportTcl::logEnd() } void -ReportTcl::redirectFileBegin(const char *filename) +ReportTcl::redirectFileBegin(std::string_view filename) { flush(); Report::redirectFileBegin(filename); } void -ReportTcl::redirectFileAppendBegin(const char *filename) +ReportTcl::redirectFileAppendBegin(std::string_view filename) { flush(); Report::redirectFileAppendBegin(filename); diff --git a/util/Stats.cc b/util/Stats.cc index ac6d98dd..07fb62a1 100644 --- a/util/Stats.cc +++ b/util/Stats.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "Stats.hh" @@ -57,12 +57,11 @@ Stats::report(const char *step) double memory_begin = static_cast(memory_begin_); double memory_end = static_cast(memoryUsage()); double memory_delta = memory_end - memory_begin; - report_->reportLine("stats: %5.1f/%5.1fe %5.1f/%5.1fu %5.1f/%5.1fMB %s", - elapsed_end - elapsed_begin_, elapsed_end, - user_end - user_begin_, user_end, - memory_delta * 1e-6, memory_end * 1e-6, - step); + report_->report("stats: {:5.1f}/{:5.1f}e {:5.1f}/{:5.1f}u {:5.1f}/{:5.1f}MB {}", + elapsed_end - elapsed_begin_, elapsed_end, + user_end - user_begin_, user_end, memory_delta * 1e-6, + memory_end * 1e-6, step); } } -} // namespace +} // namespace sta diff --git a/util/StringUtil.cc b/util/StringUtil.cc index 2b93704c..cc77196c 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -32,20 +32,10 @@ #include "Machine.hh" #include "Mutex.hh" +#include "Error.hh" namespace sta { -static void -stringPrintTmp(const char *fmt, - va_list args, - // Return values. - char *&str, - size_t &length); -static void -getTmpString(// Return values. - char *&str, - size_t &length); - char * stringCopy(const char *str) { @@ -70,112 +60,6 @@ isDigits(const char *str) //////////////////////////////////////////////////////////////// -// print for c++ strings. -void -stringPrint(std::string &str, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - str = tmp; -} - -void -stringAppend(std::string &str, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - str += tmp; -} - -std::string -stdstrPrint(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - return tmp; -} - -char * -stringPrint(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *result = stringPrintArgs(fmt, args); - va_end(args); - return result; -} - -char * -stringPrintArgs(const char *fmt, - va_list args) -{ - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - char *result = new char[tmp_length + 1]; - strcpy(result, tmp); - return result; -} - -char * -stringPrintTmp(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - char *tmp; - size_t tmp_length; - stringPrintTmp(fmt, args, tmp, tmp_length); - va_end(args); - return tmp; -} - -static void -stringPrintTmp(const char *fmt, - va_list args, - // Return values. - char *&tmp, - // strlen(tmp), not including terminating '\0'. - size_t &tmp_length) -{ - size_t tmp_length1; - getTmpString(tmp, tmp_length1); - - va_list args_copy; - va_copy(args_copy, args); - // Returned length does NOT include trailing '\0'. - tmp_length = vsnprint(tmp, tmp_length1, fmt, args_copy); - va_end(args_copy); - - if (tmp_length >= tmp_length1) { - tmp_length1 = tmp_length + 1; - tmp = makeTmpString(tmp_length1); - va_copy(args_copy, args); - tmp_length = vsnprint(tmp, tmp_length1, fmt, args_copy); - va_end(args_copy); - } -} - -//////////////////////////////////////////////////////////////// - static constexpr size_t tmp_string_count = 256; static constexpr size_t tmp_string_initial_length = 256; thread_local static std::array tmp_strings; @@ -193,22 +77,6 @@ deleteTmpStrings() tmp_string_next = 0; } -static void -getTmpString(// Return values. - char *&str, - size_t &length) -{ - if (tmp_string_next == tmp_string_count) - tmp_string_next = 0; - str = tmp_strings[tmp_string_next]; - length = tmp_string_lengths[tmp_string_next]; - if (str == nullptr) { - str = tmp_strings[tmp_string_next] = new char[tmp_string_initial_length]; - length = tmp_string_lengths[tmp_string_next] = tmp_string_initial_length; - } - tmp_string_next++; -} - char * makeTmpString(size_t length) { @@ -239,10 +107,8 @@ makeTmpString(std::string &str) void stringDeleteCheck(const char *str) { - if (isTmpString(str)) { - printf("Critical error: stringDelete for tmp string."); - exit(1); - } + if (isTmpString(str)) + criticalError(2600, "stringDelete for tmp string."); } bool diff --git a/util/Util.i b/util/Util.i index 32d9e0c1..e68de5ef 100644 --- a/util/Util.i +++ b/util/Util.i @@ -115,7 +115,7 @@ report_error(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->error(id, "%s", msg); + report->error(id, "{}", msg); } void @@ -125,7 +125,7 @@ report_file_error(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->error(id, filename, line, "%s", msg); + report->error(id, filename, line, "{}", msg); } void @@ -133,7 +133,7 @@ report_warn(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->warn(id, "%s", msg); + report->warn(id, "{}", msg); } void @@ -143,7 +143,7 @@ report_file_warn(int id, const char *msg) { Report *report = Sta::sta()->report(); - report->fileWarn(id, filename, line, "%s", msg); + report->fileWarn(id, filename, line, "{}", msg); } void @@ -151,9 +151,9 @@ report_line(const char *msg) { Sta *sta = Sta::sta(); if (sta) - sta->report()->reportLineString(msg); + sta->report()->reportLine(msg); else - // After sta::delete_all_memory souce -echo prints the cmd file line + // After sta::delete_all_memory include -echo prints the cmd file line. printf("%s\n", msg); } @@ -471,7 +471,7 @@ unit_scale(const char *unit_name) // format_unit functions print with fixed digits and suffix. // Pass value arg as string to support NaNs. -const char * +std::string format_time(const char *value, int digits) { @@ -479,7 +479,7 @@ format_time(const char *value, return Sta::sta()->units()->timeUnit()->asString(value1, digits); } -const char * +std::string format_capacitance(const char *value, int digits) { @@ -487,7 +487,7 @@ format_capacitance(const char *value, return Sta::sta()->units()->capacitanceUnit()->asString(value1, digits); } -const char * +std::string format_resistance(const char *value, int digits) { @@ -495,7 +495,7 @@ format_resistance(const char *value, return Sta::sta()->units()->resistanceUnit()->asString(value1, digits); } -const char * +std::string format_voltage(const char *value, int digits) { @@ -503,7 +503,7 @@ format_voltage(const char *value, return Sta::sta()->units()->voltageUnit()->asString(value1, digits); } -const char * +std::string format_current(const char *value, int digits) { @@ -511,7 +511,7 @@ format_current(const char *value, return Sta::sta()->units()->currentUnit()->asString(value1, digits); } -const char * +std::string format_power(const char *value, int digits) { @@ -519,7 +519,7 @@ format_power(const char *value, return Sta::sta()->units()->powerUnit()->asString(value1, digits); } -const char * +std::string format_distance(const char *value, int digits) { @@ -528,7 +528,7 @@ format_distance(const char *value, return dist_unit->asString(value1, digits); } -const char * +std::string format_area(const char *value, int digits) { diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 71f056ad..5888f993 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -44,8 +44,7 @@ void sta::VerilogParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164,reader->filename(),loc.begin.line, - "%s",msg.c_str()); + reader->report()->fileError(171,reader->filename(),loc.begin.line, "{}",msg); } %} diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index e018fae9..a539a311 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1,25 +1,25 @@ // OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 . -// +// // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. -// +// // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. -// +// // This notice may not be removed or altered from any source distribution. #include "VerilogReader.hh" @@ -73,35 +73,10 @@ deleteVerilogReader(VerilogReader *verilog_reader) //////////////////////////////////////////////////////////////// -class VerilogError -{ -public: - VerilogError(int id, - const char *filename, - int line, - const char *msg, - bool warn); - ~VerilogError(); - const char *msg() const { return msg_; } - const char *filename() const { return filename_; } - int id() const { return id_; } - int line() const { return line_; } - bool warn() const { return warn_; } - -private: - int id_; - const char *filename_; - int line_; - const char *msg_; - bool warn_; - - friend class VerilogErrorCmp; -}; - VerilogError::VerilogError(int id, - const char *filename, + std::string_view filename, int line, - const char *msg, + std::string_view msg, bool warn) : id_(id), filename_(filename), @@ -111,22 +86,16 @@ VerilogError::VerilogError(int id, { } -VerilogError::~VerilogError() -{ - // filename is owned by VerilogReader. - stringDelete(msg_); -} - class VerilogErrorCmp { public: bool operator()(const VerilogError *error1, const VerilogError *error2) const { - int file_cmp = strcmp(error1->filename_, error2->filename_); + int file_cmp = error1->filename_.compare(error2->filename_); if (file_cmp == 0) { if (error1->line_ == error2->line_) - return strcmp(error1->msg_, error2->msg_) < 0; + return error1->msg_ < error2->msg_; else return error1->line_ < error2->line_; } @@ -146,17 +115,14 @@ VerilogReader::VerilogReader(NetworkReader *network) : zero_net_name_("zero_"), one_net_name_("one_") { - network->setLinkFunc([this] (const char *top_cell_name, - bool make_black_boxes) -> Instance* { - return linkNetwork(top_cell_name, make_black_boxes, true); - }); - constant10_max_ = stdstrPrint("%llu", std::numeric_limits::max()); + network->setLinkFunc( + [this](const char *top_cell_name, bool make_black_boxes) -> Instance * { + return linkNetwork(top_cell_name, make_black_boxes, true); + }); + constant10_max_ = std::to_string(std::numeric_limits::max()); } -VerilogReader::~VerilogReader() -{ - deleteModules(); -} +VerilogReader::~VerilogReader() { deleteModules(); } void VerilogReader::deleteModules() @@ -232,7 +198,7 @@ VerilogReader::makeModule(const std::string *module_vname, VerilogAttrStmtSeq *attr_stmts, int line) { - const std::string module_name = moduleVerilogToSta(module_vname); + const std::string module_name = moduleVerilogToSta(*module_vname); Cell *cell = network_->findCell(library_, module_name.c_str()); if (cell) { VerilogModule *module = module_map_[cell]; @@ -267,7 +233,7 @@ VerilogReader::makeModule(const std::string *module_name, // Pull the port names out of the port declarations. for (VerilogStmt *dcl : *port_dcls) { if (dcl->isDeclaration()) { - VerilogDcl *dcl1 = dynamic_cast(dcl); + VerilogDcl *dcl1 = dynamic_cast(dcl); for (VerilogDclArg *arg : *dcl1->args()) { VerilogNetNamed *port = new VerilogNetScalar(arg->netName()); ports->push_back(port); @@ -299,8 +265,7 @@ VerilogReader::makeCellPorts(Cell *cell, } else warn(165, module->filename(), module->line(), - "module %s repeated port name %s.", - module->name().c_str(), + "module {} repeated port name {}.", module->name().c_str(), port_name.c_str()); } checkModuleDcls(module, port_names); @@ -314,18 +279,17 @@ VerilogReader::makeCellPort(Cell *cell, VerilogDcl *dcl = module->declaration(port_name.c_str()); if (dcl) { PortDirection *dir = dcl->direction(); - VerilogDclBus *dcl_bus = dynamic_cast(dcl); + VerilogDclBus *dcl_bus = dynamic_cast(dcl); Port *port = dcl->isBus() - ? network_->makeBusPort(cell, port_name.c_str(), dcl_bus->fromIndex(), - dcl_bus->toIndex()) - : network_->makePort(cell, port_name.c_str()); + ? network_->makeBusPort(cell, port_name.c_str(), dcl_bus->fromIndex(), + dcl_bus->toIndex()) + : network_->makePort(cell, port_name.c_str()); network_->setDirection(port, dir); return port; } else { warn(166, module->filename(), module->line(), - "module %s missing declaration for port %s.", - module->name().c_str(), + "module {} missing declaration for port {}.", module->name().c_str(), port_name.c_str()); return network_->makePort(cell, port_name.c_str()); } @@ -338,7 +302,7 @@ VerilogReader::makeNamedPortRefCellPorts(Cell *cell, StringSet &port_names) { PortSeq *member_ports = new PortSeq; - VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module,this); + VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, this); while (net_name_iter->hasNext()) { const std::string &net_name = net_name_iter->next(); port_names.insert(net_name); @@ -355,16 +319,13 @@ void VerilogReader::checkModuleDcls(VerilogModule *module, std::set &port_names) { - for (auto const & [port_name, dcl] : *module->declarationMap()) { + for (auto const &[port_name, dcl] : *module->declarationMap()) { PortDirection *dir = dcl->direction(); - if (dir->isInput() - || dir->isOutput() - || dir->isBidirect()) { + if (dir->isInput() || dir->isOutput() || dir->isBidirect()) { if (!port_names.contains(port_name)) linkWarn(197, module->filename(), module->line(), - "module %s declared signal %s is not in the port list.", - module->name().c_str(), - port_name.c_str()); + "module {} declared signal {} is not in the port list.", + module->name(), port_name); } } } @@ -425,8 +386,7 @@ VerilogReader::makeDclBus(PortDirection *dir, int line) { dcl_bus_count_++; - return new VerilogDclBus(dir, from_index, to_index, arg, attr_stmts, - line); + return new VerilogDclBus(dir, from_index, to_index, arg, attr_stmts, line); } VerilogDclBus * @@ -438,16 +398,15 @@ VerilogReader::makeDclBus(PortDirection *dir, int line) { dcl_bus_count_++; - return new VerilogDclBus(dir, from_index, to_index, args, attr_stmts, - line); + return new VerilogDclBus(dir, from_index, to_index, args, attr_stmts, line); } VerilogDclArg * VerilogReader::makeDclArg(const std::string *net_vname) { dcl_arg_count_++; - const std::string net_name = netVerilogToSta(net_vname); - VerilogDclArg *dcl =new VerilogDclArg(net_name); + const std::string net_name = netVerilogToSta(*net_vname); + VerilogDclArg *dcl = new VerilogDclArg(net_name); delete net_vname; return dcl; } @@ -467,10 +426,9 @@ VerilogReader::makeNetPartSelect(const std::string *net_vname, net_part_select_count_++; if (report_stmt_stats_) net_bus_names_ += net_vname->size() + 1; - const std::string net_name = netVerilogToSta(net_vname); - VerilogNetPartSelect *select = new VerilogNetPartSelect(net_name, - from_index, - to_index); + const std::string net_name = netVerilogToSta(*net_vname); + VerilogNetPartSelect *select = + new VerilogNetPartSelect(net_name, from_index, to_index); delete net_vname; return select; } @@ -489,7 +447,7 @@ VerilogReader::makeNetScalar(const std::string *net_vname) net_scalar_count_++; if (report_stmt_stats_) net_scalar_names_ += net_vname->size() + 1; - const std::string net_name = netVerilogToSta(net_vname); + const std::string net_name = netVerilogToSta(*net_vname); VerilogNetScalar *scalar = new VerilogNetScalar(net_name); delete net_vname; return scalar; @@ -502,7 +460,7 @@ VerilogReader::makeNetBitSelect(const std::string *net_vname, net_bit_select_count_++; if (report_stmt_stats_) net_bus_names_ += net_vname->size() + 1; - const std::string net_name = netVerilogToSta(net_vname); + const std::string net_name = netVerilogToSta(*net_vname); VerilogNetBitSelect *select = new VerilogNetBitSelect(net_name, index); delete net_vname; return select; @@ -524,21 +482,20 @@ VerilogReader::makeModuleInst(const std::string *module_vname, VerilogAttrStmtSeq *attr_stmts, const int line) { - const std::string module_name = moduleVerilogToSta(module_vname); - const std::string inst_name = instanceVerilogToSta(inst_vname); + const std::string module_name = moduleVerilogToSta(*module_vname); + const std::string inst_name = instanceVerilogToSta(*inst_vname); Cell *cell = network_->findAnyCell(module_name.c_str()); LibertyCell *liberty_cell = nullptr; if (cell) liberty_cell = network_->libertyCell(cell); // Instances of liberty with scalar ports are special cased // to reduce the memory footprint of the verilog parser. - if (liberty_cell - && hasScalarNamedPortRefs(liberty_cell, pins)) { + if (liberty_cell && hasScalarNamedPortRefs(liberty_cell, pins)) { const int port_count = liberty_cell->portBitCount(); StringSeq net_names(port_count); for (VerilogNet *vnet : *pins) { VerilogNetPortRefScalarNet *vpin = - dynamic_cast(vnet); + dynamic_cast(vnet); const char *port_name = vpin->name().c_str(); const std::string &net_name = vpin->netName(); Port *port = network_->findPort(cell, port_name); @@ -552,8 +509,8 @@ VerilogReader::makeModuleInst(const std::string *module_vname, delete vpin; net_port_ref_scalar_net_count_--; } - VerilogInst *inst = new VerilogLibertyInst(liberty_cell, inst_name, - net_names, attr_stmts, line); + VerilogInst *inst = + new VerilogLibertyInst(liberty_cell, inst_name, net_names, attr_stmts, line); delete pins; if (report_stmt_stats_) { inst_names_ += inst_name.size() + 1; @@ -565,11 +522,8 @@ VerilogReader::makeModuleInst(const std::string *module_vname, return inst; } else { - VerilogInst *inst = new VerilogModuleInst(module_name.c_str(), - inst_name.c_str(), - pins, - attr_stmts, - line); + VerilogInst *inst = new VerilogModuleInst(module_name.c_str(), inst_name.c_str(), + pins, attr_stmts, line); if (report_stmt_stats_) { inst_module_names_ += module_name.size() + 1; inst_names_ += inst_name.size() + 1; @@ -585,15 +539,12 @@ bool VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, VerilogNetSeq *pins) { - if (pins - && pins->size() > 0 - && (*pins)[0]->isNamedPortRef()) { + if (pins && pins->size() > 0 && (*pins)[0]->isNamedPortRef()) { for (VerilogNet *vpin : *pins) { const char *port_name = vpin->name().c_str(); LibertyPort *port = liberty_cell->findLibertyPort(port_name); if (port) { - if (!(port->size() == 1 - && (vpin->isNamedPortRefScalarNet()))) + if (!(port->size() == 1 && (vpin->isNamedPortRefScalarNet()))) return false; } else @@ -611,7 +562,7 @@ VerilogReader::makeNetNamedPortRefScalarNet(const std::string *port_vname) net_port_ref_scalar_net_count_++; if (report_stmt_stats_) port_names_ += port_vname->size() + 1; - const std::string port_name = portVerilogToSta(port_vname); + const std::string port_name = portVerilogToSta(*port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str()); delete port_vname; return ref; @@ -627,10 +578,10 @@ VerilogReader::makeNetNamedPortRefScalarNet(const std::string *port_vname, net_scalar_names_ += net_vname->size() + 1; port_names_ += port_vname->size() + 1; } - const std::string port_name = portVerilogToSta(port_vname); - const std::string net_name = netVerilogToSta(net_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str(), - net_name.c_str()); + const std::string port_name = portVerilogToSta(*port_vname); + const std::string net_name = netVerilogToSta(*net_vname); + VerilogNetPortRef *ref = + new VerilogNetPortRefScalarNet(port_name.c_str(), net_name.c_str()); delete port_vname; delete net_vname; return ref; @@ -642,15 +593,15 @@ VerilogReader::makeNetNamedPortRefBitSelect(const std::string *port_vname, int index) { net_port_ref_scalar_net_count_++; - const std::string bus_name = portVerilogToSta(bus_vname); + const std::string bus_name = portVerilogToSta(*bus_vname); const std::string net_name = verilogBusBitName(bus_name, index); if (report_stmt_stats_) { net_scalar_names_ += net_name.length() + 1; port_names_ += port_vname->size() + 1; } - const std::string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str(), - net_name.c_str()); + const std::string port_name = portVerilogToSta(*port_vname); + VerilogNetPortRef *ref = + new VerilogNetPortRefScalarNet(port_name.c_str(), net_name.c_str()); delete port_vname; delete bus_vname; return ref; @@ -663,7 +614,7 @@ VerilogReader::makeNetNamedPortRefScalar(const std::string *port_vname, net_port_ref_scalar_count_++; if (report_stmt_stats_) port_names_ += port_vname->size() + 1; - const std::string port_name = portVerilogToSta(port_vname); + const std::string port_name = portVerilogToSta(*port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefScalar(port_name.c_str(), net); delete port_vname; return ref; @@ -675,9 +626,8 @@ VerilogReader::makeNetNamedPortRefBit(const std::string *port_vname, VerilogNet *net) { net_port_ref_bit_count_++; - const std::string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefBit(port_name.c_str(), - index, net); + const std::string port_name = portVerilogToSta(*port_vname); + VerilogNetPortRef *ref = new VerilogNetPortRefBit(port_name.c_str(), index, net); delete port_vname; return ref; } @@ -689,10 +639,9 @@ VerilogReader::makeNetNamedPortRefPart(const std::string *port_vname, VerilogNet *net) { net_port_ref_part_count_++; - const std::string port_name = portVerilogToSta(port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefPart(port_name, - from_index, - to_index, net); + const std::string port_name = portVerilogToSta(*port_vname); + VerilogNetPortRef *ref = + new VerilogNetPortRefPart(port_name, from_index, to_index, net); delete port_vname; return ref; } @@ -704,21 +653,18 @@ VerilogReader::makeNetConcat(VerilogNetSeq *nets) return new VerilogNetConcat(nets); } -#define printClassMemory(name, class_name, count) \ - report_->reportLine(" %-20s %9d * %3zu = %6.1fMb\n", \ - name, \ - count, \ - sizeof(class_name), \ - (count * sizeof(class_name) * 1e-6)) +#define printClassMemory(name, class_name, count) \ + report_->report(" {:<20} {:9} * {:3} = {:6.1f}Mb\n", name, count, \ + sizeof(class_name), (count * sizeof(class_name) * 1e-6)) -#define printStringMemory(name, count) \ - report_->reportLine(" %-20s %6.1fMb", name, count * 1e-6) +#define printStringMemory(name, count) \ + report_->report(" {:<20} {:6.1f}Mb", name, count * 1e-6) void VerilogReader::reportStmtCounts() { if (debug_->check("verilog", 1)) { - report_->reportLine("Verilog stats"); + report_->report("Verilog stats"); printClassMemory("modules", VerilogModule, module_count_); printClassMemory("module insts", VerilogModuleInst, inst_mod_count_); printClassMemory("liberty insts", VerilogLibertyInst, inst_lib_count_); @@ -730,14 +676,12 @@ VerilogReader::reportStmtCounts() net_port_ref_scalar_count_); printClassMemory("port ref scalar net", VerilogNetPortRefScalarNet, net_port_ref_scalar_net_count_); - printClassMemory("port ref bit", VerilogNetPortRefBit, - net_port_ref_bit_count_); + printClassMemory("port ref bit", VerilogNetPortRefBit, net_port_ref_bit_count_); printClassMemory("port ref part", VerilogNetPortRefPart, net_port_ref_part_count_); printClassMemory("scalar nets", VerilogNetScalar, net_scalar_count_); - printClassMemory("bus bit nets",VerilogNetBitSelect,net_bit_select_count_); - printClassMemory("bus range nets", VerilogNetPartSelect, - net_part_select_count_); + printClassMemory("bus bit nets", VerilogNetBitSelect, net_bit_select_count_); + printClassMemory("bus range nets", VerilogNetPartSelect, net_part_select_count_); printClassMemory("constant nets", VerilogNetConstant, net_constant_count_); printClassMemory("concats", VerilogNetConcat, concat_count_); printClassMemory("assigns", VerilogAssign, assign_count_); @@ -749,30 +693,6 @@ VerilogReader::reportStmtCounts() } } -void -VerilogReader::error(int id, - const char *filename, - int line, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename, line, fmt, args); - va_end(args); -} - -void -VerilogReader::warn(int id, - const char *filename, - int line, - const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename, line, fmt, args); - va_end(args); -} - //////////////////////////////////////////////////////////////// VerilogModule::VerilogModule(const std::string &name, @@ -808,10 +728,9 @@ VerilogModule::parseStmts(VerilogReader *reader) StringSet inst_names; for (VerilogStmt *stmt : *stmts_) { if (stmt->isDeclaration()) - parseDcl(dynamic_cast(stmt), reader); + parseDcl(dynamic_cast(stmt), reader); else if (stmt->isInstance()) - checkInstanceName(dynamic_cast(stmt), inst_names, - reader); + checkInstanceName(dynamic_cast(stmt), inst_names, reader); } } @@ -837,18 +756,16 @@ VerilogModule::parseDcl(VerilogDcl *dcl, dcl_map_[net_name.c_str()] = dcl; } else if (dcl->direction()->isPowerGround() - && (existing_dir->isOutput() - || existing_dir->isInput() + && (existing_dir->isOutput() || existing_dir->isInput() || existing_dir->isBidirect())) // supply0/supply1 dcl can be used as modifier for // input/output/inout dcls. dcl_map_[net_name.c_str()] = dcl; else if (!dcl->direction()->isInternal()) { std::string net_vname = netVerilogName(net_name.c_str()); - reader->warn(1395, filename_.c_str(), dcl->line(), - "signal %s previously declared on line %d.", - net_vname.c_str(), - existing_dcl->line()); + reader->warn(1395, filename_, dcl->line(), + "signal {} previously declared on line {}.", + net_vname, existing_dcl->line()); } } else @@ -869,13 +786,12 @@ VerilogModule::checkInstanceName(VerilogInst *inst, int i = 1; std::string replacement_name; do { - replacement_name = stdstrPrint("%s_%d", inst_name.c_str(), i++); + replacement_name = sta::format("{}_{}", inst_name, i++); } while (inst_names.contains(replacement_name)); std::string inst_vname = instanceVerilogName(inst_name.c_str()); - reader->warn(1396, filename_.c_str(), inst->line(), - "instance name %s duplicated - renamed to %s.", - inst_vname.c_str(), - replacement_name.c_str()); + reader->warn(1396, filename_, inst->line(), + "instance name {} duplicated - renamed to {}.", inst_vname, + replacement_name); inst_name = replacement_name; inst->setInstanceName(inst_name); } @@ -921,7 +837,9 @@ VerilogModuleInst::VerilogModuleInst(const std::string &module_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, int line) : - VerilogInst(inst_name, attr_stmts, line), + VerilogInst(inst_name, + attr_stmts, + line), module_name_(module_name), pins_(pins) { @@ -938,17 +856,13 @@ VerilogModuleInst::~VerilogModuleInst() bool VerilogModuleInst::hasPins() { - return pins_ - && pins_->size() > 0; - + return pins_ && pins_->size() > 0; } bool VerilogModuleInst::namedPins() { - return pins_ - && pins_->size() > 0 - && (*pins_)[0]->isNamedPortRef(); + return pins_ && pins_->size() > 0 && (*pins_)[0]->isNamedPortRef(); } VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, @@ -956,7 +870,9 @@ VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, const int line) : - VerilogInst(inst_name, attr_stmts, line), + VerilogInst(inst_name, + attr_stmts, + line), cell_(cell), net_names_(net_names) { @@ -1011,7 +927,10 @@ VerilogDclBus::VerilogDclBus(PortDirection *dir, VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, int line) : - VerilogDcl(dir, args, attr_stmts, line), + VerilogDcl(dir, + args, + attr_stmts, + line), from_index_(from_index), to_index_(to_index) { @@ -1023,7 +942,10 @@ VerilogDclBus::VerilogDclBus(PortDirection *dir, VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line) : - VerilogDcl(dir, arg, attr_stmts, line), + VerilogDcl(dir, + arg, + attr_stmts, + line), from_index_(from_index), to_index_(to_index) { @@ -1046,10 +968,7 @@ VerilogDclArg::VerilogDclArg(VerilogAssign *assign) : { } -VerilogDclArg::~VerilogDclArg() -{ - delete assign_; -} +VerilogDclArg::~VerilogDclArg() { delete assign_; } const std::string & VerilogDclArg::netName() @@ -1152,10 +1071,8 @@ VerilogBusNetNameIterator::VerilogBusNetNameIterator(const std::string bus_name, bool VerilogBusNetNameIterator::hasNext() { - return (to_index_ > from_index_ - && index_ <= to_index_) - || (to_index_ <= from_index_ - && index_ >= to_index_); + return (to_index_ > from_index_ && index_ <= to_index_) + || (to_index_ <= from_index_ && index_ >= to_index_); } const std::string & @@ -1173,7 +1090,7 @@ static std::string verilogBusBitName(const std::string &bus_name, int index) { - return stdstrPrint("%s[%d]", bus_name.c_str(), index); + return sta::format("{}[{}]", bus_name.c_str(), index); } class VerilogConstantNetNameIterator : public VerilogNetNameIterator @@ -1192,10 +1109,10 @@ private: int bit_index_; }; -VerilogConstantNetNameIterator:: -VerilogConstantNetNameIterator(VerilogConstantValue *value, - const std::string &zero, - const std::string &one) : +VerilogConstantNetNameIterator::VerilogConstantNetNameIterator( + VerilogConstantValue *value, + const std::string &zero, + const std::string &one) : value_(value), zero_(zero), one_(one), @@ -1233,10 +1150,9 @@ private: VerilogNetNameIterator *net_name_iter_; }; -VerilogNetConcatNameIterator:: -VerilogNetConcatNameIterator(VerilogNetSeq *nets, - VerilogModule *module, - VerilogReader *reader) : +VerilogNetConcatNameIterator::VerilogNetConcatNameIterator(VerilogNetSeq *nets, + VerilogModule *module, + VerilogReader *reader) : module_(module), reader_(reader), nets_(nets), @@ -1257,8 +1173,7 @@ VerilogNetConcatNameIterator::~VerilogNetConcatNameIterator() bool VerilogNetConcatNameIterator::hasNext() { - return (net_name_iter_ && net_name_iter_->hasNext()) - || net_iter_ != nets_->end(); + return (net_name_iter_ && net_name_iter_->hasNext()) || net_iter_ != nets_->end(); } const std::string & @@ -1289,9 +1204,7 @@ VerilogNetNamed::VerilogNetNamed(const std::string &name) : { } -VerilogNetNamed::~VerilogNetNamed() -{ -} +VerilogNetNamed::~VerilogNetNamed() {} VerilogNetScalar::VerilogNetScalar(const std::string &name) : VerilogNetNamed(name) @@ -1340,7 +1253,8 @@ VerilogNetScalar::nameIterator(VerilogModule *module, VerilogNetBitSelect::VerilogNetBitSelect(const std::string &name, int index) : - VerilogNetNamed(verilogBusBitName(name, index)), + VerilogNetNamed(verilogBusBitName(name, + index)), index_(index) { } @@ -1360,7 +1274,7 @@ VerilogNetBitSelect::nameIterator(VerilogModule *, VerilogNetPartSelect::VerilogNetPartSelect(const std::string &name, int from_index, - int to_index): + int to_index) : VerilogNetNamed(name), from_index_(from_index), to_index_(to_index) @@ -1407,27 +1321,27 @@ VerilogNetConstant::parseConstant(const std::string *constant, size_t base_idx = csize_end + 1; char base = constant->at(base_idx); switch (base) { - case 'b': - case 'B': - parseConstant(constant, base_idx, 2, 1); - break; - case 'o': - case 'O': - parseConstant(constant, base_idx, 8, 3); - break; - case 'h': - case 'H': - parseConstant(constant, base_idx, 16, 4); - break; - case 'd': - case 'D': - parseConstant10(constant, base_idx, reader, line); - break; - default: - case '\0': - reader->report()->fileWarn(1861, reader->filename(), line, - "unknown constant base."); - break; + case 'b': + case 'B': + parseConstant(constant, base_idx, 2, 1); + break; + case 'o': + case 'O': + parseConstant(constant, base_idx, 8, 3); + break; + case 'h': + case 'H': + parseConstant(constant, base_idx, 16, 4); + break; + case 'd': + case 'D': + parseConstant10(constant, base_idx, reader, line); + break; + default: + case '\0': + reader->report()->fileWarn(1861, reader->filename(), line, + "unknown constant base."); + break; } delete constant; } @@ -1472,19 +1386,17 @@ VerilogNetConstant::parseConstant10(const std::string *constant, for (size_t i = base_idx + 1; i < constant->size(); i++) { char ch = constant->at(i); if (ch != '_') - tmp += ch; + tmp += ch; } size_t size = value_->size(); size_t length = tmp.size(); const std::string &constant10_max = reader->constant10Max(); size_t max_length = constant10_max.size(); - if (length > max_length - || (length == max_length - && tmp > constant10_max)) + if (length > max_length || (length == max_length && tmp > constant10_max)) reader->warn(1397, reader->filename(), line, - "base 10 constant greater than %s not supported.", - constant10_max.c_str()); + "base 10 constant greater than {} not supported.", + constant10_max); else { size_t *end = nullptr; VerilogConstant10 value = std::stoull(tmp, end, 10); @@ -1496,28 +1408,22 @@ VerilogNetConstant::parseConstant10(const std::string *constant, } } -VerilogNetConstant::~VerilogNetConstant() -{ - delete value_; -} +VerilogNetConstant::~VerilogNetConstant() { delete value_; } VerilogNetNameIterator * VerilogNetConstant::nameIterator(VerilogModule *, VerilogReader *reader) { - return new VerilogConstantNetNameIterator(value_, - reader->zeroNetName(), + return new VerilogConstantNetNameIterator(value_, reader->zeroNetName(), reader->oneNetName()); } - int VerilogNetConstant::size(VerilogModule *) { return value_->size(); } - VerilogNetConcat::VerilogNetConcat(VerilogNetSeq *nets) : nets_(nets) { @@ -1590,10 +1496,7 @@ VerilogNetPortRefScalar::VerilogNetPortRefScalar(const std::string &name, { } -VerilogNetPortRefScalar::~VerilogNetPortRefScalar() -{ - delete net_; -} +VerilogNetPortRefScalar::~VerilogNetPortRefScalar() { delete net_; } int VerilogNetPortRefScalar::size(VerilogModule *module) @@ -1617,8 +1520,10 @@ VerilogNetPortRefScalar::nameIterator(VerilogModule *module, VerilogNetPortRefBit::VerilogNetPortRefBit(const std::string &name, int index, VerilogNet *net) : - VerilogNetPortRefScalar(name, net), - bit_name_(verilogBusBitName(name, index)) + VerilogNetPortRefScalar(name, + net), + bit_name_(verilogBusBitName(name, + index)) { } @@ -1626,7 +1531,9 @@ VerilogNetPortRefPart::VerilogNetPortRefPart(const std::string &name, int from_index, int to_index, VerilogNet *net) : - VerilogNetPortRefBit(name, from_index, net), + VerilogNetPortRefBit(name, + from_index, + net), to_index_(to_index) { } @@ -1656,8 +1563,8 @@ VerilogAttrEntry::value() return value_; } -VerilogAttrStmt::VerilogAttrStmt(VerilogAttrEntrySeq *attrs): - attrs_(attrs) +VerilogAttrStmt::VerilogAttrStmt(VerilogAttrEntrySeq *attrs) : + attrs_(attrs) { } @@ -1667,13 +1574,12 @@ VerilogAttrStmt::~VerilogAttrStmt() delete attrs_; } -VerilogAttrEntrySeq* +VerilogAttrEntrySeq * VerilogAttrStmt::attrs() { return attrs_; } - //////////////////////////////////////////////////////////////// // // Link verilog network @@ -1681,7 +1587,7 @@ VerilogAttrStmt::attrs() //////////////////////////////////////////////////////////////// // Verilog net name to network net map. -using BindingMap = std::map; +using BindingMap = std::map; class VerilogBindingTbl { @@ -1712,15 +1618,16 @@ VerilogReader::linkNetwork(const char *top_cell_name, VerilogModule *module = this->module(top_cell); if (module) { // Seed the recursion for expansion with the top level instance. - Instance *top_instance = network_->makeInstance(top_cell, top_cell_name, nullptr); + Instance *top_instance = + network_->makeInstance(top_cell, top_cell_name, nullptr); VerilogBindingTbl bindings(zero_net_name_, one_net_name_); for (VerilogNet *mod_port : *module->ports()) { - VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, - this); + VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, this); while (net_name_iter->hasNext()) { const std::string &net_name = net_name_iter->next(); Port *port = network_->findPort(top_cell, net_name.c_str()); - Net *net = bindings.ensureNetBinding(net_name.c_str(), top_instance, network_); + Net *net = + bindings.ensureNetBinding(net_name.c_str(), top_instance, network_); // Guard against repeated port name. if (network_->findPin(top_instance, port) == nullptr) { Pin *pin = network_->makePin(top_instance, port, nullptr); @@ -1741,12 +1648,12 @@ VerilogReader::linkNetwork(const char *top_cell_name, return top_instance; } else { - report_->error(1398, "%s is not a verilog module.", top_cell_name); + report_->error(1398, "{} is not a verilog module.", top_cell_name); return nullptr; } } else { - report_->error(1399, "%s is not a verilog module.", top_cell_name); + report_->error(1399, "{} is not a verilog module.", top_cell_name); return nullptr; } } @@ -1759,31 +1666,32 @@ VerilogReader::makeModuleInstBody(VerilogModule *module, { for (VerilogStmt *stmt : *module->stmts()) { if (stmt->isModuleInst()) - makeModuleInstNetwork(dynamic_cast(stmt), - inst, module, bindings, make_black_boxes); + makeModuleInstNetwork(dynamic_cast(stmt), inst, module, + bindings, make_black_boxes); else if (stmt->isLibertyInst()) - makeLibertyInst(dynamic_cast(stmt), - inst, module, bindings); + makeLibertyInst(dynamic_cast(stmt), inst, module, + bindings); else if (stmt->isDeclaration()) { - VerilogDcl *dcl = dynamic_cast(stmt); + VerilogDcl *dcl = dynamic_cast(stmt); PortDirection *dir = dcl->direction(); for (VerilogDclArg *arg : *dcl->args()) { VerilogAssign *assign = arg->assign(); if (assign) mergeAssignNet(assign, module, inst, bindings); if (dir->isGround()) { - Net *net = bindings->ensureNetBinding(arg->netName().c_str(),inst,network_); + Net *net = + bindings->ensureNetBinding(arg->netName().c_str(), inst, network_); network_->addConstantNet(net, LogicValue::zero); } if (dir->isPower()) { - Net *net = bindings->ensureNetBinding(arg->netName().c_str(),inst,network_); + Net *net = + bindings->ensureNetBinding(arg->netName().c_str(), inst, network_); network_->addConstantNet(net, LogicValue::one); } } } else if (stmt->isAssign()) - mergeAssignNet(dynamic_cast(stmt), module, inst, - bindings); + mergeAssignNet(dynamic_cast(stmt), module, inst, bindings); } } @@ -1801,22 +1709,20 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, if (make_black_boxes) { cell = makeBlackBox(mod_inst, parent_module); linkWarn(198, parent_module->filename(), mod_inst->line(), - "module %s not found. Creating black box for %s.", - mod_inst->moduleName().c_str(), - inst_vname.c_str()); + "module {} not found. Creating black box for {}.", + mod_inst->moduleName(), inst_vname); } else linkError(199, parent_module->filename(), mod_inst->line(), - "module %s not found for instance %s.", - mod_inst->moduleName().c_str(), - inst_vname.c_str()); + "module {} not found for instance {}.", + mod_inst->moduleName(), inst_vname); } if (cell) { LibertyCell *lib_cell = network_->libertyCell(cell); if (lib_cell) cell = network_->cell(lib_cell); - Instance *inst = network_->makeInstance(cell, mod_inst->instanceName().c_str(), - parent); + Instance *inst = + network_->makeInstance(cell, mod_inst->instanceName().c_str(), parent); VerilogAttrStmtSeq *attr_stmts = mod_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { @@ -1835,11 +1741,11 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, VerilogBindingTbl bindings(zero_net_name_, one_net_name_); if (mod_inst->hasPins()) { if (mod_inst->namedPins()) - makeNamedInstPins(cell, inst, mod_inst, &bindings, parent, - parent_module, parent_bindings, is_leaf); + makeNamedInstPins(cell, inst, mod_inst, &bindings, parent, parent_module, + parent_bindings, is_leaf); else - makeOrderedInstPins(cell, inst, mod_inst, &bindings, parent, - parent_module, parent_bindings, is_leaf); + makeOrderedInstPins(cell, inst, mod_inst, &bindings, parent, parent_module, + parent_bindings, is_leaf); } if (!is_leaf) { VerilogModule *module = this->module(cell); @@ -1861,43 +1767,38 @@ VerilogReader::makeNamedInstPins(Cell *cell, { std::string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); for (auto mpin : *mod_inst->pins()) { - VerilogNetPortRef *vpin = dynamic_cast(mpin); + VerilogNetPortRef *vpin = dynamic_cast(mpin); const char *port_name = vpin->name().c_str(); Port *port = network_->findPort(cell, port_name); if (port) { - if (vpin->hasNet() - && network_->size(port) != vpin->size(parent_module)) { + if (vpin->hasNet() && network_->size(port) != vpin->size(parent_module)) { linkWarn(200, parent_module->filename(), mod_inst->line(), - "instance %s port %s size %d does not match net size %d.", - inst_vname.c_str(), - network_->name(port), - network_->size(port), + "instance {} port {} size {} does not match net size {}.", + inst_vname, network_->name(port), network_->size(port), vpin->size(parent_module)); } else { VerilogNetNameIterator *net_name_iter = - vpin->nameIterator(parent_module, this); + vpin->nameIterator(parent_module, this); if (network_->hasMembers(port)) { PortMemberIterator *port_iter = network_->memberIterator(port); while (port_iter->hasNext()) { Port *port = port_iter->next(); - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); } delete port_iter; } else { - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); } delete net_name_iter; } } else linkWarn(201, parent_module->filename(), mod_inst->line(), - "instance %s port %s not found.", - inst_vname.c_str(), - port_name); + "instance {} port {} not found.", inst_vname, port_name); } } @@ -1914,34 +1815,30 @@ VerilogReader::makeOrderedInstPins(Cell *cell, CellPortIterator *port_iter = network_->portIterator(cell); VerilogNetSeq *mod_pins = mod_inst->pins(); VerilogNetSeq::iterator pin_iter = mod_pins->begin(); - while (pin_iter != mod_pins->end() - && port_iter->hasNext()) { + while (pin_iter != mod_pins->end() && port_iter->hasNext()) { VerilogNet *net = *pin_iter++; Port *port = port_iter->next(); if (network_->size(port) != net->size(parent_module)) { std::string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); linkWarn(202, parent_module->filename(), mod_inst->line(), - "instance %s port %s size %d does not match net size %d.", - inst_vname.c_str(), - network_->name(port), - network_->size(port), + "instance {} port {} size {} does not match net size {}.", + inst_vname, network_->name(port), network_->size(port), net->size(parent_module)); } else { - VerilogNetNameIterator *net_name_iter=net->nameIterator(parent_module, - this); + VerilogNetNameIterator *net_name_iter = net->nameIterator(parent_module, this); if (network_->isBus(port)) { PortMemberIterator *member_iter = network_->memberIterator(port); while (member_iter->hasNext() && net_name_iter->hasNext()) { Port *port = member_iter->next(); - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); } delete member_iter; } else - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); + makeInstPin(inst, port, net_name_iter, bindings, parent, parent_bindings, + is_leaf); delete net_name_iter; } } @@ -1960,8 +1857,7 @@ VerilogReader::makeInstPin(Instance *inst, std::string net_name; if (net_name_iter->hasNext()) net_name = net_name_iter->next(); - makeInstPin(inst, port, net_name, bindings, parent, parent_bindings, - is_leaf); + makeInstPin(inst, port, net_name, bindings, parent, parent_bindings, is_leaf); } void @@ -2001,9 +1897,9 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, VerilogBindingTbl *parent_bindings) { LibertyCell *lib_cell = lib_inst->cell(); - Cell *cell = reinterpret_cast(lib_cell); - Instance *inst = network_->makeInstance(cell, lib_inst->instanceName().c_str(), - parent); + Cell *cell = reinterpret_cast(lib_cell); + Instance *inst = + network_->makeInstance(cell, lib_inst->instanceName().c_str(), parent); VerilogAttrStmtSeq *attr_stmts = lib_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { @@ -2029,11 +1925,11 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, } else net = parent_bindings->ensureNetBinding(net_name.c_str(), parent, network_); - network_->makePin(inst, reinterpret_cast(port), net); + network_->makePin(inst, reinterpret_cast(port), net); } else // Make unconnected pin. - network_->makePin(inst, reinterpret_cast(port), nullptr); + network_->makePin(inst, reinterpret_cast(port), nullptr); } } @@ -2059,12 +1955,11 @@ VerilogReader::makeBlackBoxNamedPorts(Cell *cell, VerilogModule *parent_module) { for (VerilogNet *mpin : *mod_inst->pins()) { - VerilogNetNamed *vpin = dynamic_cast(mpin); + VerilogNetNamed *vpin = dynamic_cast(mpin); const char *port_name = vpin->name().c_str(); size_t size = vpin->size(parent_module); - Port *port = (size == 1) - ? network_->makePort(cell, port_name) - : network_->makeBusPort(cell, port_name, 0, size - 1); + Port *port = (size == 1) ? network_->makePort(cell, port_name) + : network_->makeBusPort(cell, port_name, 0, size - 1); network_->setDirection(port, PortDirection::unknown()); } } @@ -2079,11 +1974,10 @@ VerilogReader::makeBlackBoxOrderedPorts(Cell *cell, if (nets) { for (VerilogNet *net : *nets) { size_t size = net->size(parent_module); - char *port_name = stringPrint("p_%d", port_index); + std::string port_name = format("p_{}", port_index); Port *port = (size == 1) - ? network_->makePort(cell, port_name) - : network_->makeBusPort(cell, port_name, size - 1, 0); - stringDelete(port_name); + ? network_->makePort(cell, port_name.c_str()) + : network_->makeBusPort(cell, port_name.c_str(), size - 1, 0); network_->setDirection(port, PortDirection::unknown()); port_index++; } @@ -2117,7 +2011,7 @@ VerilogReader::mergeAssignNet(VerilogAssign *assign, // Merge lower level net into higher level net so that deleting // instances from the bottom up does not reference deleted nets // by referencing the mergedInto field. - if (hierarchyLevel(lhs_net,network_) >= hierarchyLevel(rhs_net,network_)) + if (hierarchyLevel(lhs_net, network_) >= hierarchyLevel(rhs_net, network_)) network_->mergeInto(lhs_net, rhs_net); else network_->mergeInto(rhs_net, lhs_net); @@ -2129,9 +2023,8 @@ VerilogReader::mergeAssignNet(VerilogAssign *assign, } else linkWarn(203, module->filename(), assign->line(), - "assign left hand side size %d not equal right hand size %d.", - lhs->size(module), - rhs->size(module)); + "assign left hand side size {} not equal right hand size {}.", + lhs->size(module), rhs->size(module)); } static int @@ -2160,7 +2053,8 @@ VerilogBindingTbl::VerilogBindingTbl(const std::string &zero_net_name, // binding tables up the call tree when nodes are merged // because the name changes up the hierarchy. Net * -VerilogBindingTbl::find(const char *name, NetworkReader *network) +VerilogBindingTbl::find(const char *name, + NetworkReader *network) { Net *net = findKey(map_, name); while (net && network->mergedInto(net)) @@ -2194,34 +2088,6 @@ VerilogBindingTbl::ensureNetBinding(const char *net_name, //////////////////////////////////////////////////////////////// -void -VerilogReader::linkWarn(int id, - const char *filename, - int line, - const char *msg, ...) -{ - va_list args; - va_start(args, msg); - char *msg_str = stringPrintArgs(msg, args); - VerilogError *error = new VerilogError(id, filename, line, msg_str, true); - link_errors_.push_back(error); - va_end(args); -} - -void -VerilogReader::linkError(int id, - const char *filename, - int line, - const char *msg, ...) -{ - va_list args; - va_start(args, msg); - char *msg_str = stringPrintArgs(msg, args); - VerilogError *error = new VerilogError(id, filename, line, msg_str, false); - link_errors_.push_back(error); - va_end(args); -} - bool VerilogReader::reportLinkErrors() { @@ -2231,7 +2097,8 @@ VerilogReader::reportLinkErrors() bool errors = false; for (VerilogError *error : link_errors_) { // Report as warnings to avoid throwing. - report_->fileWarn(error->id(), error->filename(), error->line(), "%s", error->msg()); + report_->fileWarn(error->id(), error->filename(), error->line(), "{}", + error->msg()); errors |= !error->warn(); delete error; } @@ -2253,7 +2120,7 @@ VerilogScanner::VerilogScanner(std::istream *stream, void VerilogScanner::error(const char *msg) { - report_->fileError(1870, filename_, lineno(), "%s", msg); + report_->fileError(1870, filename_, lineno(), "{}", msg); } -} // namespace +} // namespace sta diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index d8ee7c6f..3531b865 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -30,6 +30,7 @@ #include #include "Error.hh" +#include "Format.hh" #include "Liberty.hh" #include "PortDirection.hh" #include "Network.hh" @@ -173,15 +174,15 @@ VerilogWriter::writeModule(const Instance *inst) { Cell *cell = network_->cell(inst); std::string cell_vname = cellVerilogName(network_->name(cell)); - fprintf(stream_, "module %s (", cell_vname.c_str()); + sta::print(stream_, "module {} (", cell_vname); writePorts(cell); writePortDcls(cell); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeWireDcls(inst); - fprintf(stream_, "\n"); + sta::print(stream_, "\n"); writeChildren(inst); writeAssigns(inst); - fprintf(stream_, "endmodule\n"); + sta::print(stream_, "endmodule\n"); } void @@ -194,14 +195,14 @@ VerilogWriter::writePorts(const Cell *cell) if (include_pwr_gnd_ || !network_->direction(port)->isPowerGround()) { if (!first) - fprintf(stream_, ",\n "); + sta::print(stream_, ",\n "); std::string verilog_name = portVerilogName(network_->name(port)); - fprintf(stream_, "%s", verilog_name.c_str()); + sta::print(stream_, "{}", verilog_name); first = false; } } delete port_iter; - fprintf(stream_, ");\n"); + sta::print(stream_, ");\n"); } void @@ -216,19 +217,19 @@ VerilogWriter::writePortDcls(const Cell *cell) std::string port_vname = portVerilogName(network_->name(port)); const char *vtype = verilogPortDir(dir); if (vtype) { - fprintf(stream_, " %s", vtype); + sta::print(stream_, " {}", vtype); if (network_->isBus(port)) - fprintf(stream_, " [%d:%d]", - network_->fromIndex(port), - network_->toIndex(port)); - fprintf(stream_, " %s;\n", port_vname.c_str()); - if (dir->isTristate()) { - fprintf(stream_, " tri"); - if (network_->isBus(port)) - fprintf(stream_, " [%d:%d]", + sta::print(stream_, " [{}:{}]", network_->fromIndex(port), network_->toIndex(port)); - fprintf(stream_, " %s;\n", port_vname.c_str()); + sta::print(stream_, " {};\n", port_vname); + if (dir->isTristate()) { + sta::print(stream_, " tri"); + if (network_->isBus(port)) + sta::print(stream_, " [{}:{}]", + network_->fromIndex(port), + network_->toIndex(port)); + sta::print(stream_, " {};\n", port_vname); } } } @@ -286,7 +287,7 @@ VerilogWriter::writeWireDcls(const Instance *inst) } else { std::string net_vname = netVerilogName(net_name); - fprintf(stream_, " wire %s;\n", net_vname.c_str());; + sta::print(stream_, " wire {};\n", net_vname); } } } @@ -296,16 +297,16 @@ VerilogWriter::writeWireDcls(const Instance *inst) for (const auto& [bus_name1, range] : bus_ranges) { const char *bus_name = bus_name1.c_str(); std::string net_vname = netVerilogName(bus_name); - fprintf(stream_, " wire [%d:%d] %s;\n", - range.first, - range.second, - net_vname.c_str());; + sta::print(stream_, " wire [{}:{}] {};\n", + range.first, + range.second, + net_vname); } // Wire net dcls for writeInstBusPinBit. int nc_count = findUnconnectedNetCount(inst); for (int i = 1; i < nc_count + 1; i++) - fprintf(stream_, " wire _NC%d;\n", i); + sta::print(stream_, " wire _NC{};\n", i); } void @@ -336,9 +337,9 @@ VerilogWriter::writeChild(const Instance *child) const char *child_name = network_->name(child); std::string child_vname = instanceVerilogName(child_name); std::string child_cell_vname = cellVerilogName(network_->name(child_cell)); - fprintf(stream_, " %s %s (", - child_cell_vname.c_str(), - child_vname.c_str()); + sta::print(stream_, " {} {} (", + child_cell_vname, + child_vname); bool first_port = true; CellPortIterator *port_iter = network_->portIterator(child_cell); while (port_iter->hasNext()) { @@ -352,7 +353,7 @@ VerilogWriter::writeChild(const Instance *child) } } delete port_iter; - fprintf(stream_, ");\n"); + sta::print(stream_, ");\n"); } } @@ -368,11 +369,11 @@ VerilogWriter::writeInstPin(const Instance *inst, const char *net_name = network_->name(net); std::string net_vname = netVerilogName(net_name); if (!first_port) - fprintf(stream_, ",\n "); + sta::print(stream_, ",\n "); std::string port_vname = portVerilogName(network_->name(port)); - fprintf(stream_, ".%s(%s)", - port_vname.c_str(), - net_vname.c_str()); + sta::print(stream_, ".{}({})", + port_vname, + net_vname); first_port = false; } } @@ -384,10 +385,10 @@ VerilogWriter::writeInstBusPin(const Instance *inst, bool &first_port) { if (!first_port) - fprintf(stream_, ",\n "); + sta::print(stream_, ",\n "); std::string port_vname = portVerilogName(network_->name(port)); - fprintf(stream_, ".%s({", port_vname.c_str()); + sta::print(stream_, ".{}({{", port_vname); first_port = false; bool first_member = true; @@ -410,7 +411,7 @@ VerilogWriter::writeInstBusPin(const Instance *inst, } delete member_iter; } - fprintf(stream_, "})"); + sta::print(stream_, "}})"); } void @@ -420,16 +421,15 @@ VerilogWriter::writeInstBusPinBit(const Instance *inst, { Pin *pin = network_->findPin(inst, port); Net *net = pin ? network_->net(pin) : nullptr; - std::string net_name; - if (net) - net_name = network_->name(net); - else + std::string net_name = net + ? network_->name(net) // There is no verilog syntax to "skip" a bit in the concatentation. - stringPrint(net_name, "_NC%d", unconnected_net_index_++); + : sta::format("_NC{}", unconnected_net_index_++); + std::string net_vname = netVerilogName(net_name.c_str()); if (!first_member) - fprintf(stream_, ",\n "); - fprintf(stream_, "%s", net_vname.c_str()); + sta::print(stream_, ",\n "); + sta::print(stream_, "{}", net_vname); first_member = false; } @@ -455,9 +455,9 @@ VerilogWriter::writeAssigns(const Instance *inst) // Port name is different from net name. std::string port_vname = netVerilogName(network_->name(port)); std::string net_vname = netVerilogName(network_->name(net)); - fprintf(stream_, " assign %s = %s;\n", - port_vname.c_str(), - net_vname.c_str()); + sta::print(stream_, " assign {} = {};\n", + port_vname, + net_vname); } } } From 5a1b4cb74b82af72517d203e1f75b896595f84f9 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 15 Mar 2026 14:51:13 -0700 Subject: [PATCH 06/14] sta::format Signed-off-by: James Cherry --- network/Network.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/Network.i b/network/Network.i index 870399df..c07f6de8 100644 --- a/network/Network.i +++ b/network/Network.i @@ -520,7 +520,7 @@ pin_location(const Pin *pin) network->location(pin, x, y, exists); // return x/y as tcl list if (exists) - return std::format("{} {}", x, y); + return sta::format("{} {}", x, y); else return ""; } From 61b2a4b2c31ac745411e6dbaa4ff5ebeac60fd5b Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 16 Mar 2026 16:29:10 -0700 Subject: [PATCH 07/14] indent Signed-off-by: James Cherry --- dcalc/GraphDelayCalc.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 6d859755..8c12cdf1 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -534,23 +534,23 @@ GraphDelayCalc::seedLoadSlew(Vertex *vertex) const Sdc *sdc = scene->sdc(); for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - for (const RiseFall *rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { ClockSet *clks = sdc->findLeafPinClocks(pin); if (!vertex->slewAnnotated(rf, min_max)) { - float slew = 0.0; - if (clks) { + float slew = 0.0; + if (clks) { slew = min_max->initValue(); for (Clock *clk : *clks) { float clk_slew = clk->slew(rf, min_max); if (min_max->compare(clk_slew, slew)) - slew = clk_slew; - } - } - graph_->setSlew(vertex, rf, ap_index, slew); + slew = clk_slew; + } + } + graph_->setSlew(vertex, rf, ap_index, slew); + } } } } - } } // If a driving cell does not specify a -from_pin, the first port From 90df0cfea564a9fec649719d0565217fb08a95f2 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 18 Mar 2026 12:15:57 -0700 Subject: [PATCH 08/14] LibertyScanner includes Signed-off-by: James Cherry --- liberty/LibertyScanner.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/liberty/LibertyScanner.hh b/liberty/LibertyScanner.hh index b32369cd..a649f514 100644 --- a/liberty/LibertyScanner.hh +++ b/liberty/LibertyScanner.hh @@ -24,6 +24,10 @@ #pragma once +#include +#include + +#include "LibertyParser.hh" #include "LibertyLocation.hh" #include "LibertyParse.hh" From 464bc3ae4ff6f0d980ea31937f45a3425e97122f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 18 Mar 2026 13:01:05 -0700 Subject: [PATCH 09/14] Report use string instead of string_view Signed-off-by: James Cherry --- include/sta/Report.hh | 6 +++--- include/sta/ReportTcl.hh | 6 +++--- liberty/LibertyScanner.hh | 2 -- util/Report.cc | 21 +++++++++------------ util/ReportTcl.cc | 6 +++--- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/include/sta/Report.hh b/include/sta/Report.hh index fb42f00b..7e9aab55 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -138,13 +138,13 @@ public: } // Log output to filename until logEnd is called. - virtual void logBegin(std::string_view filename); + virtual void logBegin(std::string filename); virtual void logEnd(); // Redirect output to filename until redirectFileEnd is called. - virtual void redirectFileBegin(std::string_view filename); + virtual void redirectFileBegin(std::string filename); // Redirect append output to filename until redirectFileEnd is called. - virtual void redirectFileAppendBegin(std::string_view filename); + virtual void redirectFileAppendBegin(std::string filename); virtual void redirectFileEnd(); // Redirect output to a string until redirectStringEnd is called. virtual void redirectStringBegin(); diff --git a/include/sta/ReportTcl.hh b/include/sta/ReportTcl.hh index 5f4a6cd0..acdf84dc 100644 --- a/include/sta/ReportTcl.hh +++ b/include/sta/ReportTcl.hh @@ -44,10 +44,10 @@ class ReportTcl : public Report public: ReportTcl(); virtual ~ReportTcl(); - void logBegin(std::string_view filename) override; + void logBegin(std::string filename) override; void logEnd() override; - void redirectFileBegin(std::string_view filename) override; - void redirectFileAppendBegin(std::string_view filename) override; + void redirectFileBegin(std::string filename) override; + void redirectFileAppendBegin(std::string filename) override; void redirectFileEnd() override; void redirectStringBegin() override; const char *redirectStringEnd() override; diff --git a/liberty/LibertyScanner.hh b/liberty/LibertyScanner.hh index a649f514..3623940c 100644 --- a/liberty/LibertyScanner.hh +++ b/liberty/LibertyScanner.hh @@ -27,8 +27,6 @@ #include #include -#include "LibertyParser.hh" -#include "LibertyLocation.hh" #include "LibertyParse.hh" #ifndef __FLEX_LEXER_H diff --git a/util/Report.cc b/util/Report.cc index d539c574..d8a6a04d 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -185,12 +185,11 @@ Report::isSuppressed(int id) //////////////////////////////////////////////////////////////// void -Report::logBegin(std::string_view filename) +Report::logBegin(std::string filename) { - std::string filename_str(filename); - log_stream_ = fopen(filename_str.c_str(), "w"); + log_stream_ = fopen(filename.c_str(), "w"); if (log_stream_ == nullptr) - throw FileNotWritable(std::move(filename_str)); + throw FileNotWritable(std::move(filename)); } void @@ -202,21 +201,19 @@ Report::logEnd() } void -Report::redirectFileBegin(std::string_view filename) +Report::redirectFileBegin(std::string filename) { - std::string filename_str(filename); - redirect_stream_ = fopen(filename_str.c_str(), "w"); + redirect_stream_ = fopen(filename.c_str(), "w"); if (redirect_stream_ == nullptr) - throw FileNotWritable(std::move(filename_str)); + throw FileNotWritable(std::move(filename)); } void -Report::redirectFileAppendBegin(std::string_view filename) +Report::redirectFileAppendBegin(std::string filename) { - std::string filename_str(filename); - redirect_stream_ = fopen(filename_str.c_str(), "a"); + redirect_stream_ = fopen(filename.c_str(), "a"); if (redirect_stream_ == nullptr) - throw FileNotWritable(std::move(filename_str)); + throw FileNotWritable(std::move(filename)); } void diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc index a418a53a..97eef4af 100644 --- a/util/ReportTcl.cc +++ b/util/ReportTcl.cc @@ -183,7 +183,7 @@ ReportTcl::flush() // Tcl_Main can eval multiple commands before the flushing the command // output, so the log/redirect commands must force a flush. void -ReportTcl::logBegin(std::string_view filename) +ReportTcl::logBegin(std::string filename) { flush(); Report::logBegin(filename); @@ -197,14 +197,14 @@ ReportTcl::logEnd() } void -ReportTcl::redirectFileBegin(std::string_view filename) +ReportTcl::redirectFileBegin(std::string filename) { flush(); Report::redirectFileBegin(filename); } void -ReportTcl::redirectFileAppendBegin(std::string_view filename) +ReportTcl::redirectFileAppendBegin(std::string filename) { flush(); Report::redirectFileAppendBegin(filename); From 0b491ce1906dc9d96a1a3342cf518bde401a252b Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 18 Mar 2026 16:12:04 -0700 Subject: [PATCH 10/14] Delay float coercion for non-ssta applications Signed-off-by: James Cherry --- include/sta/Delay.hh | 4 ++++ search/Path.cc | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index dae429af..d34f635c 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -63,6 +63,10 @@ public: void setSkewness(float skewness); void operator=(float delay); + // This allows applications that do not support statistical timing + // to treat Delays as floats without explicitly converting with + // delayAsFloat. + operator float() const { return mean(); } private: std::array values_; diff --git a/search/Path.cc b/search/Path.cc index c0bb35d1..e7f78bde 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -50,8 +50,8 @@ Path::Path() : Path::Path(const Path *path) : prev_path_(path ? path->prev_path_ : nullptr), - arrival_(path ? path->arrival_ : 0.0), - required_(path ? path->required_ : 0.0), + arrival_(path ? path->arrival_ : delay_zero), + required_(path ? path->required_ : delay_zero), vertex_id_(path ? path->vertex_id_ : vertex_id_null), tag_index_(path ? path->tag_index_ : tag_index_null), is_enum_(path ? path->is_enum_ : false), From 73c2cca24c930abd573c9d33b3d2b158319693bb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 18 Mar 2026 16:33:15 -0700 Subject: [PATCH 11/14] delayAsString Signed-off-by: James Cherry --- dcalc/Delay.cc | 8 ++++++++ include/sta/Delay.hh | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/dcalc/Delay.cc b/dcalc/Delay.cc index c40bd429..48c867a3 100644 --- a/dcalc/Delay.cc +++ b/dcalc/Delay.cc @@ -223,6 +223,14 @@ delayAsString(const Delay &delay, sta->units()->timeUnit()->digits(), sta); } +std::string +delayAsString(const Delay &delay, + int digits, + const StaState *sta) +{ + return delayAsString(delay, EarlyLate::late(), digits, sta); +} + std::string delayAsString(const Delay &delay, const EarlyLate *early_late, diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index d34f635c..e2547557 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -207,9 +207,15 @@ void delaySetMean(Delay &delay, float mean); +// early_late == late std::string delayAsString(const Delay &delay, const StaState *sta); +// early_late == late +std::string +delayAsString(const Delay &delay, + int digits, + const StaState *sta); std::string delayAsString(const Delay &delay, const EarlyLate *early_late, From 07c7eeb624468ff1a046444c673076f49863d1aa Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 20 Mar 2026 09:05:07 -0700 Subject: [PATCH 12/14] Report::warn etc virtuals Signed-off-by: James Cherry --- include/sta/Report.hh | 66 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 7e9aab55..28f685a9 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -61,7 +61,11 @@ public: void report(std::string_view fmt, Args &&...args) { - reportLine(sta::vformat(fmt, sta::make_format_args(args...))); + report(sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void report(const std::string &formatted_msg) + { + reportLine(formatted_msg); } //////////////////////////////////////////////////////////////// @@ -73,10 +77,14 @@ public: Args &&...args) { if (!isSuppressed(id)) { - reportLine(sta::format( - "Warning {}: {}", id, sta::vformat(fmt, sta::make_format_args(args...)))); + warn(id, sta::vformat(fmt, sta::make_format_args(args...))); } } + virtual void warn(int id, + const std::string &formatted_msg) { + reportLine(sta::format("Warning {}: {}", id, formatted_msg)); + } + // Report warning in a file. template void fileWarn(int id, @@ -86,19 +94,30 @@ public: Args &&...args) { if (!isSuppressed(id)) { - reportLine( - sta::format("Warning {}: {} line {}, {}", id, filename, line, - sta::vformat(fmt, sta::make_format_args(args...)))); + fileWarn(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); } } + virtual void + fileWarn(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) { + reportLine(sta::format("Warning {}: {} line {}, {}", + id, filename, line, formatted_msg)); + } template void error(int id, std::string_view fmt, Args &&...args) { - std::string msg = sta::vformat(fmt, sta::make_format_args(args...)); - reportThrowExceptionMsg(sta::format("{} {}", id, msg), isSuppressed(id)); + error(id, sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void error(int id, + const std::string &formatted_msg) + { + reportThrowExceptionMsg(sta::format("{} {}", id, formatted_msg), isSuppressed(id)); } // Report error in a file. template @@ -108,8 +127,16 @@ public: std::string_view fmt, Args &&...args) { - const std::string msg = sta::vformat(fmt, sta::make_format_args(args...)); - reportThrowExceptionMsg(sta::format("{} {} line {}, {}", id, filename, line, msg), + fileError(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void fileError(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) + { + reportThrowExceptionMsg(sta::format("{} {} line {}, {}", + id, filename, line, formatted_msg), isSuppressed(id)); } @@ -121,19 +148,32 @@ public: std::string_view fmt, Args &&...args) { - reportLine(sta::format("Critical {}: {}", id, - sta::vformat(fmt, sta::make_format_args(args...)))); + critical(id, sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void critical(int id, + const std::string &formatted_msg) + { + reportLine(sta::format("Critical {}: {}", id, formatted_msg)); exit(1); } + template void fileCritical(int id, std::string_view filename, int line, std::string_view fmt, Args &&...args) + { + fileCritical(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); + } + virtual void fileCritical(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) { reportLine(sta::format("Critical {}: {} line {}, {}", id, filename, line, - sta::vformat(fmt, sta::make_format_args(args...)))); + formatted_msg)); exit(1); } From 2ed1c2c06bb5edf177c34ffde3bad9d10a95719a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 20 Mar 2026 09:11:39 -0700 Subject: [PATCH 13/14] report json Signed-off-by: James Cherry --- search/ReportPath.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 158b7919..21a4c485 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1057,14 +1057,14 @@ ReportPath::pathEndpoint(const PathEnd *end) const void ReportPath::reportJsonHeader() const { - report_->report("{{\"checks\": ["); + report_->report("{\"checks\": ["); } void ReportPath::reportJsonFooter() const { report_->report("]"); - report_->report("}}"); + report_->report("}"); } void From d90bf7d93bf84b6619ae02e91f737929a4679dd6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 20 Mar 2026 10:14:57 -0700 Subject: [PATCH 14/14] rename Report virtuals to warnMsg etc This reverts commit 2ed1c2c06bb5edf177c34ffde3bad9d10a95719a. Signed-off-by: James Cherry --- include/sta/Report.hh | 61 +++++++++++++++++++++---------------------- search/ReportPath.cc | 4 +-- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 28f685a9..51cde2ed 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -61,9 +61,9 @@ public: void report(std::string_view fmt, Args &&...args) { - report(sta::vformat(fmt, sta::make_format_args(args...))); + reportMsg(sta::vformat(fmt, sta::make_format_args(args...))); } - virtual void report(const std::string &formatted_msg) + virtual void reportMsg(const std::string &formatted_msg) { reportLine(formatted_msg); } @@ -76,12 +76,11 @@ public: std::string_view fmt, Args &&...args) { - if (!isSuppressed(id)) { - warn(id, sta::vformat(fmt, sta::make_format_args(args...))); - } + if (!isSuppressed(id)) + warnMsg(id, sta::vformat(fmt, sta::make_format_args(args...))); } - virtual void warn(int id, - const std::string &formatted_msg) { + virtual void warnMsg(int id, + const std::string &formatted_msg) { reportLine(sta::format("Warning {}: {}", id, formatted_msg)); } @@ -94,15 +93,15 @@ public: Args &&...args) { if (!isSuppressed(id)) { - fileWarn(id, filename, line, - sta::vformat(fmt, sta::make_format_args(args...))); + fileWarnMsg(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); } } virtual void - fileWarn(int id, - std::string_view filename, - int line, - const std::string &formatted_msg) { + fileWarnMsg(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) { reportLine(sta::format("Warning {}: {} line {}, {}", id, filename, line, formatted_msg)); } @@ -112,10 +111,10 @@ public: std::string_view fmt, Args &&...args) { - error(id, sta::vformat(fmt, sta::make_format_args(args...))); + errorMsg(id, sta::vformat(fmt, sta::make_format_args(args...))); } - virtual void error(int id, - const std::string &formatted_msg) + virtual void errorMsg(int id, + const std::string &formatted_msg) { reportThrowExceptionMsg(sta::format("{} {}", id, formatted_msg), isSuppressed(id)); } @@ -127,13 +126,13 @@ public: std::string_view fmt, Args &&...args) { - fileError(id, filename, line, - sta::vformat(fmt, sta::make_format_args(args...))); + fileErrorMsg(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); } - virtual void fileError(int id, - std::string_view filename, - int line, - const std::string &formatted_msg) + virtual void fileErrorMsg(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) { reportThrowExceptionMsg(sta::format("{} {} line {}, {}", id, filename, line, formatted_msg), @@ -148,10 +147,10 @@ public: std::string_view fmt, Args &&...args) { - critical(id, sta::vformat(fmt, sta::make_format_args(args...))); + criticalMsg(id, sta::vformat(fmt, sta::make_format_args(args...))); } - virtual void critical(int id, - const std::string &formatted_msg) + virtual void criticalMsg(int id, + const std::string &formatted_msg) { reportLine(sta::format("Critical {}: {}", id, formatted_msg)); exit(1); @@ -164,13 +163,13 @@ public: std::string_view fmt, Args &&...args) { - fileCritical(id, filename, line, - sta::vformat(fmt, sta::make_format_args(args...))); + fileCriticalMsg(id, filename, line, + sta::vformat(fmt, sta::make_format_args(args...))); } - virtual void fileCritical(int id, - std::string_view filename, - int line, - const std::string &formatted_msg) + virtual void fileCriticalMsg(int id, + std::string_view filename, + int line, + const std::string &formatted_msg) { reportLine(sta::format("Critical {}: {} line {}, {}", id, filename, line, formatted_msg)); diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 21a4c485..158b7919 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1057,14 +1057,14 @@ ReportPath::pathEndpoint(const PathEnd *end) const void ReportPath::reportJsonHeader() const { - report_->report("{\"checks\": ["); + report_->report("{{\"checks\": ["); } void ReportPath::reportJsonFooter() const { report_->report("]"); - report_->report("}"); + report_->report("}}"); } void